@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.
- package/cjs/geometry/Line.d.ts +21 -0
- package/cjs/geometry/Line.d.ts.map +1 -0
- package/cjs/geometry/Line.js +88 -0
- package/cjs/geometry/Point.d.ts +49 -0
- package/cjs/geometry/Point.d.ts.map +1 -0
- package/cjs/geometry/Point.js +94 -0
- package/cjs/geometry/Rectangle.d.ts +41 -0
- package/cjs/geometry/Rectangle.d.ts.map +1 -0
- package/cjs/geometry/Rectangle.js +65 -0
- package/cjs/geometry/collision.d.ts +15 -0
- package/cjs/geometry/collision.d.ts.map +1 -0
- package/cjs/geometry/collision.js +81 -0
- package/cjs/geometry/index.d.ts +5 -0
- package/cjs/geometry/index.d.ts.map +1 -0
- package/cjs/geometry/index.js +45 -0
- package/cjs/getManHattanPath.d.ts +53 -0
- package/cjs/getManHattanPath.d.ts.map +1 -0
- package/cjs/getManHattanPath.js +449 -0
- package/cjs/index.d.ts +16 -0
- package/cjs/index.d.ts.map +1 -0
- package/cjs/index.js +117 -0
- package/cjs/obstacle/ObstacleMap.d.ts +66 -0
- package/cjs/obstacle/ObstacleMap.d.ts.map +1 -0
- package/cjs/obstacle/ObstacleMap.js +328 -0
- package/cjs/obstacle/QuadTree.d.ts +119 -0
- package/cjs/obstacle/QuadTree.d.ts.map +1 -0
- package/cjs/obstacle/QuadTree.js +334 -0
- package/cjs/obstacle/index.d.ts +2 -0
- package/cjs/obstacle/index.d.ts.map +1 -0
- package/cjs/obstacle/index.js +12 -0
- package/cjs/options/defaults.d.ts +16 -0
- package/cjs/options/defaults.d.ts.map +1 -0
- package/cjs/options/defaults.js +39 -0
- package/cjs/options/index.d.ts +4 -0
- package/cjs/options/index.d.ts.map +1 -0
- package/cjs/options/index.js +38 -0
- package/cjs/options/resolver.d.ts +10 -0
- package/cjs/options/resolver.d.ts.map +1 -0
- package/cjs/options/resolver.js +248 -0
- package/cjs/options/types.d.ts +210 -0
- package/cjs/options/types.d.ts.map +1 -0
- package/cjs/options/types.js +5 -0
- package/cjs/pathfinder/PathCache.d.ts +92 -0
- package/cjs/pathfinder/PathCache.d.ts.map +1 -0
- package/cjs/pathfinder/PathCache.js +249 -0
- package/cjs/pathfinder/SortedSet.d.ts +35 -0
- package/cjs/pathfinder/SortedSet.d.ts.map +1 -0
- package/cjs/pathfinder/SortedSet.js +95 -0
- package/cjs/pathfinder/findRoute.d.ts +8 -0
- package/cjs/pathfinder/findRoute.d.ts.map +1 -0
- package/cjs/pathfinder/findRoute.js +395 -0
- package/cjs/pathfinder/index.d.ts +4 -0
- package/cjs/pathfinder/index.d.ts.map +1 -0
- package/cjs/pathfinder/index.js +44 -0
- package/cjs/svg/index.d.ts +3 -0
- package/cjs/svg/index.d.ts.map +1 -0
- package/cjs/svg/index.js +31 -0
- package/cjs/svg/pathConverter.d.ts +23 -0
- package/cjs/svg/pathConverter.d.ts.map +1 -0
- package/cjs/svg/pathConverter.js +285 -0
- package/cjs/svg/pathParser.d.ts +11 -0
- package/cjs/svg/pathParser.d.ts.map +1 -0
- package/cjs/svg/pathParser.js +76 -0
- package/cjs/utils/AdaptiveStepCalculator.d.ts +90 -0
- package/cjs/utils/AdaptiveStepCalculator.d.ts.map +1 -0
- package/cjs/utils/AdaptiveStepCalculator.js +224 -0
- package/cjs/utils/ErrorRecovery.d.ts +182 -0
- package/cjs/utils/ErrorRecovery.d.ts.map +1 -0
- package/cjs/utils/ErrorRecovery.js +413 -0
- package/cjs/utils/GlobalGrid.d.ts +99 -0
- package/cjs/utils/GlobalGrid.d.ts.map +1 -0
- package/cjs/utils/GlobalGrid.js +224 -0
- package/cjs/utils/PerformanceMonitor.d.ts +139 -0
- package/cjs/utils/PerformanceMonitor.d.ts.map +1 -0
- package/cjs/utils/PerformanceMonitor.js +305 -0
- package/cjs/utils/direction.d.ts +24 -0
- package/cjs/utils/direction.d.ts.map +1 -0
- package/cjs/utils/direction.js +54 -0
- package/cjs/utils/getAnchorPoints.d.ts +15 -0
- package/cjs/utils/getAnchorPoints.d.ts.map +1 -0
- package/cjs/utils/getAnchorPoints.js +71 -0
- package/cjs/utils/grid.d.ts +42 -0
- package/cjs/utils/grid.d.ts.map +1 -0
- package/cjs/utils/grid.js +73 -0
- package/cjs/utils/heuristics.d.ts +61 -0
- package/cjs/utils/heuristics.d.ts.map +1 -0
- package/cjs/utils/heuristics.js +141 -0
- package/cjs/utils/index.d.ts +14 -0
- package/cjs/utils/index.d.ts.map +1 -0
- package/cjs/utils/index.js +148 -0
- package/cjs/utils/node.d.ts +27 -0
- package/cjs/utils/node.d.ts.map +1 -0
- package/cjs/utils/node.js +36 -0
- package/cjs/utils/pathProcessing.d.ts +45 -0
- package/cjs/utils/pathProcessing.d.ts.map +1 -0
- package/cjs/utils/pathProcessing.js +270 -0
- package/cjs/utils/pathValidation.d.ts +11 -0
- package/cjs/utils/pathValidation.d.ts.map +1 -0
- package/cjs/utils/pathValidation.js +129 -0
- package/cjs/utils/rect.d.ts +9 -0
- package/cjs/utils/rect.d.ts.map +1 -0
- package/cjs/utils/rect.js +110 -0
- package/cjs/utils/route.d.ts +19 -0
- package/cjs/utils/route.d.ts.map +1 -0
- package/cjs/utils/route.js +92 -0
- package/esm/geometry/Line.d.ts +21 -0
- package/esm/geometry/Line.d.ts.map +1 -0
- package/esm/geometry/Point.d.ts +49 -0
- package/esm/geometry/Point.d.ts.map +1 -0
- package/esm/geometry/Rectangle.d.ts +41 -0
- package/esm/geometry/Rectangle.d.ts.map +1 -0
- package/esm/geometry/collision.d.ts +15 -0
- package/esm/geometry/collision.d.ts.map +1 -0
- package/esm/geometry/index.d.ts +5 -0
- package/esm/geometry/index.d.ts.map +1 -0
- package/esm/getManHattanPath.d.ts +53 -0
- package/esm/getManHattanPath.d.ts.map +1 -0
- package/esm/index.d.ts +16 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/obstacle/ObstacleMap.d.ts +66 -0
- package/esm/obstacle/ObstacleMap.d.ts.map +1 -0
- package/esm/obstacle/QuadTree.d.ts +119 -0
- package/esm/obstacle/QuadTree.d.ts.map +1 -0
- package/esm/obstacle/index.d.ts +2 -0
- package/esm/obstacle/index.d.ts.map +1 -0
- package/esm/options/defaults.d.ts +16 -0
- package/esm/options/defaults.d.ts.map +1 -0
- package/esm/options/index.d.ts +4 -0
- package/esm/options/index.d.ts.map +1 -0
- package/esm/options/resolver.d.ts +10 -0
- package/esm/options/resolver.d.ts.map +1 -0
- package/esm/options/types.d.ts +210 -0
- package/esm/options/types.d.ts.map +1 -0
- package/esm/pathfinder/PathCache.d.ts +92 -0
- package/esm/pathfinder/PathCache.d.ts.map +1 -0
- package/esm/pathfinder/SortedSet.d.ts +35 -0
- package/esm/pathfinder/SortedSet.d.ts.map +1 -0
- package/esm/pathfinder/findRoute.d.ts +8 -0
- package/esm/pathfinder/findRoute.d.ts.map +1 -0
- package/esm/pathfinder/index.d.ts +4 -0
- package/esm/pathfinder/index.d.ts.map +1 -0
- package/esm/svg/index.d.ts +3 -0
- package/esm/svg/index.d.ts.map +1 -0
- package/esm/svg/pathConverter.d.ts +23 -0
- package/esm/svg/pathConverter.d.ts.map +1 -0
- package/esm/svg/pathParser.d.ts +11 -0
- package/esm/svg/pathParser.d.ts.map +1 -0
- package/esm/utils/AdaptiveStepCalculator.d.ts +90 -0
- package/esm/utils/AdaptiveStepCalculator.d.ts.map +1 -0
- package/esm/utils/ErrorRecovery.d.ts +182 -0
- package/esm/utils/ErrorRecovery.d.ts.map +1 -0
- package/esm/utils/GlobalGrid.d.ts +99 -0
- package/esm/utils/GlobalGrid.d.ts.map +1 -0
- package/esm/utils/PerformanceMonitor.d.ts +139 -0
- package/esm/utils/PerformanceMonitor.d.ts.map +1 -0
- package/esm/utils/direction.d.ts +24 -0
- package/esm/utils/direction.d.ts.map +1 -0
- package/esm/utils/getAnchorPoints.d.ts +15 -0
- package/esm/utils/getAnchorPoints.d.ts.map +1 -0
- package/esm/utils/grid.d.ts +42 -0
- package/esm/utils/grid.d.ts.map +1 -0
- package/esm/utils/heuristics.d.ts +61 -0
- package/esm/utils/heuristics.d.ts.map +1 -0
- package/esm/utils/index.d.ts +14 -0
- package/esm/utils/index.d.ts.map +1 -0
- package/esm/utils/node.d.ts +27 -0
- package/esm/utils/node.d.ts.map +1 -0
- package/esm/utils/pathProcessing.d.ts +45 -0
- package/esm/utils/pathProcessing.d.ts.map +1 -0
- package/esm/utils/pathValidation.d.ts +11 -0
- package/esm/utils/pathValidation.d.ts.map +1 -0
- package/esm/utils/rect.d.ts +9 -0
- package/esm/utils/rect.d.ts.map +1 -0
- package/esm/utils/route.d.ts +19 -0
- package/esm/utils/route.d.ts.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/cjs/svg/index.js
ADDED
|
@@ -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"}
|