@rxflow/manhattan 0.0.1-alpha.2 → 0.0.1-alpha.4
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.js +1 -1
- package/cjs/svg/pathParser.d.ts.map +1 -1
- package/cjs/svg/pathParser.js +3 -2
- 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.js +1 -1
- package/esm/svg/pathParser.d.ts.map +1 -1
- package/esm/svg/pathParser.js +3 -2
- 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
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
|
}
|
|
@@ -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));
|
|
@@ -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
|
}
|
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
|
}
|
|
@@ -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));
|
|
@@ -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);
|