@rxflow/manhattan 0.0.1-alpha.10 → 0.0.1-alpha.12
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 +184 -13
- 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 +206 -21
- 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
|
@@ -4,8 +4,114 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.findRoute = findRoute;
|
|
7
|
+
var _geometry = require("../geometry");
|
|
7
8
|
var _SortedSet = require("./SortedSet");
|
|
8
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
|
+
console.log(`[generateSmartPoints] ${isTarget ? 'Target' : 'Source'} position=${position}, trying extension point: (${extensionPoint.x}, ${extensionPoint.y})`);
|
|
52
|
+
if (map.isAccessible(extensionPoint)) {
|
|
53
|
+
points.push(extensionPoint);
|
|
54
|
+
console.log(`[generateSmartPoints] Extension point is accessible`);
|
|
55
|
+
return points;
|
|
56
|
+
}
|
|
57
|
+
console.log(`[generateSmartPoints] Extension point blocked, using step-based search`);
|
|
58
|
+
|
|
59
|
+
// 2. Step-based search with binary refinement
|
|
60
|
+
// First, extend outward by step increments until we find an accessible point
|
|
61
|
+
let stepMultiplier = 1;
|
|
62
|
+
let maxSteps = 20; // Prevent infinite loop
|
|
63
|
+
let foundAccessibleDistance = -1;
|
|
64
|
+
console.log(`[generateSmartPoints] Starting outward search with step=${options.step}`);
|
|
65
|
+
while (stepMultiplier <= maxSteps) {
|
|
66
|
+
const distance = stepMultiplier * options.step;
|
|
67
|
+
const testPoint = new _geometry.Point(anchor.x + actualDirection.x * distance, anchor.y + actualDirection.y * distance).round(options.precision);
|
|
68
|
+
console.log(`[generateSmartPoints] Testing ${stepMultiplier}*step (distance=${distance}): (${testPoint.x}, ${testPoint.y})`);
|
|
69
|
+
if (map.isAccessible(testPoint)) {
|
|
70
|
+
foundAccessibleDistance = distance;
|
|
71
|
+
console.log(`[generateSmartPoints] Found accessible point at ${stepMultiplier}*step (distance=${distance})`);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
stepMultiplier++;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 3. If we found an accessible point, refine by binary search within the last step interval
|
|
78
|
+
if (foundAccessibleDistance > 0) {
|
|
79
|
+
const outerDistance = foundAccessibleDistance;
|
|
80
|
+
const innerDistance = foundAccessibleDistance - options.step;
|
|
81
|
+
console.log(`[generateSmartPoints] Refining between ${innerDistance} and ${outerDistance}`);
|
|
82
|
+
|
|
83
|
+
// Binary search within the last step interval to find the closest accessible point
|
|
84
|
+
let left = innerDistance;
|
|
85
|
+
let right = outerDistance;
|
|
86
|
+
let bestDistance = outerDistance;
|
|
87
|
+
|
|
88
|
+
// Binary search with precision of 1px
|
|
89
|
+
while (right - left > 1) {
|
|
90
|
+
const mid = (left + right) / 2;
|
|
91
|
+
const testPoint = new _geometry.Point(anchor.x + actualDirection.x * mid, anchor.y + actualDirection.y * mid).round(options.precision);
|
|
92
|
+
console.log(`[generateSmartPoints] Binary search testing distance ${mid.toFixed(1)}: (${testPoint.x}, ${testPoint.y})`);
|
|
93
|
+
if (map.isAccessible(testPoint)) {
|
|
94
|
+
bestDistance = mid;
|
|
95
|
+
right = mid;
|
|
96
|
+
console.log(`[generateSmartPoints] Point accessible, searching closer (right=${right})`);
|
|
97
|
+
} else {
|
|
98
|
+
left = mid;
|
|
99
|
+
console.log(`[generateSmartPoints] Point blocked, searching further (left=${left})`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Use the best distance found
|
|
104
|
+
const finalPoint = new _geometry.Point(anchor.x + actualDirection.x * bestDistance, anchor.y + actualDirection.y * bestDistance).round(options.precision);
|
|
105
|
+
points.push(finalPoint);
|
|
106
|
+
console.log(`[generateSmartPoints] Final point at distance ${bestDistance}: (${finalPoint.x}, ${finalPoint.y})`);
|
|
107
|
+
} else {
|
|
108
|
+
// 4. If no accessible point found after maxSteps, use anchor as fallback
|
|
109
|
+
console.log(`[generateSmartPoints] No accessible point found after ${maxSteps} steps, using anchor: (${anchor.x}, ${anchor.y})`);
|
|
110
|
+
points.push(anchor);
|
|
111
|
+
}
|
|
112
|
+
return points;
|
|
113
|
+
}
|
|
114
|
+
|
|
9
115
|
/**
|
|
10
116
|
* Find route between two points using A* algorithm
|
|
11
117
|
*/
|
|
@@ -24,24 +130,41 @@ function findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, map, opti
|
|
|
24
130
|
const endPoint = targetEndpoint;
|
|
25
131
|
|
|
26
132
|
// Get start and end points around rectangles
|
|
27
|
-
|
|
28
|
-
let
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
133
|
+
// Use smart point generation based on position if available
|
|
134
|
+
let startPoints;
|
|
135
|
+
let endPoints;
|
|
136
|
+
|
|
137
|
+
// Generate smart start points based on sourcePosition
|
|
138
|
+
if (options.sourcePosition) {
|
|
139
|
+
startPoints = generateSmartPoints(startPoint, sourceBBox, options.sourcePosition, grid, map, options, false);
|
|
140
|
+
console.log('[findRoute] Start points from smart generation:', startPoints.map(p => `(${p.x}, ${p.y})`));
|
|
141
|
+
} else {
|
|
142
|
+
startPoints = (0, _utils.getRectPoints)(startPoint, sourceBBox, options.startDirections, grid, options);
|
|
143
|
+
console.log('[findRoute] Start points from getRectPoints:', startPoints.map(p => `(${p.x}, ${p.y})`));
|
|
144
|
+
// Take into account only accessible rect points
|
|
145
|
+
startPoints = startPoints.filter(p => map.isAccessible(p));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Generate smart end points based on targetPosition
|
|
149
|
+
if (options.targetPosition) {
|
|
150
|
+
endPoints = generateSmartPoints(targetEndpoint, targetBBox, options.targetPosition, grid, map, options, true);
|
|
151
|
+
console.log('[findRoute] End points from smart generation:', endPoints.map(p => `(${p.x}, ${p.y})`));
|
|
152
|
+
} else {
|
|
153
|
+
endPoints = (0, _utils.getRectPoints)(targetEndpoint, targetBBox, options.endDirections, grid, options);
|
|
154
|
+
console.log('[findRoute] End points from getRectPoints:', endPoints.map(p => `(${p.x}, ${p.y})`));
|
|
155
|
+
// Take into account only accessible rect points
|
|
156
|
+
endPoints = endPoints.filter(p => map.isAccessible(p));
|
|
157
|
+
}
|
|
35
158
|
console.log('[findRoute] Start points after filter:', startPoints.map(p => `(${p.x}, ${p.y})`));
|
|
36
159
|
console.log('[findRoute] End points after filter:', endPoints.map(p => `(${p.x}, ${p.y})`));
|
|
37
160
|
|
|
38
161
|
// Ensure we always have at least the anchor points
|
|
39
162
|
// This handles edge cases where anchor is on the node boundary
|
|
40
163
|
if (startPoints.length === 0) {
|
|
41
|
-
startPoints = [(0, _utils.
|
|
164
|
+
startPoints = [(0, _utils.round)(startPoint, precision)];
|
|
42
165
|
}
|
|
43
166
|
if (endPoints.length === 0) {
|
|
44
|
-
endPoints = [(0, _utils.
|
|
167
|
+
endPoints = [(0, _utils.round)(endPoint, precision)];
|
|
45
168
|
}
|
|
46
169
|
|
|
47
170
|
// Initialize A* data structures
|
|
@@ -111,8 +234,8 @@ function findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, map, opti
|
|
|
111
234
|
continue;
|
|
112
235
|
}
|
|
113
236
|
|
|
114
|
-
// Calculate neighbor point
|
|
115
|
-
const neighborPoint = (0, _utils.
|
|
237
|
+
// Calculate neighbor point (no grid alignment)
|
|
238
|
+
const neighborPoint = (0, _utils.round)(currentPoint.clone().translate(direction.gridOffsetX || 0, direction.gridOffsetY || 0), precision);
|
|
116
239
|
const neighborKey = (0, _utils.getKey)(neighborPoint);
|
|
117
240
|
|
|
118
241
|
// Skip if closed or not accessible
|
|
@@ -120,7 +243,55 @@ function findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, map, opti
|
|
|
120
243
|
continue;
|
|
121
244
|
}
|
|
122
245
|
|
|
123
|
-
// Check if
|
|
246
|
+
// Check if we can reach any end point directly from this neighbor
|
|
247
|
+
// This allows connecting to end points that are not on the grid
|
|
248
|
+
let canReachEndPoint = false;
|
|
249
|
+
let reachableEndPoint = null;
|
|
250
|
+
for (const endPt of endPoints) {
|
|
251
|
+
const distanceToEnd = neighborPoint.manhattanDistance(endPt);
|
|
252
|
+
|
|
253
|
+
// If close enough to end point (within step distance), try direct connection
|
|
254
|
+
if (distanceToEnd < options.step * 1.5) {
|
|
255
|
+
// Check if we can move directly to the end point
|
|
256
|
+
const dx = endPt.x - neighborPoint.x;
|
|
257
|
+
const dy = endPt.y - neighborPoint.y;
|
|
258
|
+
|
|
259
|
+
// Allow direct connection if it's orthogonal or close to orthogonal
|
|
260
|
+
const isOrthogonal = Math.abs(dx) < 0.1 || Math.abs(dy) < 0.1;
|
|
261
|
+
if (isOrthogonal && map.isAccessible(endPt)) {
|
|
262
|
+
canReachEndPoint = true;
|
|
263
|
+
reachableEndPoint = endPt;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// If we can reach an end point directly, add it as the final step
|
|
270
|
+
if (canReachEndPoint && reachableEndPoint) {
|
|
271
|
+
const endKey = (0, _utils.getKey)(reachableEndPoint);
|
|
272
|
+
const endCost = neighborPoint.manhattanDistance(reachableEndPoint);
|
|
273
|
+
const totalCost = currentCost + direction.cost + endCost;
|
|
274
|
+
if (!openSet.isOpen(endKey) || totalCost < (costs.get(endKey) || Infinity)) {
|
|
275
|
+
points.set(endKey, reachableEndPoint);
|
|
276
|
+
parents.set(endKey, neighborPoint);
|
|
277
|
+
costs.set(endKey, totalCost);
|
|
278
|
+
|
|
279
|
+
// Also add the neighbor point if not already added
|
|
280
|
+
if (!points.has(neighborKey)) {
|
|
281
|
+
points.set(neighborKey, neighborPoint);
|
|
282
|
+
parents.set(neighborKey, currentPoint);
|
|
283
|
+
costs.set(neighborKey, currentCost + direction.cost);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Check if this is our target end point
|
|
287
|
+
if (endPointsKeys.has(endKey)) {
|
|
288
|
+
options.previousDirectionAngle = directionAngle;
|
|
289
|
+
return (0, _utils.reconstructRoute)(parents, points, reachableEndPoint, startPoint, endPoint);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Check if neighbor is an end point (exact match)
|
|
124
295
|
if (endPointsKeys.has(neighborKey)) {
|
|
125
296
|
const isEndPoint = neighborPoint.equals(endPoint);
|
|
126
297
|
if (!isEndPoint) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathConverter.d.ts","sourceRoot":"","sources":["../../src/svg/pathConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"pathConverter.d.ts","sourceRoot":"","sources":["../../src/svg/pathConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,GAAG,MAAM,CA2EjG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,CAgCzE"}
|
package/cjs/svg/pathConverter.js
CHANGED
|
@@ -36,31 +36,42 @@ function pointsToPath(points, precision, borderRadius = 0) {
|
|
|
36
36
|
const current = points[i];
|
|
37
37
|
const next = points[i + 1];
|
|
38
38
|
|
|
39
|
-
// Calculate
|
|
40
|
-
const
|
|
41
|
-
const
|
|
39
|
+
// Calculate direction vectors
|
|
40
|
+
const dx1 = current.x - prev.x;
|
|
41
|
+
const dy1 = current.y - prev.y;
|
|
42
|
+
const dx2 = next.x - current.x;
|
|
43
|
+
const dy2 = next.y - current.y;
|
|
44
|
+
|
|
45
|
+
// Calculate distances
|
|
46
|
+
const dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
|
|
47
|
+
const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
|
42
48
|
|
|
43
49
|
// Use the smaller of borderRadius or half the segment length
|
|
44
|
-
const radius = Math.min(borderRadius,
|
|
50
|
+
const radius = Math.min(borderRadius, dist1 / 2, dist2 / 2);
|
|
51
|
+
|
|
52
|
+
// Normalize direction vectors
|
|
53
|
+
const ndx1 = dx1 / dist1;
|
|
54
|
+
const ndy1 = dy1 / dist1;
|
|
55
|
+
const ndx2 = dx2 / dist2;
|
|
56
|
+
const ndy2 = dy2 / dist2;
|
|
45
57
|
|
|
46
|
-
// Calculate the point before the corner
|
|
47
|
-
const beforeCornerRatio = (distToPrev - radius) / distToPrev;
|
|
58
|
+
// Calculate the point before the corner
|
|
48
59
|
const beforeCorner = {
|
|
49
|
-
x:
|
|
50
|
-
y:
|
|
60
|
+
x: current.x - ndx1 * radius,
|
|
61
|
+
y: current.y - ndy1 * radius
|
|
51
62
|
};
|
|
52
63
|
|
|
53
|
-
// Calculate the point after the corner
|
|
54
|
-
const afterCornerRatio = radius / distToNext;
|
|
64
|
+
// Calculate the point after the corner
|
|
55
65
|
const afterCorner = {
|
|
56
|
-
x: current.x +
|
|
57
|
-
y: current.y +
|
|
66
|
+
x: current.x + ndx2 * radius,
|
|
67
|
+
y: current.y + ndy2 * radius
|
|
58
68
|
};
|
|
59
69
|
|
|
60
70
|
// Draw line to the point before corner
|
|
61
71
|
path += ` L ${roundCoord(beforeCorner.x)} ${roundCoord(beforeCorner.y)}`;
|
|
62
72
|
|
|
63
73
|
// Draw quadratic bezier curve for the rounded corner
|
|
74
|
+
// The control point is the actual corner point
|
|
64
75
|
path += ` Q ${roundCoord(current.x)} ${roundCoord(current.y)} ${roundCoord(afterCorner.x)} ${roundCoord(afterCorner.y)}`;
|
|
65
76
|
}
|
|
66
77
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Point } from '../geometry';
|
|
2
|
+
import type { ObstacleMap } from '../obstacle';
|
|
3
|
+
import type { Direction } from '../options';
|
|
4
|
+
/**
|
|
5
|
+
* Get accessible anchor points using binary search optimization
|
|
6
|
+
*
|
|
7
|
+
* @param anchor - The anchor point (on node edge)
|
|
8
|
+
* @param position - The position/direction (right, left, top, bottom)
|
|
9
|
+
* @param extensionDistance - The preferred extension distance
|
|
10
|
+
* @param step - The step size for binary search
|
|
11
|
+
* @param obstacleMap - The obstacle map for accessibility checking
|
|
12
|
+
* @returns Array of accessible points, prioritized by distance
|
|
13
|
+
*/
|
|
14
|
+
export declare function getAnchorPoints(anchor: Point, position: Direction, extensionDistance: number, step: number, obstacleMap: ObstacleMap): Point[];
|
|
15
|
+
//# sourceMappingURL=getAnchorPoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAnchorPoints.d.ts","sourceRoot":"","sources":["../../src/utils/getAnchorPoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,KAAK,EACb,QAAQ,EAAE,SAAS,EACnB,iBAAiB,EAAE,MAAM,EACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,GACvB,KAAK,EAAE,CA0DT"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getAnchorPoints = getAnchorPoints;
|
|
7
|
+
var _geometry = require("../geometry");
|
|
8
|
+
/**
|
|
9
|
+
* Get accessible anchor points using binary search optimization
|
|
10
|
+
*
|
|
11
|
+
* @param anchor - The anchor point (on node edge)
|
|
12
|
+
* @param position - The position/direction (right, left, top, bottom)
|
|
13
|
+
* @param extensionDistance - The preferred extension distance
|
|
14
|
+
* @param step - The step size for binary search
|
|
15
|
+
* @param obstacleMap - The obstacle map for accessibility checking
|
|
16
|
+
* @returns Array of accessible points, prioritized by distance
|
|
17
|
+
*/
|
|
18
|
+
function getAnchorPoints(anchor, position, extensionDistance, step, obstacleMap) {
|
|
19
|
+
const points = [];
|
|
20
|
+
|
|
21
|
+
// Determine direction vector based on position
|
|
22
|
+
const directionMap = {
|
|
23
|
+
'right': {
|
|
24
|
+
dx: 1,
|
|
25
|
+
dy: 0
|
|
26
|
+
},
|
|
27
|
+
'left': {
|
|
28
|
+
dx: -1,
|
|
29
|
+
dy: 0
|
|
30
|
+
},
|
|
31
|
+
'top': {
|
|
32
|
+
dx: 0,
|
|
33
|
+
dy: -1
|
|
34
|
+
},
|
|
35
|
+
'bottom': {
|
|
36
|
+
dx: 0,
|
|
37
|
+
dy: 1
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const dir = directionMap[position];
|
|
41
|
+
if (!dir) {
|
|
42
|
+
console.warn(`[getAnchorPoints] Invalid position: ${position}`);
|
|
43
|
+
return [anchor];
|
|
44
|
+
}
|
|
45
|
+
console.log(`[getAnchorPoints] Finding points for position '${position}' from (${anchor.x}, ${anchor.y})`);
|
|
46
|
+
|
|
47
|
+
// 1. First try extensionDistance
|
|
48
|
+
const extensionPoint = new _geometry.Point(anchor.x + dir.dx * extensionDistance, anchor.y + dir.dy * extensionDistance);
|
|
49
|
+
if (obstacleMap.isAccessible(extensionPoint)) {
|
|
50
|
+
console.log(`[getAnchorPoints] Extension point (${extensionPoint.x}, ${extensionPoint.y}) is accessible`);
|
|
51
|
+
points.push(extensionPoint);
|
|
52
|
+
return points;
|
|
53
|
+
}
|
|
54
|
+
console.log(`[getAnchorPoints] Extension point (${extensionPoint.x}, ${extensionPoint.y}) is blocked, trying binary search`);
|
|
55
|
+
|
|
56
|
+
// 2. If extensionDistance point is blocked, use binary search with step
|
|
57
|
+
// Try: step -> step/2 -> step/4 -> ... -> 1px
|
|
58
|
+
let distance = step;
|
|
59
|
+
while (distance >= 1) {
|
|
60
|
+
const testPoint = new _geometry.Point(anchor.x + dir.dx * distance, anchor.y + dir.dy * distance);
|
|
61
|
+
if (obstacleMap.isAccessible(testPoint)) {
|
|
62
|
+
console.log(`[getAnchorPoints] Found accessible point at distance ${distance}px: (${testPoint.x}, ${testPoint.y})`);
|
|
63
|
+
points.push(testPoint);
|
|
64
|
+
return points;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Halve the distance for next iteration
|
|
68
|
+
distance = Math.floor(distance / 2);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 3. If still no accessible point found, return the anchor itself
|
|
72
|
+
console.warn(`[getAnchorPoints] No accessible point found, using anchor itself: (${anchor.x}, ${anchor.y})`);
|
|
73
|
+
points.push(anchor);
|
|
74
|
+
return points;
|
|
75
|
+
}
|
package/cjs/utils/index.d.ts
CHANGED
package/cjs/utils/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AACtB,cAAc,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AACtB,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA"}
|
package/cjs/utils/index.js
CHANGED
|
@@ -68,4 +68,15 @@ Object.keys(_pathValidation).forEach(function (key) {
|
|
|
68
68
|
return _pathValidation[key];
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
|
+
});
|
|
72
|
+
var _getAnchorPoints = require("./getAnchorPoints");
|
|
73
|
+
Object.keys(_getAnchorPoints).forEach(function (key) {
|
|
74
|
+
if (key === "default" || key === "__esModule") return;
|
|
75
|
+
if (key in exports && exports[key] === _getAnchorPoints[key]) return;
|
|
76
|
+
Object.defineProperty(exports, key, {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
get: function () {
|
|
79
|
+
return _getAnchorPoints[key];
|
|
80
|
+
}
|
|
81
|
+
});
|
|
71
82
|
});
|
|
@@ -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
|
/**
|
|
@@ -60,12 +60,12 @@ export var Rectangle = /*#__PURE__*/function () {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* Check if a point is contained within this rectangle
|
|
63
|
+
* Check if a point is contained within this rectangle (interior only, excluding edges)
|
|
64
64
|
*/
|
|
65
65
|
}, {
|
|
66
66
|
key: "containsPoint",
|
|
67
67
|
value: function containsPoint(point) {
|
|
68
|
-
return point.x
|
|
68
|
+
return point.x > this.x && point.x < this.x + this.width && point.y > this.y && point.y < this.y + this.height;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
@@ -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"}
|