@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,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getNodeDimensions = getNodeDimensions;
|
|
7
|
+
exports.getNodePosition = getNodePosition;
|
|
8
|
+
/**
|
|
9
|
+
* Node dimensions interface
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get node dimensions following ReactFlow's internal logic
|
|
14
|
+
* Priority: measured > direct property > initialWidth/Height > 0
|
|
15
|
+
*
|
|
16
|
+
* @param node - ReactFlow internal node
|
|
17
|
+
* @returns Node dimensions with width and height
|
|
18
|
+
*/
|
|
19
|
+
function getNodeDimensions(node) {
|
|
20
|
+
const width = node.measured?.width ?? node.width ?? node.initialWidth ?? 0;
|
|
21
|
+
const height = node.measured?.height ?? node.height ?? node.initialHeight ?? 0;
|
|
22
|
+
return {
|
|
23
|
+
width,
|
|
24
|
+
height
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get node absolute position from internals
|
|
30
|
+
*
|
|
31
|
+
* @param node - ReactFlow internal node
|
|
32
|
+
* @returns Absolute position { x, y }
|
|
33
|
+
*/
|
|
34
|
+
function getNodePosition(node) {
|
|
35
|
+
return node.internals.positionAbsolute;
|
|
36
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Processing Utilities
|
|
3
|
+
*
|
|
4
|
+
* 提供路径后处理功能,包括:
|
|
5
|
+
* - 移除冗余扩展点
|
|
6
|
+
* - 插入扩展点
|
|
7
|
+
* - 优化锯齿模式
|
|
8
|
+
*
|
|
9
|
+
* Implements: Requirements 5.3
|
|
10
|
+
*/
|
|
11
|
+
import { Point, Rectangle } from '../geometry';
|
|
12
|
+
import type { ResolvedOptions } from '../options';
|
|
13
|
+
/**
|
|
14
|
+
* 检测锚点在边界框的哪条边上
|
|
15
|
+
*/
|
|
16
|
+
export declare function detectAnchorEdge(anchor: Point, bbox: Rectangle, tolerance?: number): 'left' | 'right' | 'top' | 'bottom' | null;
|
|
17
|
+
/**
|
|
18
|
+
* 移除路径起点的扩展点
|
|
19
|
+
*/
|
|
20
|
+
export declare function removeSourceExtensionPoint(route: Point[], sourceAnchor: Point, sourceBBox: Rectangle, step: number, tolerance?: number): Point[];
|
|
21
|
+
/**
|
|
22
|
+
* 移除路径终点的扩展点
|
|
23
|
+
*/
|
|
24
|
+
export declare function removeTargetExtensionPoint(route: Point[], targetAnchor: Point, targetBBox: Rectangle, step: number, tolerance?: number): Point[];
|
|
25
|
+
/**
|
|
26
|
+
* 在源点插入扩展点
|
|
27
|
+
*/
|
|
28
|
+
export declare function insertSourceExtension(route: Point[], sourceAnchor: Point, sourceBBox: Rectangle, extensionDistance: number, tolerance?: number): Point[];
|
|
29
|
+
/**
|
|
30
|
+
* 在目标点插入扩展点
|
|
31
|
+
*/
|
|
32
|
+
export declare function insertTargetExtension(route: Point[], targetAnchor: Point, targetBBox: Rectangle, extensionDistance: number, tolerance?: number): Point[];
|
|
33
|
+
/**
|
|
34
|
+
* 检测并移除锯齿模式
|
|
35
|
+
*/
|
|
36
|
+
export declare function removeZigzagPatterns(route: Point[], targetBBox: Rectangle, tolerance?: number): Point[];
|
|
37
|
+
/**
|
|
38
|
+
* 移除冗余点
|
|
39
|
+
*/
|
|
40
|
+
export declare function removeRedundantPoints(route: Point[], anchor: Point, tolerance?: number): Point[];
|
|
41
|
+
/**
|
|
42
|
+
* 完整的路径后处理流程
|
|
43
|
+
*/
|
|
44
|
+
export declare function processRoute(route: Point[], sourceAnchor: Point, targetAnchor: Point, sourceBBox: Rectangle, targetBBox: Rectangle, options: ResolvedOptions): Point[];
|
|
45
|
+
//# sourceMappingURL=pathProcessing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathProcessing.d.ts","sourceRoot":"","sources":["../../src/utils/pathProcessing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,KAAK,EACb,IAAI,EAAE,SAAS,EACf,SAAS,GAAE,MAAU,GACpB,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,IAAI,CAM5C;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,EAAE,EACd,YAAY,EAAE,KAAK,EACnB,UAAU,EAAE,SAAS,EACrB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAU,GACpB,KAAK,EAAE,CAoBT;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,EAAE,EACd,YAAY,EAAE,KAAK,EACnB,UAAU,EAAE,SAAS,EACrB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAU,GACpB,KAAK,EAAE,CAoBT;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,KAAK,EAAE,EACd,YAAY,EAAE,KAAK,EACnB,UAAU,EAAE,SAAS,EACrB,iBAAiB,EAAE,MAAM,EACzB,SAAS,GAAE,MAAU,GACpB,KAAK,EAAE,CA6CT;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,KAAK,EAAE,EACd,YAAY,EAAE,KAAK,EACnB,UAAU,EAAE,SAAS,EACrB,iBAAiB,EAAE,MAAM,EACzB,SAAS,GAAE,MAAU,GACpB,KAAK,EAAE,CA6CT;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,EAAE,EACd,UAAU,EAAE,SAAS,EACrB,SAAS,GAAE,MAAU,GACpB,KAAK,EAAE,CA2BT;AAmCD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,KAAK,EAAE,EACd,MAAM,EAAE,KAAK,EACb,SAAS,GAAE,MAAU,GACpB,KAAK,EAAE,CA2BT;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,EAAE,EACd,YAAY,EAAE,KAAK,EACnB,YAAY,EAAE,KAAK,EACnB,UAAU,EAAE,SAAS,EACrB,UAAU,EAAE,SAAS,EACrB,OAAO,EAAE,eAAe,GACvB,KAAK,EAAE,CA2ET"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.detectAnchorEdge = detectAnchorEdge;
|
|
7
|
+
exports.insertSourceExtension = insertSourceExtension;
|
|
8
|
+
exports.insertTargetExtension = insertTargetExtension;
|
|
9
|
+
exports.processRoute = processRoute;
|
|
10
|
+
exports.removeRedundantPoints = removeRedundantPoints;
|
|
11
|
+
exports.removeSourceExtensionPoint = removeSourceExtensionPoint;
|
|
12
|
+
exports.removeTargetExtensionPoint = removeTargetExtensionPoint;
|
|
13
|
+
exports.removeZigzagPatterns = removeZigzagPatterns;
|
|
14
|
+
var _geometry = require("../geometry");
|
|
15
|
+
/**
|
|
16
|
+
* Path Processing Utilities
|
|
17
|
+
*
|
|
18
|
+
* 提供路径后处理功能,包括:
|
|
19
|
+
* - 移除冗余扩展点
|
|
20
|
+
* - 插入扩展点
|
|
21
|
+
* - 优化锯齿模式
|
|
22
|
+
*
|
|
23
|
+
* Implements: Requirements 5.3
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 检测锚点在边界框的哪条边上
|
|
28
|
+
*/
|
|
29
|
+
function detectAnchorEdge(anchor, bbox, tolerance = 1) {
|
|
30
|
+
if (Math.abs(anchor.x - bbox.x) < tolerance) return 'left';
|
|
31
|
+
if (Math.abs(anchor.x - (bbox.x + bbox.width)) < tolerance) return 'right';
|
|
32
|
+
if (Math.abs(anchor.y - bbox.y) < tolerance) return 'top';
|
|
33
|
+
if (Math.abs(anchor.y - (bbox.y + bbox.height)) < tolerance) return 'bottom';
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 移除路径起点的扩展点
|
|
39
|
+
*/
|
|
40
|
+
function removeSourceExtensionPoint(route, sourceAnchor, sourceBBox, step, tolerance = 1) {
|
|
41
|
+
if (route.length === 0) return route;
|
|
42
|
+
const firstPoint = route[0];
|
|
43
|
+
const edge = detectAnchorEdge(sourceAnchor, sourceBBox, tolerance);
|
|
44
|
+
const distToFirst = sourceAnchor.manhattanDistance(firstPoint);
|
|
45
|
+
if (distToFirst >= step * 2) return route;
|
|
46
|
+
const shouldRemove = edge === 'right' && firstPoint.x > sourceAnchor.x || edge === 'left' && firstPoint.x < sourceAnchor.x || edge === 'bottom' && firstPoint.y > sourceAnchor.y || edge === 'top' && firstPoint.y < sourceAnchor.y;
|
|
47
|
+
if (shouldRemove) {
|
|
48
|
+
return route.slice(1);
|
|
49
|
+
}
|
|
50
|
+
return route;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 移除路径终点的扩展点
|
|
55
|
+
*/
|
|
56
|
+
function removeTargetExtensionPoint(route, targetAnchor, targetBBox, step, tolerance = 1) {
|
|
57
|
+
if (route.length === 0) return route;
|
|
58
|
+
const lastPoint = route[route.length - 1];
|
|
59
|
+
const edge = detectAnchorEdge(targetAnchor, targetBBox, tolerance);
|
|
60
|
+
const distToLast = targetAnchor.manhattanDistance(lastPoint);
|
|
61
|
+
if (distToLast >= step * 2) return route;
|
|
62
|
+
const shouldRemove = edge === 'left' && lastPoint.x < targetAnchor.x || edge === 'right' && lastPoint.x > targetAnchor.x || edge === 'top' && lastPoint.y < targetAnchor.y || edge === 'bottom' && lastPoint.y > targetAnchor.y;
|
|
63
|
+
if (shouldRemove) {
|
|
64
|
+
return route.slice(0, -1);
|
|
65
|
+
}
|
|
66
|
+
return route;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 在源点插入扩展点
|
|
71
|
+
*/
|
|
72
|
+
function insertSourceExtension(route, sourceAnchor, sourceBBox, extensionDistance, tolerance = 1) {
|
|
73
|
+
if (route.length === 0) return route;
|
|
74
|
+
const result = [...route];
|
|
75
|
+
const firstPoint = result[0];
|
|
76
|
+
const edge = detectAnchorEdge(sourceAnchor, sourceBBox, tolerance);
|
|
77
|
+
if (!edge) return result;
|
|
78
|
+
let extensionPoint;
|
|
79
|
+
let cornerPoint = null;
|
|
80
|
+
switch (edge) {
|
|
81
|
+
case 'right':
|
|
82
|
+
extensionPoint = new _geometry.Point(sourceAnchor.x + extensionDistance, sourceAnchor.y);
|
|
83
|
+
if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
|
|
84
|
+
cornerPoint = new _geometry.Point(extensionPoint.x, firstPoint.y);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
case 'left':
|
|
88
|
+
extensionPoint = new _geometry.Point(sourceAnchor.x - extensionDistance, sourceAnchor.y);
|
|
89
|
+
if (Math.abs(extensionPoint.y - firstPoint.y) > tolerance) {
|
|
90
|
+
cornerPoint = new _geometry.Point(extensionPoint.x, firstPoint.y);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
case 'bottom':
|
|
94
|
+
extensionPoint = new _geometry.Point(sourceAnchor.x, sourceAnchor.y + extensionDistance);
|
|
95
|
+
if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
|
|
96
|
+
cornerPoint = new _geometry.Point(firstPoint.x, extensionPoint.y);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case 'top':
|
|
100
|
+
extensionPoint = new _geometry.Point(sourceAnchor.x, sourceAnchor.y - extensionDistance);
|
|
101
|
+
if (Math.abs(extensionPoint.x - firstPoint.x) > tolerance) {
|
|
102
|
+
cornerPoint = new _geometry.Point(firstPoint.x, extensionPoint.y);
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
if (cornerPoint) {
|
|
107
|
+
result.unshift(cornerPoint);
|
|
108
|
+
}
|
|
109
|
+
result.unshift(extensionPoint);
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 在目标点插入扩展点
|
|
115
|
+
*/
|
|
116
|
+
function insertTargetExtension(route, targetAnchor, targetBBox, extensionDistance, tolerance = 1) {
|
|
117
|
+
if (route.length === 0) return route;
|
|
118
|
+
const result = [...route];
|
|
119
|
+
const lastPoint = result[result.length - 1];
|
|
120
|
+
const edge = detectAnchorEdge(targetAnchor, targetBBox, tolerance);
|
|
121
|
+
if (!edge) return result;
|
|
122
|
+
let extensionPoint;
|
|
123
|
+
let cornerPoint = null;
|
|
124
|
+
switch (edge) {
|
|
125
|
+
case 'left':
|
|
126
|
+
extensionPoint = new _geometry.Point(targetAnchor.x - extensionDistance, targetAnchor.y);
|
|
127
|
+
if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
|
|
128
|
+
cornerPoint = new _geometry.Point(extensionPoint.x, lastPoint.y);
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
case 'right':
|
|
132
|
+
extensionPoint = new _geometry.Point(targetAnchor.x + extensionDistance, targetAnchor.y);
|
|
133
|
+
if (Math.abs(extensionPoint.y - lastPoint.y) > tolerance) {
|
|
134
|
+
cornerPoint = new _geometry.Point(extensionPoint.x, lastPoint.y);
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
case 'top':
|
|
138
|
+
extensionPoint = new _geometry.Point(targetAnchor.x, targetAnchor.y - extensionDistance);
|
|
139
|
+
if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
|
|
140
|
+
cornerPoint = new _geometry.Point(lastPoint.x, extensionPoint.y);
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case 'bottom':
|
|
144
|
+
extensionPoint = new _geometry.Point(targetAnchor.x, targetAnchor.y + extensionDistance);
|
|
145
|
+
if (Math.abs(extensionPoint.x - lastPoint.x) > tolerance) {
|
|
146
|
+
cornerPoint = new _geometry.Point(lastPoint.x, extensionPoint.y);
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (cornerPoint) {
|
|
151
|
+
result.push(cornerPoint);
|
|
152
|
+
}
|
|
153
|
+
result.push(extensionPoint);
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 检测并移除锯齿模式
|
|
159
|
+
*/
|
|
160
|
+
function removeZigzagPatterns(route, targetBBox, tolerance = 1) {
|
|
161
|
+
if (route.length < 3) return route;
|
|
162
|
+
const result = [...route];
|
|
163
|
+
let i = 0;
|
|
164
|
+
while (i < result.length - 2) {
|
|
165
|
+
const p1 = result[i];
|
|
166
|
+
const p2 = result[i + 1];
|
|
167
|
+
const p3 = result[i + 2];
|
|
168
|
+
|
|
169
|
+
// 检查 p2 是否在目标边界框边缘
|
|
170
|
+
const p2OnEdge = isPointOnBBoxEdge(p2, targetBBox, tolerance);
|
|
171
|
+
if (p2OnEdge) {
|
|
172
|
+
const isZigzag = isZigzagPattern(p1, p2, p3, tolerance);
|
|
173
|
+
if (isZigzag) {
|
|
174
|
+
result.splice(i + 1, 2);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
i++;
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 检查点是否在边界框边缘
|
|
185
|
+
*/
|
|
186
|
+
function isPointOnBBoxEdge(point, bbox, tolerance) {
|
|
187
|
+
return Math.abs(point.x - bbox.x) < tolerance || Math.abs(point.x - (bbox.x + bbox.width)) < tolerance || Math.abs(point.y - bbox.y) < tolerance || Math.abs(point.y - (bbox.y + bbox.height)) < tolerance;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 检查三个点是否形成锯齿模式
|
|
192
|
+
*/
|
|
193
|
+
function isZigzagPattern(p1, p2, p3, tolerance) {
|
|
194
|
+
const p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
|
|
195
|
+
const p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
|
|
196
|
+
const p1ToP2Vertical = Math.abs(p1.x - p2.x) < tolerance;
|
|
197
|
+
const p2ToP3Horizontal = Math.abs(p2.y - p3.y) < tolerance;
|
|
198
|
+
return p1ToP2Horizontal && p2ToP3Vertical || p1ToP2Vertical && p2ToP3Horizontal;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 移除冗余点
|
|
203
|
+
*/
|
|
204
|
+
function removeRedundantPoints(route, anchor, tolerance = 1) {
|
|
205
|
+
if (route.length <= 3) return route;
|
|
206
|
+
const result = [...route];
|
|
207
|
+
|
|
208
|
+
// 检查第三个点是否冗余
|
|
209
|
+
const thirdPoint = result[2];
|
|
210
|
+
const sameX = Math.abs(thirdPoint.x - anchor.x) < tolerance;
|
|
211
|
+
const sameY = Math.abs(thirdPoint.y - anchor.y) < tolerance;
|
|
212
|
+
if ((sameX || sameY) && result.length > 3) {
|
|
213
|
+
const secondPoint = result[1];
|
|
214
|
+
const fourthPoint = result[3];
|
|
215
|
+
const cornerToThird = Math.abs(secondPoint.x - thirdPoint.x) < tolerance || Math.abs(secondPoint.y - thirdPoint.y) < tolerance;
|
|
216
|
+
const thirdToFourth = Math.abs(thirdPoint.x - fourthPoint.x) < tolerance || Math.abs(thirdPoint.y - fourthPoint.y) < tolerance;
|
|
217
|
+
if (cornerToThird && thirdToFourth) {
|
|
218
|
+
result.splice(2, 1);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 完整的路径后处理流程
|
|
226
|
+
*/
|
|
227
|
+
function processRoute(route, sourceAnchor, targetAnchor, sourceBBox, targetBBox, options) {
|
|
228
|
+
const tolerance = 1;
|
|
229
|
+
let processed = [...route];
|
|
230
|
+
|
|
231
|
+
// 1. 移除源点扩展点
|
|
232
|
+
processed = removeSourceExtensionPoint(processed, sourceAnchor, sourceBBox, options.step, tolerance);
|
|
233
|
+
|
|
234
|
+
// 2. 移除目标点扩展点
|
|
235
|
+
processed = removeTargetExtensionPoint(processed, targetAnchor, targetBBox, options.step, tolerance);
|
|
236
|
+
|
|
237
|
+
// 3. 插入源点扩展点
|
|
238
|
+
processed = insertSourceExtension(processed, sourceAnchor, sourceBBox, options.extensionDistance, tolerance);
|
|
239
|
+
|
|
240
|
+
// 4. 移除源点冗余点
|
|
241
|
+
processed = removeRedundantPoints(processed, sourceAnchor, tolerance);
|
|
242
|
+
|
|
243
|
+
// 5. 移除锯齿模式
|
|
244
|
+
const targetBBoxWithPadding = targetBBox.moveAndExpand(options.paddingBox);
|
|
245
|
+
processed = removeZigzagPatterns(processed, targetBBoxWithPadding, tolerance);
|
|
246
|
+
|
|
247
|
+
// 6. 插入目标点扩展点
|
|
248
|
+
processed = insertTargetExtension(processed, targetAnchor, targetBBox, options.extensionDistance, tolerance);
|
|
249
|
+
|
|
250
|
+
// 7. 移除目标点冗余点(从末尾)
|
|
251
|
+
if (processed.length > 3) {
|
|
252
|
+
const lastIdx = processed.length - 1;
|
|
253
|
+
const thirdLastPoint = processed[lastIdx - 2];
|
|
254
|
+
const sameX = Math.abs(thirdLastPoint.x - targetAnchor.x) < tolerance;
|
|
255
|
+
const sameY = Math.abs(thirdLastPoint.y - targetAnchor.y) < tolerance;
|
|
256
|
+
if ((sameX || sameY) && processed.length > 3) {
|
|
257
|
+
const secondLastPoint = processed[lastIdx - 1];
|
|
258
|
+
const fourthLastPoint = processed[lastIdx - 3];
|
|
259
|
+
const fourthToThird = Math.abs(fourthLastPoint.x - thirdLastPoint.x) < tolerance || Math.abs(fourthLastPoint.y - thirdLastPoint.y) < tolerance;
|
|
260
|
+
const thirdToSecond = Math.abs(thirdLastPoint.x - secondLastPoint.x) < tolerance || Math.abs(thirdLastPoint.y - secondLastPoint.y) < tolerance;
|
|
261
|
+
if (fourthToThird && thirdToSecond) {
|
|
262
|
+
processed.splice(lastIdx - 2, 1);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 8. 再次移除锯齿模式
|
|
268
|
+
processed = removeZigzagPatterns(processed, targetBBox, tolerance);
|
|
269
|
+
return processed;
|
|
270
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Point } from '../geometry';
|
|
2
|
+
import type { NodeLookup } from '../options';
|
|
3
|
+
/**
|
|
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
|
|
9
|
+
*/
|
|
10
|
+
export declare function pathIntersectsObstacles(pathPoints: Point[], nodeLookup: NodeLookup): boolean;
|
|
11
|
+
//# sourceMappingURL=pathValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,CAmC5F"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.pathIntersectsObstacles = pathIntersectsObstacles;
|
|
7
|
+
var _geometry = require("../geometry");
|
|
8
|
+
var _node = require("./node");
|
|
9
|
+
/**
|
|
10
|
+
* Get node bounding box
|
|
11
|
+
*/
|
|
12
|
+
function getNodeBBox(nodeId, nodeLookup) {
|
|
13
|
+
const node = nodeLookup.get(nodeId);
|
|
14
|
+
if (!node) return null;
|
|
15
|
+
const dimensions = (0, _node.getNodeDimensions)(node);
|
|
16
|
+
const position = (0, _node.getNodePosition)(node);
|
|
17
|
+
return new _geometry.Rectangle(position.x, position.y, dimensions.width, dimensions.height);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Find unique intersection points between a line segment and a rectangle
|
|
22
|
+
* Returns array of intersection points (deduplicated)
|
|
23
|
+
*/
|
|
24
|
+
function findSegmentRectIntersections(p1, p2, rect) {
|
|
25
|
+
const intersections = [];
|
|
26
|
+
const tolerance = 0.01;
|
|
27
|
+
|
|
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
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return intersections;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get intersection point between two line segments (if exists)
|
|
66
|
+
*/
|
|
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));
|
|
76
|
+
}
|
|
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);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
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
|
|
98
|
+
*/
|
|
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) {
|
|
104
|
+
const nodeBBox = getNodeBBox(nodeId, nodeLookup);
|
|
105
|
+
if (!nodeBBox) continue;
|
|
106
|
+
const allIntersections = [];
|
|
107
|
+
|
|
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);
|
|
113
|
+
|
|
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);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// If path has 2 or more unique intersections with this node, it crosses through it
|
|
124
|
+
if (allIntersections.length >= 2) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Point, Rectangle } from '../geometry';
|
|
2
|
+
import type { ResolvedOptions, Direction } from '../options';
|
|
3
|
+
import type { Grid } from './grid';
|
|
4
|
+
/**
|
|
5
|
+
* Get points around a rectangle taking given directions into account
|
|
6
|
+
* Lines are drawn from anchor in given directions, intersections recorded
|
|
7
|
+
*/
|
|
8
|
+
export declare function getRectPoints(anchor: Point, bbox: Rectangle, directionList: Direction[], grid: Grid, options: ResolvedOptions): Point[];
|
|
9
|
+
//# sourceMappingURL=rect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rect.d.ts","sourceRoot":"","sources":["../../src/utils/rect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAQ,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AA6ClC;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,EACb,IAAI,EAAE,SAAS,EACf,aAAa,EAAE,SAAS,EAAE,EAC1B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,eAAe,GACvB,KAAK,EAAE,CAgFT"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getRectPoints = getRectPoints;
|
|
7
|
+
var _geometry = require("../geometry");
|
|
8
|
+
var _grid = require("./grid");
|
|
9
|
+
/**
|
|
10
|
+
* Check if a point is on the edge of a rectangle
|
|
11
|
+
*/
|
|
12
|
+
function isPointOnRectangleEdge(point, bbox, tolerance = 0.01) {
|
|
13
|
+
const onLeft = Math.abs(point.x - bbox.x) < tolerance;
|
|
14
|
+
const onRight = Math.abs(point.x - (bbox.x + bbox.width)) < tolerance;
|
|
15
|
+
const onTop = Math.abs(point.y - bbox.y) < tolerance;
|
|
16
|
+
const onBottom = Math.abs(point.y - (bbox.y + bbox.height)) < tolerance;
|
|
17
|
+
const withinVerticalBounds = point.y >= bbox.y - tolerance && point.y <= bbox.y + bbox.height + tolerance;
|
|
18
|
+
const withinHorizontalBounds = point.x >= bbox.x - tolerance && point.x <= bbox.x + bbox.width + tolerance;
|
|
19
|
+
return (onLeft || onRight) && withinVerticalBounds || (onTop || onBottom) && withinHorizontalBounds;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if a direction points outward from the rectangle edge where the anchor is located
|
|
24
|
+
*/
|
|
25
|
+
function isDirectionOutward(anchor, bbox, direction, tolerance = 0.01) {
|
|
26
|
+
const onLeft = Math.abs(anchor.x - bbox.x) < tolerance;
|
|
27
|
+
const onRight = Math.abs(anchor.x - (bbox.x + bbox.width)) < tolerance;
|
|
28
|
+
const onTop = Math.abs(anchor.y - bbox.y) < tolerance;
|
|
29
|
+
const onBottom = Math.abs(anchor.y - (bbox.y + bbox.height)) < tolerance;
|
|
30
|
+
|
|
31
|
+
// Only allow outward directions from the edge
|
|
32
|
+
if (onLeft && direction.x < 0) return true;
|
|
33
|
+
if (onRight && direction.x > 0) return true;
|
|
34
|
+
if (onTop && direction.y < 0) return true;
|
|
35
|
+
if (onBottom && direction.y > 0) return true;
|
|
36
|
+
|
|
37
|
+
// For corners, allow both perpendicular directions
|
|
38
|
+
if ((onLeft || onRight) && direction.x === 0) return true;
|
|
39
|
+
if ((onTop || onBottom) && direction.y === 0) return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get points around a rectangle taking given directions into account
|
|
45
|
+
* Lines are drawn from anchor in given directions, intersections recorded
|
|
46
|
+
*/
|
|
47
|
+
function getRectPoints(anchor, bbox, directionList, grid, options) {
|
|
48
|
+
const precision = options.precision;
|
|
49
|
+
const directionMap = options.directionMap;
|
|
50
|
+
const centerVector = anchor.diff(bbox.getCenter());
|
|
51
|
+
const rectPoints = [];
|
|
52
|
+
|
|
53
|
+
// Check if anchor is on the edge of the bbox
|
|
54
|
+
const isOnEdge = isPointOnRectangleEdge(anchor, bbox);
|
|
55
|
+
|
|
56
|
+
// Check each allowed direction
|
|
57
|
+
for (const key of directionList) {
|
|
58
|
+
const direction = directionMap[key];
|
|
59
|
+
|
|
60
|
+
// If anchor is on edge, only consider outward directions
|
|
61
|
+
if (isOnEdge) {
|
|
62
|
+
const isOutward = isDirectionOutward(anchor, bbox, direction);
|
|
63
|
+
if (!isOutward) {
|
|
64
|
+
continue; // Skip inward directions
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// When anchor is on edge and direction is outward, create extension point directly
|
|
68
|
+
// instead of trying to find intersection (which won't exist for outward directions)
|
|
69
|
+
const extensionPoint = new _geometry.Point(anchor.x + direction.x * grid.x, anchor.y + direction.y * grid.y);
|
|
70
|
+
const target = (0, _grid.align)(extensionPoint, grid, precision);
|
|
71
|
+
rectPoints.push(target);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create a line that is guaranteed to intersect the bbox if bbox
|
|
76
|
+
// is in the direction even if anchor lies outside of bbox
|
|
77
|
+
const ending = new _geometry.Point(anchor.x + direction.x * (Math.abs(centerVector.x) + bbox.width), anchor.y + direction.y * (Math.abs(centerVector.y) + bbox.height));
|
|
78
|
+
const intersectionLine = new _geometry.Line(anchor, ending);
|
|
79
|
+
|
|
80
|
+
// Get the farther intersection, in case there are two
|
|
81
|
+
const intersections = intersectionLine.intersect(bbox);
|
|
82
|
+
let farthestIntersectionDistance;
|
|
83
|
+
let farthestIntersection = null;
|
|
84
|
+
for (const intersection of intersections) {
|
|
85
|
+
const distance = anchor.squaredDistance(intersection);
|
|
86
|
+
if (farthestIntersectionDistance === undefined || distance > farthestIntersectionDistance) {
|
|
87
|
+
farthestIntersectionDistance = distance;
|
|
88
|
+
farthestIntersection = intersection;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If an intersection was found in this direction, it is our rectPoint
|
|
93
|
+
if (farthestIntersection) {
|
|
94
|
+
let target = (0, _grid.align)(farthestIntersection, grid, precision);
|
|
95
|
+
|
|
96
|
+
// If the rectPoint lies inside the bbox, offset it by one more step
|
|
97
|
+
if (bbox.containsPoint(target)) {
|
|
98
|
+
target = (0, _grid.align)(target.translate(direction.x * grid.x, direction.y * grid.y), grid, precision);
|
|
99
|
+
}
|
|
100
|
+
rectPoints.push(target);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If anchor lies outside of bbox, add it to the array of points
|
|
105
|
+
// If anchor is on edge, don't add it - force path to start from extended points
|
|
106
|
+
if (!bbox.containsPoint(anchor) && !isOnEdge) {
|
|
107
|
+
rectPoints.push((0, _grid.align)(anchor, grid, precision));
|
|
108
|
+
}
|
|
109
|
+
return rectPoints;
|
|
110
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Point } from '../geometry';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a point to a unit direction vector
|
|
4
|
+
*/
|
|
5
|
+
export declare function normalizePoint(point: Point): Point;
|
|
6
|
+
/**
|
|
7
|
+
* Get string key for a point
|
|
8
|
+
*/
|
|
9
|
+
export declare function getKey(point: Point): string;
|
|
10
|
+
/**
|
|
11
|
+
* Calculate minimum Manhattan distance from a point to multiple anchors
|
|
12
|
+
*/
|
|
13
|
+
export declare function getCost(from: Point, anchors: Point[]): number;
|
|
14
|
+
/**
|
|
15
|
+
* Reconstruct route by concatenating points with their parents
|
|
16
|
+
* Removes redundant points in the same direction
|
|
17
|
+
*/
|
|
18
|
+
export declare function reconstructRoute(parents: Map<string, Point>, points: Map<string, Point>, tailPoint: Point, startPoint: Point, endPoint: Point): Point[];
|
|
19
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/utils/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAKlD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAE3C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,CAW7D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,EAC3B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,EAC1B,SAAS,EAAE,KAAK,EAChB,UAAU,EAAE,KAAK,EACjB,QAAQ,EAAE,KAAK,GACd,KAAK,EAAE,CA6DT"}
|