@rxflow/manhattan 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/cjs/geometry/Line.d.ts +21 -0
  2. package/cjs/geometry/Line.d.ts.map +1 -0
  3. package/cjs/geometry/Line.js +88 -0
  4. package/cjs/geometry/Point.d.ts +49 -0
  5. package/cjs/geometry/Point.d.ts.map +1 -0
  6. package/cjs/geometry/Point.js +94 -0
  7. package/cjs/geometry/Rectangle.d.ts +41 -0
  8. package/cjs/geometry/Rectangle.d.ts.map +1 -0
  9. package/cjs/geometry/Rectangle.js +65 -0
  10. package/cjs/geometry/collision.d.ts +15 -0
  11. package/cjs/geometry/collision.d.ts.map +1 -0
  12. package/cjs/geometry/collision.js +81 -0
  13. package/cjs/geometry/index.d.ts +5 -0
  14. package/cjs/geometry/index.d.ts.map +1 -0
  15. package/cjs/geometry/index.js +45 -0
  16. package/cjs/getManHattanPath.d.ts +53 -0
  17. package/cjs/getManHattanPath.d.ts.map +1 -0
  18. package/cjs/getManHattanPath.js +449 -0
  19. package/cjs/index.d.ts +16 -0
  20. package/cjs/index.d.ts.map +1 -0
  21. package/cjs/index.js +117 -0
  22. package/cjs/obstacle/ObstacleMap.d.ts +66 -0
  23. package/cjs/obstacle/ObstacleMap.d.ts.map +1 -0
  24. package/cjs/obstacle/ObstacleMap.js +328 -0
  25. package/cjs/obstacle/QuadTree.d.ts +119 -0
  26. package/cjs/obstacle/QuadTree.d.ts.map +1 -0
  27. package/cjs/obstacle/QuadTree.js +334 -0
  28. package/cjs/obstacle/index.d.ts +2 -0
  29. package/cjs/obstacle/index.d.ts.map +1 -0
  30. package/cjs/obstacle/index.js +12 -0
  31. package/cjs/options/defaults.d.ts +16 -0
  32. package/cjs/options/defaults.d.ts.map +1 -0
  33. package/cjs/options/defaults.js +39 -0
  34. package/cjs/options/index.d.ts +4 -0
  35. package/cjs/options/index.d.ts.map +1 -0
  36. package/cjs/options/index.js +38 -0
  37. package/cjs/options/resolver.d.ts +10 -0
  38. package/cjs/options/resolver.d.ts.map +1 -0
  39. package/cjs/options/resolver.js +248 -0
  40. package/cjs/options/types.d.ts +210 -0
  41. package/cjs/options/types.d.ts.map +1 -0
  42. package/cjs/options/types.js +5 -0
  43. package/cjs/pathfinder/PathCache.d.ts +92 -0
  44. package/cjs/pathfinder/PathCache.d.ts.map +1 -0
  45. package/cjs/pathfinder/PathCache.js +249 -0
  46. package/cjs/pathfinder/SortedSet.d.ts +35 -0
  47. package/cjs/pathfinder/SortedSet.d.ts.map +1 -0
  48. package/cjs/pathfinder/SortedSet.js +95 -0
  49. package/cjs/pathfinder/findRoute.d.ts +8 -0
  50. package/cjs/pathfinder/findRoute.d.ts.map +1 -0
  51. package/cjs/pathfinder/findRoute.js +395 -0
  52. package/cjs/pathfinder/index.d.ts +4 -0
  53. package/cjs/pathfinder/index.d.ts.map +1 -0
  54. package/cjs/pathfinder/index.js +44 -0
  55. package/cjs/svg/index.d.ts +3 -0
  56. package/cjs/svg/index.d.ts.map +1 -0
  57. package/cjs/svg/index.js +31 -0
  58. package/cjs/svg/pathConverter.d.ts +23 -0
  59. package/cjs/svg/pathConverter.d.ts.map +1 -0
  60. package/cjs/svg/pathConverter.js +285 -0
  61. package/cjs/svg/pathParser.d.ts +11 -0
  62. package/cjs/svg/pathParser.d.ts.map +1 -0
  63. package/cjs/svg/pathParser.js +76 -0
  64. package/cjs/utils/AdaptiveStepCalculator.d.ts +90 -0
  65. package/cjs/utils/AdaptiveStepCalculator.d.ts.map +1 -0
  66. package/cjs/utils/AdaptiveStepCalculator.js +224 -0
  67. package/cjs/utils/ErrorRecovery.d.ts +182 -0
  68. package/cjs/utils/ErrorRecovery.d.ts.map +1 -0
  69. package/cjs/utils/ErrorRecovery.js +413 -0
  70. package/cjs/utils/GlobalGrid.d.ts +99 -0
  71. package/cjs/utils/GlobalGrid.d.ts.map +1 -0
  72. package/cjs/utils/GlobalGrid.js +224 -0
  73. package/cjs/utils/PerformanceMonitor.d.ts +139 -0
  74. package/cjs/utils/PerformanceMonitor.d.ts.map +1 -0
  75. package/cjs/utils/PerformanceMonitor.js +305 -0
  76. package/cjs/utils/direction.d.ts +24 -0
  77. package/cjs/utils/direction.d.ts.map +1 -0
  78. package/cjs/utils/direction.js +54 -0
  79. package/cjs/utils/getAnchorPoints.d.ts +15 -0
  80. package/cjs/utils/getAnchorPoints.d.ts.map +1 -0
  81. package/cjs/utils/getAnchorPoints.js +71 -0
  82. package/cjs/utils/grid.d.ts +42 -0
  83. package/cjs/utils/grid.d.ts.map +1 -0
  84. package/cjs/utils/grid.js +73 -0
  85. package/cjs/utils/heuristics.d.ts +61 -0
  86. package/cjs/utils/heuristics.d.ts.map +1 -0
  87. package/cjs/utils/heuristics.js +141 -0
  88. package/cjs/utils/index.d.ts +14 -0
  89. package/cjs/utils/index.d.ts.map +1 -0
  90. package/cjs/utils/index.js +148 -0
  91. package/cjs/utils/node.d.ts +27 -0
  92. package/cjs/utils/node.d.ts.map +1 -0
  93. package/cjs/utils/node.js +36 -0
  94. package/cjs/utils/pathProcessing.d.ts +45 -0
  95. package/cjs/utils/pathProcessing.d.ts.map +1 -0
  96. package/cjs/utils/pathProcessing.js +270 -0
  97. package/cjs/utils/pathValidation.d.ts +11 -0
  98. package/cjs/utils/pathValidation.d.ts.map +1 -0
  99. package/cjs/utils/pathValidation.js +129 -0
  100. package/cjs/utils/rect.d.ts +9 -0
  101. package/cjs/utils/rect.d.ts.map +1 -0
  102. package/cjs/utils/rect.js +110 -0
  103. package/cjs/utils/route.d.ts +19 -0
  104. package/cjs/utils/route.d.ts.map +1 -0
  105. package/cjs/utils/route.js +92 -0
  106. package/esm/geometry/Line.d.ts +21 -0
  107. package/esm/geometry/Line.d.ts.map +1 -0
  108. package/esm/geometry/Point.d.ts +49 -0
  109. package/esm/geometry/Point.d.ts.map +1 -0
  110. package/esm/geometry/Rectangle.d.ts +41 -0
  111. package/esm/geometry/Rectangle.d.ts.map +1 -0
  112. package/esm/geometry/collision.d.ts +15 -0
  113. package/esm/geometry/collision.d.ts.map +1 -0
  114. package/esm/geometry/index.d.ts +5 -0
  115. package/esm/geometry/index.d.ts.map +1 -0
  116. package/esm/getManHattanPath.d.ts +53 -0
  117. package/esm/getManHattanPath.d.ts.map +1 -0
  118. package/esm/index.d.ts +16 -0
  119. package/esm/index.d.ts.map +1 -0
  120. package/esm/obstacle/ObstacleMap.d.ts +66 -0
  121. package/esm/obstacle/ObstacleMap.d.ts.map +1 -0
  122. package/esm/obstacle/QuadTree.d.ts +119 -0
  123. package/esm/obstacle/QuadTree.d.ts.map +1 -0
  124. package/esm/obstacle/index.d.ts +2 -0
  125. package/esm/obstacle/index.d.ts.map +1 -0
  126. package/esm/options/defaults.d.ts +16 -0
  127. package/esm/options/defaults.d.ts.map +1 -0
  128. package/esm/options/index.d.ts +4 -0
  129. package/esm/options/index.d.ts.map +1 -0
  130. package/esm/options/resolver.d.ts +10 -0
  131. package/esm/options/resolver.d.ts.map +1 -0
  132. package/esm/options/types.d.ts +210 -0
  133. package/esm/options/types.d.ts.map +1 -0
  134. package/esm/pathfinder/PathCache.d.ts +92 -0
  135. package/esm/pathfinder/PathCache.d.ts.map +1 -0
  136. package/esm/pathfinder/SortedSet.d.ts +35 -0
  137. package/esm/pathfinder/SortedSet.d.ts.map +1 -0
  138. package/esm/pathfinder/findRoute.d.ts +8 -0
  139. package/esm/pathfinder/findRoute.d.ts.map +1 -0
  140. package/esm/pathfinder/index.d.ts +4 -0
  141. package/esm/pathfinder/index.d.ts.map +1 -0
  142. package/esm/svg/index.d.ts +3 -0
  143. package/esm/svg/index.d.ts.map +1 -0
  144. package/esm/svg/pathConverter.d.ts +23 -0
  145. package/esm/svg/pathConverter.d.ts.map +1 -0
  146. package/esm/svg/pathParser.d.ts +11 -0
  147. package/esm/svg/pathParser.d.ts.map +1 -0
  148. package/esm/utils/AdaptiveStepCalculator.d.ts +90 -0
  149. package/esm/utils/AdaptiveStepCalculator.d.ts.map +1 -0
  150. package/esm/utils/ErrorRecovery.d.ts +182 -0
  151. package/esm/utils/ErrorRecovery.d.ts.map +1 -0
  152. package/esm/utils/GlobalGrid.d.ts +99 -0
  153. package/esm/utils/GlobalGrid.d.ts.map +1 -0
  154. package/esm/utils/PerformanceMonitor.d.ts +139 -0
  155. package/esm/utils/PerformanceMonitor.d.ts.map +1 -0
  156. package/esm/utils/direction.d.ts +24 -0
  157. package/esm/utils/direction.d.ts.map +1 -0
  158. package/esm/utils/getAnchorPoints.d.ts +15 -0
  159. package/esm/utils/getAnchorPoints.d.ts.map +1 -0
  160. package/esm/utils/grid.d.ts +42 -0
  161. package/esm/utils/grid.d.ts.map +1 -0
  162. package/esm/utils/heuristics.d.ts +61 -0
  163. package/esm/utils/heuristics.d.ts.map +1 -0
  164. package/esm/utils/index.d.ts +14 -0
  165. package/esm/utils/index.d.ts.map +1 -0
  166. package/esm/utils/node.d.ts +27 -0
  167. package/esm/utils/node.d.ts.map +1 -0
  168. package/esm/utils/pathProcessing.d.ts +45 -0
  169. package/esm/utils/pathProcessing.d.ts.map +1 -0
  170. package/esm/utils/pathValidation.d.ts +11 -0
  171. package/esm/utils/pathValidation.d.ts.map +1 -0
  172. package/esm/utils/rect.d.ts +9 -0
  173. package/esm/utils/rect.d.ts.map +1 -0
  174. package/esm/utils/route.d.ts +19 -0
  175. package/esm/utils/route.d.ts.map +1 -0
  176. package/package.json +1 -1
@@ -0,0 +1,449 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getManHattanPath = getManHattanPath;
7
+ var _react = require("@xyflow/react");
8
+ var _geometry = require("./geometry");
9
+ var _obstacle = require("./obstacle");
10
+ var _options = require("./options");
11
+ var _pathfinder = require("./pathfinder");
12
+ var _svg = require("./svg");
13
+ var _utils = require("./utils");
14
+ /**
15
+ * Parameters for getManHattanPath function
16
+ */
17
+
18
+ /**
19
+ * Generate Manhattan-routed path for ReactFlow edges
20
+ *
21
+ * @param params - Path generation parameters
22
+ * @returns SVG path string that can be used with ReactFlow's BaseEdge
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const path = getManHattanPath({
27
+ * sourceNodeId: 'node1',
28
+ * targetNodeId: 'node2',
29
+ * sourcePosition: { x: 100, y: 100 },
30
+ * targetPosition: { x: 300, y: 300 },
31
+ * nodeLookup: nodes,
32
+ * options: {
33
+ * step: 10,
34
+ * startDirections: ['bottom'],
35
+ * endDirections: ['top']
36
+ * }
37
+ * })
38
+ * ```
39
+ */
40
+ function getManHattanPath(params) {
41
+ const {
42
+ sourceNodeId,
43
+ targetNodeId,
44
+ sourcePosition,
45
+ targetPosition,
46
+ nodeLookup,
47
+ sourceX,
48
+ sourceY,
49
+ targetX,
50
+ targetY,
51
+ options: userOptions = {}
52
+ } = params;
53
+
54
+ // Resolve options and add position information
55
+ const options = (0, _options.resolveOptions)({
56
+ ...userOptions,
57
+ sourcePosition,
58
+ targetPosition
59
+ });
60
+
61
+ // Direction control is automatically handled by getRectPoints:
62
+ // - When anchor is on an edge, only outward directions are allowed (via isDirectionOutward)
63
+ // - For sourcePosition="right": anchor on right edge -> only extends right
64
+ // - For targetPosition="left": anchor on left edge -> path approaches from right (outward from left)
65
+ // This ensures paths follow the sourcePosition and targetPosition constraints
66
+
67
+ // Get source and target nodes
68
+ const sourceNode = nodeLookup.get(sourceNodeId);
69
+ const targetNode = nodeLookup.get(targetNodeId);
70
+ if (!sourceNode || !targetNode) {
71
+ // Fallback to simple straight line if nodes not found
72
+ console.warn('[getManHattanPath] Source or target node not found in nodeLookup');
73
+ const start = new _geometry.Point(sourceX, sourceY);
74
+ const end = new _geometry.Point(targetX, targetY);
75
+ // Use ErrorRecovery to generate a proper fallback path
76
+ const fallbackPath = _utils.ErrorRecovery.generateFallbackPath(start, end);
77
+ return (0, _svg.pointsToPath)(fallbackPath, options.precision, options.borderRadius);
78
+ }
79
+
80
+ // Get node dimensions using ReactFlow's priority logic
81
+ const sourceDimensions = (0, _utils.getNodeDimensions)(sourceNode);
82
+ const targetDimensions = (0, _utils.getNodeDimensions)(targetNode);
83
+
84
+ // Get absolute positions from internals
85
+ const sourcePos = (0, _utils.getNodePosition)(sourceNode);
86
+ const targetPos = (0, _utils.getNodePosition)(targetNode);
87
+
88
+ // Calculate bounding boxes
89
+ const sourceBBox = new _geometry.Rectangle(sourcePos.x, sourcePos.y, sourceDimensions.width, sourceDimensions.height);
90
+ const targetBBox = new _geometry.Rectangle(targetPos.x, targetPos.y, targetDimensions.width, targetDimensions.height);
91
+
92
+ // Create anchor points
93
+ const sourceAnchor = new _geometry.Point(sourceX, sourceY);
94
+ const targetAnchor = new _geometry.Point(targetX, targetY);
95
+
96
+ // Try ReactFlow's getSmoothStepPath first
97
+ const [smoothStepPath] = (0, _react.getSmoothStepPath)({
98
+ sourceX,
99
+ sourceY,
100
+ targetX,
101
+ targetY,
102
+ sourcePosition,
103
+ targetPosition,
104
+ borderRadius: options.borderRadius
105
+ });
106
+
107
+ // Parse the smooth step path to extract points
108
+ const smoothStepPoints = (0, _svg.parseSVGPath)(smoothStepPath);
109
+
110
+ // Check if smooth step path intersects with any obstacles
111
+ if (smoothStepPoints.length > 0 && !(0, _utils.pathIntersectsObstacles)(smoothStepPoints, nodeLookup)) {
112
+ return smoothStepPath;
113
+ }
114
+
115
+ // Build obstacle map with anchor information
116
+ const obstacleMap = new _obstacle.ObstacleMap(options).build(nodeLookup, sourceNodeId, targetNodeId, sourceAnchor, targetAnchor);
117
+
118
+ // Find route
119
+ let route = (0, _pathfinder.findRoute)(sourceBBox, targetBBox, sourceAnchor, targetAnchor, obstacleMap, options);
120
+
121
+ // Fallback to Z-shaped path if no route found
122
+ if (!route) {
123
+ console.warn('[getManHattanPath] Unable to find Manhattan route, using fallback path');
124
+ route = _utils.ErrorRecovery.generateFallbackPath(sourceAnchor, targetAnchor);
125
+ }
126
+
127
+ // If using smart point generation (sourcePosition/targetPosition specified),
128
+ // the route already contains the correct extension points, so skip manual processing
129
+ const useSmartPoints = sourcePosition || targetPosition;
130
+ if (useSmartPoints) {
131
+ // Post-process route to fix X coordinates for horizontal anchors
132
+ // This ensures the extension distance is fixed and not affected by grid alignment
133
+ const isSourceHorizontal = sourcePosition === _react.Position.Left || sourcePosition === _react.Position.Right;
134
+ const isTargetHorizontal = targetPosition === _react.Position.Left || targetPosition === _react.Position.Right;
135
+ if (isSourceHorizontal && isTargetHorizontal && route.length > 0) {
136
+ // Calculate expected extension X coordinates
137
+ const sourceExtensionX = sourcePosition === _react.Position.Right ? sourceAnchor.x + options.extensionDistance : sourceAnchor.x - options.extensionDistance;
138
+ const targetExtensionX = targetPosition === _react.Position.Left ? targetAnchor.x - options.extensionDistance : targetAnchor.x + options.extensionDistance;
139
+
140
+ // Find the horizontal segment (where Y stays constant but X changes significantly)
141
+ // This divides the path into source-side and target-side
142
+ let horizontalSegmentStart = -1;
143
+ let horizontalSegmentEnd = -1;
144
+ for (let i = 0; i < route.length - 1; i++) {
145
+ const curr = route[i];
146
+ const next = route[i + 1];
147
+ const isHorizontalMove = Math.abs(curr.y - next.y) < 1 && Math.abs(curr.x - next.x) > options.step;
148
+ if (isHorizontalMove) {
149
+ horizontalSegmentStart = i;
150
+ horizontalSegmentEnd = i + 1;
151
+ break;
152
+ }
153
+ }
154
+
155
+ // Fix route points
156
+ for (let i = 0; i < route.length; i++) {
157
+ const point = route[i];
158
+ if (horizontalSegmentStart >= 0) {
159
+ if (i <= horizontalSegmentStart) {
160
+ // Source side - all vertical segment points should use sourceExtensionX
161
+ route[i] = new _geometry.Point(sourceExtensionX, point.y);
162
+ } else if (i >= horizontalSegmentEnd) {
163
+ // Target side - all vertical segment points should use targetExtensionX
164
+ route[i] = new _geometry.Point(targetExtensionX, point.y);
165
+ }
166
+ }
167
+ }
168
+
169
+ // Remove redundant points (consecutive points on same line)
170
+ const optimized = [];
171
+ for (let i = 0; i < route.length; i++) {
172
+ const point = route[i];
173
+ if (optimized.length < 2) {
174
+ optimized.push(point);
175
+ } else {
176
+ const prev = optimized[optimized.length - 1];
177
+ const prevPrev = optimized[optimized.length - 2];
178
+
179
+ // Check if prev is on the same line as prevPrev and point
180
+ const sameX = Math.abs(prevPrev.x - prev.x) < 1 && Math.abs(prev.x - point.x) < 1;
181
+ const sameY = Math.abs(prevPrev.y - prev.y) < 1 && Math.abs(prev.y - point.y) < 1;
182
+ if (sameX || sameY) {
183
+ // prev is redundant, replace it with current point
184
+ optimized[optimized.length - 1] = point;
185
+ } else {
186
+ optimized.push(point);
187
+ }
188
+ }
189
+ }
190
+ route = optimized;
191
+ }
192
+
193
+ // Add source and target anchors to route
194
+ const finalRoute = [sourceAnchor, ...route, targetAnchor];
195
+ return (0, _svg.pointsToPath)(finalRoute, options.precision, options.borderRadius);
196
+ }
197
+
198
+ // Remove extension points from route that were added by getRectPoints
199
+ // We will add our own with fixed step distance
200
+ const step = options.step;
201
+ const tolerance = 1;
202
+
203
+ // Check if first point is an extension point from source
204
+ if (route.length > 0) {
205
+ const firstPoint = route[0];
206
+ const onLeft = Math.abs(sourceAnchor.x - sourceBBox.x) < tolerance;
207
+ const onRight = Math.abs(sourceAnchor.x - (sourceBBox.x + sourceBBox.width)) < tolerance;
208
+ const onTop = Math.abs(sourceAnchor.y - sourceBBox.y) < tolerance;
209
+ const onBottom = Math.abs(sourceAnchor.y - (sourceBBox.y + sourceBBox.height)) < tolerance;
210
+
211
+ // Check if firstPoint is close to source anchor (indicating it's an extension point)
212
+ const distToFirst = sourceAnchor.manhattanDistance(firstPoint);
213
+ if (distToFirst < step * 2) {
214
+ // This is likely an extension point, remove it
215
+ if (onRight && firstPoint.x > sourceAnchor.x || onLeft && firstPoint.x < sourceAnchor.x || onBottom && firstPoint.y > sourceAnchor.y || onTop && firstPoint.y < sourceAnchor.y) {
216
+ route.shift();
217
+ }
218
+ }
219
+ }
220
+
221
+ // Check if last point is an extension point from target
222
+ if (route.length > 0) {
223
+ const lastPoint = route[route.length - 1];
224
+ const onLeft = Math.abs(targetAnchor.x - targetBBox.x) < tolerance;
225
+ const onRight = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) < tolerance;
226
+ const onTop = Math.abs(targetAnchor.y - targetBBox.y) < tolerance;
227
+ const onBottom = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) < tolerance;
228
+
229
+ // Check if lastPoint is close to target anchor (indicating it's an extension point)
230
+ const distToLast = targetAnchor.manhattanDistance(lastPoint);
231
+ if (distToLast < step * 2) {
232
+ // This is likely an extension point, remove it
233
+ if (onLeft && lastPoint.x < targetAnchor.x || onRight && lastPoint.x > targetAnchor.x || onTop && lastPoint.y < targetAnchor.y || onBottom && lastPoint.y > targetAnchor.y) {
234
+ route.pop();
235
+ }
236
+ }
237
+ }
238
+
239
+ // Insert extension point at source - always extend away from node edge by fixed distance
240
+ if (route.length > 0) {
241
+ const extensionDistance = options.extensionDistance;
242
+ const firstPoint = route[0];
243
+
244
+ // Determine which edge the source anchor is on
245
+ const onLeft = Math.abs(sourceAnchor.x - sourceBBox.x) < tolerance;
246
+ const onRight = Math.abs(sourceAnchor.x - (sourceBBox.x + sourceBBox.width)) < tolerance;
247
+ const onTop = Math.abs(sourceAnchor.y - sourceBBox.y) < tolerance;
248
+ const onBottom = Math.abs(sourceAnchor.y - (sourceBBox.y + sourceBBox.height)) < tolerance;
249
+
250
+ // Insert extension point and corner point to ensure orthogonal path
251
+ if (onRight) {
252
+ // Anchor on right edge - extend right by step + borderRadius
253
+ const extendX = sourceAnchor.x + extensionDistance;
254
+ const extensionPoint = new _geometry.Point(extendX, sourceAnchor.y);
255
+ // Check if we need a corner point
256
+ if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
257
+ route.unshift(new _geometry.Point(extendX, firstPoint.y)); // Corner point
258
+ }
259
+ route.unshift(extensionPoint); // Extension point (fixed distance)
260
+ } else if (onLeft) {
261
+ // Anchor on left edge - extend left by step + borderRadius
262
+ const extendX = sourceAnchor.x - extensionDistance;
263
+ const extensionPoint = new _geometry.Point(extendX, sourceAnchor.y);
264
+ if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
265
+ route.unshift(new _geometry.Point(extendX, firstPoint.y));
266
+ }
267
+ route.unshift(extensionPoint);
268
+ } else if (onBottom) {
269
+ // Anchor on bottom edge - extend down by step + borderRadius
270
+ const extendY = sourceAnchor.y + extensionDistance;
271
+ const extensionPoint = new _geometry.Point(sourceAnchor.x, extendY);
272
+ if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
273
+ route.unshift(new _geometry.Point(firstPoint.x, extendY));
274
+ }
275
+ route.unshift(extensionPoint);
276
+ } else if (onTop) {
277
+ // Anchor on top edge - extend up by step + borderRadius
278
+ const extendY = sourceAnchor.y - extensionDistance;
279
+ const extensionPoint = new _geometry.Point(sourceAnchor.x, extendY);
280
+ if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
281
+ route.unshift(new _geometry.Point(firstPoint.x, extendY));
282
+ }
283
+ route.unshift(extensionPoint);
284
+ }
285
+ }
286
+
287
+ // Remove redundant points after source extension
288
+ // If the first route point has the same x or y coordinate as the source anchor, it's redundant
289
+ if (route.length > 3) {
290
+ const thirdRoutePoint = route[2]; // Original A* point
291
+ // Check if the third point (original A* point) is redundant
292
+ // It's redundant if it's on the same line as the corner point and can be skipped
293
+ const sameX = Math.abs(thirdRoutePoint.x - sourceAnchor.x) < tolerance;
294
+ const sameY = Math.abs(thirdRoutePoint.y - sourceAnchor.y) < tolerance;
295
+ if (sameX || sameY) {
296
+ const secondRoutePoint = route[1]; // Corner point (if exists)
297
+ const fourthPoint = route[3];
298
+ // If corner point and fourth point form a straight line, remove the third point
299
+ const cornerToThird = Math.abs(secondRoutePoint.x - thirdRoutePoint.x) < tolerance || Math.abs(secondRoutePoint.y - thirdRoutePoint.y) < tolerance;
300
+ const thirdToFourth = Math.abs(thirdRoutePoint.x - fourthPoint.x) < tolerance || Math.abs(thirdRoutePoint.y - fourthPoint.y) < tolerance;
301
+ if (cornerToThird && thirdToFourth) {
302
+ route.splice(2, 1); // Remove the third point
303
+ }
304
+ }
305
+ }
306
+
307
+ // Optimize zigzag patterns BEFORE inserting target extension
308
+ // Check for patterns like: (1360, 16) -> (815, 16) -> (815, -134)
309
+ // where the middle segment goes to target edge and then moves along it
310
+ // Use target bbox with padding (same as ObstacleMap)
311
+ const targetBBoxWithPadding = targetBBox.moveAndExpand(options.paddingBox);
312
+ if (route.length >= 3) {
313
+ let i = 0;
314
+ while (i < route.length - 2) {
315
+ const p1 = route[i];
316
+ const p2 = route[i + 1];
317
+ const p3 = route[i + 2];
318
+
319
+ // Check if p2 is on the target bbox edge (with padding)
320
+ const p2OnTargetLeftEdge = Math.abs(p2.x - targetBBoxWithPadding.x) < tolerance;
321
+ const p2OnTargetRightEdge = Math.abs(p2.x - (targetBBoxWithPadding.x + targetBBoxWithPadding.width)) < tolerance;
322
+ const p2OnTargetTopEdge = Math.abs(p2.y - targetBBoxWithPadding.y) < tolerance;
323
+ const p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBoxWithPadding.y + targetBBoxWithPadding.height)) < tolerance;
324
+ const p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
325
+ if (p2OnTargetEdge) {
326
+ // Check if p1 -> p2 -> p3 forms a zigzag
327
+ const p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
328
+ const p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
329
+ const p1ToP2Vertical = Math.abs(p1.x - p2.x) < tolerance;
330
+ const p2ToP3Horizontal = Math.abs(p2.y - p3.y) < tolerance;
331
+ if (p1ToP2Horizontal && p2ToP3Vertical || p1ToP2Vertical && p2ToP3Horizontal) {
332
+ // We have a zigzag at target edge, remove p2 and p3
333
+ route.splice(i + 1, 2); // Remove p2 and p3
334
+ continue;
335
+ }
336
+ }
337
+ i++;
338
+ }
339
+ }
340
+
341
+ // Insert extension point at target - always extend away from node edge by fixed distance
342
+ if (route.length > 0) {
343
+ const extensionDistance = options.extensionDistance;
344
+ const lastPoint = route[route.length - 1];
345
+
346
+ // Determine which edge the target anchor is on
347
+ const onLeft = Math.abs(targetAnchor.x - targetBBox.x) < tolerance;
348
+ const onRight = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) < tolerance;
349
+ const onTop = Math.abs(targetAnchor.y - targetBBox.y) < tolerance;
350
+ const onBottom = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) < tolerance;
351
+
352
+ // Insert extension point and corner point to ensure orthogonal path
353
+ if (onLeft) {
354
+ // Anchor on left edge - extend left by step + borderRadius
355
+ const extendX = targetAnchor.x - extensionDistance;
356
+ const extensionPoint = new _geometry.Point(extendX, targetAnchor.y);
357
+ if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
358
+ route.push(new _geometry.Point(extendX, lastPoint.y)); // Corner point
359
+ }
360
+ route.push(extensionPoint); // Extension point (fixed distance)
361
+ } else if (onRight) {
362
+ // Anchor on right edge - extend right by step + borderRadius
363
+ const extendX = targetAnchor.x + extensionDistance;
364
+ const extensionPoint = new _geometry.Point(extendX, targetAnchor.y);
365
+ if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
366
+ route.push(new _geometry.Point(extendX, lastPoint.y));
367
+ }
368
+ route.push(extensionPoint);
369
+ } else if (onTop) {
370
+ // Anchor on top edge - extend up by step + borderRadius
371
+ const extendY = targetAnchor.y - extensionDistance;
372
+ const extensionPoint = new _geometry.Point(targetAnchor.x, extendY);
373
+ if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
374
+ route.push(new _geometry.Point(lastPoint.x, extendY));
375
+ }
376
+ route.push(extensionPoint);
377
+ } else if (onBottom) {
378
+ // Anchor on bottom edge - extend down by step + borderRadius
379
+ const extendY = targetAnchor.y + extensionDistance;
380
+ const extensionPoint = new _geometry.Point(targetAnchor.x, extendY);
381
+ if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
382
+ route.push(new _geometry.Point(lastPoint.x, extendY));
383
+ }
384
+ route.push(extensionPoint);
385
+ }
386
+ }
387
+
388
+ // Remove redundant points before target extension
389
+ // Similar logic for target side
390
+ if (route.length > 2) {
391
+ const lastIdx = route.length - 1;
392
+ const secondLastPoint = route[lastIdx - 1]; // Corner point (if exists)
393
+ const thirdLastPoint = route[lastIdx - 2]; // Original A* point
394
+
395
+ // Check if the third-to-last point is redundant
396
+ const sameX = Math.abs(thirdLastPoint.x - targetAnchor.x) < tolerance;
397
+ const sameY = Math.abs(thirdLastPoint.y - targetAnchor.y) < tolerance;
398
+ if (sameX || sameY) {
399
+ if (route.length > 3) {
400
+ const fourthLastPoint = route[lastIdx - 3];
401
+ const fourthToThird = Math.abs(fourthLastPoint.x - thirdLastPoint.x) < tolerance || Math.abs(fourthLastPoint.y - thirdLastPoint.y) < tolerance;
402
+ const thirdToSecond = Math.abs(thirdLastPoint.x - secondLastPoint.x) < tolerance || Math.abs(thirdLastPoint.y - secondLastPoint.y) < tolerance;
403
+ if (fourthToThird && thirdToSecond) {
404
+ route.splice(lastIdx - 2, 1); // Remove the third-to-last point
405
+ }
406
+ }
407
+ }
408
+ }
409
+
410
+ // Additional optimization: Remove unnecessary zigzag patterns near target
411
+ // Check for patterns like: (1360, 16) -> (815, 16) -> (815, -134) -> (800, -134)
412
+ // where (815, 16) and (815, -134) form a zigzag at the target edge
413
+ let i = 0;
414
+ while (i < route.length - 2) {
415
+ const p1 = route[i];
416
+ const p2 = route[i + 1];
417
+ const p3 = route[i + 2];
418
+
419
+ // Check if p2 is on the target bbox edge
420
+ const p2OnTargetLeftEdge = Math.abs(p2.x - targetBBox.x) < tolerance;
421
+ const p2OnTargetRightEdge = Math.abs(p2.x - (targetBBox.x + targetBBox.width)) < tolerance;
422
+ const p2OnTargetTopEdge = Math.abs(p2.y - targetBBox.y) < tolerance;
423
+ const p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBox.y + targetBBox.height)) < tolerance;
424
+ const p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
425
+ if (p2OnTargetEdge) {
426
+ // Check if p1 -> p2 -> p3 forms a zigzag
427
+ const p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
428
+ const p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
429
+ if (p1ToP2Horizontal && p2ToP3Vertical && i < route.length - 3) {
430
+ // We have horizontal -> vertical at target edge
431
+ // Check if we can skip p2 and p3
432
+ const p4 = route[i + 3];
433
+ const p3ToP4Horizontal = Math.abs(p3.y - p4.y) < tolerance;
434
+ if (p3ToP4Horizontal) {
435
+ // Pattern: horizontal -> vertical -> horizontal (zigzag)
436
+ route.splice(i + 1, 2); // Remove p2 and p3
437
+ continue;
438
+ }
439
+ }
440
+ }
441
+ i++;
442
+ }
443
+
444
+ // Add source and target anchors to route
445
+ const finalRoute = [sourceAnchor, ...route, targetAnchor];
446
+
447
+ // Convert to SVG path string
448
+ return (0, _svg.pointsToPath)(finalRoute, options.precision, options.borderRadius);
449
+ }
package/cjs/index.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ReactFlow Manhattan Router
3
+ *
4
+ * A Manhattan routing algorithm adapted for ReactFlow from AntV X6.
5
+ * Generates orthogonal (right-angle) paths that intelligently avoid obstacles.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { getManHattanPath } from './getManHattanPath';
10
+ export type { GetManHattanPathParams } from './getManHattanPath';
11
+ export type { Direction, InternalNode, NodeLookup, ManhattanRouterOptions, ResolvedOptions, } from './options';
12
+ export { Point, Rectangle, Line } from './geometry';
13
+ export { getGrid, align, snapToGrid, getDirectionAngle, getDirectionChange, getRectPoints, getCost, getKey, reconstructRoute, normalizePoint, getNodeDimensions, getNodePosition, } from './utils';
14
+ export type { NodeDimensions } from './utils';
15
+ export { pointsToPath, snapPathToGrid } from './svg';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAGhE,YAAY,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,sBAAsB,EACtB,eAAe,GAChB,MAAM,WAAW,CAAA;AAGlB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAGnD,OAAO,EACL,OAAO,EACP,KAAK,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,SAAS,CAAA;AAGhB,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA"}
package/cjs/index.js ADDED
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "Line", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _geometry.Line;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "Point", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _geometry.Point;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "Rectangle", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _geometry.Rectangle;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "align", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _utils.align;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "getCost", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _utils.getCost;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "getDirectionAngle", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _utils.getDirectionAngle;
40
+ }
41
+ });
42
+ Object.defineProperty(exports, "getDirectionChange", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _utils.getDirectionChange;
46
+ }
47
+ });
48
+ Object.defineProperty(exports, "getGrid", {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _utils.getGrid;
52
+ }
53
+ });
54
+ Object.defineProperty(exports, "getKey", {
55
+ enumerable: true,
56
+ get: function () {
57
+ return _utils.getKey;
58
+ }
59
+ });
60
+ Object.defineProperty(exports, "getManHattanPath", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _getManHattanPath.getManHattanPath;
64
+ }
65
+ });
66
+ Object.defineProperty(exports, "getNodeDimensions", {
67
+ enumerable: true,
68
+ get: function () {
69
+ return _utils.getNodeDimensions;
70
+ }
71
+ });
72
+ Object.defineProperty(exports, "getNodePosition", {
73
+ enumerable: true,
74
+ get: function () {
75
+ return _utils.getNodePosition;
76
+ }
77
+ });
78
+ Object.defineProperty(exports, "getRectPoints", {
79
+ enumerable: true,
80
+ get: function () {
81
+ return _utils.getRectPoints;
82
+ }
83
+ });
84
+ Object.defineProperty(exports, "normalizePoint", {
85
+ enumerable: true,
86
+ get: function () {
87
+ return _utils.normalizePoint;
88
+ }
89
+ });
90
+ Object.defineProperty(exports, "pointsToPath", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _svg.pointsToPath;
94
+ }
95
+ });
96
+ Object.defineProperty(exports, "reconstructRoute", {
97
+ enumerable: true,
98
+ get: function () {
99
+ return _utils.reconstructRoute;
100
+ }
101
+ });
102
+ Object.defineProperty(exports, "snapPathToGrid", {
103
+ enumerable: true,
104
+ get: function () {
105
+ return _svg.snapPathToGrid;
106
+ }
107
+ });
108
+ Object.defineProperty(exports, "snapToGrid", {
109
+ enumerable: true,
110
+ get: function () {
111
+ return _utils.snapToGrid;
112
+ }
113
+ });
114
+ var _getManHattanPath = require("./getManHattanPath");
115
+ var _geometry = require("./geometry");
116
+ var _utils = require("./utils");
117
+ var _svg = require("./svg");
@@ -0,0 +1,66 @@
1
+ import { Point } from '../geometry';
2
+ import type { ResolvedOptions, NodeLookup } from '../options';
3
+ /**
4
+ * ObstacleMap class for managing obstacles in pathfinding
5
+ * Feature: manhattan-optimization
6
+ *
7
+ * Uses QuadTree for efficient spatial queries (O(log n) instead of O(n))
8
+ * Includes query caching for repeated accessibility checks
9
+ */
10
+ export declare class ObstacleMap {
11
+ private options;
12
+ private quadTree;
13
+ private obstacles;
14
+ private sourceAnchor?;
15
+ private targetAnchor?;
16
+ private accessibilityCache;
17
+ private cacheHits;
18
+ private cacheMisses;
19
+ constructor(options: ResolvedOptions);
20
+ /**
21
+ * Get cache statistics for performance monitoring
22
+ */
23
+ getCacheStats(): {
24
+ hits: number;
25
+ misses: number;
26
+ hitRate: number;
27
+ };
28
+ /**
29
+ * Clear the accessibility cache
30
+ */
31
+ clearCache(): void;
32
+ /**
33
+ * Build obstacle map from node lookup using QuadTree
34
+ */
35
+ build(nodeLookup: NodeLookup, sourceNodeId: string, targetNodeId: string, sourceAnchor?: Point, targetAnchor?: Point): ObstacleMap;
36
+ /**
37
+ * Check if a point is accessible (not inside any obstacle)
38
+ * Uses QuadTree for efficient spatial queries with caching
39
+ */
40
+ isAccessible(point: Point, checkRadius?: number): boolean;
41
+ /**
42
+ * Check if an anchor point has sufficient clearance around it
43
+ * This ensures the path can start/end at the anchor without being blocked
44
+ */
45
+ hasAnchorClearance(anchor: Point, direction: 'top' | 'right' | 'bottom' | 'left'): boolean;
46
+ /**
47
+ * Check accessibility without using cache (for internal use)
48
+ */
49
+ private isAccessibleWithoutCache;
50
+ /**
51
+ * Find the nearest accessible point from an anchor in a given direction
52
+ * Uses binary search for efficiency
53
+ */
54
+ findNearestAccessiblePoint(anchor: Point, direction: 'top' | 'right' | 'bottom' | 'left', maxDistance: number): Point | null;
55
+ /**
56
+ * Batch check accessibility for multiple points (optimized)
57
+ * Returns array of booleans in same order as input points
58
+ */
59
+ areAccessible(points: Point[]): boolean[];
60
+ /**
61
+ * Check accessibility using binary search optimization
62
+ * Tries step -> step/2 -> step/4 -> ... -> 1px
63
+ */
64
+ private isAccessibleWithBinarySearch;
65
+ }
66
+ //# sourceMappingURL=ObstacleMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObstacleMap.d.ts","sourceRoot":"","sources":["../../src/obstacle/ObstacleMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAI7D;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,SAAS,CAAmD;IACpE,OAAO,CAAC,YAAY,CAAC,CAAO;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAO;IAG5B,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,WAAW,CAAY;gBAEnB,OAAO,EAAE,eAAe;IAIpC;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IASlE;;OAEG;IACH,UAAU,IAAI,IAAI;IAMlB;;OAEG;IACH,KAAK,CACH,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,KAAK,EACpB,YAAY,CAAC,EAAE,KAAK,GACnB,WAAW;IA8Cd;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,GAAE,MAAU,GAAG,OAAO;IAoD5D;;;OAGG;IACH,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO;IA0C1F;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAShC;;;OAGG;IACH,0BAA0B,CACxB,MAAM,EAAE,KAAK,EACb,SAAS,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,EAC9C,WAAW,EAAE,MAAM,GAClB,KAAK,GAAG,IAAI;IAyDf;;;OAGG;IACH,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE;IAIzC;;;OAGG;IACH,OAAO,CAAC,4BAA4B;CAqCrC"}