util.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { Point, Line, Angle, GeometryUtil } from '@antv/x6-geometry';
  2. export function getSourceBBox(view, options) {
  3. const bbox = view.sourceBBox.clone();
  4. if (options && options.paddingBox) {
  5. return bbox.moveAndExpand(options.paddingBox);
  6. }
  7. return bbox;
  8. }
  9. export function getTargetBBox(view, options) {
  10. const bbox = view.targetBBox.clone();
  11. if (options && options.paddingBox) {
  12. return bbox.moveAndExpand(options.paddingBox);
  13. }
  14. return bbox;
  15. }
  16. export function getSourceEndpoint(view, options) {
  17. if (view.sourceAnchor) {
  18. return view.sourceAnchor;
  19. }
  20. const sourceBBox = getSourceBBox(view, options);
  21. return sourceBBox.getCenter();
  22. }
  23. export function getTargetEndpoint(view, options) {
  24. if (view.targetAnchor) {
  25. return view.targetAnchor;
  26. }
  27. const targetBBox = getTargetBBox(view, options);
  28. return targetBBox.getCenter();
  29. }
  30. // returns a direction index from start point to end point
  31. // corrects for grid deformation between start and end
  32. export function getDirectionAngle(start, end, directionCount, grid, options) {
  33. const quadrant = 360 / directionCount;
  34. const angleTheta = start.theta(fixAngleEnd(start, end, grid, options));
  35. const normalizedAngle = Angle.normalize(angleTheta + quadrant / 2);
  36. return quadrant * Math.floor(normalizedAngle / quadrant);
  37. }
  38. function fixAngleEnd(start, end, grid, options) {
  39. const step = options.step;
  40. const diffX = end.x - start.x;
  41. const diffY = end.y - start.y;
  42. const gridStepsX = diffX / grid.x;
  43. const gridStepsY = diffY / grid.y;
  44. const distanceX = gridStepsX * step;
  45. const distanceY = gridStepsY * step;
  46. return new Point(start.x + distanceX, start.y + distanceY);
  47. }
  48. /**
  49. * Returns the change in direction between two direction angles.
  50. */
  51. export function getDirectionChange(angle1, angle2) {
  52. const change = Math.abs(angle1 - angle2);
  53. return change > 180 ? 360 - change : change;
  54. }
  55. // fix direction offsets according to current grid
  56. export function getGridOffsets(grid, options) {
  57. const step = options.step;
  58. options.directions.forEach((direction) => {
  59. direction.gridOffsetX = (direction.offsetX / step) * grid.x;
  60. direction.gridOffsetY = (direction.offsetY / step) * grid.y;
  61. });
  62. return options.directions;
  63. }
  64. // get grid size in x and y dimensions, adapted to source and target positions
  65. export function getGrid(step, source, target) {
  66. return {
  67. source: source.clone(),
  68. x: getGridDimension(target.x - source.x, step),
  69. y: getGridDimension(target.y - source.y, step),
  70. };
  71. }
  72. function getGridDimension(diff, step) {
  73. // return step if diff = 0
  74. if (!diff) {
  75. return step;
  76. }
  77. const abs = Math.abs(diff);
  78. const count = Math.round(abs / step);
  79. // return `abs` if less than one step apart
  80. if (!count) {
  81. return abs;
  82. }
  83. // otherwise, return corrected step
  84. const roundedDiff = count * step;
  85. const remainder = abs - roundedDiff;
  86. const correction = remainder / count;
  87. return step + correction;
  88. }
  89. function snapGrid(point, grid) {
  90. const source = grid.source;
  91. const x = GeometryUtil.snapToGrid(point.x - source.x, grid.x) + source.x;
  92. const y = GeometryUtil.snapToGrid(point.y - source.y, grid.y) + source.y;
  93. return new Point(x, y);
  94. }
  95. export function round(point, precision) {
  96. return point.round(precision);
  97. }
  98. export function align(point, grid, precision) {
  99. return round(snapGrid(point.clone(), grid), precision);
  100. }
  101. export function getKey(point) {
  102. return point.toString();
  103. }
  104. export function normalizePoint(point) {
  105. return new Point(point.x === 0 ? 0 : Math.abs(point.x) / point.x, point.y === 0 ? 0 : Math.abs(point.y) / point.y);
  106. }
  107. export function getCost(from, anchors) {
  108. let min = Infinity;
  109. for (let i = 0, len = anchors.length; i < len; i += 1) {
  110. const dist = from.manhattanDistance(anchors[i]);
  111. if (dist < min) {
  112. min = dist;
  113. }
  114. }
  115. return min;
  116. }
  117. // Find points around the bbox taking given directions into account
  118. // lines are drawn from anchor in given directions, intersections recorded
  119. // if anchor is outside bbox, only those directions that intersect get a rect point
  120. // the anchor itself is returned as rect point (representing some directions)
  121. // (since those directions are unobstructed by the bbox)
  122. export function getRectPoints(anchor, bbox, directionList, grid, options) {
  123. const precision = options.precision;
  124. const directionMap = options.directionMap;
  125. const centerVector = anchor.diff(bbox.getCenter());
  126. const rectPoints = Object.keys(directionMap).reduce((res, key) => {
  127. if (directionList.includes(key)) {
  128. const direction = directionMap[key];
  129. // Create a line that is guaranteed to intersect the bbox if bbox
  130. // is in the direction even if anchor lies outside of bbox.
  131. const ending = new Point(anchor.x + direction.x * (Math.abs(centerVector.x) + bbox.width), anchor.y + direction.y * (Math.abs(centerVector.y) + bbox.height));
  132. const intersectionLine = new Line(anchor, ending);
  133. // Get the farther intersection, in case there are two
  134. // (that happens if anchor lies next to bbox)
  135. const intersections = intersectionLine.intersect(bbox) || [];
  136. let farthestIntersectionDistance;
  137. let farthestIntersection = null;
  138. for (let i = 0; i < intersections.length; i += 1) {
  139. const intersection = intersections[i];
  140. const distance = anchor.squaredDistance(intersection);
  141. if (farthestIntersectionDistance == null ||
  142. distance > farthestIntersectionDistance) {
  143. farthestIntersectionDistance = distance;
  144. farthestIntersection = intersection;
  145. }
  146. }
  147. // If an intersection was found in this direction, it is our rectPoint
  148. if (farthestIntersection) {
  149. let target = align(farthestIntersection, grid, precision);
  150. // If the rectPoint lies inside the bbox, offset it by one more step
  151. if (bbox.containsPoint(target)) {
  152. target = align(target.translate(direction.x * grid.x, direction.y * grid.y), grid, precision);
  153. }
  154. res.push(target);
  155. }
  156. }
  157. return res;
  158. }, []);
  159. // if anchor lies outside of bbox, add it to the array of points
  160. if (!bbox.containsPoint(anchor)) {
  161. rectPoints.push(align(anchor, grid, precision));
  162. }
  163. return rectPoints;
  164. }
  165. // reconstructs a route by concatenating points with their parents
  166. export function reconstructRoute(parents, points, tailPoint, from, to) {
  167. const route = [];
  168. let prevDiff = normalizePoint(to.diff(tailPoint));
  169. // tailPoint is assumed to be aligned already
  170. let currentKey = getKey(tailPoint);
  171. let parent = parents[currentKey];
  172. let point;
  173. while (parent) {
  174. // point is assumed to be aligned already
  175. point = points[currentKey];
  176. const diff = normalizePoint(point.diff(parent));
  177. if (!diff.equals(prevDiff)) {
  178. route.unshift(point);
  179. prevDiff = diff;
  180. }
  181. // parent is assumed to be aligned already
  182. currentKey = getKey(parent);
  183. parent = parents[currentKey];
  184. }
  185. // leadPoint is assumed to be aligned already
  186. const leadPoint = points[currentKey];
  187. const fromDiff = normalizePoint(leadPoint.diff(from));
  188. if (!fromDiff.equals(prevDiff)) {
  189. route.unshift(leadPoint);
  190. }
  191. return route;
  192. }
  193. //# sourceMappingURL=util.js.map