@rxflow/manhattan 0.0.1-alpha.1 → 0.0.1-alpha.11
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/getManHattanPath.d.ts.map +1 -1
- package/cjs/getManHattanPath.js +21 -19
- package/cjs/options/defaults.js +1 -1
- package/cjs/svg/pathParser.d.ts.map +1 -1
- package/cjs/svg/pathParser.js +3 -2
- package/cjs/utils/grid.d.ts.map +1 -1
- package/cjs/utils/grid.js +3 -17
- package/cjs/utils/pathValidation.d.ts +5 -1
- package/cjs/utils/pathValidation.d.ts.map +1 -1
- package/cjs/utils/pathValidation.js +86 -51
- package/esm/getManHattanPath.d.ts.map +1 -1
- package/esm/getManHattanPath.js +29 -27
- package/esm/options/defaults.js +1 -1
- package/esm/svg/pathParser.d.ts.map +1 -1
- package/esm/svg/pathParser.js +3 -2
- package/esm/utils/grid.d.ts.map +1 -1
- package/esm/utils/grid.js +3 -17
- package/esm/utils/pathValidation.d.ts +5 -1
- package/esm/utils/pathValidation.d.ts.map +1 -1
- package/esm/utils/pathValidation.js +108 -55
- package/package.json +1 -1
|
@@ -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,EAAE,MAAM,WAAW,CAAA;AAMnE;;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,
|
|
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,EAAE,MAAM,WAAW,CAAA;AAMnE;;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,CAwRvE"}
|
package/cjs/getManHattanPath.js
CHANGED
|
@@ -96,7 +96,7 @@ function getManHattanPath(params) {
|
|
|
96
96
|
const smoothStepPoints = (0, _svg.parseSVGPath)(smoothStepPath);
|
|
97
97
|
|
|
98
98
|
// Check if smooth step path intersects with any obstacles
|
|
99
|
-
if (smoothStepPoints.length > 0 && !(0, _utils.pathIntersectsObstacles)(smoothStepPoints, nodeLookup
|
|
99
|
+
if (smoothStepPoints.length > 0 && !(0, _utils.pathIntersectsObstacles)(smoothStepPoints, nodeLookup)) {
|
|
100
100
|
console.log('[getManHattanPath] Using ReactFlow getSmoothStepPath (no obstacles)');
|
|
101
101
|
return smoothStepPath;
|
|
102
102
|
}
|
|
@@ -161,7 +161,9 @@ function getManHattanPath(params) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// Insert extension point at source - always extend away from node edge by fixed step distance
|
|
164
|
+
// Add borderRadius to extension distance to account for rounded corners
|
|
164
165
|
if (route.length > 0) {
|
|
166
|
+
const extensionDistance = step + options.borderRadius;
|
|
165
167
|
const firstPoint = route[0];
|
|
166
168
|
|
|
167
169
|
// Determine which edge the source anchor is on
|
|
@@ -172,8 +174,8 @@ function getManHattanPath(params) {
|
|
|
172
174
|
|
|
173
175
|
// Insert extension point and corner point to ensure orthogonal path
|
|
174
176
|
if (onRight) {
|
|
175
|
-
// Anchor on right edge - extend right by
|
|
176
|
-
const extendX = sourceAnchor.x +
|
|
177
|
+
// Anchor on right edge - extend right by step + borderRadius
|
|
178
|
+
const extendX = sourceAnchor.x + extensionDistance;
|
|
177
179
|
const extensionPoint = new _geometry.Point(extendX, sourceAnchor.y);
|
|
178
180
|
// Check if we need a corner point
|
|
179
181
|
if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
|
|
@@ -182,8 +184,8 @@ function getManHattanPath(params) {
|
|
|
182
184
|
route.unshift(extensionPoint); // Extension point (fixed distance)
|
|
183
185
|
console.log('[getManHattanPath] Inserted source extension (right):', `(${extendX}, ${sourceAnchor.y})`);
|
|
184
186
|
} else if (onLeft) {
|
|
185
|
-
// Anchor on left edge - extend left by
|
|
186
|
-
const extendX = sourceAnchor.x -
|
|
187
|
+
// Anchor on left edge - extend left by step + borderRadius
|
|
188
|
+
const extendX = sourceAnchor.x - extensionDistance;
|
|
187
189
|
const extensionPoint = new _geometry.Point(extendX, sourceAnchor.y);
|
|
188
190
|
if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
|
|
189
191
|
route.unshift(new _geometry.Point(extendX, firstPoint.y));
|
|
@@ -191,8 +193,8 @@ function getManHattanPath(params) {
|
|
|
191
193
|
route.unshift(extensionPoint);
|
|
192
194
|
console.log('[getManHattanPath] Inserted source extension (left):', `(${extendX}, ${sourceAnchor.y})`);
|
|
193
195
|
} else if (onBottom) {
|
|
194
|
-
// Anchor on bottom edge - extend down by
|
|
195
|
-
const extendY = sourceAnchor.y +
|
|
196
|
+
// Anchor on bottom edge - extend down by step + borderRadius
|
|
197
|
+
const extendY = sourceAnchor.y + extensionDistance;
|
|
196
198
|
const extensionPoint = new _geometry.Point(sourceAnchor.x, extendY);
|
|
197
199
|
if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
|
|
198
200
|
route.unshift(new _geometry.Point(firstPoint.x, extendY));
|
|
@@ -200,8 +202,8 @@ function getManHattanPath(params) {
|
|
|
200
202
|
route.unshift(extensionPoint);
|
|
201
203
|
console.log('[getManHattanPath] Inserted source extension (down):', `(${sourceAnchor.x}, ${extendY})`);
|
|
202
204
|
} else if (onTop) {
|
|
203
|
-
// Anchor on top edge - extend up by
|
|
204
|
-
const extendY = sourceAnchor.y -
|
|
205
|
+
// Anchor on top edge - extend up by step + borderRadius
|
|
206
|
+
const extendY = sourceAnchor.y - extensionDistance;
|
|
205
207
|
const extensionPoint = new _geometry.Point(sourceAnchor.x, extendY);
|
|
206
208
|
if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
|
|
207
209
|
route.unshift(new _geometry.Point(firstPoint.x, extendY));
|
|
@@ -212,9 +214,9 @@ function getManHattanPath(params) {
|
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
// Insert extension point at target - always extend away from node edge by fixed step distance
|
|
217
|
+
// Add borderRadius to extension distance to account for rounded corners
|
|
215
218
|
if (route.length > 0) {
|
|
216
|
-
const
|
|
217
|
-
const tolerance = 1;
|
|
219
|
+
const extensionDistance = step + options.borderRadius;
|
|
218
220
|
const lastPoint = route[route.length - 1];
|
|
219
221
|
|
|
220
222
|
// Determine which edge the target anchor is on
|
|
@@ -225,8 +227,8 @@ function getManHattanPath(params) {
|
|
|
225
227
|
|
|
226
228
|
// Insert extension point and corner point to ensure orthogonal path
|
|
227
229
|
if (onLeft) {
|
|
228
|
-
// Anchor on left edge - extend left by
|
|
229
|
-
const extendX = targetAnchor.x -
|
|
230
|
+
// Anchor on left edge - extend left by step + borderRadius
|
|
231
|
+
const extendX = targetAnchor.x - extensionDistance;
|
|
230
232
|
const extensionPoint = new _geometry.Point(extendX, targetAnchor.y);
|
|
231
233
|
if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
|
|
232
234
|
route.push(new _geometry.Point(extendX, lastPoint.y)); // Corner point
|
|
@@ -234,8 +236,8 @@ function getManHattanPath(params) {
|
|
|
234
236
|
route.push(extensionPoint); // Extension point (fixed distance)
|
|
235
237
|
console.log('[getManHattanPath] Inserted target extension (left):', `(${extendX}, ${targetAnchor.y})`);
|
|
236
238
|
} else if (onRight) {
|
|
237
|
-
// Anchor on right edge - extend right by
|
|
238
|
-
const extendX = targetAnchor.x +
|
|
239
|
+
// Anchor on right edge - extend right by step + borderRadius
|
|
240
|
+
const extendX = targetAnchor.x + extensionDistance;
|
|
239
241
|
const extensionPoint = new _geometry.Point(extendX, targetAnchor.y);
|
|
240
242
|
if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
|
|
241
243
|
route.push(new _geometry.Point(extendX, lastPoint.y));
|
|
@@ -243,8 +245,8 @@ function getManHattanPath(params) {
|
|
|
243
245
|
route.push(extensionPoint);
|
|
244
246
|
console.log('[getManHattanPath] Inserted target extension (right):', `(${extendX}, ${targetAnchor.y})`);
|
|
245
247
|
} else if (onTop) {
|
|
246
|
-
// Anchor on top edge - extend up by
|
|
247
|
-
const extendY = targetAnchor.y -
|
|
248
|
+
// Anchor on top edge - extend up by step + borderRadius
|
|
249
|
+
const extendY = targetAnchor.y - extensionDistance;
|
|
248
250
|
const extensionPoint = new _geometry.Point(targetAnchor.x, extendY);
|
|
249
251
|
if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
|
|
250
252
|
route.push(new _geometry.Point(lastPoint.x, extendY));
|
|
@@ -252,8 +254,8 @@ function getManHattanPath(params) {
|
|
|
252
254
|
route.push(extensionPoint);
|
|
253
255
|
console.log('[getManHattanPath] Inserted target extension (up):', `(${targetAnchor.x}, ${extendY})`);
|
|
254
256
|
} else if (onBottom) {
|
|
255
|
-
// Anchor on bottom edge - extend down by
|
|
256
|
-
const extendY = targetAnchor.y +
|
|
257
|
+
// Anchor on bottom edge - extend down by step + borderRadius
|
|
258
|
+
const extendY = targetAnchor.y + extensionDistance;
|
|
257
259
|
const extensionPoint = new _geometry.Point(targetAnchor.x, extendY);
|
|
258
260
|
if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
|
|
259
261
|
route.push(new _geometry.Point(lastPoint.x, extendY));
|
package/cjs/options/defaults.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathParser.d.ts","sourceRoot":"","sources":["../../src/svg/pathParser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"pathParser.d.ts","sourceRoot":"","sources":["../../src/svg/pathParser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,CA2CxD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CA0BrD"}
|
package/cjs/svg/pathParser.js
CHANGED
|
@@ -33,8 +33,9 @@ function parseSVGPath(pathString) {
|
|
|
33
33
|
const x = coords[2];
|
|
34
34
|
const y = coords[3];
|
|
35
35
|
|
|
36
|
-
// Sample
|
|
37
|
-
|
|
36
|
+
// Sample 10 points along the bezier curve for better accuracy
|
|
37
|
+
// This ensures we don't miss intersections with obstacles
|
|
38
|
+
for (let t = 0.1; t <= 1; t += 0.1) {
|
|
38
39
|
const bx = (1 - t) * (1 - t) * prevPoint.x + 2 * (1 - t) * t * cx + t * t * x;
|
|
39
40
|
const by = (1 - t) * (1 - t) * prevPoint.y + 2 * (1 - t) * t * cy + t * t * y;
|
|
40
41
|
points.push(new _geometry.Point(bx, by));
|
package/cjs/utils/grid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../src/utils/grid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,KAAK,CAAA;IACb,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../src/utils/grid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,KAAK,CAAA;IACb,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAWD;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,IAAI,CAMxE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AAYD;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAExE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAE5D"}
|
package/cjs/utils/grid.js
CHANGED
|
@@ -16,23 +16,9 @@ var _geometry = require("../geometry");
|
|
|
16
16
|
* Get grid dimension for a single axis
|
|
17
17
|
*/
|
|
18
18
|
function getGridDimension(diff, step) {
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
const abs = Math.abs(diff);
|
|
24
|
-
const count = Math.round(abs / step);
|
|
25
|
-
|
|
26
|
-
// Return abs if less than one step apart
|
|
27
|
-
if (!count) {
|
|
28
|
-
return abs;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Otherwise, return corrected step
|
|
32
|
-
const roundedDiff = count * step;
|
|
33
|
-
const remainder = abs - roundedDiff;
|
|
34
|
-
const correction = remainder / count;
|
|
35
|
-
return step + correction;
|
|
19
|
+
// Always return fixed step size to maintain consistent padding
|
|
20
|
+
// This ensures paths stay at least 'padding' distance from obstacles
|
|
21
|
+
return step;
|
|
36
22
|
}
|
|
37
23
|
|
|
38
24
|
/**
|
|
@@ -2,6 +2,10 @@ import { Point } from '../geometry';
|
|
|
2
2
|
import type { NodeLookup } from '../options';
|
|
3
3
|
/**
|
|
4
4
|
* Check if a path intersects with any obstacles (nodes)
|
|
5
|
+
* A path is considered to intersect if it has >= 2 unique intersection points with a node
|
|
6
|
+
*
|
|
7
|
+
* Note: pathPoints should be pre-processed by parseSVGPath which samples bezier curves
|
|
8
|
+
* into line segments, so this function works correctly with curved paths
|
|
5
9
|
*/
|
|
6
|
-
export declare function pathIntersectsObstacles(pathPoints: Point[], nodeLookup: NodeLookup
|
|
10
|
+
export declare function pathIntersectsObstacles(pathPoints: Point[], nodeLookup: NodeLookup): boolean;
|
|
7
11
|
//# sourceMappingURL=pathValidation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathValidation.d.ts","sourceRoot":"","sources":["../../src/utils/pathValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"pathValidation.d.ts","sourceRoot":"","sources":["../../src/utils/pathValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAyF5C;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAoC5F"}
|
|
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.pathIntersectsObstacles = pathIntersectsObstacles;
|
|
7
7
|
var _geometry = require("../geometry");
|
|
8
|
-
var _collision = require("../geometry/collision");
|
|
9
8
|
var _node = require("./node");
|
|
10
9
|
/**
|
|
11
10
|
* Get node bounding box
|
|
@@ -19,77 +18,113 @@ function getNodeBBox(nodeId, nodeLookup) {
|
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
|
-
*
|
|
21
|
+
* Find unique intersection points between a line segment and a rectangle
|
|
22
|
+
* Returns array of intersection points (deduplicated)
|
|
23
23
|
*/
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
function findSegmentRectIntersections(p1, p2, rect) {
|
|
25
|
+
const intersections = [];
|
|
26
|
+
const tolerance = 0.01;
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
// Define rectangle edges
|
|
29
|
+
const edges = [{
|
|
30
|
+
start: new _geometry.Point(rect.x, rect.y),
|
|
31
|
+
end: new _geometry.Point(rect.x + rect.width, rect.y)
|
|
32
|
+
},
|
|
33
|
+
// Top
|
|
34
|
+
{
|
|
35
|
+
start: new _geometry.Point(rect.x + rect.width, rect.y),
|
|
36
|
+
end: new _geometry.Point(rect.x + rect.width, rect.y + rect.height)
|
|
37
|
+
},
|
|
38
|
+
// Right
|
|
39
|
+
{
|
|
40
|
+
start: new _geometry.Point(rect.x + rect.width, rect.y + rect.height),
|
|
41
|
+
end: new _geometry.Point(rect.x, rect.y + rect.height)
|
|
42
|
+
},
|
|
43
|
+
// Bottom
|
|
44
|
+
{
|
|
45
|
+
start: new _geometry.Point(rect.x, rect.y + rect.height),
|
|
46
|
+
end: new _geometry.Point(rect.x, rect.y)
|
|
47
|
+
} // Left
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// Find intersection point with each edge
|
|
51
|
+
for (const edge of edges) {
|
|
52
|
+
const intersection = getLineSegmentIntersection(p1, p2, edge.start, edge.end);
|
|
53
|
+
if (intersection) {
|
|
54
|
+
// Check if this point is already in the list (avoid duplicates at corners)
|
|
55
|
+
const isDuplicate = intersections.some(existing => Math.abs(existing.x - intersection.x) < tolerance && Math.abs(existing.y - intersection.y) < tolerance);
|
|
56
|
+
if (!isDuplicate) {
|
|
57
|
+
intersections.push(intersection);
|
|
58
|
+
}
|
|
43
59
|
}
|
|
44
60
|
}
|
|
45
|
-
return
|
|
61
|
+
return intersections;
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
/**
|
|
49
|
-
*
|
|
65
|
+
* Get intersection point between two line segments (if exists)
|
|
50
66
|
*/
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if ((0, _collision.lineSegmentIntersectsRect)(p1, p2, nodeBBox)) {
|
|
61
|
-
console.log(`[pathIntersectsObstacles] Segment intersects node ${nodeId}`);
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
67
|
+
function getLineSegmentIntersection(p1, p2, p3, p4) {
|
|
68
|
+
const d1 = direction(p3, p4, p1);
|
|
69
|
+
const d2 = direction(p3, p4, p2);
|
|
70
|
+
const d3 = direction(p1, p2, p3);
|
|
71
|
+
const d4 = direction(p1, p2, p4);
|
|
72
|
+
if ((d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0)) {
|
|
73
|
+
// Lines intersect, calculate intersection point
|
|
74
|
+
const t = ((p3.x - p1.x) * (p3.y - p4.y) - (p3.y - p1.y) * (p3.x - p4.x)) / ((p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x));
|
|
75
|
+
return new _geometry.Point(p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y));
|
|
64
76
|
}
|
|
65
|
-
|
|
77
|
+
|
|
78
|
+
// Check collinear cases
|
|
79
|
+
if (d1 === 0 && onSegment(p3, p1, p4)) return p1.clone();
|
|
80
|
+
if (d2 === 0 && onSegment(p3, p2, p4)) return p2.clone();
|
|
81
|
+
if (d3 === 0 && onSegment(p1, p3, p2)) return p3.clone();
|
|
82
|
+
if (d4 === 0 && onSegment(p1, p4, p2)) return p4.clone();
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
function direction(p1, p2, p3) {
|
|
86
|
+
return (p3.x - p1.x) * (p2.y - p1.y) - (p2.x - p1.x) * (p3.y - p1.y);
|
|
87
|
+
}
|
|
88
|
+
function onSegment(p1, p2, p3) {
|
|
89
|
+
return p2.x >= Math.min(p1.x, p3.x) && p2.x <= Math.max(p1.x, p3.x) && p2.y >= Math.min(p1.y, p3.y) && p2.y <= Math.max(p1.y, p3.y);
|
|
66
90
|
}
|
|
67
91
|
|
|
68
92
|
/**
|
|
69
93
|
* Check if a path intersects with any obstacles (nodes)
|
|
94
|
+
* A path is considered to intersect if it has >= 2 unique intersection points with a node
|
|
95
|
+
*
|
|
96
|
+
* Note: pathPoints should be pre-processed by parseSVGPath which samples bezier curves
|
|
97
|
+
* into line segments, so this function works correctly with curved paths
|
|
70
98
|
*/
|
|
71
|
-
function pathIntersectsObstacles(pathPoints, nodeLookup
|
|
72
|
-
|
|
73
|
-
|
|
99
|
+
function pathIntersectsObstacles(pathPoints, nodeLookup) {
|
|
100
|
+
const tolerance = 0.01;
|
|
101
|
+
|
|
102
|
+
// Iterate through all nodes (including source and target)
|
|
103
|
+
for (const [nodeId] of nodeLookup) {
|
|
74
104
|
const nodeBBox = getNodeBBox(nodeId, nodeLookup);
|
|
75
105
|
if (!nodeBBox) continue;
|
|
76
|
-
const
|
|
106
|
+
const allIntersections = [];
|
|
77
107
|
|
|
78
|
-
//
|
|
79
|
-
for (let i = 0; i < pathPoints.length; i++) {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
108
|
+
// Collect all unique intersection points for this node
|
|
109
|
+
for (let i = 0; i < pathPoints.length - 1; i++) {
|
|
110
|
+
const p1 = pathPoints[i];
|
|
111
|
+
const p2 = pathPoints[i + 1];
|
|
112
|
+
const segmentIntersections = findSegmentRectIntersections(p1, p2, nodeBBox);
|
|
84
113
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
|
|
114
|
+
// Add to global list, avoiding duplicates
|
|
115
|
+
for (const intersection of segmentIntersections) {
|
|
116
|
+
const isDuplicate = allIntersections.some(existing => Math.abs(existing.x - intersection.x) < tolerance && Math.abs(existing.y - intersection.y) < tolerance);
|
|
117
|
+
if (!isDuplicate) {
|
|
118
|
+
allIntersections.push(intersection);
|
|
90
119
|
}
|
|
91
120
|
}
|
|
92
121
|
}
|
|
122
|
+
|
|
123
|
+
// If path has 2 or more unique intersections with this node, it crosses through it
|
|
124
|
+
if (allIntersections.length >= 2) {
|
|
125
|
+
console.log(`[pathIntersectsObstacles] Path crosses node ${nodeId} with ${allIntersections.length} intersections`);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
93
128
|
}
|
|
94
129
|
return false;
|
|
95
130
|
}
|
|
@@ -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,EAAE,MAAM,WAAW,CAAA;AAMnE;;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,
|
|
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,EAAE,MAAM,WAAW,CAAA;AAMnE;;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,CAwRvE"}
|
package/esm/getManHattanPath.js
CHANGED
|
@@ -102,7 +102,7 @@ export function getManHattanPath(params) {
|
|
|
102
102
|
var smoothStepPoints = parseSVGPath(smoothStepPath);
|
|
103
103
|
|
|
104
104
|
// Check if smooth step path intersects with any obstacles
|
|
105
|
-
if (smoothStepPoints.length > 0 && !pathIntersectsObstacles(smoothStepPoints, nodeLookup
|
|
105
|
+
if (smoothStepPoints.length > 0 && !pathIntersectsObstacles(smoothStepPoints, nodeLookup)) {
|
|
106
106
|
console.log('[getManHattanPath] Using ReactFlow getSmoothStepPath (no obstacles)');
|
|
107
107
|
return smoothStepPath;
|
|
108
108
|
}
|
|
@@ -169,7 +169,9 @@ export function getManHattanPath(params) {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
// Insert extension point at source - always extend away from node edge by fixed step distance
|
|
172
|
+
// Add borderRadius to extension distance to account for rounded corners
|
|
172
173
|
if (route.length > 0) {
|
|
174
|
+
var extensionDistance = step + options.borderRadius;
|
|
173
175
|
var _firstPoint = route[0];
|
|
174
176
|
|
|
175
177
|
// Determine which edge the source anchor is on
|
|
@@ -180,8 +182,8 @@ export function getManHattanPath(params) {
|
|
|
180
182
|
|
|
181
183
|
// Insert extension point and corner point to ensure orthogonal path
|
|
182
184
|
if (_onRight2) {
|
|
183
|
-
// Anchor on right edge - extend right by
|
|
184
|
-
var extendX = sourceAnchor.x +
|
|
185
|
+
// Anchor on right edge - extend right by step + borderRadius
|
|
186
|
+
var extendX = sourceAnchor.x + extensionDistance;
|
|
185
187
|
var extensionPoint = new Point(extendX, sourceAnchor.y);
|
|
186
188
|
// Check if we need a corner point
|
|
187
189
|
if (Math.abs(extensionPoint.y - _firstPoint.y) > tolerance) {
|
|
@@ -190,8 +192,8 @@ export function getManHattanPath(params) {
|
|
|
190
192
|
route.unshift(extensionPoint); // Extension point (fixed distance)
|
|
191
193
|
console.log('[getManHattanPath] Inserted source extension (right):', "(".concat(extendX, ", ").concat(sourceAnchor.y, ")"));
|
|
192
194
|
} else if (_onLeft2) {
|
|
193
|
-
// Anchor on left edge - extend left by
|
|
194
|
-
var _extendX = sourceAnchor.x -
|
|
195
|
+
// Anchor on left edge - extend left by step + borderRadius
|
|
196
|
+
var _extendX = sourceAnchor.x - extensionDistance;
|
|
195
197
|
var _extensionPoint = new Point(_extendX, sourceAnchor.y);
|
|
196
198
|
if (Math.abs(_extensionPoint.y - _firstPoint.y) > tolerance) {
|
|
197
199
|
route.unshift(new Point(_extendX, _firstPoint.y));
|
|
@@ -199,8 +201,8 @@ export function getManHattanPath(params) {
|
|
|
199
201
|
route.unshift(_extensionPoint);
|
|
200
202
|
console.log('[getManHattanPath] Inserted source extension (left):', "(".concat(_extendX, ", ").concat(sourceAnchor.y, ")"));
|
|
201
203
|
} else if (_onBottom2) {
|
|
202
|
-
// Anchor on bottom edge - extend down by
|
|
203
|
-
var extendY = sourceAnchor.y +
|
|
204
|
+
// Anchor on bottom edge - extend down by step + borderRadius
|
|
205
|
+
var extendY = sourceAnchor.y + extensionDistance;
|
|
204
206
|
var _extensionPoint2 = new Point(sourceAnchor.x, extendY);
|
|
205
207
|
if (Math.abs(_extensionPoint2.x - _firstPoint.x) > tolerance) {
|
|
206
208
|
route.unshift(new Point(_firstPoint.x, extendY));
|
|
@@ -208,8 +210,8 @@ export function getManHattanPath(params) {
|
|
|
208
210
|
route.unshift(_extensionPoint2);
|
|
209
211
|
console.log('[getManHattanPath] Inserted source extension (down):', "(".concat(sourceAnchor.x, ", ").concat(extendY, ")"));
|
|
210
212
|
} else if (_onTop2) {
|
|
211
|
-
// Anchor on top edge - extend up by
|
|
212
|
-
var _extendY = sourceAnchor.y -
|
|
213
|
+
// Anchor on top edge - extend up by step + borderRadius
|
|
214
|
+
var _extendY = sourceAnchor.y - extensionDistance;
|
|
213
215
|
var _extensionPoint3 = new Point(sourceAnchor.x, _extendY);
|
|
214
216
|
if (Math.abs(_extensionPoint3.x - _firstPoint.x) > tolerance) {
|
|
215
217
|
route.unshift(new Point(_firstPoint.x, _extendY));
|
|
@@ -220,50 +222,50 @@ export function getManHattanPath(params) {
|
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
// Insert extension point at target - always extend away from node edge by fixed step distance
|
|
225
|
+
// Add borderRadius to extension distance to account for rounded corners
|
|
223
226
|
if (route.length > 0) {
|
|
224
|
-
var
|
|
225
|
-
var _tolerance = 1;
|
|
227
|
+
var _extensionDistance = step + options.borderRadius;
|
|
226
228
|
var _lastPoint = route[route.length - 1];
|
|
227
229
|
|
|
228
230
|
// Determine which edge the target anchor is on
|
|
229
|
-
var _onLeft3 = Math.abs(targetAnchor.x - targetBBox.x) <
|
|
230
|
-
var _onRight3 = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) <
|
|
231
|
-
var _onTop3 = Math.abs(targetAnchor.y - targetBBox.y) <
|
|
232
|
-
var _onBottom3 = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) <
|
|
231
|
+
var _onLeft3 = Math.abs(targetAnchor.x - targetBBox.x) < tolerance;
|
|
232
|
+
var _onRight3 = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) < tolerance;
|
|
233
|
+
var _onTop3 = Math.abs(targetAnchor.y - targetBBox.y) < tolerance;
|
|
234
|
+
var _onBottom3 = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) < tolerance;
|
|
233
235
|
|
|
234
236
|
// Insert extension point and corner point to ensure orthogonal path
|
|
235
237
|
if (_onLeft3) {
|
|
236
|
-
// Anchor on left edge - extend left by
|
|
237
|
-
var _extendX2 = targetAnchor.x -
|
|
238
|
+
// Anchor on left edge - extend left by step + borderRadius
|
|
239
|
+
var _extendX2 = targetAnchor.x - _extensionDistance;
|
|
238
240
|
var _extensionPoint4 = new Point(_extendX2, targetAnchor.y);
|
|
239
|
-
if (Math.abs(_extensionPoint4.y - _lastPoint.y) >
|
|
241
|
+
if (Math.abs(_extensionPoint4.y - _lastPoint.y) > tolerance) {
|
|
240
242
|
route.push(new Point(_extendX2, _lastPoint.y)); // Corner point
|
|
241
243
|
}
|
|
242
244
|
route.push(_extensionPoint4); // Extension point (fixed distance)
|
|
243
245
|
console.log('[getManHattanPath] Inserted target extension (left):', "(".concat(_extendX2, ", ").concat(targetAnchor.y, ")"));
|
|
244
246
|
} else if (_onRight3) {
|
|
245
|
-
// Anchor on right edge - extend right by
|
|
246
|
-
var _extendX3 = targetAnchor.x +
|
|
247
|
+
// Anchor on right edge - extend right by step + borderRadius
|
|
248
|
+
var _extendX3 = targetAnchor.x + _extensionDistance;
|
|
247
249
|
var _extensionPoint5 = new Point(_extendX3, targetAnchor.y);
|
|
248
|
-
if (Math.abs(_extensionPoint5.y - _lastPoint.y) >
|
|
250
|
+
if (Math.abs(_extensionPoint5.y - _lastPoint.y) > tolerance) {
|
|
249
251
|
route.push(new Point(_extendX3, _lastPoint.y));
|
|
250
252
|
}
|
|
251
253
|
route.push(_extensionPoint5);
|
|
252
254
|
console.log('[getManHattanPath] Inserted target extension (right):', "(".concat(_extendX3, ", ").concat(targetAnchor.y, ")"));
|
|
253
255
|
} else if (_onTop3) {
|
|
254
|
-
// Anchor on top edge - extend up by
|
|
255
|
-
var _extendY2 = targetAnchor.y -
|
|
256
|
+
// Anchor on top edge - extend up by step + borderRadius
|
|
257
|
+
var _extendY2 = targetAnchor.y - _extensionDistance;
|
|
256
258
|
var _extensionPoint6 = new Point(targetAnchor.x, _extendY2);
|
|
257
|
-
if (Math.abs(_extensionPoint6.x - _lastPoint.x) >
|
|
259
|
+
if (Math.abs(_extensionPoint6.x - _lastPoint.x) > tolerance) {
|
|
258
260
|
route.push(new Point(_lastPoint.x, _extendY2));
|
|
259
261
|
}
|
|
260
262
|
route.push(_extensionPoint6);
|
|
261
263
|
console.log('[getManHattanPath] Inserted target extension (up):', "(".concat(targetAnchor.x, ", ").concat(_extendY2, ")"));
|
|
262
264
|
} else if (_onBottom3) {
|
|
263
|
-
// Anchor on bottom edge - extend down by
|
|
264
|
-
var _extendY3 = targetAnchor.y +
|
|
265
|
+
// Anchor on bottom edge - extend down by step + borderRadius
|
|
266
|
+
var _extendY3 = targetAnchor.y + _extensionDistance;
|
|
265
267
|
var _extensionPoint7 = new Point(targetAnchor.x, _extendY3);
|
|
266
|
-
if (Math.abs(_extensionPoint7.x - _lastPoint.x) >
|
|
268
|
+
if (Math.abs(_extensionPoint7.x - _lastPoint.x) > tolerance) {
|
|
267
269
|
route.push(new Point(_lastPoint.x, _extendY3));
|
|
268
270
|
}
|
|
269
271
|
route.push(_extensionPoint7);
|
package/esm/options/defaults.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathParser.d.ts","sourceRoot":"","sources":["../../src/svg/pathParser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"pathParser.d.ts","sourceRoot":"","sources":["../../src/svg/pathParser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,CA2CxD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CA0BrD"}
|
package/esm/svg/pathParser.js
CHANGED
|
@@ -34,8 +34,9 @@ export function parseSVGPath(pathString) {
|
|
|
34
34
|
var x = coords[2];
|
|
35
35
|
var y = coords[3];
|
|
36
36
|
|
|
37
|
-
// Sample
|
|
38
|
-
|
|
37
|
+
// Sample 10 points along the bezier curve for better accuracy
|
|
38
|
+
// This ensures we don't miss intersections with obstacles
|
|
39
|
+
for (var t = 0.1; t <= 1; t += 0.1) {
|
|
39
40
|
var bx = (1 - t) * (1 - t) * prevPoint.x + 2 * (1 - t) * t * cx + t * t * x;
|
|
40
41
|
var by = (1 - t) * (1 - t) * prevPoint.y + 2 * (1 - t) * t * cy + t * t * y;
|
|
41
42
|
points.push(new Point(bx, by));
|
package/esm/utils/grid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../src/utils/grid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,KAAK,CAAA;IACb,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../src/utils/grid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,KAAK,CAAA;IACb,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAWD;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,IAAI,CAMxE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AAYD;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAExE;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAE5D"}
|
package/esm/utils/grid.js
CHANGED
|
@@ -8,23 +8,9 @@ import { Point } from "../geometry";
|
|
|
8
8
|
* Get grid dimension for a single axis
|
|
9
9
|
*/
|
|
10
10
|
function getGridDimension(diff, step) {
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
var abs = Math.abs(diff);
|
|
16
|
-
var count = Math.round(abs / step);
|
|
17
|
-
|
|
18
|
-
// Return abs if less than one step apart
|
|
19
|
-
if (!count) {
|
|
20
|
-
return abs;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Otherwise, return corrected step
|
|
24
|
-
var roundedDiff = count * step;
|
|
25
|
-
var remainder = abs - roundedDiff;
|
|
26
|
-
var correction = remainder / count;
|
|
27
|
-
return step + correction;
|
|
11
|
+
// Always return fixed step size to maintain consistent padding
|
|
12
|
+
// This ensures paths stay at least 'padding' distance from obstacles
|
|
13
|
+
return step;
|
|
28
14
|
}
|
|
29
15
|
|
|
30
16
|
/**
|
|
@@ -2,6 +2,10 @@ import { Point } from '../geometry';
|
|
|
2
2
|
import type { NodeLookup } from '../options';
|
|
3
3
|
/**
|
|
4
4
|
* Check if a path intersects with any obstacles (nodes)
|
|
5
|
+
* A path is considered to intersect if it has >= 2 unique intersection points with a node
|
|
6
|
+
*
|
|
7
|
+
* Note: pathPoints should be pre-processed by parseSVGPath which samples bezier curves
|
|
8
|
+
* into line segments, so this function works correctly with curved paths
|
|
5
9
|
*/
|
|
6
|
-
export declare function pathIntersectsObstacles(pathPoints: Point[], nodeLookup: NodeLookup
|
|
10
|
+
export declare function pathIntersectsObstacles(pathPoints: Point[], nodeLookup: NodeLookup): boolean;
|
|
7
11
|
//# sourceMappingURL=pathValidation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathValidation.d.ts","sourceRoot":"","sources":["../../src/utils/pathValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"pathValidation.d.ts","sourceRoot":"","sources":["../../src/utils/pathValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAyF5C;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAoC5F"}
|
|
@@ -5,8 +5,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
5
5
|
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
6
6
|
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
7
7
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
8
|
-
import { Rectangle } from "../geometry";
|
|
9
|
-
import { lineSegmentIntersectsRect, lineSegmentCrossesRect } from "../geometry/collision";
|
|
8
|
+
import { Point, Rectangle } from "../geometry";
|
|
10
9
|
import { getNodeDimensions, getNodePosition } from "./node";
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -21,84 +20,138 @@ function getNodeBBox(nodeId, nodeLookup) {
|
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
|
-
*
|
|
23
|
+
* Find unique intersection points between a line segment and a rectangle
|
|
24
|
+
* Returns array of intersection points (deduplicated)
|
|
25
25
|
*/
|
|
26
|
-
function
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
}
|
|
26
|
+
function findSegmentRectIntersections(p1, p2, rect) {
|
|
27
|
+
var intersections = [];
|
|
28
|
+
var tolerance = 0.01;
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
30
|
+
// Define rectangle edges
|
|
31
|
+
var edges = [{
|
|
32
|
+
start: new Point(rect.x, rect.y),
|
|
33
|
+
end: new Point(rect.x + rect.width, rect.y)
|
|
34
|
+
},
|
|
35
|
+
// Top
|
|
36
|
+
{
|
|
37
|
+
start: new Point(rect.x + rect.width, rect.y),
|
|
38
|
+
end: new Point(rect.x + rect.width, rect.y + rect.height)
|
|
39
|
+
},
|
|
40
|
+
// Right
|
|
41
|
+
{
|
|
42
|
+
start: new Point(rect.x + rect.width, rect.y + rect.height),
|
|
43
|
+
end: new Point(rect.x, rect.y + rect.height)
|
|
44
|
+
},
|
|
45
|
+
// Bottom
|
|
46
|
+
{
|
|
47
|
+
start: new Point(rect.x, rect.y + rect.height),
|
|
48
|
+
end: new Point(rect.x, rect.y)
|
|
49
|
+
} // Left
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// Find intersection point with each edge
|
|
53
|
+
var _loop = function _loop() {
|
|
54
|
+
var edge = _edges[_i];
|
|
55
|
+
var intersection = getLineSegmentIntersection(p1, p2, edge.start, edge.end);
|
|
56
|
+
if (intersection) {
|
|
57
|
+
// Check if this point is already in the list (avoid duplicates at corners)
|
|
58
|
+
var isDuplicate = intersections.some(function (existing) {
|
|
59
|
+
return Math.abs(existing.x - intersection.x) < tolerance && Math.abs(existing.y - intersection.y) < tolerance;
|
|
60
|
+
});
|
|
61
|
+
if (!isDuplicate) {
|
|
62
|
+
intersections.push(intersection);
|
|
63
|
+
}
|
|
46
64
|
}
|
|
65
|
+
};
|
|
66
|
+
for (var _i = 0, _edges = edges; _i < _edges.length; _i++) {
|
|
67
|
+
_loop();
|
|
47
68
|
}
|
|
48
|
-
return
|
|
69
|
+
return intersections;
|
|
49
70
|
}
|
|
50
71
|
|
|
51
72
|
/**
|
|
52
|
-
*
|
|
73
|
+
* Get intersection point between two line segments (if exists)
|
|
53
74
|
*/
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (lineSegmentIntersectsRect(p1, p2, nodeBBox)) {
|
|
64
|
-
console.log("[pathIntersectsObstacles] Segment intersects node ".concat(nodeId));
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
75
|
+
function getLineSegmentIntersection(p1, p2, p3, p4) {
|
|
76
|
+
var d1 = direction(p3, p4, p1);
|
|
77
|
+
var d2 = direction(p3, p4, p2);
|
|
78
|
+
var d3 = direction(p1, p2, p3);
|
|
79
|
+
var d4 = direction(p1, p2, p4);
|
|
80
|
+
if ((d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0)) {
|
|
81
|
+
// Lines intersect, calculate intersection point
|
|
82
|
+
var t = ((p3.x - p1.x) * (p3.y - p4.y) - (p3.y - p1.y) * (p3.x - p4.x)) / ((p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x));
|
|
83
|
+
return new Point(p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y));
|
|
67
84
|
}
|
|
68
|
-
|
|
85
|
+
|
|
86
|
+
// Check collinear cases
|
|
87
|
+
if (d1 === 0 && onSegment(p3, p1, p4)) return p1.clone();
|
|
88
|
+
if (d2 === 0 && onSegment(p3, p2, p4)) return p2.clone();
|
|
89
|
+
if (d3 === 0 && onSegment(p1, p3, p2)) return p3.clone();
|
|
90
|
+
if (d4 === 0 && onSegment(p1, p4, p2)) return p4.clone();
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function direction(p1, p2, p3) {
|
|
94
|
+
return (p3.x - p1.x) * (p2.y - p1.y) - (p2.x - p1.x) * (p3.y - p1.y);
|
|
95
|
+
}
|
|
96
|
+
function onSegment(p1, p2, p3) {
|
|
97
|
+
return p2.x >= Math.min(p1.x, p3.x) && p2.x <= Math.max(p1.x, p3.x) && p2.y >= Math.min(p1.y, p3.y) && p2.y <= Math.max(p1.y, p3.y);
|
|
69
98
|
}
|
|
70
99
|
|
|
71
100
|
/**
|
|
72
101
|
* Check if a path intersects with any obstacles (nodes)
|
|
102
|
+
* A path is considered to intersect if it has >= 2 unique intersection points with a node
|
|
103
|
+
*
|
|
104
|
+
* Note: pathPoints should be pre-processed by parseSVGPath which samples bezier curves
|
|
105
|
+
* into line segments, so this function works correctly with curved paths
|
|
73
106
|
*/
|
|
74
|
-
export function pathIntersectsObstacles(pathPoints, nodeLookup
|
|
75
|
-
|
|
107
|
+
export function pathIntersectsObstacles(pathPoints, nodeLookup) {
|
|
108
|
+
var tolerance = 0.01;
|
|
109
|
+
|
|
110
|
+
// Iterate through all nodes (including source and target)
|
|
76
111
|
var _iterator = _createForOfIteratorHelper(nodeLookup),
|
|
77
112
|
_step;
|
|
78
113
|
try {
|
|
79
114
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
80
|
-
var _step$value = _slicedToArray(_step.value,
|
|
81
|
-
nodeId = _step$value[0]
|
|
82
|
-
node = _step$value[1];
|
|
115
|
+
var _step$value = _slicedToArray(_step.value, 1),
|
|
116
|
+
nodeId = _step$value[0];
|
|
83
117
|
var nodeBBox = getNodeBBox(nodeId, nodeLookup);
|
|
84
118
|
if (!nodeBBox) continue;
|
|
85
|
-
var
|
|
119
|
+
var allIntersections = [];
|
|
86
120
|
|
|
87
|
-
//
|
|
88
|
-
for (var i = 0; i < pathPoints.length; i++) {
|
|
89
|
-
var
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
121
|
+
// Collect all unique intersection points for this node
|
|
122
|
+
for (var i = 0; i < pathPoints.length - 1; i++) {
|
|
123
|
+
var p1 = pathPoints[i];
|
|
124
|
+
var p2 = pathPoints[i + 1];
|
|
125
|
+
var segmentIntersections = findSegmentRectIntersections(p1, p2, nodeBBox);
|
|
93
126
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
127
|
+
// Add to global list, avoiding duplicates
|
|
128
|
+
var _iterator2 = _createForOfIteratorHelper(segmentIntersections),
|
|
129
|
+
_step2;
|
|
130
|
+
try {
|
|
131
|
+
var _loop2 = function _loop2() {
|
|
132
|
+
var intersection = _step2.value;
|
|
133
|
+
var isDuplicate = allIntersections.some(function (existing) {
|
|
134
|
+
return Math.abs(existing.x - intersection.x) < tolerance && Math.abs(existing.y - intersection.y) < tolerance;
|
|
135
|
+
});
|
|
136
|
+
if (!isDuplicate) {
|
|
137
|
+
allIntersections.push(intersection);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
141
|
+
_loop2();
|
|
99
142
|
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
_iterator2.e(err);
|
|
145
|
+
} finally {
|
|
146
|
+
_iterator2.f();
|
|
100
147
|
}
|
|
101
148
|
}
|
|
149
|
+
|
|
150
|
+
// If path has 2 or more unique intersections with this node, it crosses through it
|
|
151
|
+
if (allIntersections.length >= 2) {
|
|
152
|
+
console.log("[pathIntersectsObstacles] Path crosses node ".concat(nodeId, " with ").concat(allIntersections.length, " intersections"));
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
102
155
|
}
|
|
103
156
|
} catch (err) {
|
|
104
157
|
_iterator.e(err);
|
package/package.json
CHANGED