b4395fcd29ff7b2ed0f555ad895b1f97b388581b07a4a982840ca0b243b6df97df929b30ba9897a4c0643b662a0a8ab0cea352f6bfe19ddc826148c7022a11 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /**
  2. * @module echarts/core/BoundingRect
  3. */
  4. import * as matrix from './matrix';
  5. import Point, { PointLike } from './Point';
  6. const mathMin = Math.min;
  7. const mathMax = Math.max;
  8. const lt = new Point();
  9. const rb = new Point();
  10. const lb = new Point();
  11. const rt = new Point();
  12. const minTv = new Point();
  13. const maxTv = new Point();
  14. class BoundingRect {
  15. x: number
  16. y: number
  17. width: number
  18. height: number
  19. constructor(x: number, y: number, width: number, height: number) {
  20. if (width < 0) {
  21. x = x + width;
  22. width = -width;
  23. }
  24. if (height < 0) {
  25. y = y + height;
  26. height = -height;
  27. }
  28. this.x = x;
  29. this.y = y;
  30. this.width = width;
  31. this.height = height;
  32. }
  33. union(other: BoundingRect) {
  34. const x = mathMin(other.x, this.x);
  35. const y = mathMin(other.y, this.y);
  36. // If x is -Infinity and width is Infinity (like in the case of
  37. // IncrementalDisplayble), x + width would be NaN
  38. if (isFinite(this.x) && isFinite(this.width)) {
  39. this.width = mathMax(
  40. other.x + other.width,
  41. this.x + this.width
  42. ) - x;
  43. }
  44. else {
  45. this.width = other.width;
  46. }
  47. if (isFinite(this.y) && isFinite(this.height)) {
  48. this.height = mathMax(
  49. other.y + other.height,
  50. this.y + this.height
  51. ) - y;
  52. }
  53. else {
  54. this.height = other.height;
  55. }
  56. this.x = x;
  57. this.y = y;
  58. }
  59. applyTransform(m: matrix.MatrixArray) {
  60. BoundingRect.applyTransform(this, this, m);
  61. }
  62. calculateTransform(b: RectLike): matrix.MatrixArray {
  63. const a = this;
  64. const sx = b.width / a.width;
  65. const sy = b.height / a.height;
  66. const m = matrix.create();
  67. // 矩阵右乘
  68. matrix.translate(m, m, [-a.x, -a.y]);
  69. matrix.scale(m, m, [sx, sy]);
  70. matrix.translate(m, m, [b.x, b.y]);
  71. return m;
  72. }
  73. intersect(b: RectLike, mtv?: PointLike): boolean {
  74. if (!b) {
  75. return false;
  76. }
  77. if (!(b instanceof BoundingRect)) {
  78. // Normalize negative width/height.
  79. b = BoundingRect.create(b);
  80. }
  81. const a = this;
  82. const ax0 = a.x;
  83. const ax1 = a.x + a.width;
  84. const ay0 = a.y;
  85. const ay1 = a.y + a.height;
  86. const bx0 = b.x;
  87. const bx1 = b.x + b.width;
  88. const by0 = b.y;
  89. const by1 = b.y + b.height;
  90. let overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
  91. if (mtv) {
  92. let dMin = Infinity;
  93. let dMax = 0;
  94. const d0 = Math.abs(ax1 - bx0);
  95. const d1 = Math.abs(bx1 - ax0);
  96. const d2 = Math.abs(ay1 - by0);
  97. const d3 = Math.abs(by1 - ay0);
  98. const dx = Math.min(d0, d1);
  99. const dy = Math.min(d2, d3);
  100. // On x axis
  101. if (ax1 < bx0 || bx1 < ax0) {
  102. if (dx > dMax) {
  103. dMax = dx;
  104. if (d0 < d1) {
  105. Point.set(maxTv, -d0, 0); // b is on the right
  106. }
  107. else {
  108. Point.set(maxTv, d1, 0); // b is on the left
  109. }
  110. }
  111. }
  112. else {
  113. if (dx < dMin) {
  114. dMin = dx;
  115. if (d0 < d1) {
  116. Point.set(minTv, d0, 0); // b is on the right
  117. }
  118. else {
  119. Point.set(minTv, -d1, 0); // b is on the left
  120. }
  121. }
  122. }
  123. // On y axis
  124. if (ay1 < by0 || by1 < ay0) {
  125. if (dy > dMax) {
  126. dMax = dy;
  127. if (d2 < d3) {
  128. Point.set(maxTv, 0, -d2); // b is on the bottom(larger y)
  129. }
  130. else {
  131. Point.set(maxTv, 0, d3); // b is on the top(smaller y)
  132. }
  133. }
  134. }
  135. else {
  136. if (dx < dMin) {
  137. dMin = dx;
  138. if (d2 < d3) {
  139. Point.set(minTv, 0, d2); // b is on the bottom
  140. }
  141. else {
  142. Point.set(minTv, 0, -d3); // b is on the top
  143. }
  144. }
  145. }
  146. }
  147. if (mtv) {
  148. Point.copy(mtv, overlap ? minTv : maxTv);
  149. }
  150. return overlap;
  151. }
  152. contain(x: number, y: number): boolean {
  153. const rect = this;
  154. return x >= rect.x
  155. && x <= (rect.x + rect.width)
  156. && y >= rect.y
  157. && y <= (rect.y + rect.height);
  158. }
  159. clone() {
  160. return new BoundingRect(this.x, this.y, this.width, this.height);
  161. }
  162. /**
  163. * Copy from another rect
  164. */
  165. copy(other: RectLike) {
  166. BoundingRect.copy(this, other);
  167. }
  168. plain(): RectLike {
  169. return {
  170. x: this.x,
  171. y: this.y,
  172. width: this.width,
  173. height: this.height
  174. };
  175. }
  176. /**
  177. * If not having NaN or Infinity with attributes
  178. */
  179. isFinite(): boolean {
  180. return isFinite(this.x)
  181. && isFinite(this.y)
  182. && isFinite(this.width)
  183. && isFinite(this.height);
  184. }
  185. isZero(): boolean {
  186. return this.width === 0 || this.height === 0;
  187. }
  188. static create(rect: RectLike): BoundingRect {
  189. return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
  190. }
  191. static copy(target: RectLike, source: RectLike) {
  192. target.x = source.x;
  193. target.y = source.y;
  194. target.width = source.width;
  195. target.height = source.height;
  196. }
  197. static applyTransform(target: RectLike, source: RectLike, m: matrix.MatrixArray) {
  198. // In case usage like this
  199. // el.getBoundingRect().applyTransform(el.transform)
  200. // And element has no transform
  201. if (!m) {
  202. if (target !== source) {
  203. BoundingRect.copy(target, source);
  204. }
  205. return;
  206. }
  207. // Fast path when there is no rotation in matrix.
  208. if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) {
  209. const sx = m[0];
  210. const sy = m[3];
  211. const tx = m[4];
  212. const ty = m[5];
  213. target.x = source.x * sx + tx;
  214. target.y = source.y * sy + ty;
  215. target.width = source.width * sx;
  216. target.height = source.height * sy;
  217. if (target.width < 0) {
  218. target.x += target.width;
  219. target.width = -target.width;
  220. }
  221. if (target.height < 0) {
  222. target.y += target.height;
  223. target.height = -target.height;
  224. }
  225. return;
  226. }
  227. // source and target can be same instance.
  228. lt.x = lb.x = source.x;
  229. lt.y = rt.y = source.y;
  230. rb.x = rt.x = source.x + source.width;
  231. rb.y = lb.y = source.y + source.height;
  232. lt.transform(m);
  233. rt.transform(m);
  234. rb.transform(m);
  235. lb.transform(m);
  236. target.x = mathMin(lt.x, rb.x, lb.x, rt.x);
  237. target.y = mathMin(lt.y, rb.y, lb.y, rt.y);
  238. const maxX = mathMax(lt.x, rb.x, lb.x, rt.x);
  239. const maxY = mathMax(lt.y, rb.y, lb.y, rt.y);
  240. target.width = maxX - target.x;
  241. target.height = maxY - target.y;
  242. }
  243. }
  244. export type RectLike = {
  245. x: number
  246. y: number
  247. width: number
  248. height: number
  249. }
  250. export default BoundingRect;