UNPKG

zrender

Version:

A lightweight graphic library providing 2d draw for Apache ECharts

289 lines (250 loc) 7.8 kB
/** * @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;