index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Util = void 0;
  4. const x6_geometry_1 = require("@antv/x6-geometry");
  5. const x6_common_1 = require("@antv/x6-common");
  6. const util_1 = require("../registry/marker/util");
  7. var Util;
  8. (function (Util) {
  9. Util.normalizeMarker = util_1.normalize;
  10. /**
  11. * Transforms point by an SVG transformation represented by `matrix`.
  12. */
  13. function transformPoint(point, matrix) {
  14. const ret = x6_common_1.Dom.createSVGPoint(point.x, point.y).matrixTransform(matrix);
  15. return new x6_geometry_1.Point(ret.x, ret.y);
  16. }
  17. Util.transformPoint = transformPoint;
  18. /**
  19. * Transforms line by an SVG transformation represented by `matrix`.
  20. */
  21. function transformLine(line, matrix) {
  22. return new x6_geometry_1.Line(transformPoint(line.start, matrix), transformPoint(line.end, matrix));
  23. }
  24. Util.transformLine = transformLine;
  25. /**
  26. * Transforms polyline by an SVG transformation represented by `matrix`.
  27. */
  28. function transformPolyline(polyline, matrix) {
  29. let points = polyline instanceof x6_geometry_1.Polyline ? polyline.points : polyline;
  30. if (!Array.isArray(points)) {
  31. points = [];
  32. }
  33. return new x6_geometry_1.Polyline(points.map((p) => transformPoint(p, matrix)));
  34. }
  35. Util.transformPolyline = transformPolyline;
  36. function transformRectangle(rect, matrix) {
  37. const svgDocument = x6_common_1.Dom.createSvgElement('svg');
  38. const p = svgDocument.createSVGPoint();
  39. p.x = rect.x;
  40. p.y = rect.y;
  41. const corner1 = p.matrixTransform(matrix);
  42. p.x = rect.x + rect.width;
  43. p.y = rect.y;
  44. const corner2 = p.matrixTransform(matrix);
  45. p.x = rect.x + rect.width;
  46. p.y = rect.y + rect.height;
  47. const corner3 = p.matrixTransform(matrix);
  48. p.x = rect.x;
  49. p.y = rect.y + rect.height;
  50. const corner4 = p.matrixTransform(matrix);
  51. const minX = Math.min(corner1.x, corner2.x, corner3.x, corner4.x);
  52. const maxX = Math.max(corner1.x, corner2.x, corner3.x, corner4.x);
  53. const minY = Math.min(corner1.y, corner2.y, corner3.y, corner4.y);
  54. const maxY = Math.max(corner1.y, corner2.y, corner3.y, corner4.y);
  55. return new x6_geometry_1.Rectangle(minX, minY, maxX - minX, maxY - minY);
  56. }
  57. Util.transformRectangle = transformRectangle;
  58. /**
  59. * Returns the bounding box of the element after transformations are
  60. * applied. If `withoutTransformations` is `true`, transformations of
  61. * the element will not be considered when computing the bounding box.
  62. * If `target` is specified, bounding box will be computed relatively
  63. * to the `target` element.
  64. */
  65. function bbox(elem, withoutTransformations, target) {
  66. let box;
  67. const ownerSVGElement = elem.ownerSVGElement;
  68. // If the element is not in the live DOM, it does not have a bounding
  69. // box defined and so fall back to 'zero' dimension element.
  70. if (!ownerSVGElement) {
  71. return new x6_geometry_1.Rectangle(0, 0, 0, 0);
  72. }
  73. try {
  74. box = elem.getBBox();
  75. }
  76. catch (e) {
  77. // Fallback for IE.
  78. box = {
  79. x: elem.clientLeft,
  80. y: elem.clientTop,
  81. width: elem.clientWidth,
  82. height: elem.clientHeight,
  83. };
  84. }
  85. if (withoutTransformations) {
  86. return x6_geometry_1.Rectangle.create(box);
  87. }
  88. const matrix = x6_common_1.Dom.getTransformToElement(elem, target || ownerSVGElement);
  89. return transformRectangle(box, matrix);
  90. }
  91. Util.bbox = bbox;
  92. /**
  93. * Returns the bounding box of the element after transformations are
  94. * applied. Unlike `bbox()`, this function fixes a browser implementation
  95. * bug to return the correct bounding box if this elemenent is a group of
  96. * svg elements (if `options.recursive` is specified).
  97. */
  98. function getBBox(elem, options = {}) {
  99. let outputBBox;
  100. const ownerSVGElement = elem.ownerSVGElement;
  101. // If the element is not in the live DOM, it does not have a bounding box
  102. // defined and so fall back to 'zero' dimension element.
  103. // If the element is not an SVGGraphicsElement, we could not measure the
  104. // bounding box either
  105. if (!ownerSVGElement || !x6_common_1.Dom.isSVGGraphicsElement(elem)) {
  106. if (x6_common_1.Dom.isHTMLElement(elem)) {
  107. // If the element is a HTMLElement, return the position relative to the body
  108. const { left, top, width, height } = getBoundingOffsetRect(elem);
  109. return new x6_geometry_1.Rectangle(left, top, width, height);
  110. }
  111. return new x6_geometry_1.Rectangle(0, 0, 0, 0);
  112. }
  113. let target = options.target;
  114. const recursive = options.recursive;
  115. if (!recursive) {
  116. try {
  117. outputBBox = elem.getBBox();
  118. }
  119. catch (e) {
  120. outputBBox = {
  121. x: elem.clientLeft,
  122. y: elem.clientTop,
  123. width: elem.clientWidth,
  124. height: elem.clientHeight,
  125. };
  126. }
  127. if (!target) {
  128. return x6_geometry_1.Rectangle.create(outputBBox);
  129. }
  130. // transform like target
  131. const matrix = x6_common_1.Dom.getTransformToElement(elem, target);
  132. return transformRectangle(outputBBox, matrix);
  133. }
  134. // recursive
  135. {
  136. const children = elem.childNodes;
  137. const n = children.length;
  138. if (n === 0) {
  139. return getBBox(elem, {
  140. target,
  141. });
  142. }
  143. if (!target) {
  144. target = elem; // eslint-disable-line
  145. }
  146. for (let i = 0; i < n; i += 1) {
  147. const child = children[i];
  148. let childBBox;
  149. if (child.childNodes.length === 0) {
  150. childBBox = getBBox(child, {
  151. target,
  152. });
  153. }
  154. else {
  155. // if child is a group element, enter it with a recursive call
  156. childBBox = getBBox(child, {
  157. target,
  158. recursive: true,
  159. });
  160. }
  161. if (!outputBBox) {
  162. outputBBox = childBBox;
  163. }
  164. else {
  165. outputBBox = outputBBox.union(childBBox);
  166. }
  167. }
  168. return outputBBox;
  169. }
  170. }
  171. Util.getBBox = getBBox;
  172. function getBoundingOffsetRect(elem) {
  173. let left = 0;
  174. let top = 0;
  175. let width = 0;
  176. let height = 0;
  177. if (elem) {
  178. let current = elem;
  179. while (current) {
  180. left += current.offsetLeft;
  181. top += current.offsetTop;
  182. current = current.offsetParent;
  183. if (current) {
  184. left += parseInt(x6_common_1.Dom.getComputedStyle(current, 'borderLeft'), 10);
  185. top += parseInt(x6_common_1.Dom.getComputedStyle(current, 'borderTop'), 10);
  186. }
  187. }
  188. width = elem.offsetWidth;
  189. height = elem.offsetHeight;
  190. }
  191. return {
  192. left,
  193. top,
  194. width,
  195. height,
  196. };
  197. }
  198. Util.getBoundingOffsetRect = getBoundingOffsetRect;
  199. /**
  200. * Convert the SVGElement to an equivalent geometric shape. The element's
  201. * transformations are not taken into account.
  202. *
  203. * SVGRectElement => Rectangle
  204. *
  205. * SVGLineElement => Line
  206. *
  207. * SVGCircleElement => Ellipse
  208. *
  209. * SVGEllipseElement => Ellipse
  210. *
  211. * SVGPolygonElement => Polyline
  212. *
  213. * SVGPolylineElement => Polyline
  214. *
  215. * SVGPathElement => Path
  216. *
  217. * others => Rectangle
  218. */
  219. function toGeometryShape(elem) {
  220. const attr = (name) => {
  221. const s = elem.getAttribute(name);
  222. const v = s ? parseFloat(s) : 0;
  223. return Number.isNaN(v) ? 0 : v;
  224. };
  225. switch (elem instanceof SVGElement && elem.nodeName.toLowerCase()) {
  226. case 'rect':
  227. return new x6_geometry_1.Rectangle(attr('x'), attr('y'), attr('width'), attr('height'));
  228. case 'circle':
  229. return new x6_geometry_1.Ellipse(attr('cx'), attr('cy'), attr('r'), attr('r'));
  230. case 'ellipse':
  231. return new x6_geometry_1.Ellipse(attr('cx'), attr('cy'), attr('rx'), attr('ry'));
  232. case 'polyline': {
  233. const points = x6_common_1.Dom.getPointsFromSvgElement(elem);
  234. return new x6_geometry_1.Polyline(points);
  235. }
  236. case 'polygon': {
  237. const points = x6_common_1.Dom.getPointsFromSvgElement(elem);
  238. if (points.length > 1) {
  239. points.push(points[0]);
  240. }
  241. return new x6_geometry_1.Polyline(points);
  242. }
  243. case 'path': {
  244. let d = elem.getAttribute('d');
  245. if (!x6_geometry_1.Path.isValid(d)) {
  246. d = x6_geometry_1.Path.normalize(d);
  247. }
  248. return x6_geometry_1.Path.parse(d);
  249. }
  250. case 'line': {
  251. return new x6_geometry_1.Line(attr('x1'), attr('y1'), attr('x2'), attr('y2'));
  252. }
  253. default:
  254. break;
  255. }
  256. // Anything else is a rectangle
  257. return getBBox(elem);
  258. }
  259. Util.toGeometryShape = toGeometryShape;
  260. function translateAndAutoOrient(elem, position, reference, target) {
  261. const pos = x6_geometry_1.Point.create(position);
  262. const ref = x6_geometry_1.Point.create(reference);
  263. if (!target) {
  264. const svg = elem instanceof SVGSVGElement ? elem : elem.ownerSVGElement;
  265. target = svg; // eslint-disable-line
  266. }
  267. // Clean-up previously set transformations except the scale.
  268. // If we didn't clean up the previous transformations then they'd
  269. // add up with the old ones. Scale is an exception as it doesn't
  270. // add up, consider: `this.scale(2).scale(2).scale(2)`. The result
  271. // is that the element is scaled by the factor 2, not 8.
  272. const s = x6_common_1.Dom.scale(elem);
  273. elem.setAttribute('transform', '');
  274. const bbox = getBBox(elem, {
  275. target,
  276. }).scale(s.sx, s.sy);
  277. // 1. Translate to origin.
  278. const translateToOrigin = x6_common_1.Dom.createSVGTransform();
  279. translateToOrigin.setTranslate(-bbox.x - bbox.width / 2, -bbox.y - bbox.height / 2);
  280. // 2. Rotate around origin.
  281. const rotateAroundOrigin = x6_common_1.Dom.createSVGTransform();
  282. const angle = pos.angleBetween(ref, pos.clone().translate(1, 0));
  283. if (angle)
  284. rotateAroundOrigin.setRotate(angle, 0, 0);
  285. // 3. Translate to the `position` + the offset (half my width)
  286. // towards the `reference` point.
  287. const translateFromOrigin = x6_common_1.Dom.createSVGTransform();
  288. const finalPosition = pos.clone().move(ref, bbox.width / 2);
  289. translateFromOrigin.setTranslate(2 * pos.x - finalPosition.x, 2 * pos.y - finalPosition.y);
  290. // 4. Get the current transformation matrix of this node
  291. const ctm = x6_common_1.Dom.getTransformToElement(elem, target);
  292. // 5. Apply transformations and the scale
  293. const transform = x6_common_1.Dom.createSVGTransform();
  294. transform.setMatrix(translateFromOrigin.matrix.multiply(rotateAroundOrigin.matrix.multiply(translateToOrigin.matrix.multiply(ctm.scale(s.sx, s.sy)))));
  295. elem.setAttribute('transform', x6_common_1.Dom.matrixToTransformString(transform.matrix));
  296. }
  297. Util.translateAndAutoOrient = translateAndAutoOrient;
  298. function findShapeNode(magnet) {
  299. if (magnet == null) {
  300. return null;
  301. }
  302. let node = magnet;
  303. do {
  304. let tagName = node.tagName;
  305. if (typeof tagName !== 'string')
  306. return null;
  307. tagName = tagName.toUpperCase();
  308. if (x6_common_1.Dom.hasClass(node, 'x6-port')) {
  309. node = node.nextElementSibling;
  310. }
  311. else if (tagName === 'G') {
  312. node = node.firstElementChild;
  313. }
  314. else if (tagName === 'TITLE') {
  315. node = node.nextElementSibling;
  316. }
  317. else
  318. break;
  319. } while (node);
  320. return node;
  321. }
  322. Util.findShapeNode = findShapeNode;
  323. // BBox is calculated by the attribute and shape of the node.
  324. // Because of the reduction in DOM API calls, there is a significant performance improvement.
  325. function getBBoxV2(elem) {
  326. const node = findShapeNode(elem);
  327. if (!x6_common_1.Dom.isSVGGraphicsElement(node)) {
  328. if (x6_common_1.Dom.isHTMLElement(elem)) {
  329. const { left, top, width, height } = getBoundingOffsetRect(elem);
  330. return new x6_geometry_1.Rectangle(left, top, width, height);
  331. }
  332. return new x6_geometry_1.Rectangle(0, 0, 0, 0);
  333. }
  334. const shape = toGeometryShape(node);
  335. const bbox = shape.bbox() || x6_geometry_1.Rectangle.create();
  336. // const transform = node.getAttribute('transform')
  337. // if (transform) {
  338. // const nodeMatrix = Dom.transformStringToMatrix(transform)
  339. // return transformRectangle(bbox, nodeMatrix)
  340. // }
  341. return bbox;
  342. }
  343. Util.getBBoxV2 = getBBoxV2;
  344. })(Util = exports.Util || (exports.Util = {}));
  345. //# sourceMappingURL=index.js.map