@rxflow/manhattan 0.0.2-alpha.8 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -10
- package/esm/getManHattanPath.js +92 -69
- package/esm/obstacle/ObstacleMap.js +218 -99
- package/esm/obstacle/QuadTree.js +488 -0
- package/esm/options/resolver.js +147 -18
- package/esm/pathfinder/PathCache.js +278 -0
- package/esm/pathfinder/findRoute.js +98 -44
- package/esm/pathfinder/index.js +2 -1
- package/esm/svg/pathConverter.js +170 -1
- package/esm/utils/AdaptiveStepCalculator.js +252 -0
- package/esm/utils/ErrorRecovery.js +499 -0
- package/esm/utils/GlobalGrid.js +259 -0
- package/esm/utils/PerformanceMonitor.js +360 -0
- package/esm/utils/getAnchorPoints.js +0 -4
- package/esm/utils/grid.js +18 -13
- package/esm/utils/heuristics.js +144 -0
- package/esm/utils/index.js +7 -1
- package/esm/utils/pathProcessing.js +270 -0
- package/esm/utils/pathValidation.js +0 -1
- package/esm/utils/rect.js +11 -4
- package/esm/utils/route.js +18 -2
- package/package.json +10 -2
- package/cjs/geometry/Line.d.ts +0 -21
- package/cjs/geometry/Line.d.ts.map +0 -1
- package/cjs/geometry/Line.js +0 -88
- package/cjs/geometry/Point.d.ts +0 -49
- package/cjs/geometry/Point.d.ts.map +0 -1
- package/cjs/geometry/Point.js +0 -94
- package/cjs/geometry/Rectangle.d.ts +0 -41
- package/cjs/geometry/Rectangle.d.ts.map +0 -1
- package/cjs/geometry/Rectangle.js +0 -65
- package/cjs/geometry/collision.d.ts +0 -15
- package/cjs/geometry/collision.d.ts.map +0 -1
- package/cjs/geometry/collision.js +0 -81
- package/cjs/geometry/index.d.ts +0 -5
- package/cjs/geometry/index.d.ts.map +0 -1
- package/cjs/geometry/index.js +0 -45
- package/cjs/getManHattanPath.d.ts +0 -53
- package/cjs/getManHattanPath.d.ts.map +0 -1
- package/cjs/getManHattanPath.js +0 -418
- package/cjs/index.d.ts +0 -16
- package/cjs/index.d.ts.map +0 -1
- package/cjs/index.js +0 -117
- package/cjs/obstacle/ObstacleMap.d.ts +0 -34
- package/cjs/obstacle/ObstacleMap.d.ts.map +0 -1
- package/cjs/obstacle/ObstacleMap.js +0 -223
- package/cjs/obstacle/index.d.ts +0 -2
- package/cjs/obstacle/index.d.ts.map +0 -1
- package/cjs/obstacle/index.js +0 -12
- package/cjs/options/defaults.d.ts +0 -16
- package/cjs/options/defaults.d.ts.map +0 -1
- package/cjs/options/defaults.js +0 -39
- package/cjs/options/index.d.ts +0 -4
- package/cjs/options/index.d.ts.map +0 -1
- package/cjs/options/index.js +0 -38
- package/cjs/options/resolver.d.ts +0 -10
- package/cjs/options/resolver.d.ts.map +0 -1
- package/cjs/options/resolver.js +0 -120
- package/cjs/options/types.d.ts +0 -169
- package/cjs/options/types.d.ts.map +0 -1
- package/cjs/options/types.js +0 -5
- package/cjs/pathfinder/SortedSet.d.ts +0 -35
- package/cjs/pathfinder/SortedSet.d.ts.map +0 -1
- package/cjs/pathfinder/SortedSet.js +0 -95
- package/cjs/pathfinder/findRoute.d.ts +0 -8
- package/cjs/pathfinder/findRoute.d.ts.map +0 -1
- package/cjs/pathfinder/findRoute.js +0 -330
- package/cjs/pathfinder/index.d.ts +0 -3
- package/cjs/pathfinder/index.d.ts.map +0 -1
- package/cjs/pathfinder/index.js +0 -19
- package/cjs/svg/index.d.ts +0 -3
- package/cjs/svg/index.d.ts.map +0 -1
- package/cjs/svg/index.js +0 -31
- package/cjs/svg/pathConverter.d.ts +0 -10
- package/cjs/svg/pathConverter.d.ts.map +0 -1
- package/cjs/svg/pathConverter.js +0 -116
- package/cjs/svg/pathParser.d.ts +0 -11
- package/cjs/svg/pathParser.d.ts.map +0 -1
- package/cjs/svg/pathParser.js +0 -76
- package/cjs/utils/direction.d.ts +0 -24
- package/cjs/utils/direction.d.ts.map +0 -1
- package/cjs/utils/direction.js +0 -54
- package/cjs/utils/getAnchorPoints.d.ts +0 -15
- package/cjs/utils/getAnchorPoints.d.ts.map +0 -1
- package/cjs/utils/getAnchorPoints.js +0 -75
- package/cjs/utils/grid.d.ts +0 -27
- package/cjs/utils/grid.d.ts.map +0 -1
- package/cjs/utils/grid.js +0 -66
- package/cjs/utils/index.d.ts +0 -8
- package/cjs/utils/index.d.ts.map +0 -1
- package/cjs/utils/index.js +0 -82
- package/cjs/utils/node.d.ts +0 -27
- package/cjs/utils/node.d.ts.map +0 -1
- package/cjs/utils/node.js +0 -36
- package/cjs/utils/pathValidation.d.ts +0 -11
- package/cjs/utils/pathValidation.d.ts.map +0 -1
- package/cjs/utils/pathValidation.js +0 -130
- package/cjs/utils/rect.d.ts +0 -9
- package/cjs/utils/rect.d.ts.map +0 -1
- package/cjs/utils/rect.js +0 -103
- package/cjs/utils/route.d.ts +0 -19
- package/cjs/utils/route.d.ts.map +0 -1
- package/cjs/utils/route.js +0 -76
- package/esm/geometry/Line.d.ts +0 -21
- package/esm/geometry/Line.d.ts.map +0 -1
- package/esm/geometry/Point.d.ts +0 -49
- package/esm/geometry/Point.d.ts.map +0 -1
- package/esm/geometry/Rectangle.d.ts +0 -41
- package/esm/geometry/Rectangle.d.ts.map +0 -1
- package/esm/geometry/collision.d.ts +0 -15
- package/esm/geometry/collision.d.ts.map +0 -1
- package/esm/geometry/index.d.ts +0 -5
- package/esm/geometry/index.d.ts.map +0 -1
- package/esm/getManHattanPath.d.ts +0 -53
- package/esm/getManHattanPath.d.ts.map +0 -1
- package/esm/index.d.ts +0 -16
- package/esm/index.d.ts.map +0 -1
- package/esm/obstacle/ObstacleMap.d.ts +0 -34
- package/esm/obstacle/ObstacleMap.d.ts.map +0 -1
- package/esm/obstacle/index.d.ts +0 -2
- package/esm/obstacle/index.d.ts.map +0 -1
- package/esm/options/defaults.d.ts +0 -16
- package/esm/options/defaults.d.ts.map +0 -1
- package/esm/options/index.d.ts +0 -4
- package/esm/options/index.d.ts.map +0 -1
- package/esm/options/resolver.d.ts +0 -10
- package/esm/options/resolver.d.ts.map +0 -1
- package/esm/options/types.d.ts +0 -169
- package/esm/options/types.d.ts.map +0 -1
- package/esm/pathfinder/SortedSet.d.ts +0 -35
- package/esm/pathfinder/SortedSet.d.ts.map +0 -1
- package/esm/pathfinder/findRoute.d.ts +0 -8
- package/esm/pathfinder/findRoute.d.ts.map +0 -1
- package/esm/pathfinder/index.d.ts +0 -3
- package/esm/pathfinder/index.d.ts.map +0 -1
- package/esm/svg/index.d.ts +0 -3
- package/esm/svg/index.d.ts.map +0 -1
- package/esm/svg/pathConverter.d.ts +0 -10
- package/esm/svg/pathConverter.d.ts.map +0 -1
- package/esm/svg/pathParser.d.ts +0 -11
- package/esm/svg/pathParser.d.ts.map +0 -1
- package/esm/utils/direction.d.ts +0 -24
- package/esm/utils/direction.d.ts.map +0 -1
- package/esm/utils/getAnchorPoints.d.ts +0 -15
- package/esm/utils/getAnchorPoints.d.ts.map +0 -1
- package/esm/utils/grid.d.ts +0 -27
- package/esm/utils/grid.d.ts.map +0 -1
- package/esm/utils/index.d.ts +0 -8
- package/esm/utils/index.d.ts.map +0 -1
- package/esm/utils/node.d.ts +0 -27
- package/esm/utils/node.d.ts.map +0 -1
- package/esm/utils/pathValidation.d.ts +0 -11
- package/esm/utils/pathValidation.d.ts.map +0 -1
- package/esm/utils/rect.d.ts +0 -9
- package/esm/utils/rect.d.ts.map +0 -1
- package/esm/utils/route.d.ts +0 -19
- package/esm/utils/route.d.ts.map +0 -1
|
@@ -10,28 +10,59 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
|
|
|
10
10
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
11
11
|
import { Point, Rectangle } from "../geometry";
|
|
12
12
|
import { getNodePosition, getNodeDimensions } from "../utils";
|
|
13
|
+
import { createQuadTreeFromRects } from "./QuadTree";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* ObstacleMap class for managing obstacles in pathfinding
|
|
16
|
-
*
|
|
17
|
+
* Feature: manhattan-optimization
|
|
18
|
+
*
|
|
19
|
+
* Uses QuadTree for efficient spatial queries (O(log n) instead of O(n))
|
|
20
|
+
* Includes query caching for repeated accessibility checks
|
|
17
21
|
*/
|
|
18
22
|
export var ObstacleMap = /*#__PURE__*/function () {
|
|
19
23
|
function ObstacleMap(options) {
|
|
20
24
|
_classCallCheck(this, ObstacleMap);
|
|
21
25
|
_defineProperty(this, "options", void 0);
|
|
22
|
-
_defineProperty(this, "
|
|
23
|
-
_defineProperty(this, "
|
|
26
|
+
_defineProperty(this, "quadTree", null);
|
|
27
|
+
_defineProperty(this, "obstacles", []);
|
|
24
28
|
_defineProperty(this, "sourceAnchor", void 0);
|
|
25
29
|
_defineProperty(this, "targetAnchor", void 0);
|
|
30
|
+
// Query cache for repeated accessibility checks
|
|
31
|
+
_defineProperty(this, "accessibilityCache", new Map());
|
|
32
|
+
_defineProperty(this, "cacheHits", 0);
|
|
33
|
+
_defineProperty(this, "cacheMisses", 0);
|
|
26
34
|
this.options = options;
|
|
27
|
-
this.mapGridSize = 100;
|
|
28
|
-
this.map = new Map();
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
/**
|
|
32
|
-
*
|
|
38
|
+
* Get cache statistics for performance monitoring
|
|
33
39
|
*/
|
|
34
40
|
_createClass(ObstacleMap, [{
|
|
41
|
+
key: "getCacheStats",
|
|
42
|
+
value: function getCacheStats() {
|
|
43
|
+
var total = this.cacheHits + this.cacheMisses;
|
|
44
|
+
return {
|
|
45
|
+
hits: this.cacheHits,
|
|
46
|
+
misses: this.cacheMisses,
|
|
47
|
+
hitRate: total > 0 ? this.cacheHits / total : 0
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Clear the accessibility cache
|
|
53
|
+
*/
|
|
54
|
+
}, {
|
|
55
|
+
key: "clearCache",
|
|
56
|
+
value: function clearCache() {
|
|
57
|
+
this.accessibilityCache.clear();
|
|
58
|
+
this.cacheHits = 0;
|
|
59
|
+
this.cacheMisses = 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build obstacle map from node lookup using QuadTree
|
|
64
|
+
*/
|
|
65
|
+
}, {
|
|
35
66
|
key: "build",
|
|
36
67
|
value: function build(nodeLookup, sourceNodeId, targetNodeId, sourceAnchor, targetAnchor) {
|
|
37
68
|
var _this = this;
|
|
@@ -45,17 +76,19 @@ export var ObstacleMap = /*#__PURE__*/function () {
|
|
|
45
76
|
this.sourceAnchor = sourceAnchor;
|
|
46
77
|
this.targetAnchor = targetAnchor;
|
|
47
78
|
|
|
48
|
-
//
|
|
79
|
+
// Clear previous data
|
|
80
|
+
this.obstacles = [];
|
|
81
|
+
this.clearCache();
|
|
82
|
+
|
|
83
|
+
// Collect all obstacles
|
|
49
84
|
nodeLookup.forEach(function (node) {
|
|
50
85
|
// Check if node should be excluded
|
|
51
86
|
var isExcludedNode = excludeNodes.includes(node.id);
|
|
52
87
|
var isExcludedShape = node.type ? excludeShapes.includes(node.type) : false;
|
|
53
88
|
var isSourceTerminal = excludeTerminals.includes('source') && node.id === sourceNodeId;
|
|
54
89
|
var isTargetTerminal = excludeTerminals.includes('target') && node.id === targetNodeId;
|
|
55
|
-
var isSource = node.id === sourceNodeId;
|
|
56
|
-
var isTarget = node.id === targetNodeId;
|
|
57
90
|
|
|
58
|
-
// Skip if node should be excluded
|
|
91
|
+
// Skip if node should be excluded
|
|
59
92
|
if (isExcludedNode || isExcludedShape || isSourceTerminal || isTargetTerminal) {
|
|
60
93
|
return;
|
|
61
94
|
}
|
|
@@ -64,129 +97,215 @@ export var ObstacleMap = /*#__PURE__*/function () {
|
|
|
64
97
|
var position = getNodePosition(node);
|
|
65
98
|
var dimensions = getNodeDimensions(node);
|
|
66
99
|
var bbox = new Rectangle(position.x, position.y, dimensions.width, dimensions.height).moveAndExpand(paddingBox);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
console.log('[ObstacleMap] Adding source node as obstacle:');
|
|
72
|
-
console.log(' Node ID:', node.id);
|
|
73
|
-
console.log(' BBox:', "(".concat(bbox.x, ", ").concat(bbox.y, ", ").concat(bbox.width, ", ").concat(bbox.height, ")"));
|
|
74
|
-
console.log(' Anchor:', sourceAnchor ? "(".concat(sourceAnchor.x, ", ").concat(sourceAnchor.y, ")") : 'none');
|
|
75
|
-
} else if (isTarget) {
|
|
76
|
-
console.log('[ObstacleMap] Adding target node as obstacle:');
|
|
77
|
-
console.log(' Node ID:', node.id);
|
|
78
|
-
console.log(' BBox:', "(".concat(bbox.x, ", ").concat(bbox.y, ", ").concat(bbox.width, ", ").concat(bbox.height, ")"));
|
|
79
|
-
console.log(' Anchor:', targetAnchor ? "(".concat(targetAnchor.x, ", ").concat(targetAnchor.y, ")") : 'none');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Map bbox to grid cells
|
|
83
|
-
var origin = bbox.getOrigin().snapToGrid(_this.mapGridSize);
|
|
84
|
-
var corner = bbox.getCorner().snapToGrid(_this.mapGridSize);
|
|
85
|
-
for (var x = origin.x; x <= corner.x; x += _this.mapGridSize) {
|
|
86
|
-
for (var y = origin.y; y <= corner.y; y += _this.mapGridSize) {
|
|
87
|
-
var key = new Point(x, y).toString();
|
|
88
|
-
if (!_this.map.has(key)) {
|
|
89
|
-
_this.map.set(key, []);
|
|
90
|
-
}
|
|
91
|
-
_this.map.get(key).push(bbox);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
100
|
+
_this.obstacles.push({
|
|
101
|
+
bounds: bbox,
|
|
102
|
+
nodeId: node.id
|
|
103
|
+
});
|
|
94
104
|
});
|
|
95
|
-
return this;
|
|
96
|
-
}
|
|
97
105
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
var step = this.options.step || 10;
|
|
107
|
-
var margin = step * 3; // Increased margin for better clearance
|
|
108
|
-
|
|
109
|
-
// Determine which edge the anchor is on (relative to original node position)
|
|
110
|
-
var onLeft = Math.abs(anchor.x - nodePosition.x) < tolerance;
|
|
111
|
-
var onRight = Math.abs(anchor.x - (nodePosition.x + nodeDimensions.width)) < tolerance;
|
|
112
|
-
var onTop = Math.abs(anchor.y - nodePosition.y) < tolerance;
|
|
113
|
-
var onBottom = Math.abs(anchor.y - (nodePosition.y + nodeDimensions.height)) < tolerance;
|
|
114
|
-
console.log('[shrinkBBoxAroundAnchor] Edge detection:');
|
|
115
|
-
console.log(' anchor.x:', anchor.x, 'nodePosition.x:', nodePosition.x, 'diff:', Math.abs(anchor.x - nodePosition.x));
|
|
116
|
-
console.log(' anchor.x:', anchor.x, 'nodeRight:', nodePosition.x + nodeDimensions.width, 'diff:', Math.abs(anchor.x - (nodePosition.x + nodeDimensions.width)));
|
|
117
|
-
console.log(' onLeft:', onLeft, 'onRight:', onRight, 'onTop:', onTop, 'onBottom:', onBottom);
|
|
118
|
-
console.log(' margin:', margin);
|
|
119
|
-
|
|
120
|
-
// Shrink bbox based on anchor position
|
|
121
|
-
if (onLeft) {
|
|
122
|
-
// Anchor on left edge - exclude left portion
|
|
123
|
-
return new Rectangle(bbox.x + margin, bbox.y, Math.max(0, bbox.width - margin), bbox.height);
|
|
124
|
-
} else if (onRight) {
|
|
125
|
-
// Anchor on right edge - exclude right portion
|
|
126
|
-
return new Rectangle(bbox.x, bbox.y, Math.max(0, bbox.width - margin), bbox.height);
|
|
127
|
-
} else if (onTop) {
|
|
128
|
-
// Anchor on top edge - exclude top portion
|
|
129
|
-
return new Rectangle(bbox.x, bbox.y + margin, bbox.width, Math.max(0, bbox.height - margin));
|
|
130
|
-
} else if (onBottom) {
|
|
131
|
-
// Anchor on bottom edge - exclude bottom portion
|
|
132
|
-
return new Rectangle(bbox.x, bbox.y, bbox.width, Math.max(0, bbox.height - margin));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// If anchor is not on an edge, return original bbox
|
|
136
|
-
return bbox;
|
|
106
|
+
// Build QuadTree from obstacles
|
|
107
|
+
this.quadTree = createQuadTreeFromRects(this.obstacles.map(function (o) {
|
|
108
|
+
return {
|
|
109
|
+
bounds: o.bounds,
|
|
110
|
+
data: o.nodeId
|
|
111
|
+
};
|
|
112
|
+
}));
|
|
113
|
+
return this;
|
|
137
114
|
}
|
|
138
115
|
|
|
139
116
|
/**
|
|
140
117
|
* Check if a point is accessible (not inside any obstacle)
|
|
141
|
-
* Uses
|
|
118
|
+
* Uses QuadTree for efficient spatial queries with caching
|
|
142
119
|
*/
|
|
143
120
|
}, {
|
|
144
121
|
key: "isAccessible",
|
|
145
122
|
value: function isAccessible(point) {
|
|
146
123
|
var checkRadius = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
147
|
-
|
|
148
|
-
var
|
|
149
|
-
|
|
150
|
-
|
|
124
|
+
// Generate cache key
|
|
125
|
+
var cacheKey = "".concat(point.x, ",").concat(point.y, ",").concat(checkRadius);
|
|
126
|
+
|
|
127
|
+
// Check cache first
|
|
128
|
+
if (this.accessibilityCache.has(cacheKey)) {
|
|
129
|
+
this.cacheHits++;
|
|
130
|
+
return this.accessibilityCache.get(cacheKey);
|
|
151
131
|
}
|
|
132
|
+
this.cacheMisses++;
|
|
152
133
|
|
|
153
134
|
// Check if point is near an anchor - if so, allow it
|
|
154
135
|
var margin = (this.options.step || 10) * 3;
|
|
155
136
|
if (this.sourceAnchor) {
|
|
156
137
|
var distToSource = Math.abs(point.x - this.sourceAnchor.x) + Math.abs(point.y - this.sourceAnchor.y);
|
|
157
138
|
if (distToSource < margin) {
|
|
158
|
-
|
|
139
|
+
this.accessibilityCache.set(cacheKey, true);
|
|
159
140
|
return true;
|
|
160
141
|
}
|
|
161
142
|
}
|
|
162
143
|
if (this.targetAnchor) {
|
|
163
144
|
var distToTarget = Math.abs(point.x - this.targetAnchor.x) + Math.abs(point.y - this.targetAnchor.y);
|
|
164
145
|
if (distToTarget < margin) {
|
|
165
|
-
|
|
146
|
+
this.accessibilityCache.set(cacheKey, true);
|
|
166
147
|
return true;
|
|
167
148
|
}
|
|
168
149
|
}
|
|
169
150
|
|
|
151
|
+
// If no QuadTree, point is accessible
|
|
152
|
+
if (!this.quadTree) {
|
|
153
|
+
this.accessibilityCache.set(cacheKey, true);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Query QuadTree for obstacles at this point
|
|
158
|
+
var obstacles = this.quadTree.queryPoint(point);
|
|
159
|
+
|
|
170
160
|
// If checkRadius is specified, use binary search to find accessible points
|
|
171
|
-
if (checkRadius > 0) {
|
|
172
|
-
|
|
161
|
+
if (checkRadius > 0 && obstacles.length > 0) {
|
|
162
|
+
var result = this.isAccessibleWithBinarySearch(point, checkRadius, obstacles.map(function (o) {
|
|
163
|
+
return o.bounds;
|
|
164
|
+
}));
|
|
165
|
+
this.accessibilityCache.set(cacheKey, result);
|
|
166
|
+
return result;
|
|
173
167
|
}
|
|
174
|
-
var accessible =
|
|
175
|
-
|
|
176
|
-
|
|
168
|
+
var accessible = obstacles.length === 0;
|
|
169
|
+
this.accessibilityCache.set(cacheKey, accessible);
|
|
170
|
+
return accessible;
|
|
171
|
+
}
|
|
177
172
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Check if an anchor point has sufficient clearance around it
|
|
175
|
+
* This ensures the path can start/end at the anchor without being blocked
|
|
176
|
+
*/
|
|
177
|
+
}, {
|
|
178
|
+
key: "hasAnchorClearance",
|
|
179
|
+
value: function hasAnchorClearance(anchor, direction) {
|
|
180
|
+
var step = this.options.step || 10;
|
|
181
|
+
var clearanceDistance = step * 2;
|
|
182
|
+
|
|
183
|
+
// Direction vectors
|
|
184
|
+
var directionVectors = {
|
|
185
|
+
top: {
|
|
186
|
+
dx: 0,
|
|
187
|
+
dy: -1
|
|
188
|
+
},
|
|
189
|
+
right: {
|
|
190
|
+
dx: 1,
|
|
191
|
+
dy: 0
|
|
192
|
+
},
|
|
193
|
+
bottom: {
|
|
194
|
+
dx: 0,
|
|
195
|
+
dy: 1
|
|
196
|
+
},
|
|
197
|
+
left: {
|
|
198
|
+
dx: -1,
|
|
199
|
+
dy: 0
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var dir = directionVectors[direction];
|
|
203
|
+
if (!dir) return true;
|
|
204
|
+
|
|
205
|
+
// Check points along the direction
|
|
206
|
+
for (var dist = step; dist <= clearanceDistance; dist += step) {
|
|
207
|
+
var testPoint = new Point(anchor.x + dir.dx * dist, anchor.y + dir.dy * dist);
|
|
208
|
+
|
|
209
|
+
// Temporarily remove anchor from consideration
|
|
210
|
+
var originalSourceAnchor = this.sourceAnchor;
|
|
211
|
+
var originalTargetAnchor = this.targetAnchor;
|
|
212
|
+
this.sourceAnchor = undefined;
|
|
213
|
+
this.targetAnchor = undefined;
|
|
214
|
+
var accessible = this.isAccessibleWithoutCache(testPoint);
|
|
215
|
+
|
|
216
|
+
// Restore anchors
|
|
217
|
+
this.sourceAnchor = originalSourceAnchor;
|
|
218
|
+
this.targetAnchor = originalTargetAnchor;
|
|
181
219
|
if (!accessible) {
|
|
182
|
-
|
|
183
|
-
if (rect.containsPoint(point)) {
|
|
184
|
-
console.log(" Blocked by rect ".concat(i, ": (").concat(rect.x, ", ").concat(rect.y, ", ").concat(rect.width, ", ").concat(rect.height, ")"));
|
|
185
|
-
}
|
|
186
|
-
});
|
|
220
|
+
return false;
|
|
187
221
|
}
|
|
188
222
|
}
|
|
189
|
-
return
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Check accessibility without using cache (for internal use)
|
|
228
|
+
*/
|
|
229
|
+
}, {
|
|
230
|
+
key: "isAccessibleWithoutCache",
|
|
231
|
+
value: function isAccessibleWithoutCache(point) {
|
|
232
|
+
if (!this.quadTree) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
var obstacles = this.quadTree.queryPoint(point);
|
|
236
|
+
return obstacles.length === 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Find the nearest accessible point from an anchor in a given direction
|
|
241
|
+
* Uses binary search for efficiency
|
|
242
|
+
*/
|
|
243
|
+
}, {
|
|
244
|
+
key: "findNearestAccessiblePoint",
|
|
245
|
+
value: function findNearestAccessiblePoint(anchor, direction, maxDistance) {
|
|
246
|
+
var directionVectors = {
|
|
247
|
+
top: {
|
|
248
|
+
dx: 0,
|
|
249
|
+
dy: -1
|
|
250
|
+
},
|
|
251
|
+
right: {
|
|
252
|
+
dx: 1,
|
|
253
|
+
dy: 0
|
|
254
|
+
},
|
|
255
|
+
bottom: {
|
|
256
|
+
dx: 0,
|
|
257
|
+
dy: 1
|
|
258
|
+
},
|
|
259
|
+
left: {
|
|
260
|
+
dx: -1,
|
|
261
|
+
dy: 0
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var dir = directionVectors[direction];
|
|
265
|
+
if (!dir) return null;
|
|
266
|
+
var step = this.options.step || 10;
|
|
267
|
+
|
|
268
|
+
// First, find an accessible point using linear search
|
|
269
|
+
var foundDistance = -1;
|
|
270
|
+
for (var dist = step; dist <= maxDistance; dist += step) {
|
|
271
|
+
var testPoint = new Point(anchor.x + dir.dx * dist, anchor.y + dir.dy * dist);
|
|
272
|
+
if (this.isAccessible(testPoint)) {
|
|
273
|
+
foundDistance = dist;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (foundDistance < 0) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Binary search to find the closest accessible point
|
|
282
|
+
var left = Math.max(0, foundDistance - step);
|
|
283
|
+
var right = foundDistance;
|
|
284
|
+
var bestDistance = foundDistance;
|
|
285
|
+
while (right - left > 1) {
|
|
286
|
+
var mid = Math.floor((left + right) / 2);
|
|
287
|
+
var _testPoint = new Point(anchor.x + dir.dx * mid, anchor.y + dir.dy * mid);
|
|
288
|
+
if (this.isAccessible(_testPoint)) {
|
|
289
|
+
bestDistance = mid;
|
|
290
|
+
right = mid;
|
|
291
|
+
} else {
|
|
292
|
+
left = mid;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return new Point(anchor.x + dir.dx * bestDistance, anchor.y + dir.dy * bestDistance);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Batch check accessibility for multiple points (optimized)
|
|
300
|
+
* Returns array of booleans in same order as input points
|
|
301
|
+
*/
|
|
302
|
+
}, {
|
|
303
|
+
key: "areAccessible",
|
|
304
|
+
value: function areAccessible(points) {
|
|
305
|
+
var _this2 = this;
|
|
306
|
+
return points.map(function (point) {
|
|
307
|
+
return _this2.isAccessible(point);
|
|
308
|
+
});
|
|
190
309
|
}
|
|
191
310
|
|
|
192
311
|
/**
|