@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,449 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getManHattanPath = getManHattanPath;
|
|
7
|
+
var _react = require("@xyflow/react");
|
|
8
|
+
var _geometry = require("./geometry");
|
|
9
|
+
var _obstacle = require("./obstacle");
|
|
10
|
+
var _options = require("./options");
|
|
11
|
+
var _pathfinder = require("./pathfinder");
|
|
12
|
+
var _svg = require("./svg");
|
|
13
|
+
var _utils = require("./utils");
|
|
14
|
+
/**
|
|
15
|
+
* Parameters for getManHattanPath function
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate Manhattan-routed path for ReactFlow edges
|
|
20
|
+
*
|
|
21
|
+
* @param params - Path generation parameters
|
|
22
|
+
* @returns SVG path string that can be used with ReactFlow's BaseEdge
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const path = getManHattanPath({
|
|
27
|
+
* sourceNodeId: 'node1',
|
|
28
|
+
* targetNodeId: 'node2',
|
|
29
|
+
* sourcePosition: { x: 100, y: 100 },
|
|
30
|
+
* targetPosition: { x: 300, y: 300 },
|
|
31
|
+
* nodeLookup: nodes,
|
|
32
|
+
* options: {
|
|
33
|
+
* step: 10,
|
|
34
|
+
* startDirections: ['bottom'],
|
|
35
|
+
* endDirections: ['top']
|
|
36
|
+
* }
|
|
37
|
+
* })
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
function getManHattanPath(params) {
|
|
41
|
+
const {
|
|
42
|
+
sourceNodeId,
|
|
43
|
+
targetNodeId,
|
|
44
|
+
sourcePosition,
|
|
45
|
+
targetPosition,
|
|
46
|
+
nodeLookup,
|
|
47
|
+
sourceX,
|
|
48
|
+
sourceY,
|
|
49
|
+
targetX,
|
|
50
|
+
targetY,
|
|
51
|
+
options: userOptions = {}
|
|
52
|
+
} = params;
|
|
53
|
+
|
|
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
|
|
66
|
+
|
|
67
|
+
// Get source and target nodes
|
|
68
|
+
const sourceNode = nodeLookup.get(sourceNodeId);
|
|
69
|
+
const targetNode = nodeLookup.get(targetNodeId);
|
|
70
|
+
if (!sourceNode || !targetNode) {
|
|
71
|
+
// Fallback to simple straight line if nodes not found
|
|
72
|
+
console.warn('[getManHattanPath] Source or target node not found in nodeLookup');
|
|
73
|
+
const start = new _geometry.Point(sourceX, sourceY);
|
|
74
|
+
const end = new _geometry.Point(targetX, targetY);
|
|
75
|
+
// Use ErrorRecovery to generate a proper fallback path
|
|
76
|
+
const fallbackPath = _utils.ErrorRecovery.generateFallbackPath(start, end);
|
|
77
|
+
return (0, _svg.pointsToPath)(fallbackPath, options.precision, options.borderRadius);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Get node dimensions using ReactFlow's priority logic
|
|
81
|
+
const sourceDimensions = (0, _utils.getNodeDimensions)(sourceNode);
|
|
82
|
+
const targetDimensions = (0, _utils.getNodeDimensions)(targetNode);
|
|
83
|
+
|
|
84
|
+
// Get absolute positions from internals
|
|
85
|
+
const sourcePos = (0, _utils.getNodePosition)(sourceNode);
|
|
86
|
+
const targetPos = (0, _utils.getNodePosition)(targetNode);
|
|
87
|
+
|
|
88
|
+
// Calculate bounding boxes
|
|
89
|
+
const sourceBBox = new _geometry.Rectangle(sourcePos.x, sourcePos.y, sourceDimensions.width, sourceDimensions.height);
|
|
90
|
+
const targetBBox = new _geometry.Rectangle(targetPos.x, targetPos.y, targetDimensions.width, targetDimensions.height);
|
|
91
|
+
|
|
92
|
+
// Create anchor points
|
|
93
|
+
const sourceAnchor = new _geometry.Point(sourceX, sourceY);
|
|
94
|
+
const targetAnchor = new _geometry.Point(targetX, targetY);
|
|
95
|
+
|
|
96
|
+
// Try ReactFlow's getSmoothStepPath first
|
|
97
|
+
const [smoothStepPath] = (0, _react.getSmoothStepPath)({
|
|
98
|
+
sourceX,
|
|
99
|
+
sourceY,
|
|
100
|
+
targetX,
|
|
101
|
+
targetY,
|
|
102
|
+
sourcePosition,
|
|
103
|
+
targetPosition,
|
|
104
|
+
borderRadius: options.borderRadius
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Parse the smooth step path to extract points
|
|
108
|
+
const smoothStepPoints = (0, _svg.parseSVGPath)(smoothStepPath);
|
|
109
|
+
|
|
110
|
+
// Check if smooth step path intersects with any obstacles
|
|
111
|
+
if (smoothStepPoints.length > 0 && !(0, _utils.pathIntersectsObstacles)(smoothStepPoints, nodeLookup)) {
|
|
112
|
+
return smoothStepPath;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Build obstacle map with anchor information
|
|
116
|
+
const obstacleMap = new _obstacle.ObstacleMap(options).build(nodeLookup, sourceNodeId, targetNodeId, sourceAnchor, targetAnchor);
|
|
117
|
+
|
|
118
|
+
// Find route
|
|
119
|
+
let route = (0, _pathfinder.findRoute)(sourceBBox, targetBBox, sourceAnchor, targetAnchor, obstacleMap, options);
|
|
120
|
+
|
|
121
|
+
// Fallback to Z-shaped path if no route found
|
|
122
|
+
if (!route) {
|
|
123
|
+
console.warn('[getManHattanPath] Unable to find Manhattan route, using fallback path');
|
|
124
|
+
route = _utils.ErrorRecovery.generateFallbackPath(sourceAnchor, targetAnchor);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// If using smart point generation (sourcePosition/targetPosition specified),
|
|
128
|
+
// the route already contains the correct extension points, so skip manual processing
|
|
129
|
+
const useSmartPoints = sourcePosition || targetPosition;
|
|
130
|
+
if (useSmartPoints) {
|
|
131
|
+
// Post-process route to fix X coordinates for horizontal anchors
|
|
132
|
+
// This ensures the extension distance is fixed and not affected by grid alignment
|
|
133
|
+
const isSourceHorizontal = sourcePosition === _react.Position.Left || sourcePosition === _react.Position.Right;
|
|
134
|
+
const isTargetHorizontal = targetPosition === _react.Position.Left || targetPosition === _react.Position.Right;
|
|
135
|
+
if (isSourceHorizontal && isTargetHorizontal && route.length > 0) {
|
|
136
|
+
// Calculate expected extension X coordinates
|
|
137
|
+
const sourceExtensionX = sourcePosition === _react.Position.Right ? sourceAnchor.x + options.extensionDistance : sourceAnchor.x - options.extensionDistance;
|
|
138
|
+
const targetExtensionX = targetPosition === _react.Position.Left ? targetAnchor.x - options.extensionDistance : targetAnchor.x + options.extensionDistance;
|
|
139
|
+
|
|
140
|
+
// Find the horizontal segment (where Y stays constant but X changes significantly)
|
|
141
|
+
// This divides the path into source-side and target-side
|
|
142
|
+
let horizontalSegmentStart = -1;
|
|
143
|
+
let horizontalSegmentEnd = -1;
|
|
144
|
+
for (let i = 0; i < route.length - 1; i++) {
|
|
145
|
+
const curr = route[i];
|
|
146
|
+
const next = route[i + 1];
|
|
147
|
+
const isHorizontalMove = Math.abs(curr.y - next.y) < 1 && Math.abs(curr.x - next.x) > options.step;
|
|
148
|
+
if (isHorizontalMove) {
|
|
149
|
+
horizontalSegmentStart = i;
|
|
150
|
+
horizontalSegmentEnd = i + 1;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Fix route points
|
|
156
|
+
for (let i = 0; i < route.length; i++) {
|
|
157
|
+
const point = route[i];
|
|
158
|
+
if (horizontalSegmentStart >= 0) {
|
|
159
|
+
if (i <= horizontalSegmentStart) {
|
|
160
|
+
// Source side - all vertical segment points should use sourceExtensionX
|
|
161
|
+
route[i] = new _geometry.Point(sourceExtensionX, point.y);
|
|
162
|
+
} else if (i >= horizontalSegmentEnd) {
|
|
163
|
+
// Target side - all vertical segment points should use targetExtensionX
|
|
164
|
+
route[i] = new _geometry.Point(targetExtensionX, point.y);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Remove redundant points (consecutive points on same line)
|
|
170
|
+
const optimized = [];
|
|
171
|
+
for (let i = 0; i < route.length; i++) {
|
|
172
|
+
const point = route[i];
|
|
173
|
+
if (optimized.length < 2) {
|
|
174
|
+
optimized.push(point);
|
|
175
|
+
} else {
|
|
176
|
+
const prev = optimized[optimized.length - 1];
|
|
177
|
+
const prevPrev = optimized[optimized.length - 2];
|
|
178
|
+
|
|
179
|
+
// Check if prev is on the same line as prevPrev and point
|
|
180
|
+
const sameX = Math.abs(prevPrev.x - prev.x) < 1 && Math.abs(prev.x - point.x) < 1;
|
|
181
|
+
const sameY = Math.abs(prevPrev.y - prev.y) < 1 && Math.abs(prev.y - point.y) < 1;
|
|
182
|
+
if (sameX || sameY) {
|
|
183
|
+
// prev is redundant, replace it with current point
|
|
184
|
+
optimized[optimized.length - 1] = point;
|
|
185
|
+
} else {
|
|
186
|
+
optimized.push(point);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
route = optimized;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Add source and target anchors to route
|
|
194
|
+
const finalRoute = [sourceAnchor, ...route, targetAnchor];
|
|
195
|
+
return (0, _svg.pointsToPath)(finalRoute, options.precision, options.borderRadius);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Remove extension points from route that were added by getRectPoints
|
|
199
|
+
// We will add our own with fixed step distance
|
|
200
|
+
const step = options.step;
|
|
201
|
+
const tolerance = 1;
|
|
202
|
+
|
|
203
|
+
// Check if first point is an extension point from source
|
|
204
|
+
if (route.length > 0) {
|
|
205
|
+
const firstPoint = route[0];
|
|
206
|
+
const onLeft = Math.abs(sourceAnchor.x - sourceBBox.x) < tolerance;
|
|
207
|
+
const onRight = Math.abs(sourceAnchor.x - (sourceBBox.x + sourceBBox.width)) < tolerance;
|
|
208
|
+
const onTop = Math.abs(sourceAnchor.y - sourceBBox.y) < tolerance;
|
|
209
|
+
const onBottom = Math.abs(sourceAnchor.y - (sourceBBox.y + sourceBBox.height)) < tolerance;
|
|
210
|
+
|
|
211
|
+
// Check if firstPoint is close to source anchor (indicating it's an extension point)
|
|
212
|
+
const distToFirst = sourceAnchor.manhattanDistance(firstPoint);
|
|
213
|
+
if (distToFirst < step * 2) {
|
|
214
|
+
// This is likely an extension point, remove it
|
|
215
|
+
if (onRight && firstPoint.x > sourceAnchor.x || onLeft && firstPoint.x < sourceAnchor.x || onBottom && firstPoint.y > sourceAnchor.y || onTop && firstPoint.y < sourceAnchor.y) {
|
|
216
|
+
route.shift();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Check if last point is an extension point from target
|
|
222
|
+
if (route.length > 0) {
|
|
223
|
+
const lastPoint = route[route.length - 1];
|
|
224
|
+
const onLeft = Math.abs(targetAnchor.x - targetBBox.x) < tolerance;
|
|
225
|
+
const onRight = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) < tolerance;
|
|
226
|
+
const onTop = Math.abs(targetAnchor.y - targetBBox.y) < tolerance;
|
|
227
|
+
const onBottom = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) < tolerance;
|
|
228
|
+
|
|
229
|
+
// Check if lastPoint is close to target anchor (indicating it's an extension point)
|
|
230
|
+
const distToLast = targetAnchor.manhattanDistance(lastPoint);
|
|
231
|
+
if (distToLast < step * 2) {
|
|
232
|
+
// This is likely an extension point, remove it
|
|
233
|
+
if (onLeft && lastPoint.x < targetAnchor.x || onRight && lastPoint.x > targetAnchor.x || onTop && lastPoint.y < targetAnchor.y || onBottom && lastPoint.y > targetAnchor.y) {
|
|
234
|
+
route.pop();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Insert extension point at source - always extend away from node edge by fixed distance
|
|
240
|
+
if (route.length > 0) {
|
|
241
|
+
const extensionDistance = options.extensionDistance;
|
|
242
|
+
const firstPoint = route[0];
|
|
243
|
+
|
|
244
|
+
// Determine which edge the source anchor is on
|
|
245
|
+
const onLeft = Math.abs(sourceAnchor.x - sourceBBox.x) < tolerance;
|
|
246
|
+
const onRight = Math.abs(sourceAnchor.x - (sourceBBox.x + sourceBBox.width)) < tolerance;
|
|
247
|
+
const onTop = Math.abs(sourceAnchor.y - sourceBBox.y) < tolerance;
|
|
248
|
+
const onBottom = Math.abs(sourceAnchor.y - (sourceBBox.y + sourceBBox.height)) < tolerance;
|
|
249
|
+
|
|
250
|
+
// Insert extension point and corner point to ensure orthogonal path
|
|
251
|
+
if (onRight) {
|
|
252
|
+
// Anchor on right edge - extend right by step + borderRadius
|
|
253
|
+
const extendX = sourceAnchor.x + extensionDistance;
|
|
254
|
+
const extensionPoint = new _geometry.Point(extendX, sourceAnchor.y);
|
|
255
|
+
// Check if we need a corner point
|
|
256
|
+
if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
|
|
257
|
+
route.unshift(new _geometry.Point(extendX, firstPoint.y)); // Corner point
|
|
258
|
+
}
|
|
259
|
+
route.unshift(extensionPoint); // Extension point (fixed distance)
|
|
260
|
+
} else if (onLeft) {
|
|
261
|
+
// Anchor on left edge - extend left by step + borderRadius
|
|
262
|
+
const extendX = sourceAnchor.x - extensionDistance;
|
|
263
|
+
const extensionPoint = new _geometry.Point(extendX, sourceAnchor.y);
|
|
264
|
+
if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
|
|
265
|
+
route.unshift(new _geometry.Point(extendX, firstPoint.y));
|
|
266
|
+
}
|
|
267
|
+
route.unshift(extensionPoint);
|
|
268
|
+
} else if (onBottom) {
|
|
269
|
+
// Anchor on bottom edge - extend down by step + borderRadius
|
|
270
|
+
const extendY = sourceAnchor.y + extensionDistance;
|
|
271
|
+
const extensionPoint = new _geometry.Point(sourceAnchor.x, extendY);
|
|
272
|
+
if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
|
|
273
|
+
route.unshift(new _geometry.Point(firstPoint.x, extendY));
|
|
274
|
+
}
|
|
275
|
+
route.unshift(extensionPoint);
|
|
276
|
+
} else if (onTop) {
|
|
277
|
+
// Anchor on top edge - extend up by step + borderRadius
|
|
278
|
+
const extendY = sourceAnchor.y - extensionDistance;
|
|
279
|
+
const extensionPoint = new _geometry.Point(sourceAnchor.x, extendY);
|
|
280
|
+
if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
|
|
281
|
+
route.unshift(new _geometry.Point(firstPoint.x, extendY));
|
|
282
|
+
}
|
|
283
|
+
route.unshift(extensionPoint);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Remove redundant points after source extension
|
|
288
|
+
// If the first route point has the same x or y coordinate as the source anchor, it's redundant
|
|
289
|
+
if (route.length > 3) {
|
|
290
|
+
const thirdRoutePoint = route[2]; // Original A* point
|
|
291
|
+
// Check if the third point (original A* point) is redundant
|
|
292
|
+
// It's redundant if it's on the same line as the corner point and can be skipped
|
|
293
|
+
const sameX = Math.abs(thirdRoutePoint.x - sourceAnchor.x) < tolerance;
|
|
294
|
+
const sameY = Math.abs(thirdRoutePoint.y - sourceAnchor.y) < tolerance;
|
|
295
|
+
if (sameX || sameY) {
|
|
296
|
+
const secondRoutePoint = route[1]; // Corner point (if exists)
|
|
297
|
+
const fourthPoint = route[3];
|
|
298
|
+
// If corner point and fourth point form a straight line, remove the third point
|
|
299
|
+
const cornerToThird = Math.abs(secondRoutePoint.x - thirdRoutePoint.x) < tolerance || Math.abs(secondRoutePoint.y - thirdRoutePoint.y) < tolerance;
|
|
300
|
+
const thirdToFourth = Math.abs(thirdRoutePoint.x - fourthPoint.x) < tolerance || Math.abs(thirdRoutePoint.y - fourthPoint.y) < tolerance;
|
|
301
|
+
if (cornerToThird && thirdToFourth) {
|
|
302
|
+
route.splice(2, 1); // Remove the third point
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Optimize zigzag patterns BEFORE inserting target extension
|
|
308
|
+
// Check for patterns like: (1360, 16) -> (815, 16) -> (815, -134)
|
|
309
|
+
// where the middle segment goes to target edge and then moves along it
|
|
310
|
+
// Use target bbox with padding (same as ObstacleMap)
|
|
311
|
+
const targetBBoxWithPadding = targetBBox.moveAndExpand(options.paddingBox);
|
|
312
|
+
if (route.length >= 3) {
|
|
313
|
+
let i = 0;
|
|
314
|
+
while (i < route.length - 2) {
|
|
315
|
+
const p1 = route[i];
|
|
316
|
+
const p2 = route[i + 1];
|
|
317
|
+
const p3 = route[i + 2];
|
|
318
|
+
|
|
319
|
+
// Check if p2 is on the target bbox edge (with padding)
|
|
320
|
+
const p2OnTargetLeftEdge = Math.abs(p2.x - targetBBoxWithPadding.x) < tolerance;
|
|
321
|
+
const p2OnTargetRightEdge = Math.abs(p2.x - (targetBBoxWithPadding.x + targetBBoxWithPadding.width)) < tolerance;
|
|
322
|
+
const p2OnTargetTopEdge = Math.abs(p2.y - targetBBoxWithPadding.y) < tolerance;
|
|
323
|
+
const p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBoxWithPadding.y + targetBBoxWithPadding.height)) < tolerance;
|
|
324
|
+
const p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
|
|
325
|
+
if (p2OnTargetEdge) {
|
|
326
|
+
// Check if p1 -> p2 -> p3 forms a zigzag
|
|
327
|
+
const p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
|
|
328
|
+
const p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
|
|
329
|
+
const p1ToP2Vertical = Math.abs(p1.x - p2.x) < tolerance;
|
|
330
|
+
const p2ToP3Horizontal = Math.abs(p2.y - p3.y) < tolerance;
|
|
331
|
+
if (p1ToP2Horizontal && p2ToP3Vertical || p1ToP2Vertical && p2ToP3Horizontal) {
|
|
332
|
+
// We have a zigzag at target edge, remove p2 and p3
|
|
333
|
+
route.splice(i + 1, 2); // Remove p2 and p3
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
i++;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Insert extension point at target - always extend away from node edge by fixed distance
|
|
342
|
+
if (route.length > 0) {
|
|
343
|
+
const extensionDistance = options.extensionDistance;
|
|
344
|
+
const lastPoint = route[route.length - 1];
|
|
345
|
+
|
|
346
|
+
// Determine which edge the target anchor is on
|
|
347
|
+
const onLeft = Math.abs(targetAnchor.x - targetBBox.x) < tolerance;
|
|
348
|
+
const onRight = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) < tolerance;
|
|
349
|
+
const onTop = Math.abs(targetAnchor.y - targetBBox.y) < tolerance;
|
|
350
|
+
const onBottom = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) < tolerance;
|
|
351
|
+
|
|
352
|
+
// Insert extension point and corner point to ensure orthogonal path
|
|
353
|
+
if (onLeft) {
|
|
354
|
+
// Anchor on left edge - extend left by step + borderRadius
|
|
355
|
+
const extendX = targetAnchor.x - extensionDistance;
|
|
356
|
+
const extensionPoint = new _geometry.Point(extendX, targetAnchor.y);
|
|
357
|
+
if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
|
|
358
|
+
route.push(new _geometry.Point(extendX, lastPoint.y)); // Corner point
|
|
359
|
+
}
|
|
360
|
+
route.push(extensionPoint); // Extension point (fixed distance)
|
|
361
|
+
} else if (onRight) {
|
|
362
|
+
// Anchor on right edge - extend right by step + borderRadius
|
|
363
|
+
const extendX = targetAnchor.x + extensionDistance;
|
|
364
|
+
const extensionPoint = new _geometry.Point(extendX, targetAnchor.y);
|
|
365
|
+
if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
|
|
366
|
+
route.push(new _geometry.Point(extendX, lastPoint.y));
|
|
367
|
+
}
|
|
368
|
+
route.push(extensionPoint);
|
|
369
|
+
} else if (onTop) {
|
|
370
|
+
// Anchor on top edge - extend up by step + borderRadius
|
|
371
|
+
const extendY = targetAnchor.y - extensionDistance;
|
|
372
|
+
const extensionPoint = new _geometry.Point(targetAnchor.x, extendY);
|
|
373
|
+
if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
|
|
374
|
+
route.push(new _geometry.Point(lastPoint.x, extendY));
|
|
375
|
+
}
|
|
376
|
+
route.push(extensionPoint);
|
|
377
|
+
} else if (onBottom) {
|
|
378
|
+
// Anchor on bottom edge - extend down by step + borderRadius
|
|
379
|
+
const extendY = targetAnchor.y + extensionDistance;
|
|
380
|
+
const extensionPoint = new _geometry.Point(targetAnchor.x, extendY);
|
|
381
|
+
if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
|
|
382
|
+
route.push(new _geometry.Point(lastPoint.x, extendY));
|
|
383
|
+
}
|
|
384
|
+
route.push(extensionPoint);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Remove redundant points before target extension
|
|
389
|
+
// Similar logic for target side
|
|
390
|
+
if (route.length > 2) {
|
|
391
|
+
const lastIdx = route.length - 1;
|
|
392
|
+
const secondLastPoint = route[lastIdx - 1]; // Corner point (if exists)
|
|
393
|
+
const thirdLastPoint = route[lastIdx - 2]; // Original A* point
|
|
394
|
+
|
|
395
|
+
// Check if the third-to-last point is redundant
|
|
396
|
+
const sameX = Math.abs(thirdLastPoint.x - targetAnchor.x) < tolerance;
|
|
397
|
+
const sameY = Math.abs(thirdLastPoint.y - targetAnchor.y) < tolerance;
|
|
398
|
+
if (sameX || sameY) {
|
|
399
|
+
if (route.length > 3) {
|
|
400
|
+
const fourthLastPoint = route[lastIdx - 3];
|
|
401
|
+
const fourthToThird = Math.abs(fourthLastPoint.x - thirdLastPoint.x) < tolerance || Math.abs(fourthLastPoint.y - thirdLastPoint.y) < tolerance;
|
|
402
|
+
const thirdToSecond = Math.abs(thirdLastPoint.x - secondLastPoint.x) < tolerance || Math.abs(thirdLastPoint.y - secondLastPoint.y) < tolerance;
|
|
403
|
+
if (fourthToThird && thirdToSecond) {
|
|
404
|
+
route.splice(lastIdx - 2, 1); // Remove the third-to-last point
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Additional optimization: Remove unnecessary zigzag patterns near target
|
|
411
|
+
// Check for patterns like: (1360, 16) -> (815, 16) -> (815, -134) -> (800, -134)
|
|
412
|
+
// where (815, 16) and (815, -134) form a zigzag at the target edge
|
|
413
|
+
let i = 0;
|
|
414
|
+
while (i < route.length - 2) {
|
|
415
|
+
const p1 = route[i];
|
|
416
|
+
const p2 = route[i + 1];
|
|
417
|
+
const p3 = route[i + 2];
|
|
418
|
+
|
|
419
|
+
// Check if p2 is on the target bbox edge
|
|
420
|
+
const p2OnTargetLeftEdge = Math.abs(p2.x - targetBBox.x) < tolerance;
|
|
421
|
+
const p2OnTargetRightEdge = Math.abs(p2.x - (targetBBox.x + targetBBox.width)) < tolerance;
|
|
422
|
+
const p2OnTargetTopEdge = Math.abs(p2.y - targetBBox.y) < tolerance;
|
|
423
|
+
const p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBox.y + targetBBox.height)) < tolerance;
|
|
424
|
+
const p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
|
|
425
|
+
if (p2OnTargetEdge) {
|
|
426
|
+
// Check if p1 -> p2 -> p3 forms a zigzag
|
|
427
|
+
const p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
|
|
428
|
+
const p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
|
|
429
|
+
if (p1ToP2Horizontal && p2ToP3Vertical && i < route.length - 3) {
|
|
430
|
+
// We have horizontal -> vertical at target edge
|
|
431
|
+
// Check if we can skip p2 and p3
|
|
432
|
+
const p4 = route[i + 3];
|
|
433
|
+
const p3ToP4Horizontal = Math.abs(p3.y - p4.y) < tolerance;
|
|
434
|
+
if (p3ToP4Horizontal) {
|
|
435
|
+
// Pattern: horizontal -> vertical -> horizontal (zigzag)
|
|
436
|
+
route.splice(i + 1, 2); // Remove p2 and p3
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
i++;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Add source and target anchors to route
|
|
445
|
+
const finalRoute = [sourceAnchor, ...route, targetAnchor];
|
|
446
|
+
|
|
447
|
+
// Convert to SVG path string
|
|
448
|
+
return (0, _svg.pointsToPath)(finalRoute, options.precision, options.borderRadius);
|
|
449
|
+
}
|
package/cjs/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReactFlow Manhattan Router
|
|
3
|
+
*
|
|
4
|
+
* A Manhattan routing algorithm adapted for ReactFlow from AntV X6.
|
|
5
|
+
* Generates orthogonal (right-angle) paths that intelligently avoid obstacles.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { getManHattanPath } from './getManHattanPath';
|
|
10
|
+
export type { GetManHattanPathParams } from './getManHattanPath';
|
|
11
|
+
export type { Direction, InternalNode, NodeLookup, ManhattanRouterOptions, ResolvedOptions, } from './options';
|
|
12
|
+
export { Point, Rectangle, Line } from './geometry';
|
|
13
|
+
export { getGrid, align, snapToGrid, getDirectionAngle, getDirectionChange, getRectPoints, getCost, getKey, reconstructRoute, normalizePoint, getNodeDimensions, getNodePosition, } from './utils';
|
|
14
|
+
export type { NodeDimensions } from './utils';
|
|
15
|
+
export { pointsToPath, snapPathToGrid } from './svg';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAGhE,YAAY,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,sBAAsB,EACtB,eAAe,GAChB,MAAM,WAAW,CAAA;AAGlB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAGnD,OAAO,EACL,OAAO,EACP,KAAK,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,SAAS,CAAA;AAGhB,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA"}
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "Line", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _geometry.Line;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "Point", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _geometry.Point;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "Rectangle", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _geometry.Rectangle;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "align", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _utils.align;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "getCost", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _utils.getCost;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "getDirectionAngle", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _utils.getDirectionAngle;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(exports, "getDirectionChange", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _utils.getDirectionChange;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
Object.defineProperty(exports, "getGrid", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
get: function () {
|
|
51
|
+
return _utils.getGrid;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
Object.defineProperty(exports, "getKey", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
get: function () {
|
|
57
|
+
return _utils.getKey;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
Object.defineProperty(exports, "getManHattanPath", {
|
|
61
|
+
enumerable: true,
|
|
62
|
+
get: function () {
|
|
63
|
+
return _getManHattanPath.getManHattanPath;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
Object.defineProperty(exports, "getNodeDimensions", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
get: function () {
|
|
69
|
+
return _utils.getNodeDimensions;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
Object.defineProperty(exports, "getNodePosition", {
|
|
73
|
+
enumerable: true,
|
|
74
|
+
get: function () {
|
|
75
|
+
return _utils.getNodePosition;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
Object.defineProperty(exports, "getRectPoints", {
|
|
79
|
+
enumerable: true,
|
|
80
|
+
get: function () {
|
|
81
|
+
return _utils.getRectPoints;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
Object.defineProperty(exports, "normalizePoint", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: function () {
|
|
87
|
+
return _utils.normalizePoint;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
Object.defineProperty(exports, "pointsToPath", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
get: function () {
|
|
93
|
+
return _svg.pointsToPath;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
Object.defineProperty(exports, "reconstructRoute", {
|
|
97
|
+
enumerable: true,
|
|
98
|
+
get: function () {
|
|
99
|
+
return _utils.reconstructRoute;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
Object.defineProperty(exports, "snapPathToGrid", {
|
|
103
|
+
enumerable: true,
|
|
104
|
+
get: function () {
|
|
105
|
+
return _svg.snapPathToGrid;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
Object.defineProperty(exports, "snapToGrid", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
get: function () {
|
|
111
|
+
return _utils.snapToGrid;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
var _getManHattanPath = require("./getManHattanPath");
|
|
115
|
+
var _geometry = require("./geometry");
|
|
116
|
+
var _utils = require("./utils");
|
|
117
|
+
var _svg = require("./svg");
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Point } from '../geometry';
|
|
2
|
+
import type { ResolvedOptions, NodeLookup } from '../options';
|
|
3
|
+
/**
|
|
4
|
+
* ObstacleMap class for managing obstacles in pathfinding
|
|
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
|
|
9
|
+
*/
|
|
10
|
+
export declare class ObstacleMap {
|
|
11
|
+
private options;
|
|
12
|
+
private quadTree;
|
|
13
|
+
private obstacles;
|
|
14
|
+
private sourceAnchor?;
|
|
15
|
+
private targetAnchor?;
|
|
16
|
+
private accessibilityCache;
|
|
17
|
+
private cacheHits;
|
|
18
|
+
private cacheMisses;
|
|
19
|
+
constructor(options: ResolvedOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Get cache statistics for performance monitoring
|
|
22
|
+
*/
|
|
23
|
+
getCacheStats(): {
|
|
24
|
+
hits: number;
|
|
25
|
+
misses: number;
|
|
26
|
+
hitRate: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Clear the accessibility cache
|
|
30
|
+
*/
|
|
31
|
+
clearCache(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Build obstacle map from node lookup using QuadTree
|
|
34
|
+
*/
|
|
35
|
+
build(nodeLookup: NodeLookup, sourceNodeId: string, targetNodeId: string, sourceAnchor?: Point, targetAnchor?: Point): ObstacleMap;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a point is accessible (not inside any obstacle)
|
|
38
|
+
* Uses QuadTree for efficient spatial queries with caching
|
|
39
|
+
*/
|
|
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[];
|
|
60
|
+
/**
|
|
61
|
+
* Check accessibility using binary search optimization
|
|
62
|
+
* Tries step -> step/2 -> step/4 -> ... -> 1px
|
|
63
|
+
*/
|
|
64
|
+
private isAccessibleWithBinarySearch;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=ObstacleMap.d.ts.map
|
|
@@ -0,0 +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;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"}
|