@rxflow/manhattan 0.0.1-alpha.11 → 0.0.1-alpha.13
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/Rectangle.d.ts +1 -1
- package/cjs/geometry/Rectangle.js +2 -2
- package/cjs/getManHattanPath.d.ts.map +1 -1
- package/cjs/getManHattanPath.js +153 -20
- package/cjs/obstacle/ObstacleMap.d.ts +7 -1
- package/cjs/obstacle/ObstacleMap.d.ts.map +1 -1
- package/cjs/obstacle/ObstacleMap.js +53 -1
- package/cjs/options/defaults.d.ts +1 -1
- package/cjs/options/defaults.d.ts.map +1 -1
- package/cjs/options/defaults.js +1 -1
- package/cjs/options/resolver.d.ts.map +1 -1
- package/cjs/options/resolver.js +4 -2
- package/cjs/options/types.d.ts +19 -6
- package/cjs/options/types.d.ts.map +1 -1
- package/cjs/pathfinder/findRoute.d.ts.map +1 -1
- package/cjs/pathfinder/findRoute.js +239 -15
- package/cjs/svg/pathConverter.d.ts.map +1 -1
- package/cjs/svg/pathConverter.js +23 -12
- package/cjs/utils/getAnchorPoints.d.ts +15 -0
- package/cjs/utils/getAnchorPoints.d.ts.map +1 -0
- package/cjs/utils/getAnchorPoints.js +75 -0
- package/cjs/utils/index.d.ts +1 -0
- package/cjs/utils/index.d.ts.map +1 -1
- package/cjs/utils/index.js +11 -0
- package/esm/geometry/Rectangle.d.ts +1 -1
- package/esm/geometry/Rectangle.js +2 -2
- package/esm/getManHattanPath.d.ts.map +1 -1
- package/esm/getManHattanPath.js +162 -22
- package/esm/obstacle/ObstacleMap.d.ts +7 -1
- package/esm/obstacle/ObstacleMap.d.ts.map +1 -1
- package/esm/obstacle/ObstacleMap.js +78 -0
- package/esm/options/defaults.d.ts +1 -1
- package/esm/options/defaults.d.ts.map +1 -1
- package/esm/options/defaults.js +1 -1
- package/esm/options/resolver.d.ts.map +1 -1
- package/esm/options/resolver.js +5 -3
- package/esm/options/types.d.ts +19 -6
- package/esm/options/types.d.ts.map +1 -1
- package/esm/pathfinder/findRoute.d.ts.map +1 -1
- package/esm/pathfinder/findRoute.js +262 -23
- package/esm/svg/pathConverter.d.ts.map +1 -1
- package/esm/svg/pathConverter.js +23 -12
- package/esm/utils/getAnchorPoints.d.ts +15 -0
- package/esm/utils/getAnchorPoints.d.ts.map +1 -0
- package/esm/utils/getAnchorPoints.js +69 -0
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +2 -1
- package/package.json +1 -1
|
@@ -25,7 +25,7 @@ export declare class Rectangle {
|
|
|
25
25
|
*/
|
|
26
26
|
getCorner(): Point;
|
|
27
27
|
/**
|
|
28
|
-
* Check if a point is contained within this rectangle
|
|
28
|
+
* Check if a point is contained within this rectangle (interior only, excluding edges)
|
|
29
29
|
*/
|
|
30
30
|
containsPoint(point: Point): boolean;
|
|
31
31
|
/**
|
|
@@ -49,10 +49,10 @@ class Rectangle {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* Check if a point is contained within this rectangle
|
|
52
|
+
* Check if a point is contained within this rectangle (interior only, excluding edges)
|
|
53
53
|
*/
|
|
54
54
|
containsPoint(point) {
|
|
55
|
-
return point.x
|
|
55
|
+
return point.x > this.x && point.x < this.x + this.width && point.y > this.y && point.y < this.y + this.height;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getManHattanPath.d.ts","sourceRoot":"","sources":["../src/getManHattanPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,MAAM,eAAe,CAAA;AAG3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"getManHattanPath.d.ts","sourceRoot":"","sources":["../src/getManHattanPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,MAAM,eAAe,CAAA;AAG3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAa,MAAM,WAAW,CAAA;AAM9E;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAEhB,cAAc,EAAE,QAAQ,CAAC;IACzB,cAAc,EAAE,QAAQ,CAAC;IACzB;;OAEG;IACH,UAAU,EAAE,UAAU,CAAA;IAEtB;;OAEG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAA;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAgbvE"}
|
package/cjs/getManHattanPath.js
CHANGED
|
@@ -51,8 +51,18 @@ function getManHattanPath(params) {
|
|
|
51
51
|
options: userOptions = {}
|
|
52
52
|
} = params;
|
|
53
53
|
|
|
54
|
-
// Resolve options
|
|
55
|
-
const options = (0, _options.resolveOptions)(
|
|
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
|
|
56
66
|
|
|
57
67
|
// Get source and target nodes
|
|
58
68
|
const sourceNode = nodeLookup.get(sourceNodeId);
|
|
@@ -117,6 +127,17 @@ function getManHattanPath(params) {
|
|
|
117
127
|
console.log('[getManHattanPath] Source anchor:', `(${sourceAnchor.x}, ${sourceAnchor.y})`);
|
|
118
128
|
console.log('[getManHattanPath] Target anchor:', `(${targetAnchor.x}, ${targetAnchor.y})`);
|
|
119
129
|
|
|
130
|
+
// If using smart point generation (sourcePosition/targetPosition specified),
|
|
131
|
+
// the route already contains the correct extension points, so skip manual processing
|
|
132
|
+
const useSmartPoints = sourcePosition || targetPosition;
|
|
133
|
+
if (useSmartPoints) {
|
|
134
|
+
console.log('[getManHattanPath] Using smart points, skipping manual extension point processing');
|
|
135
|
+
// Add source and target anchors to route
|
|
136
|
+
const finalRoute = [sourceAnchor, ...route, targetAnchor];
|
|
137
|
+
console.log('[getManHattanPath] Final route:', finalRoute.map(p => `(${p.x}, ${p.y})`));
|
|
138
|
+
return (0, _svg.pointsToPath)(finalRoute, options.precision, options.borderRadius);
|
|
139
|
+
}
|
|
140
|
+
|
|
120
141
|
// Remove extension points from route that were added by getRectPoints
|
|
121
142
|
// We will add our own with fixed step distance
|
|
122
143
|
const step = options.step;
|
|
@@ -160,10 +181,9 @@ function getManHattanPath(params) {
|
|
|
160
181
|
}
|
|
161
182
|
}
|
|
162
183
|
|
|
163
|
-
// Insert extension point at source - always extend away from node edge by fixed
|
|
164
|
-
// Add borderRadius to extension distance to account for rounded corners
|
|
184
|
+
// Insert extension point at source - always extend away from node edge by fixed distance
|
|
165
185
|
if (route.length > 0) {
|
|
166
|
-
const extensionDistance =
|
|
186
|
+
const extensionDistance = options.extensionDistance;
|
|
167
187
|
const firstPoint = route[0];
|
|
168
188
|
|
|
169
189
|
// Determine which edge the source anchor is on
|
|
@@ -213,10 +233,75 @@ function getManHattanPath(params) {
|
|
|
213
233
|
}
|
|
214
234
|
}
|
|
215
235
|
|
|
216
|
-
//
|
|
217
|
-
//
|
|
236
|
+
// Remove redundant points after source extension
|
|
237
|
+
// If the first route point has the same x or y coordinate as the source anchor, it's redundant
|
|
238
|
+
if (route.length > 2) {
|
|
239
|
+
const firstRoutePoint = route[0]; // Extension point
|
|
240
|
+
const secondRoutePoint = route[1]; // Corner point (if exists)
|
|
241
|
+
const thirdRoutePoint = route[2]; // Original A* point
|
|
242
|
+
|
|
243
|
+
// Check if the third point (original A* point) is redundant
|
|
244
|
+
// It's redundant if it's on the same line as the corner point and can be skipped
|
|
245
|
+
const sameX = Math.abs(thirdRoutePoint.x - sourceAnchor.x) < tolerance;
|
|
246
|
+
const sameY = Math.abs(thirdRoutePoint.y - sourceAnchor.y) < tolerance;
|
|
247
|
+
if (sameX || sameY) {
|
|
248
|
+
// The third point is aligned with the source anchor, likely redundant
|
|
249
|
+
// Check if we can skip it by connecting corner point directly to the next point
|
|
250
|
+
if (route.length > 3) {
|
|
251
|
+
const fourthPoint = route[3];
|
|
252
|
+
// If corner point and fourth point form a straight line, remove the third point
|
|
253
|
+
const cornerToThird = Math.abs(secondRoutePoint.x - thirdRoutePoint.x) < tolerance || Math.abs(secondRoutePoint.y - thirdRoutePoint.y) < tolerance;
|
|
254
|
+
const thirdToFourth = Math.abs(thirdRoutePoint.x - fourthPoint.x) < tolerance || Math.abs(thirdRoutePoint.y - fourthPoint.y) < tolerance;
|
|
255
|
+
if (cornerToThird && thirdToFourth) {
|
|
256
|
+
console.log('[getManHattanPath] Removing redundant point:', `(${thirdRoutePoint.x}, ${thirdRoutePoint.y})`);
|
|
257
|
+
route.splice(2, 1); // Remove the third point
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Optimize zigzag patterns BEFORE inserting target extension
|
|
264
|
+
// Check for patterns like: (1360, 16) -> (815, 16) -> (815, -134)
|
|
265
|
+
// where the middle segment goes to target edge and then moves along it
|
|
266
|
+
// Use target bbox with padding (same as ObstacleMap)
|
|
267
|
+
const targetBBoxWithPadding = targetBBox.moveAndExpand(options.paddingBox);
|
|
268
|
+
console.log('[getManHattanPath] Route before zigzag check:', route.map(p => `(${p.x}, ${p.y})`));
|
|
269
|
+
console.log('[getManHattanPath] Target BBox with padding:', `x=${targetBBoxWithPadding.x}, y=${targetBBoxWithPadding.y}`);
|
|
270
|
+
if (route.length >= 3) {
|
|
271
|
+
let i = 0;
|
|
272
|
+
while (i < route.length - 2) {
|
|
273
|
+
const p1 = route[i];
|
|
274
|
+
const p2 = route[i + 1];
|
|
275
|
+
const p3 = route[i + 2];
|
|
276
|
+
|
|
277
|
+
// Check if p2 is on the target bbox edge (with padding)
|
|
278
|
+
const p2OnTargetLeftEdge = Math.abs(p2.x - targetBBoxWithPadding.x) < tolerance;
|
|
279
|
+
const p2OnTargetRightEdge = Math.abs(p2.x - (targetBBoxWithPadding.x + targetBBoxWithPadding.width)) < tolerance;
|
|
280
|
+
const p2OnTargetTopEdge = Math.abs(p2.y - targetBBoxWithPadding.y) < tolerance;
|
|
281
|
+
const p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBoxWithPadding.y + targetBBoxWithPadding.height)) < tolerance;
|
|
282
|
+
const p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
|
|
283
|
+
console.log(`[getManHattanPath] Checking i=${i}: p2=(${p2.x}, ${p2.y}), onEdge=${p2OnTargetEdge}`);
|
|
284
|
+
if (p2OnTargetEdge) {
|
|
285
|
+
// Check if p1 -> p2 -> p3 forms a zigzag
|
|
286
|
+
const p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
|
|
287
|
+
const p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
|
|
288
|
+
const p1ToP2Vertical = Math.abs(p1.x - p2.x) < tolerance;
|
|
289
|
+
const p2ToP3Horizontal = Math.abs(p2.y - p3.y) < tolerance;
|
|
290
|
+
console.log(`[getManHattanPath] Zigzag pattern: H->V=${p1ToP2Horizontal && p2ToP3Vertical}, V->H=${p1ToP2Vertical && p2ToP3Horizontal}`);
|
|
291
|
+
if (p1ToP2Horizontal && p2ToP3Vertical || p1ToP2Vertical && p2ToP3Horizontal) {
|
|
292
|
+
// We have a zigzag at target edge, remove p2 and p3
|
|
293
|
+
console.log('[getManHattanPath] Removing zigzag at target edge:', `(${p2.x}, ${p2.y})`, `and (${p3.x}, ${p3.y})`);
|
|
294
|
+
route.splice(i + 1, 2); // Remove p2 and p3
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
i++;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Insert extension point at target - always extend away from node edge by fixed distance
|
|
218
303
|
if (route.length > 0) {
|
|
219
|
-
const extensionDistance =
|
|
304
|
+
const extensionDistance = options.extensionDistance;
|
|
220
305
|
const lastPoint = route[route.length - 1];
|
|
221
306
|
|
|
222
307
|
// Determine which edge the target anchor is on
|
|
@@ -265,20 +350,68 @@ function getManHattanPath(params) {
|
|
|
265
350
|
}
|
|
266
351
|
}
|
|
267
352
|
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
353
|
+
// Remove redundant points before target extension
|
|
354
|
+
// Similar logic for target side
|
|
355
|
+
if (route.length > 2) {
|
|
356
|
+
const lastIdx = route.length - 1;
|
|
357
|
+
const lastRoutePoint = route[lastIdx]; // Extension point
|
|
358
|
+
const secondLastPoint = route[lastIdx - 1]; // Corner point (if exists)
|
|
359
|
+
const thirdLastPoint = route[lastIdx - 2]; // Original A* point
|
|
360
|
+
|
|
361
|
+
// Check if the third-to-last point is redundant
|
|
362
|
+
const sameX = Math.abs(thirdLastPoint.x - targetAnchor.x) < tolerance;
|
|
363
|
+
const sameY = Math.abs(thirdLastPoint.y - targetAnchor.y) < tolerance;
|
|
364
|
+
if (sameX || sameY) {
|
|
365
|
+
if (route.length > 3) {
|
|
366
|
+
const fourthLastPoint = route[lastIdx - 3];
|
|
367
|
+
const fourthToThird = Math.abs(fourthLastPoint.x - thirdLastPoint.x) < tolerance || Math.abs(fourthLastPoint.y - thirdLastPoint.y) < tolerance;
|
|
368
|
+
const thirdToSecond = Math.abs(thirdLastPoint.x - secondLastPoint.x) < tolerance || Math.abs(thirdLastPoint.y - secondLastPoint.y) < tolerance;
|
|
369
|
+
if (fourthToThird && thirdToSecond) {
|
|
370
|
+
console.log('[getManHattanPath] Removing redundant point:', `(${thirdLastPoint.x}, ${thirdLastPoint.y})`);
|
|
371
|
+
route.splice(lastIdx - 2, 1); // Remove the third-to-last point
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
277
375
|
}
|
|
278
376
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
377
|
+
// Additional optimization: Remove unnecessary zigzag patterns near target
|
|
378
|
+
// Check for patterns like: (1360, 16) -> (815, 16) -> (815, -134) -> (800, -134)
|
|
379
|
+
// where (815, 16) and (815, -134) form a zigzag at the target edge
|
|
380
|
+
let i = 0;
|
|
381
|
+
while (i < route.length - 2) {
|
|
382
|
+
const p1 = route[i];
|
|
383
|
+
const p2 = route[i + 1];
|
|
384
|
+
const p3 = route[i + 2];
|
|
385
|
+
|
|
386
|
+
// Check if p2 is on the target bbox edge
|
|
387
|
+
const p2OnTargetLeftEdge = Math.abs(p2.x - targetBBox.x) < tolerance;
|
|
388
|
+
const p2OnTargetRightEdge = Math.abs(p2.x - (targetBBox.x + targetBBox.width)) < tolerance;
|
|
389
|
+
const p2OnTargetTopEdge = Math.abs(p2.y - targetBBox.y) < tolerance;
|
|
390
|
+
const p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBox.y + targetBBox.height)) < tolerance;
|
|
391
|
+
const p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
|
|
392
|
+
if (p2OnTargetEdge) {
|
|
393
|
+
// Check if p1 -> p2 -> p3 forms a zigzag
|
|
394
|
+
const p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
|
|
395
|
+
const p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
|
|
396
|
+
if (p1ToP2Horizontal && p2ToP3Vertical && i < route.length - 3) {
|
|
397
|
+
// We have horizontal -> vertical at target edge
|
|
398
|
+
// Check if we can skip p2 and p3
|
|
399
|
+
const p4 = route[i + 3];
|
|
400
|
+
const p3ToP4Horizontal = Math.abs(p3.y - p4.y) < tolerance;
|
|
401
|
+
if (p3ToP4Horizontal) {
|
|
402
|
+
// Pattern: horizontal -> vertical -> horizontal (zigzag)
|
|
403
|
+
console.log('[getManHattanPath] Removing zigzag at target edge:', `(${p2.x}, ${p2.y})`, `and (${p3.x}, ${p3.y})`);
|
|
404
|
+
route.splice(i + 1, 2); // Remove p2 and p3
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
i++;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Add source and target anchors to route
|
|
413
|
+
const finalRoute = [sourceAnchor, ...route, targetAnchor];
|
|
414
|
+
console.log('[getManHattanPath] Final route:', finalRoute.map(p => `(${p.x}, ${p.y})`));
|
|
282
415
|
|
|
283
416
|
// Convert to SVG path string
|
|
284
417
|
return (0, _svg.pointsToPath)(finalRoute, options.precision, options.borderRadius);
|
|
@@ -22,7 +22,13 @@ export declare class ObstacleMap {
|
|
|
22
22
|
private shrinkBBoxAroundAnchor;
|
|
23
23
|
/**
|
|
24
24
|
* Check if a point is accessible (not inside any obstacle)
|
|
25
|
+
* Uses binary search optimization: step -> step/2 -> step/4 -> ... -> 1px
|
|
25
26
|
*/
|
|
26
|
-
isAccessible(point: Point): boolean;
|
|
27
|
+
isAccessible(point: Point, checkRadius?: number): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Check accessibility using binary search optimization
|
|
30
|
+
* Tries step -> step/2 -> step/4 -> ... -> 1px
|
|
31
|
+
*/
|
|
32
|
+
private isAccessibleWithBinarySearch;
|
|
27
33
|
}
|
|
28
34
|
//# sourceMappingURL=ObstacleMap.d.ts.map
|
|
@@ -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;AAG7D;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,YAAY,CAAC,CAAO;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAO;gBAEhB,OAAO,EAAE,eAAe;IAMpC;;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;IAiEd;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA6D9B
|
|
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;AAG7D;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,YAAY,CAAC,CAAO;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAO;gBAEhB,OAAO,EAAE,eAAe;IAMpC;;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;IAiEd;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA6D9B;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,GAAE,MAAU,GAAG,OAAO;IAiD5D;;;OAGG;IACH,OAAO,CAAC,4BAA4B;CAqCrC"}
|
|
@@ -128,8 +128,9 @@ class ObstacleMap {
|
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
130
|
* Check if a point is accessible (not inside any obstacle)
|
|
131
|
+
* Uses binary search optimization: step -> step/2 -> step/4 -> ... -> 1px
|
|
131
132
|
*/
|
|
132
|
-
isAccessible(point) {
|
|
133
|
+
isAccessible(point, checkRadius = 0) {
|
|
133
134
|
const key = point.clone().snapToGrid(this.mapGridSize).toString();
|
|
134
135
|
const rects = this.map.get(key);
|
|
135
136
|
if (!rects) {
|
|
@@ -152,6 +153,11 @@ class ObstacleMap {
|
|
|
152
153
|
return true;
|
|
153
154
|
}
|
|
154
155
|
}
|
|
156
|
+
|
|
157
|
+
// If checkRadius is specified, use binary search to find accessible points
|
|
158
|
+
if (checkRadius > 0) {
|
|
159
|
+
return this.isAccessibleWithBinarySearch(point, checkRadius, rects);
|
|
160
|
+
}
|
|
155
161
|
const accessible = rects.every(rect => !rect.containsPoint(point));
|
|
156
162
|
|
|
157
163
|
// Debug: log points on the direct path
|
|
@@ -167,5 +173,51 @@ class ObstacleMap {
|
|
|
167
173
|
}
|
|
168
174
|
return accessible;
|
|
169
175
|
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check accessibility using binary search optimization
|
|
179
|
+
* Tries step -> step/2 -> step/4 -> ... -> 1px
|
|
180
|
+
*/
|
|
181
|
+
isAccessibleWithBinarySearch(point, maxRadius, rects) {
|
|
182
|
+
// First check the point itself
|
|
183
|
+
if (rects.every(rect => !rect.containsPoint(point))) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Binary search: start with step, then halve until we reach 1px
|
|
188
|
+
let radius = maxRadius;
|
|
189
|
+
const offsets = [{
|
|
190
|
+
dx: 1,
|
|
191
|
+
dy: 0
|
|
192
|
+
},
|
|
193
|
+
// right
|
|
194
|
+
{
|
|
195
|
+
dx: -1,
|
|
196
|
+
dy: 0
|
|
197
|
+
},
|
|
198
|
+
// left
|
|
199
|
+
{
|
|
200
|
+
dx: 0,
|
|
201
|
+
dy: 1
|
|
202
|
+
},
|
|
203
|
+
// down
|
|
204
|
+
{
|
|
205
|
+
dx: 0,
|
|
206
|
+
dy: -1
|
|
207
|
+
} // up
|
|
208
|
+
];
|
|
209
|
+
while (radius >= 1) {
|
|
210
|
+
for (const offset of offsets) {
|
|
211
|
+
const testPoint = new _geometry.Point(point.x + offset.dx * radius, point.y + offset.dy * radius);
|
|
212
|
+
if (rects.every(rect => !rect.containsPoint(testPoint))) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Halve the radius for next iteration
|
|
218
|
+
radius = Math.floor(radius / 2);
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
170
222
|
}
|
|
171
223
|
exports.ObstacleMap = ObstacleMap;
|
|
@@ -3,7 +3,7 @@ import type { ManhattanRouterOptions } from './types';
|
|
|
3
3
|
/**
|
|
4
4
|
* Default configuration for Manhattan router
|
|
5
5
|
*/
|
|
6
|
-
export declare const defaults: Required<Omit<ManhattanRouterOptions, 'fallbackRoute'>>;
|
|
6
|
+
export declare const defaults: Required<Omit<ManhattanRouterOptions, 'fallbackRoute' | 'sourcePosition' | 'targetPosition'>>;
|
|
7
7
|
/**
|
|
8
8
|
* Direction map - maps direction names to unit vectors
|
|
9
9
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/options/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,EAAE,sBAAsB,EAAa,MAAM,SAAS,CAAA;AAEhE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,sBAAsB,EAAE,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/options/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,EAAE,sBAAsB,EAAa,MAAM,SAAS,CAAA;AAEhE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,sBAAsB,EAAE,eAAe,GAAG,gBAAgB,GAAG,gBAAgB,CAAC,CAkBlH,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;CAKxB,CAAA"}
|
package/cjs/options/defaults.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/options/resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAetE;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/options/resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAetE;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CAiEpF"}
|
package/cjs/options/resolver.js
CHANGED
|
@@ -48,8 +48,8 @@ function resolveOptions(options = {}) {
|
|
|
48
48
|
const excludeNodes = options.excludeNodes ?? _defaults.defaults.excludeNodes;
|
|
49
49
|
const excludeShapes = options.excludeShapes ?? _defaults.defaults.excludeShapes;
|
|
50
50
|
const excludeTerminals = options.excludeTerminals ?? _defaults.defaults.excludeTerminals;
|
|
51
|
-
const snapToGrid = options.snapToGrid ?? _defaults.defaults.snapToGrid;
|
|
52
51
|
const borderRadius = options.borderRadius ?? _defaults.defaults.borderRadius;
|
|
52
|
+
const extensionDistance = options.extensionDistance ?? _defaults.defaults.extensionDistance;
|
|
53
53
|
const penalties = options.penalties ?? _defaults.defaults.penalties;
|
|
54
54
|
const fallbackRoute = options.fallbackRoute;
|
|
55
55
|
|
|
@@ -106,8 +106,10 @@ function resolveOptions(options = {}) {
|
|
|
106
106
|
excludeShapes,
|
|
107
107
|
excludeTerminals,
|
|
108
108
|
paddingBox,
|
|
109
|
-
snapToGrid,
|
|
110
109
|
borderRadius,
|
|
110
|
+
extensionDistance,
|
|
111
|
+
sourcePosition: options.sourcePosition,
|
|
112
|
+
targetPosition: options.targetPosition,
|
|
111
113
|
directionMap: _defaults.directionMap,
|
|
112
114
|
directions,
|
|
113
115
|
penalties,
|
package/cjs/options/types.d.ts
CHANGED
|
@@ -89,17 +89,28 @@ export interface ManhattanRouterOptions {
|
|
|
89
89
|
bottom: number;
|
|
90
90
|
left: number;
|
|
91
91
|
};
|
|
92
|
-
/**
|
|
93
|
-
* Whether the calculation results are aligned with the grid
|
|
94
|
-
* @default true
|
|
95
|
-
*/
|
|
96
|
-
snapToGrid?: boolean;
|
|
97
92
|
/**
|
|
98
93
|
* Border radius for rounded corners at path turns (in pixels)
|
|
99
94
|
* Set to 0 for sharp corners
|
|
100
95
|
* @default 5
|
|
101
96
|
*/
|
|
102
97
|
borderRadius?: number;
|
|
98
|
+
/**
|
|
99
|
+
* Extension distance from node edge for path start/end points (in pixels)
|
|
100
|
+
* This controls how far the path extends away from the node before turning
|
|
101
|
+
* @default 20
|
|
102
|
+
*/
|
|
103
|
+
extensionDistance?: number;
|
|
104
|
+
/**
|
|
105
|
+
* Source position (from ReactFlow)
|
|
106
|
+
* Used for smart point generation
|
|
107
|
+
*/
|
|
108
|
+
sourcePosition?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Target position (from ReactFlow)
|
|
111
|
+
* Used for smart point generation
|
|
112
|
+
*/
|
|
113
|
+
targetPosition?: string;
|
|
103
114
|
/**
|
|
104
115
|
* A penalty received for direction change
|
|
105
116
|
*/
|
|
@@ -130,8 +141,10 @@ export interface ResolvedOptions {
|
|
|
130
141
|
width: number;
|
|
131
142
|
height: number;
|
|
132
143
|
};
|
|
133
|
-
snapToGrid: boolean;
|
|
134
144
|
borderRadius: number;
|
|
145
|
+
extensionDistance: number;
|
|
146
|
+
sourcePosition?: string;
|
|
147
|
+
targetPosition?: string;
|
|
135
148
|
directionMap: {
|
|
136
149
|
top: Point;
|
|
137
150
|
right: Point;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/options/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE3D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAClC,SAAS,EAAE;QACT,gBAAgB,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAC3C,CAAA;IACD,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE3B;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,EAAE,CAAA;IAE7B;;;OAGG;IACH,aAAa,CAAC,EAAE,SAAS,EAAE,CAAA;IAE3B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IAExB;;OAEG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAA;IAE1C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAE/E
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/options/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"findRoute.d.ts","sourceRoot":"","sources":["../../src/pathfinder/findRoute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"findRoute.d.ts","sourceRoot":"","sources":["../../src/pathfinder/findRoute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAyN9C;;GAEG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,SAAS,EACrB,UAAU,EAAE,SAAS,EACrB,YAAY,EAAE,KAAK,EACnB,YAAY,EAAE,KAAK,EACnB,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,eAAe,GACvB,KAAK,EAAE,GAAG,IAAI,CAqThB"}
|