zrender
Version:
A lightweight graphic library providing 2d draw for Apache ECharts
289 lines (250 loc) • 7.8 kB
text/typescript
/**
* @module echarts/core/BoundingRect
*/
import * as matrix from './matrix';
import Point, { PointLike } from './Point';
const mathMin = Math.min;
const mathMax = Math.max;
const lt = new Point();
const rb = new Point();
const lb = new Point();
const rt = new Point();
const minTv = new Point();
const maxTv = new Point();
class BoundingRect {
x: number
y: number
width: number
height: number
constructor(x: number, y: number, width: number, height: number) {
if (width < 0) {
x = x + width;
width = -width;
}
if (height < 0) {
y = y + height;
height = -height;
}
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
union(other: BoundingRect) {
const x = mathMin(other.x, this.x);
const y = mathMin(other.y, this.y);
// If x is -Infinity and width is Infinity (like in the case of
// IncrementalDisplayble), x + width would be NaN
if (isFinite(this.x) && isFinite(this.width)) {
this.width = mathMax(
other.x + other.width,
this.x + this.width
) - x;
}
else {
this.width = other.width;
}
if (isFinite(this.y) && isFinite(this.height)) {
this.height = mathMax(
other.y + other.height,
this.y + this.height
) - y;
}
else {
this.height = other.height;
}
this.x = x;
this.y = y;
}
applyTransform(m: matrix.MatrixArray) {
BoundingRect.applyTransform(this, this, m);
}
calculateTransform(b: RectLike): matrix.MatrixArray {
const a = this;
const sx = b.width / a.width;
const sy = b.height / a.height;
const m = matrix.create();
// 矩阵右乘
matrix.translate(m, m, [-a.x, -a.y]);
matrix.scale(m, m, [sx, sy]);
matrix.translate(m, m, [b.x, b.y]);
return m;
}
intersect(b: RectLike, mtv?: PointLike): boolean {
if (!b) {
return false;
}
if (!(b instanceof BoundingRect)) {
// Normalize negative width/height.
b = BoundingRect.create(b);
}
const a = this;
const ax0 = a.x;
const ax1 = a.x + a.width;
const ay0 = a.y;
const ay1 = a.y + a.height;
const bx0 = b.x;
const bx1 = b.x + b.width;
const by0 = b.y;
const by1 = b.y + b.height;
let overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
if (mtv) {
let dMin = Infinity;
let dMax = 0;
const d0 = Math.abs(ax1 - bx0);
const d1 = Math.abs(bx1 - ax0);
const d2 = Math.abs(ay1 - by0);
const d3 = Math.abs(by1 - ay0);
const dx = Math.min(d0, d1);
const dy = Math.min(d2, d3);
// On x axis
if (ax1 < bx0 || bx1 < ax0) {
if (dx > dMax) {
dMax = dx;
if (d0 < d1) {
Point.set(maxTv, -d0, 0); // b is on the right
}
else {
Point.set(maxTv, d1, 0); // b is on the left
}
}
}
else {
if (dx < dMin) {
dMin = dx;
if (d0 < d1) {
Point.set(minTv, d0, 0); // b is on the right
}
else {
Point.set(minTv, -d1, 0); // b is on the left
}
}
}
// On y axis
if (ay1 < by0 || by1 < ay0) {
if (dy > dMax) {
dMax = dy;
if (d2 < d3) {
Point.set(maxTv, 0, -d2); // b is on the bottom(larger y)
}
else {
Point.set(maxTv, 0, d3); // b is on the top(smaller y)
}
}
}
else {
if (dx < dMin) {
dMin = dx;
if (d2 < d3) {
Point.set(minTv, 0, d2); // b is on the bottom
}
else {
Point.set(minTv, 0, -d3); // b is on the top
}
}
}
}
if (mtv) {
Point.copy(mtv, overlap ? minTv : maxTv);
}
return overlap;
}
contain(x: number, y: number): boolean {
const rect = this;
return x >= rect.x
&& x <= (rect.x + rect.width)
&& y >= rect.y
&& y <= (rect.y + rect.height);
}
clone() {
return new BoundingRect(this.x, this.y, this.width, this.height);
}
/**
* Copy from another rect
*/
copy(other: RectLike) {
BoundingRect.copy(this, other);
}
plain(): RectLike {
return {
x: this.x,
y: this.y,
width: this.width,
height: this.height
};
}
/**
* If not having NaN or Infinity with attributes
*/
isFinite(): boolean {
return isFinite(this.x)
&& isFinite(this.y)
&& isFinite(this.width)
&& isFinite(this.height);
}
isZero(): boolean {
return this.width === 0 || this.height === 0;
}
static create(rect: RectLike): BoundingRect {
return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
}
static copy(target: RectLike, source: RectLike) {
target.x = source.x;
target.y = source.y;
target.width = source.width;
target.height = source.height;
}
static applyTransform(target: RectLike, source: RectLike, m: matrix.MatrixArray) {
// In case usage like this
// el.getBoundingRect().applyTransform(el.transform)
// And element has no transform
if (!m) {
if (target !== source) {
BoundingRect.copy(target, source);
}
return;
}
// Fast path when there is no rotation in matrix.
if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) {
const sx = m[0];
const sy = m[3];
const tx = m[4];
const ty = m[5];
target.x = source.x * sx + tx;
target.y = source.y * sy + ty;
target.width = source.width * sx;
target.height = source.height * sy;
if (target.width < 0) {
target.x += target.width;
target.width = -target.width;
}
if (target.height < 0) {
target.y += target.height;
target.height = -target.height;
}
return;
}
// source and target can be same instance.
lt.x = lb.x = source.x;
lt.y = rt.y = source.y;
rb.x = rt.x = source.x + source.width;
rb.y = lb.y = source.y + source.height;
lt.transform(m);
rt.transform(m);
rb.transform(m);
lb.transform(m);
target.x = mathMin(lt.x, rb.x, lb.x, rt.x);
target.y = mathMin(lt.y, rb.y, lb.y, rt.y);
const maxX = mathMax(lt.x, rb.x, lb.x, rt.x);
const maxY = mathMax(lt.y, rb.y, lb.y, rt.y);
target.width = maxX - target.x;
target.height = maxY - target.y;
}
}
export type RectLike = {
x: number
y: number
width: number
height: number
}
export default BoundingRect;