@rxflow/manhattan 0.0.2-alpha.8 → 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/README.md +214 -10
- package/cjs/getManHattanPath.d.ts.map +1 -1
- package/cjs/getManHattanPath.js +77 -46
- package/cjs/obstacle/ObstacleMap.d.ts +41 -9
- package/cjs/obstacle/ObstacleMap.d.ts.map +1 -1
- package/cjs/obstacle/ObstacleMap.js +201 -96
- 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/options/defaults.d.ts +1 -1
- package/cjs/options/defaults.d.ts.map +1 -1
- package/cjs/options/resolver.d.ts.map +1 -1
- package/cjs/options/resolver.js +145 -17
- package/cjs/options/types.d.ts +41 -0
- package/cjs/options/types.d.ts.map +1 -1
- 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/findRoute.d.ts.map +1 -1
- package/cjs/pathfinder/findRoute.js +96 -31
- package/cjs/pathfinder/index.d.ts +1 -0
- package/cjs/pathfinder/index.d.ts.map +1 -1
- package/cjs/pathfinder/index.js +26 -1
- package/cjs/svg/pathConverter.d.ts +13 -0
- package/cjs/svg/pathConverter.d.ts.map +1 -1
- package/cjs/svg/pathConverter.js +170 -1
- 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/getAnchorPoints.d.ts.map +1 -1
- package/cjs/utils/getAnchorPoints.js +0 -4
- package/cjs/utils/grid.d.ts +15 -0
- package/cjs/utils/grid.d.ts.map +1 -1
- package/cjs/utils/grid.js +19 -12
- 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 +6 -0
- package/cjs/utils/index.d.ts.map +1 -1
- package/cjs/utils/index.js +66 -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.map +1 -1
- package/cjs/utils/pathValidation.js +0 -1
- package/cjs/utils/rect.d.ts.map +1 -1
- package/cjs/utils/rect.js +7 -0
- package/cjs/utils/route.d.ts.map +1 -1
- package/cjs/utils/route.js +18 -2
- package/esm/getManHattanPath.d.ts.map +1 -1
- package/esm/getManHattanPath.js +92 -69
- package/esm/obstacle/ObstacleMap.d.ts +41 -9
- package/esm/obstacle/ObstacleMap.d.ts.map +1 -1
- package/esm/obstacle/ObstacleMap.js +218 -99
- package/esm/obstacle/QuadTree.d.ts +119 -0
- package/esm/obstacle/QuadTree.d.ts.map +1 -0
- package/esm/obstacle/QuadTree.js +488 -0
- package/esm/options/defaults.d.ts +1 -1
- package/esm/options/defaults.d.ts.map +1 -1
- package/esm/options/resolver.d.ts.map +1 -1
- package/esm/options/resolver.js +147 -18
- package/esm/options/types.d.ts +41 -0
- package/esm/options/types.d.ts.map +1 -1
- package/esm/pathfinder/PathCache.d.ts +92 -0
- package/esm/pathfinder/PathCache.d.ts.map +1 -0
- package/esm/pathfinder/PathCache.js +278 -0
- package/esm/pathfinder/findRoute.d.ts.map +1 -1
- package/esm/pathfinder/findRoute.js +98 -44
- package/esm/pathfinder/index.d.ts +1 -0
- package/esm/pathfinder/index.d.ts.map +1 -1
- package/esm/pathfinder/index.js +2 -1
- package/esm/svg/pathConverter.d.ts +13 -0
- package/esm/svg/pathConverter.d.ts.map +1 -1
- package/esm/svg/pathConverter.js +170 -1
- package/esm/utils/AdaptiveStepCalculator.d.ts +90 -0
- package/esm/utils/AdaptiveStepCalculator.d.ts.map +1 -0
- package/esm/utils/AdaptiveStepCalculator.js +252 -0
- package/esm/utils/ErrorRecovery.d.ts +182 -0
- package/esm/utils/ErrorRecovery.d.ts.map +1 -0
- package/esm/utils/ErrorRecovery.js +499 -0
- package/esm/utils/GlobalGrid.d.ts +99 -0
- package/esm/utils/GlobalGrid.d.ts.map +1 -0
- package/esm/utils/GlobalGrid.js +259 -0
- package/esm/utils/PerformanceMonitor.d.ts +139 -0
- package/esm/utils/PerformanceMonitor.d.ts.map +1 -0
- package/esm/utils/PerformanceMonitor.js +360 -0
- package/esm/utils/getAnchorPoints.d.ts.map +1 -1
- package/esm/utils/getAnchorPoints.js +0 -4
- package/esm/utils/grid.d.ts +15 -0
- package/esm/utils/grid.d.ts.map +1 -1
- package/esm/utils/grid.js +18 -13
- package/esm/utils/heuristics.d.ts +61 -0
- package/esm/utils/heuristics.d.ts.map +1 -0
- package/esm/utils/heuristics.js +144 -0
- package/esm/utils/index.d.ts +6 -0
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +7 -1
- package/esm/utils/pathProcessing.d.ts +45 -0
- package/esm/utils/pathProcessing.d.ts.map +1 -0
- package/esm/utils/pathProcessing.js +270 -0
- package/esm/utils/pathValidation.d.ts.map +1 -1
- package/esm/utils/pathValidation.js +0 -1
- package/esm/utils/rect.d.ts.map +1 -1
- package/esm/utils/rect.js +11 -4
- package/esm/utils/route.d.ts.map +1 -1
- package/esm/utils/route.js +18 -2
- package/package.json +10 -2
package/esm/getManHattanPath.js
CHANGED
|
@@ -14,13 +14,13 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
14
14
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
15
15
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
16
16
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
17
|
-
import { getSmoothStepPath } from '@xyflow/react';
|
|
17
|
+
import { getSmoothStepPath, Position } from '@xyflow/react';
|
|
18
18
|
import { Point, Rectangle } from "./geometry";
|
|
19
19
|
import { ObstacleMap } from "./obstacle";
|
|
20
20
|
import { resolveOptions } from "./options";
|
|
21
21
|
import { findRoute } from "./pathfinder";
|
|
22
22
|
import { pointsToPath, parseSVGPath } from "./svg";
|
|
23
|
-
import { getNodeDimensions, getNodePosition, pathIntersectsObstacles } from "./utils";
|
|
23
|
+
import { getNodeDimensions, getNodePosition, pathIntersectsObstacles, ErrorRecovery } from "./utils";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Parameters for getManHattanPath function
|
|
@@ -78,10 +78,12 @@ export function getManHattanPath(params) {
|
|
|
78
78
|
var targetNode = nodeLookup.get(targetNodeId);
|
|
79
79
|
if (!sourceNode || !targetNode) {
|
|
80
80
|
// Fallback to simple straight line if nodes not found
|
|
81
|
-
console.warn('Source or target node not found in nodeLookup');
|
|
81
|
+
console.warn('[getManHattanPath] Source or target node not found in nodeLookup');
|
|
82
82
|
var start = new Point(sourceX, sourceY);
|
|
83
83
|
var end = new Point(targetX, targetY);
|
|
84
|
-
|
|
84
|
+
// Use ErrorRecovery to generate a proper fallback path
|
|
85
|
+
var fallbackPath = ErrorRecovery.generateFallbackPath(start, end);
|
|
86
|
+
return pointsToPath(fallbackPath, options.precision, options.borderRadius);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
// Get node dimensions using ReactFlow's priority logic
|
|
@@ -118,10 +120,8 @@ export function getManHattanPath(params) {
|
|
|
118
120
|
|
|
119
121
|
// Check if smooth step path intersects with any obstacles
|
|
120
122
|
if (smoothStepPoints.length > 0 && !pathIntersectsObstacles(smoothStepPoints, nodeLookup)) {
|
|
121
|
-
console.log('[getManHattanPath] Using ReactFlow getSmoothStepPath (no obstacles)');
|
|
122
123
|
return smoothStepPath;
|
|
123
124
|
}
|
|
124
|
-
console.log('[getManHattanPath] SmoothStepPath intersects obstacles, using Manhattan routing');
|
|
125
125
|
|
|
126
126
|
// Build obstacle map with anchor information
|
|
127
127
|
var obstacleMap = new ObstacleMap(options).build(nodeLookup, sourceNodeId, targetNodeId, sourceAnchor, targetAnchor);
|
|
@@ -129,27 +129,80 @@ export function getManHattanPath(params) {
|
|
|
129
129
|
// Find route
|
|
130
130
|
var route = findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, obstacleMap, options);
|
|
131
131
|
|
|
132
|
-
// Fallback to
|
|
132
|
+
// Fallback to Z-shaped path if no route found
|
|
133
133
|
if (!route) {
|
|
134
|
-
console.warn('Unable to find Manhattan route, using
|
|
135
|
-
route =
|
|
134
|
+
console.warn('[getManHattanPath] Unable to find Manhattan route, using fallback path');
|
|
135
|
+
route = ErrorRecovery.generateFallbackPath(sourceAnchor, targetAnchor);
|
|
136
136
|
}
|
|
137
|
-
console.log('[getManHattanPath] Route from findRoute:', route.map(function (p) {
|
|
138
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
139
|
-
}));
|
|
140
|
-
console.log('[getManHattanPath] Source anchor:', "(".concat(sourceAnchor.x, ", ").concat(sourceAnchor.y, ")"));
|
|
141
|
-
console.log('[getManHattanPath] Target anchor:', "(".concat(targetAnchor.x, ", ").concat(targetAnchor.y, ")"));
|
|
142
137
|
|
|
143
138
|
// If using smart point generation (sourcePosition/targetPosition specified),
|
|
144
139
|
// the route already contains the correct extension points, so skip manual processing
|
|
145
140
|
var useSmartPoints = sourcePosition || targetPosition;
|
|
146
141
|
if (useSmartPoints) {
|
|
147
|
-
|
|
142
|
+
// Post-process route to fix X coordinates for horizontal anchors
|
|
143
|
+
// This ensures the extension distance is fixed and not affected by grid alignment
|
|
144
|
+
var isSourceHorizontal = sourcePosition === Position.Left || sourcePosition === Position.Right;
|
|
145
|
+
var isTargetHorizontal = targetPosition === Position.Left || targetPosition === Position.Right;
|
|
146
|
+
if (isSourceHorizontal && isTargetHorizontal && route.length > 0) {
|
|
147
|
+
// Calculate expected extension X coordinates
|
|
148
|
+
var sourceExtensionX = sourcePosition === Position.Right ? sourceAnchor.x + options.extensionDistance : sourceAnchor.x - options.extensionDistance;
|
|
149
|
+
var targetExtensionX = targetPosition === Position.Left ? targetAnchor.x - options.extensionDistance : targetAnchor.x + options.extensionDistance;
|
|
150
|
+
|
|
151
|
+
// Find the horizontal segment (where Y stays constant but X changes significantly)
|
|
152
|
+
// This divides the path into source-side and target-side
|
|
153
|
+
var horizontalSegmentStart = -1;
|
|
154
|
+
var horizontalSegmentEnd = -1;
|
|
155
|
+
for (var _i = 0; _i < route.length - 1; _i++) {
|
|
156
|
+
var curr = route[_i];
|
|
157
|
+
var next = route[_i + 1];
|
|
158
|
+
var isHorizontalMove = Math.abs(curr.y - next.y) < 1 && Math.abs(curr.x - next.x) > options.step;
|
|
159
|
+
if (isHorizontalMove) {
|
|
160
|
+
horizontalSegmentStart = _i;
|
|
161
|
+
horizontalSegmentEnd = _i + 1;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Fix route points
|
|
167
|
+
for (var _i2 = 0; _i2 < route.length; _i2++) {
|
|
168
|
+
var point = route[_i2];
|
|
169
|
+
if (horizontalSegmentStart >= 0) {
|
|
170
|
+
if (_i2 <= horizontalSegmentStart) {
|
|
171
|
+
// Source side - all vertical segment points should use sourceExtensionX
|
|
172
|
+
route[_i2] = new Point(sourceExtensionX, point.y);
|
|
173
|
+
} else if (_i2 >= horizontalSegmentEnd) {
|
|
174
|
+
// Target side - all vertical segment points should use targetExtensionX
|
|
175
|
+
route[_i2] = new Point(targetExtensionX, point.y);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Remove redundant points (consecutive points on same line)
|
|
181
|
+
var optimized = [];
|
|
182
|
+
for (var _i3 = 0; _i3 < route.length; _i3++) {
|
|
183
|
+
var _point = route[_i3];
|
|
184
|
+
if (optimized.length < 2) {
|
|
185
|
+
optimized.push(_point);
|
|
186
|
+
} else {
|
|
187
|
+
var prev = optimized[optimized.length - 1];
|
|
188
|
+
var prevPrev = optimized[optimized.length - 2];
|
|
189
|
+
|
|
190
|
+
// Check if prev is on the same line as prevPrev and point
|
|
191
|
+
var sameX = Math.abs(prevPrev.x - prev.x) < 1 && Math.abs(prev.x - _point.x) < 1;
|
|
192
|
+
var sameY = Math.abs(prevPrev.y - prev.y) < 1 && Math.abs(prev.y - _point.y) < 1;
|
|
193
|
+
if (sameX || sameY) {
|
|
194
|
+
// prev is redundant, replace it with current point
|
|
195
|
+
optimized[optimized.length - 1] = _point;
|
|
196
|
+
} else {
|
|
197
|
+
optimized.push(_point);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
route = optimized;
|
|
202
|
+
}
|
|
203
|
+
|
|
148
204
|
// Add source and target anchors to route
|
|
149
205
|
var _finalRoute = [sourceAnchor].concat(_toConsumableArray(route), [targetAnchor]);
|
|
150
|
-
console.log('[getManHattanPath] Final route:', _finalRoute.map(function (p) {
|
|
151
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
152
|
-
}));
|
|
153
206
|
return pointsToPath(_finalRoute, options.precision, options.borderRadius);
|
|
154
207
|
}
|
|
155
208
|
|
|
@@ -172,7 +225,6 @@ export function getManHattanPath(params) {
|
|
|
172
225
|
// This is likely an extension point, remove it
|
|
173
226
|
if (onRight && firstPoint.x > sourceAnchor.x || onLeft && firstPoint.x < sourceAnchor.x || onBottom && firstPoint.y > sourceAnchor.y || onTop && firstPoint.y < sourceAnchor.y) {
|
|
174
227
|
route.shift();
|
|
175
|
-
console.log('[getManHattanPath] Removed extension point from route start');
|
|
176
228
|
}
|
|
177
229
|
}
|
|
178
230
|
}
|
|
@@ -191,7 +243,6 @@ export function getManHattanPath(params) {
|
|
|
191
243
|
// This is likely an extension point, remove it
|
|
192
244
|
if (_onLeft && lastPoint.x < targetAnchor.x || _onRight && lastPoint.x > targetAnchor.x || _onTop && lastPoint.y < targetAnchor.y || _onBottom && lastPoint.y > targetAnchor.y) {
|
|
193
245
|
route.pop();
|
|
194
|
-
console.log('[getManHattanPath] Removed extension point from route end');
|
|
195
246
|
}
|
|
196
247
|
}
|
|
197
248
|
}
|
|
@@ -217,7 +268,6 @@ export function getManHattanPath(params) {
|
|
|
217
268
|
route.unshift(new Point(extendX, _firstPoint.y)); // Corner point
|
|
218
269
|
}
|
|
219
270
|
route.unshift(extensionPoint); // Extension point (fixed distance)
|
|
220
|
-
console.log('[getManHattanPath] Inserted source extension (right):', "(".concat(extendX, ", ").concat(sourceAnchor.y, ")"));
|
|
221
271
|
} else if (_onLeft2) {
|
|
222
272
|
// Anchor on left edge - extend left by step + borderRadius
|
|
223
273
|
var _extendX = sourceAnchor.x - extensionDistance;
|
|
@@ -226,7 +276,6 @@ export function getManHattanPath(params) {
|
|
|
226
276
|
route.unshift(new Point(_extendX, _firstPoint.y));
|
|
227
277
|
}
|
|
228
278
|
route.unshift(_extensionPoint);
|
|
229
|
-
console.log('[getManHattanPath] Inserted source extension (left):', "(".concat(_extendX, ", ").concat(sourceAnchor.y, ")"));
|
|
230
279
|
} else if (_onBottom2) {
|
|
231
280
|
// Anchor on bottom edge - extend down by step + borderRadius
|
|
232
281
|
var extendY = sourceAnchor.y + extensionDistance;
|
|
@@ -235,7 +284,6 @@ export function getManHattanPath(params) {
|
|
|
235
284
|
route.unshift(new Point(_firstPoint.x, extendY));
|
|
236
285
|
}
|
|
237
286
|
route.unshift(_extensionPoint2);
|
|
238
|
-
console.log('[getManHattanPath] Inserted source extension (down):', "(".concat(sourceAnchor.x, ", ").concat(extendY, ")"));
|
|
239
287
|
} else if (_onTop2) {
|
|
240
288
|
// Anchor on top edge - extend up by step + borderRadius
|
|
241
289
|
var _extendY = sourceAnchor.y - extensionDistance;
|
|
@@ -244,33 +292,25 @@ export function getManHattanPath(params) {
|
|
|
244
292
|
route.unshift(new Point(_firstPoint.x, _extendY));
|
|
245
293
|
}
|
|
246
294
|
route.unshift(_extensionPoint3);
|
|
247
|
-
console.log('[getManHattanPath] Inserted source extension (up):', "(".concat(sourceAnchor.x, ", ").concat(_extendY, ")"));
|
|
248
295
|
}
|
|
249
296
|
}
|
|
250
297
|
|
|
251
298
|
// Remove redundant points after source extension
|
|
252
299
|
// If the first route point has the same x or y coordinate as the source anchor, it's redundant
|
|
253
|
-
if (route.length >
|
|
254
|
-
var firstRoutePoint = route[0]; // Extension point
|
|
255
|
-
var secondRoutePoint = route[1]; // Corner point (if exists)
|
|
300
|
+
if (route.length > 3) {
|
|
256
301
|
var thirdRoutePoint = route[2]; // Original A* point
|
|
257
|
-
|
|
258
302
|
// Check if the third point (original A* point) is redundant
|
|
259
303
|
// It's redundant if it's on the same line as the corner point and can be skipped
|
|
260
|
-
var
|
|
261
|
-
var
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (cornerToThird && thirdToFourth) {
|
|
271
|
-
console.log('[getManHattanPath] Removing redundant point:', "(".concat(thirdRoutePoint.x, ", ").concat(thirdRoutePoint.y, ")"));
|
|
272
|
-
route.splice(2, 1); // Remove the third point
|
|
273
|
-
}
|
|
304
|
+
var _sameX = Math.abs(thirdRoutePoint.x - sourceAnchor.x) < tolerance;
|
|
305
|
+
var _sameY = Math.abs(thirdRoutePoint.y - sourceAnchor.y) < tolerance;
|
|
306
|
+
if (_sameX || _sameY) {
|
|
307
|
+
var secondRoutePoint = route[1]; // Corner point (if exists)
|
|
308
|
+
var fourthPoint = route[3];
|
|
309
|
+
// If corner point and fourth point form a straight line, remove the third point
|
|
310
|
+
var cornerToThird = Math.abs(secondRoutePoint.x - thirdRoutePoint.x) < tolerance || Math.abs(secondRoutePoint.y - thirdRoutePoint.y) < tolerance;
|
|
311
|
+
var thirdToFourth = Math.abs(thirdRoutePoint.x - fourthPoint.x) < tolerance || Math.abs(thirdRoutePoint.y - fourthPoint.y) < tolerance;
|
|
312
|
+
if (cornerToThird && thirdToFourth) {
|
|
313
|
+
route.splice(2, 1); // Remove the third point
|
|
274
314
|
}
|
|
275
315
|
}
|
|
276
316
|
}
|
|
@@ -280,16 +320,12 @@ export function getManHattanPath(params) {
|
|
|
280
320
|
// where the middle segment goes to target edge and then moves along it
|
|
281
321
|
// Use target bbox with padding (same as ObstacleMap)
|
|
282
322
|
var targetBBoxWithPadding = targetBBox.moveAndExpand(options.paddingBox);
|
|
283
|
-
console.log('[getManHattanPath] Route before zigzag check:', route.map(function (p) {
|
|
284
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
285
|
-
}));
|
|
286
|
-
console.log('[getManHattanPath] Target BBox with padding:', "x=".concat(targetBBoxWithPadding.x, ", y=").concat(targetBBoxWithPadding.y));
|
|
287
323
|
if (route.length >= 3) {
|
|
288
|
-
var
|
|
289
|
-
while (
|
|
290
|
-
var p1 = route[
|
|
291
|
-
var p2 = route[
|
|
292
|
-
var p3 = route[
|
|
324
|
+
var _i4 = 0;
|
|
325
|
+
while (_i4 < route.length - 2) {
|
|
326
|
+
var p1 = route[_i4];
|
|
327
|
+
var p2 = route[_i4 + 1];
|
|
328
|
+
var p3 = route[_i4 + 2];
|
|
293
329
|
|
|
294
330
|
// Check if p2 is on the target bbox edge (with padding)
|
|
295
331
|
var p2OnTargetLeftEdge = Math.abs(p2.x - targetBBoxWithPadding.x) < tolerance;
|
|
@@ -297,22 +333,19 @@ export function getManHattanPath(params) {
|
|
|
297
333
|
var p2OnTargetTopEdge = Math.abs(p2.y - targetBBoxWithPadding.y) < tolerance;
|
|
298
334
|
var p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBoxWithPadding.y + targetBBoxWithPadding.height)) < tolerance;
|
|
299
335
|
var p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
|
|
300
|
-
console.log("[getManHattanPath] Checking i=".concat(_i, ": p2=(").concat(p2.x, ", ").concat(p2.y, "), onEdge=").concat(p2OnTargetEdge));
|
|
301
336
|
if (p2OnTargetEdge) {
|
|
302
337
|
// Check if p1 -> p2 -> p3 forms a zigzag
|
|
303
338
|
var p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
|
|
304
339
|
var p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
|
|
305
340
|
var p1ToP2Vertical = Math.abs(p1.x - p2.x) < tolerance;
|
|
306
341
|
var p2ToP3Horizontal = Math.abs(p2.y - p3.y) < tolerance;
|
|
307
|
-
console.log("[getManHattanPath] Zigzag pattern: H->V=".concat(p1ToP2Horizontal && p2ToP3Vertical, ", V->H=").concat(p1ToP2Vertical && p2ToP3Horizontal));
|
|
308
342
|
if (p1ToP2Horizontal && p2ToP3Vertical || p1ToP2Vertical && p2ToP3Horizontal) {
|
|
309
343
|
// We have a zigzag at target edge, remove p2 and p3
|
|
310
|
-
|
|
311
|
-
route.splice(_i + 1, 2); // Remove p2 and p3
|
|
344
|
+
route.splice(_i4 + 1, 2); // Remove p2 and p3
|
|
312
345
|
continue;
|
|
313
346
|
}
|
|
314
347
|
}
|
|
315
|
-
|
|
348
|
+
_i4++;
|
|
316
349
|
}
|
|
317
350
|
}
|
|
318
351
|
|
|
@@ -336,7 +369,6 @@ export function getManHattanPath(params) {
|
|
|
336
369
|
route.push(new Point(_extendX2, _lastPoint.y)); // Corner point
|
|
337
370
|
}
|
|
338
371
|
route.push(_extensionPoint4); // Extension point (fixed distance)
|
|
339
|
-
console.log('[getManHattanPath] Inserted target extension (left):', "(".concat(_extendX2, ", ").concat(targetAnchor.y, ")"));
|
|
340
372
|
} else if (_onRight3) {
|
|
341
373
|
// Anchor on right edge - extend right by step + borderRadius
|
|
342
374
|
var _extendX3 = targetAnchor.x + _extensionDistance;
|
|
@@ -345,7 +377,6 @@ export function getManHattanPath(params) {
|
|
|
345
377
|
route.push(new Point(_extendX3, _lastPoint.y));
|
|
346
378
|
}
|
|
347
379
|
route.push(_extensionPoint5);
|
|
348
|
-
console.log('[getManHattanPath] Inserted target extension (right):', "(".concat(_extendX3, ", ").concat(targetAnchor.y, ")"));
|
|
349
380
|
} else if (_onTop3) {
|
|
350
381
|
// Anchor on top edge - extend up by step + borderRadius
|
|
351
382
|
var _extendY2 = targetAnchor.y - _extensionDistance;
|
|
@@ -354,7 +385,6 @@ export function getManHattanPath(params) {
|
|
|
354
385
|
route.push(new Point(_lastPoint.x, _extendY2));
|
|
355
386
|
}
|
|
356
387
|
route.push(_extensionPoint6);
|
|
357
|
-
console.log('[getManHattanPath] Inserted target extension (up):', "(".concat(targetAnchor.x, ", ").concat(_extendY2, ")"));
|
|
358
388
|
} else if (_onBottom3) {
|
|
359
389
|
// Anchor on bottom edge - extend down by step + borderRadius
|
|
360
390
|
var _extendY3 = targetAnchor.y + _extensionDistance;
|
|
@@ -363,7 +393,6 @@ export function getManHattanPath(params) {
|
|
|
363
393
|
route.push(new Point(_lastPoint.x, _extendY3));
|
|
364
394
|
}
|
|
365
395
|
route.push(_extensionPoint7);
|
|
366
|
-
console.log('[getManHattanPath] Inserted target extension (down):', "(".concat(targetAnchor.x, ", ").concat(_extendY3, ")"));
|
|
367
396
|
}
|
|
368
397
|
}
|
|
369
398
|
|
|
@@ -371,20 +400,18 @@ export function getManHattanPath(params) {
|
|
|
371
400
|
// Similar logic for target side
|
|
372
401
|
if (route.length > 2) {
|
|
373
402
|
var lastIdx = route.length - 1;
|
|
374
|
-
var lastRoutePoint = route[lastIdx]; // Extension point
|
|
375
403
|
var secondLastPoint = route[lastIdx - 1]; // Corner point (if exists)
|
|
376
404
|
var thirdLastPoint = route[lastIdx - 2]; // Original A* point
|
|
377
405
|
|
|
378
406
|
// Check if the third-to-last point is redundant
|
|
379
|
-
var
|
|
380
|
-
var
|
|
381
|
-
if (
|
|
407
|
+
var _sameX2 = Math.abs(thirdLastPoint.x - targetAnchor.x) < tolerance;
|
|
408
|
+
var _sameY2 = Math.abs(thirdLastPoint.y - targetAnchor.y) < tolerance;
|
|
409
|
+
if (_sameX2 || _sameY2) {
|
|
382
410
|
if (route.length > 3) {
|
|
383
411
|
var fourthLastPoint = route[lastIdx - 3];
|
|
384
412
|
var fourthToThird = Math.abs(fourthLastPoint.x - thirdLastPoint.x) < tolerance || Math.abs(fourthLastPoint.y - thirdLastPoint.y) < tolerance;
|
|
385
413
|
var thirdToSecond = Math.abs(thirdLastPoint.x - secondLastPoint.x) < tolerance || Math.abs(thirdLastPoint.y - secondLastPoint.y) < tolerance;
|
|
386
414
|
if (fourthToThird && thirdToSecond) {
|
|
387
|
-
console.log('[getManHattanPath] Removing redundant point:', "(".concat(thirdLastPoint.x, ", ").concat(thirdLastPoint.y, ")"));
|
|
388
415
|
route.splice(lastIdx - 2, 1); // Remove the third-to-last point
|
|
389
416
|
}
|
|
390
417
|
}
|
|
@@ -417,7 +444,6 @@ export function getManHattanPath(params) {
|
|
|
417
444
|
var p3ToP4Horizontal = Math.abs(_p3.y - p4.y) < tolerance;
|
|
418
445
|
if (p3ToP4Horizontal) {
|
|
419
446
|
// Pattern: horizontal -> vertical -> horizontal (zigzag)
|
|
420
|
-
console.log('[getManHattanPath] Removing zigzag at target edge:', "(".concat(_p2.x, ", ").concat(_p2.y, ")"), "and (".concat(_p3.x, ", ").concat(_p3.y, ")"));
|
|
421
447
|
route.splice(i + 1, 2); // Remove p2 and p3
|
|
422
448
|
continue;
|
|
423
449
|
}
|
|
@@ -428,9 +454,6 @@ export function getManHattanPath(params) {
|
|
|
428
454
|
|
|
429
455
|
// Add source and target anchors to route
|
|
430
456
|
var finalRoute = [sourceAnchor].concat(_toConsumableArray(route), [targetAnchor]);
|
|
431
|
-
console.log('[getManHattanPath] Final route:', finalRoute.map(function (p) {
|
|
432
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
433
|
-
}));
|
|
434
457
|
|
|
435
458
|
// Convert to SVG path string
|
|
436
459
|
return pointsToPath(finalRoute, options.precision, options.borderRadius);
|
|
@@ -2,29 +2,61 @@ import { Point } from '../geometry';
|
|
|
2
2
|
import type { ResolvedOptions, NodeLookup } from '../options';
|
|
3
3
|
/**
|
|
4
4
|
* ObstacleMap class for managing obstacles in pathfinding
|
|
5
|
-
*
|
|
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
|
|
6
9
|
*/
|
|
7
10
|
export declare class ObstacleMap {
|
|
8
11
|
private options;
|
|
9
|
-
private
|
|
10
|
-
private
|
|
12
|
+
private quadTree;
|
|
13
|
+
private obstacles;
|
|
11
14
|
private sourceAnchor?;
|
|
12
15
|
private targetAnchor?;
|
|
16
|
+
private accessibilityCache;
|
|
17
|
+
private cacheHits;
|
|
18
|
+
private cacheMisses;
|
|
13
19
|
constructor(options: ResolvedOptions);
|
|
14
20
|
/**
|
|
15
|
-
*
|
|
21
|
+
* Get cache statistics for performance monitoring
|
|
16
22
|
*/
|
|
17
|
-
|
|
23
|
+
getCacheStats(): {
|
|
24
|
+
hits: number;
|
|
25
|
+
misses: number;
|
|
26
|
+
hitRate: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Clear the accessibility cache
|
|
30
|
+
*/
|
|
31
|
+
clearCache(): void;
|
|
18
32
|
/**
|
|
19
|
-
*
|
|
20
|
-
* This allows paths to start/end at the anchor but prevents crossing the node
|
|
33
|
+
* Build obstacle map from node lookup using QuadTree
|
|
21
34
|
*/
|
|
22
|
-
|
|
35
|
+
build(nodeLookup: NodeLookup, sourceNodeId: string, targetNodeId: string, sourceAnchor?: Point, targetAnchor?: Point): ObstacleMap;
|
|
23
36
|
/**
|
|
24
37
|
* Check if a point is accessible (not inside any obstacle)
|
|
25
|
-
* Uses
|
|
38
|
+
* Uses QuadTree for efficient spatial queries with caching
|
|
26
39
|
*/
|
|
27
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[];
|
|
28
60
|
/**
|
|
29
61
|
* Check accessibility using binary search optimization
|
|
30
62
|
* Tries step -> step/2 -> step/4 -> ... -> 1px
|
|
@@ -1 +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;
|
|
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"}
|