@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,395 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.findRoute = findRoute;
7
+ var _geometry = require("../geometry");
8
+ var _SortedSet = require("./SortedSet");
9
+ var _utils = require("../utils");
10
+ /**
11
+ * Generate smart points based on position using extensionDistance and binary search
12
+ *
13
+ * Algorithm:
14
+ * 1. First try extensionDistance in the specified direction
15
+ * 2. If blocked, use binary search starting from step, halving until finding accessible point
16
+ * 3. For target points, approach from opposite direction
17
+ */
18
+ function generateSmartPoints(anchor, bbox, position, grid, map, options, isTarget = false) {
19
+ const directionMap = {
20
+ 'right': {
21
+ x: 1,
22
+ y: 0
23
+ },
24
+ 'left': {
25
+ x: -1,
26
+ y: 0
27
+ },
28
+ 'top': {
29
+ x: 0,
30
+ y: -1
31
+ },
32
+ 'bottom': {
33
+ x: 0,
34
+ y: 1
35
+ }
36
+ };
37
+ const direction = directionMap[position];
38
+ if (!direction) {
39
+ console.warn(`[generateSmartPoints] Unknown position: ${position}, falling back to anchor`);
40
+ return [anchor];
41
+ }
42
+
43
+ // Both source and target extend in the specified direction
44
+ // - Source: extends away from node in sourcePosition direction
45
+ // - Target: extends away from node in targetPosition direction (path approaches from this direction)
46
+ const actualDirection = direction;
47
+ const points = [];
48
+
49
+ // 1. First try extensionDistance
50
+ const extensionPoint = new _geometry.Point(anchor.x + actualDirection.x * options.extensionDistance, anchor.y + actualDirection.y * options.extensionDistance).round(options.precision);
51
+ if (map.isAccessible(extensionPoint)) {
52
+ points.push(extensionPoint);
53
+ return points;
54
+ }
55
+
56
+ // 2. Step-based search with binary refinement
57
+ // First, extend outward by step increments until we find an accessible point
58
+ let stepMultiplier = 1;
59
+ let maxSteps = 20; // Prevent infinite loop
60
+ let foundAccessibleDistance = -1;
61
+ while (stepMultiplier <= maxSteps) {
62
+ const distance = stepMultiplier * options.step;
63
+ const testPoint = new _geometry.Point(anchor.x + actualDirection.x * distance, anchor.y + actualDirection.y * distance).round(options.precision);
64
+ if (map.isAccessible(testPoint)) {
65
+ foundAccessibleDistance = distance;
66
+ break;
67
+ }
68
+ stepMultiplier++;
69
+ }
70
+
71
+ // 3. If we found an accessible point, refine by binary search within the last step interval
72
+ if (foundAccessibleDistance > 0) {
73
+ const outerDistance = foundAccessibleDistance;
74
+ const innerDistance = foundAccessibleDistance - options.step;
75
+
76
+ // Binary search within the last step interval to find the closest accessible point
77
+ let left = innerDistance;
78
+ let right = outerDistance;
79
+ let bestDistance = outerDistance;
80
+ let binarySearchIterations = 0;
81
+ const maxBinarySearchIterations = 50; // Prevent infinite loop
82
+
83
+ // Binary search with precision of 1px
84
+ while (right - left > 1 && binarySearchIterations < maxBinarySearchIterations) {
85
+ binarySearchIterations++;
86
+ const mid = (left + right) / 2;
87
+ const testPoint = new _geometry.Point(anchor.x + actualDirection.x * mid, anchor.y + actualDirection.y * mid).round(options.precision);
88
+ if (map.isAccessible(testPoint)) {
89
+ bestDistance = mid;
90
+ right = mid;
91
+ } else {
92
+ left = mid;
93
+ }
94
+ }
95
+
96
+ // Use the best distance found
97
+ const finalPoint = new _geometry.Point(anchor.x + actualDirection.x * bestDistance, anchor.y + actualDirection.y * bestDistance).round(options.precision);
98
+ points.push(finalPoint);
99
+ } else {
100
+ // 4. If no accessible point found after maxSteps, use anchor as fallback
101
+ points.push(anchor);
102
+ }
103
+ return points;
104
+ }
105
+
106
+ /**
107
+ * Find route between two points using A* algorithm
108
+ */
109
+ function findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, map, options) {
110
+ const precision = options.precision;
111
+
112
+ // Round anchor points
113
+ const sourceEndpoint = (0, _utils.round)(sourceAnchor.clone(), precision);
114
+ const targetEndpoint = (0, _utils.round)(targetAnchor.clone(), precision);
115
+
116
+ // Get grid for this route
117
+ const grid = (0, _utils.getGrid)(options.step, sourceEndpoint, targetEndpoint);
118
+
119
+ // DEBUG: Check for invalid grid values
120
+ if (grid.x === 0 || grid.y === 0 || !isFinite(grid.x) || !isFinite(grid.y)) {
121
+ console.error('[findRoute] INVALID GRID:', grid, 'step:', options.step);
122
+ return _utils.ErrorRecovery.generateFallbackPath(sourceEndpoint, targetEndpoint);
123
+ }
124
+
125
+ // Get pathfinding points
126
+ const startPoint = sourceEndpoint;
127
+ const endPoint = targetEndpoint;
128
+
129
+ // Get start and end points around rectangles
130
+ // Use smart point generation based on position if available
131
+ let startPoints;
132
+ let endPoints;
133
+
134
+ // Generate smart start points based on sourcePosition
135
+ if (options.sourcePosition) {
136
+ startPoints = generateSmartPoints(startPoint, sourceBBox, options.sourcePosition, grid, map, options, false);
137
+ } else {
138
+ startPoints = (0, _utils.getRectPoints)(startPoint, sourceBBox, options.startDirections, grid, options);
139
+ // Take into account only accessible rect points
140
+ startPoints = startPoints.filter(p => map.isAccessible(p));
141
+ }
142
+
143
+ // Generate smart end points based on targetPosition
144
+ if (options.targetPosition) {
145
+ endPoints = generateSmartPoints(targetEndpoint, targetBBox, options.targetPosition, grid, map, options, true);
146
+ } else {
147
+ endPoints = (0, _utils.getRectPoints)(targetEndpoint, targetBBox, options.endDirections, grid, options);
148
+ // Take into account only accessible rect points
149
+ endPoints = endPoints.filter(p => map.isAccessible(p));
150
+ }
151
+
152
+ // Ensure we always have at least the anchor points
153
+ // This handles edge cases where anchor is on the node boundary
154
+ if (startPoints.length === 0) {
155
+ startPoints = [(0, _utils.round)(startPoint, precision)];
156
+ }
157
+
158
+ // CRITICAL: If no accessible end points found, use fallback immediately
159
+ // This prevents A* from running 5000 iterations searching for unreachable points
160
+ if (endPoints.length === 0) {
161
+ return _utils.ErrorRecovery.generateFallbackPath(startPoint, endPoint);
162
+ }
163
+
164
+ // Initialize A* data structures
165
+ const openSet = new _SortedSet.SortedSet();
166
+ const points = new Map();
167
+ const parents = new Map();
168
+ const costs = new Map();
169
+
170
+ // Add all start points to open set
171
+ for (const startPoint of startPoints) {
172
+ const key = (0, _utils.getKey)(startPoint);
173
+ openSet.add(key, (0, _utils.getCost)(startPoint, endPoints));
174
+ points.set(key, startPoint);
175
+ costs.set(key, 0);
176
+ }
177
+ const previousRouteDirectionAngle = options.previousDirectionAngle;
178
+ const isPathBeginning = previousRouteDirectionAngle === undefined;
179
+
180
+ // Get directions with grid offsets
181
+ const directions = (0, _utils.getGridOffsets)(grid, options);
182
+ const numDirections = directions.length;
183
+
184
+ // Create set of end point keys for quick lookup
185
+ const endPointsKeys = new Set(endPoints.map(p => (0, _utils.getKey)(p)));
186
+
187
+ // Check if start and end points are the same
188
+ const sameStartEndPoints = startPoints.length === endPoints.length && startPoints.every((sp, i) => sp.equals(endPoints[i]));
189
+
190
+ // Calculate optimal path cost estimate for early termination
191
+ const optimalCostEstimate = startPoint.manhattanDistance(endPoint);
192
+ const earlyTerminationThreshold = options.performance?.earlyTermination ? 1.5 : Infinity;
193
+ let bestPathFound = null;
194
+ let bestPathCost = Infinity;
195
+
196
+ // Main A* loop
197
+ const maxIterations = options.maxLoopCount;
198
+ let iterationCount = 0;
199
+
200
+ // DEBUG: Log loop start
201
+ const loopStartTime = performance.now();
202
+ while (!openSet.isEmpty() && iterationCount < maxIterations) {
203
+ iterationCount++;
204
+
205
+ // DEBUG: Log every 10 iterations
206
+ if (iterationCount % 10 === 0) {
207
+ const elapsed = performance.now() - loopStartTime;
208
+ if (elapsed > 1000) {
209
+ console.error(`[findRoute] Loop running for ${elapsed.toFixed(0)}ms, iteration ${iterationCount}`);
210
+ }
211
+ }
212
+
213
+ // Early termination: if we've searched too many points without finding a path,
214
+ // the path is likely blocked or very complex - use fallback
215
+ if (iterationCount > 2000 && bestPathFound === null) {
216
+ return _utils.ErrorRecovery.generateFallbackPath(startPoint, endPoint);
217
+ }
218
+
219
+ // Get the closest item and mark it CLOSED
220
+ const currentKey = openSet.pop();
221
+ if (!currentKey) break;
222
+ const currentPoint = points.get(currentKey);
223
+ const currentParent = parents.get(currentKey);
224
+ const currentCost = costs.get(currentKey);
225
+ const isStartPoint = currentPoint.equals(startPoint);
226
+ const isRouteBeginning = currentParent === undefined;
227
+
228
+ // Calculate previous direction angle
229
+ let previousDirectionAngle;
230
+ if (!isRouteBeginning) {
231
+ previousDirectionAngle = (0, _utils.getDirectionAngle)(currentParent, currentPoint, numDirections, grid, options);
232
+ } else if (!isPathBeginning) {
233
+ previousDirectionAngle = previousRouteDirectionAngle;
234
+ } else if (!isStartPoint) {
235
+ previousDirectionAngle = (0, _utils.getDirectionAngle)(startPoint, currentPoint, numDirections, grid, options);
236
+ } else {
237
+ previousDirectionAngle = null;
238
+ }
239
+
240
+ // Check if we reached any endpoint
241
+ const skipEndCheck = isRouteBeginning && sameStartEndPoints;
242
+ if (!skipEndCheck && endPointsKeys.has(currentKey)) {
243
+ const route = (0, _utils.reconstructRoute)(parents, points, currentPoint, startPoint, endPoint);
244
+
245
+ // Early termination: if path cost is within threshold of optimal, return immediately
246
+ if (currentCost <= optimalCostEstimate * earlyTerminationThreshold) {
247
+ options.previousDirectionAngle = previousDirectionAngle;
248
+ return route;
249
+ }
250
+
251
+ // Track best path found so far
252
+ if (currentCost < bestPathCost) {
253
+ bestPathCost = currentCost;
254
+ bestPathFound = route;
255
+ }
256
+ options.previousDirectionAngle = previousDirectionAngle;
257
+ return route;
258
+ }
259
+
260
+ // Explore neighbors in all directions
261
+ for (const direction of directions) {
262
+ const directionAngle = direction.angle;
263
+ const directionChange = (0, _utils.getDirectionChange)(previousDirectionAngle ?? 0, directionAngle);
264
+
265
+ // Don't use the point if direction changed too rapidly
266
+ if (!(isPathBeginning && isStartPoint) && directionChange > options.maxDirectionChange) {
267
+ continue;
268
+ }
269
+
270
+ // Calculate neighbor point and align to global grid
271
+ const rawNeighbor = currentPoint.clone().translate(direction.gridOffsetX || 0, direction.gridOffsetY || 0);
272
+
273
+ // Align to global grid for consistent path alignment
274
+ const neighborPoint = new _geometry.Point(Math.round(rawNeighbor.x / grid.x) * grid.x, Math.round(rawNeighbor.y / grid.y) * grid.y).round(precision);
275
+ const neighborKey = (0, _utils.getKey)(neighborPoint);
276
+
277
+ // Skip if closed or not accessible
278
+ if (openSet.isClose(neighborKey) || !map.isAccessible(neighborPoint)) {
279
+ continue;
280
+ }
281
+
282
+ // Check if we can reach any end point directly from this neighbor
283
+ // This allows connecting to end points that are not on the grid
284
+ let canReachEndPoint = false;
285
+ let reachableEndPoint = null;
286
+ for (const endPt of endPoints) {
287
+ const distanceToEnd = neighborPoint.manhattanDistance(endPt);
288
+
289
+ // If close enough to end point (within step distance), try direct connection
290
+ if (distanceToEnd < options.step * 1.5) {
291
+ // Check if we can move directly to the end point
292
+ const dx = endPt.x - neighborPoint.x;
293
+ const dy = endPt.y - neighborPoint.y;
294
+
295
+ // Allow direct connection if it's orthogonal or close to orthogonal
296
+ const isOrthogonal = Math.abs(dx) < 0.1 || Math.abs(dy) < 0.1;
297
+ if (isOrthogonal) {
298
+ const accessible = map.isAccessible(endPt);
299
+ if (accessible) {
300
+ canReachEndPoint = true;
301
+ reachableEndPoint = endPt;
302
+ break;
303
+ }
304
+ }
305
+ }
306
+ }
307
+
308
+ // If we can reach an end point directly, add it as the final step
309
+ if (canReachEndPoint && reachableEndPoint) {
310
+ const endKey = (0, _utils.getKey)(reachableEndPoint);
311
+ const endCost = neighborPoint.manhattanDistance(reachableEndPoint);
312
+ const totalCost = currentCost + direction.cost + endCost;
313
+ if (!openSet.isOpen(endKey) || totalCost < (costs.get(endKey) || Infinity)) {
314
+ points.set(endKey, reachableEndPoint);
315
+
316
+ // CRITICAL FIX: Don't set parent if neighbor and endpoint are the same
317
+ // This prevents circular reference in reconstructRoute
318
+ if (!neighborPoint.equals(reachableEndPoint)) {
319
+ parents.set(endKey, neighborPoint);
320
+ } else {
321
+ // If neighbor IS the endpoint, use currentPoint as parent
322
+ parents.set(endKey, currentPoint);
323
+ }
324
+ costs.set(endKey, totalCost);
325
+
326
+ // Also add the neighbor point if not already added
327
+ if (!points.has(neighborKey)) {
328
+ points.set(neighborKey, neighborPoint);
329
+ parents.set(neighborKey, currentPoint);
330
+ costs.set(neighborKey, currentCost + direction.cost);
331
+ }
332
+
333
+ // Check if this is our target end point
334
+ if (endPointsKeys.has(endKey)) {
335
+ const route = (0, _utils.reconstructRoute)(parents, points, reachableEndPoint, startPoint, endPoint);
336
+
337
+ // Early termination check
338
+ if (totalCost <= optimalCostEstimate * earlyTerminationThreshold) {
339
+ options.previousDirectionAngle = directionAngle;
340
+ return route;
341
+ }
342
+
343
+ // Track best path
344
+ if (totalCost < bestPathCost) {
345
+ bestPathCost = totalCost;
346
+ bestPathFound = route;
347
+ }
348
+ options.previousDirectionAngle = directionAngle;
349
+ return route;
350
+ }
351
+ }
352
+ }
353
+
354
+ // Check if neighbor is an end point (exact match)
355
+ if (endPointsKeys.has(neighborKey)) {
356
+ const isEndPoint = neighborPoint.equals(endPoint);
357
+ if (!isEndPoint) {
358
+ const endDirectionAngle = (0, _utils.getDirectionAngle)(neighborPoint, endPoint, numDirections, grid, options);
359
+ const endDirectionChange = (0, _utils.getDirectionChange)(directionAngle, endDirectionAngle);
360
+ if (endDirectionChange > options.maxDirectionChange) {
361
+ continue;
362
+ }
363
+ }
364
+ }
365
+
366
+ // Calculate costs
367
+ const neighborCost = direction.cost;
368
+ const neighborPenalty = isStartPoint ? 0 : options.penalties[directionChange] || 0;
369
+ const costFromStart = currentCost + neighborCost + neighborPenalty;
370
+
371
+ // Update if not in open set or found better path
372
+ if (!openSet.isOpen(neighborKey) || costFromStart < (costs.get(neighborKey) || Infinity)) {
373
+ points.set(neighborKey, neighborPoint);
374
+ parents.set(neighborKey, currentPoint);
375
+ costs.set(neighborKey, costFromStart);
376
+ openSet.add(neighborKey, costFromStart + (0, _utils.getCost)(neighborPoint, endPoints));
377
+ }
378
+ }
379
+
380
+ // Loop completed - check iteration count
381
+ }
382
+
383
+ // Return best path found if any
384
+ if (bestPathFound) {
385
+ return bestPathFound;
386
+ }
387
+
388
+ // No path found, try fallback
389
+ if (options.fallbackRoute) {
390
+ return options.fallbackRoute(startPoint, endPoint);
391
+ }
392
+
393
+ // Use ErrorRecovery to generate fallback path
394
+ return _utils.ErrorRecovery.generateFallbackPath(startPoint, endPoint);
395
+ }
@@ -0,0 +1,4 @@
1
+ export { SortedSet } from './SortedSet';
2
+ export { findRoute } from './findRoute';
3
+ export { PathCache, generateCacheKey, getGlobalPathCache, resetGlobalPathCache, type CacheEntry, type PathCacheConfig, } from './PathCache';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pathfinder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EACL,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,UAAU,EACf,KAAK,eAAe,GACrB,MAAM,aAAa,CAAA"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "PathCache", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _PathCache.PathCache;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "SortedSet", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _SortedSet.SortedSet;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "findRoute", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _findRoute.findRoute;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "generateCacheKey", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _PathCache.generateCacheKey;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "getGlobalPathCache", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _PathCache.getGlobalPathCache;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "resetGlobalPathCache", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _PathCache.resetGlobalPathCache;
40
+ }
41
+ });
42
+ var _SortedSet = require("./SortedSet");
43
+ var _findRoute = require("./findRoute");
44
+ var _PathCache = require("./PathCache");
@@ -0,0 +1,3 @@
1
+ export { pointsToPath, snapPathToGrid } from './pathConverter';
2
+ export { parseSVGPath, simplifyPath } from './pathParser';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svg/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "parseSVGPath", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _pathParser.parseSVGPath;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "pointsToPath", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _pathConverter.pointsToPath;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "simplifyPath", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _pathParser.simplifyPath;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "snapPathToGrid", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _pathConverter.snapPathToGrid;
28
+ }
29
+ });
30
+ var _pathConverter = require("./pathConverter");
31
+ var _pathParser = require("./pathParser");
@@ -0,0 +1,23 @@
1
+ import { Point } from '../geometry';
2
+ /**
3
+ * Convert array of points to SVG path string
4
+ */
5
+ export declare function pointsToPath(points: Point[], precision: number, borderRadius?: number): string;
6
+ /**
7
+ * Convert array of points to SVG path string with cubic bezier curves
8
+ * Provides smoother corners than quadratic bezier
9
+ */
10
+ export declare function pointsToPathCubic(points: Point[], precision: number, borderRadius?: number): string;
11
+ /**
12
+ * Snap path to grid by aligning consecutive points
13
+ */
14
+ export declare function snapPathToGrid(points: Point[], gridSize: number): Point[];
15
+ /**
16
+ * Check if an SVG path contains rounded corners (Q or C commands)
17
+ */
18
+ export declare function hasRoundedCorners(path: string): boolean;
19
+ /**
20
+ * Count the number of corners in a path
21
+ */
22
+ export declare function countCorners(points: Point[]): number;
23
+ //# sourceMappingURL=pathConverter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pathConverter.d.ts","sourceRoot":"","sources":["../../src/svg/pathConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AA4CnC;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,GAAG,MAAM,CAwFjG;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,GAAG,MAAM,CAgGtG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,CAgCzE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAUpD"}