@rxflow/manhattan 0.0.2-alpha.7 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -10
- package/esm/getManHattanPath.js +92 -69
- package/esm/obstacle/ObstacleMap.js +218 -99
- package/esm/obstacle/QuadTree.js +488 -0
- package/esm/options/resolver.js +147 -18
- package/esm/pathfinder/PathCache.js +278 -0
- package/esm/pathfinder/findRoute.js +98 -44
- package/esm/pathfinder/index.js +2 -1
- package/esm/svg/pathConverter.js +170 -1
- package/esm/utils/AdaptiveStepCalculator.js +252 -0
- package/esm/utils/ErrorRecovery.js +499 -0
- package/esm/utils/GlobalGrid.js +259 -0
- package/esm/utils/PerformanceMonitor.js +360 -0
- package/esm/utils/getAnchorPoints.js +0 -4
- package/esm/utils/grid.js +18 -13
- package/esm/utils/heuristics.js +144 -0
- package/esm/utils/index.js +7 -1
- package/esm/utils/pathProcessing.js +270 -0
- package/esm/utils/pathValidation.js +0 -1
- package/esm/utils/rect.js +11 -4
- package/esm/utils/route.js +18 -2
- package/package.json +10 -2
- package/cjs/geometry/Line.d.ts +0 -21
- package/cjs/geometry/Line.d.ts.map +0 -1
- package/cjs/geometry/Line.js +0 -88
- package/cjs/geometry/Point.d.ts +0 -49
- package/cjs/geometry/Point.d.ts.map +0 -1
- package/cjs/geometry/Point.js +0 -94
- package/cjs/geometry/Rectangle.d.ts +0 -41
- package/cjs/geometry/Rectangle.d.ts.map +0 -1
- package/cjs/geometry/Rectangle.js +0 -65
- package/cjs/geometry/collision.d.ts +0 -15
- package/cjs/geometry/collision.d.ts.map +0 -1
- package/cjs/geometry/collision.js +0 -81
- package/cjs/geometry/index.d.ts +0 -5
- package/cjs/geometry/index.d.ts.map +0 -1
- package/cjs/geometry/index.js +0 -45
- package/cjs/getManHattanPath.d.ts +0 -53
- package/cjs/getManHattanPath.d.ts.map +0 -1
- package/cjs/getManHattanPath.js +0 -418
- package/cjs/index.d.ts +0 -16
- package/cjs/index.d.ts.map +0 -1
- package/cjs/index.js +0 -117
- package/cjs/obstacle/ObstacleMap.d.ts +0 -34
- package/cjs/obstacle/ObstacleMap.d.ts.map +0 -1
- package/cjs/obstacle/ObstacleMap.js +0 -223
- package/cjs/obstacle/index.d.ts +0 -2
- package/cjs/obstacle/index.d.ts.map +0 -1
- package/cjs/obstacle/index.js +0 -12
- package/cjs/options/defaults.d.ts +0 -16
- package/cjs/options/defaults.d.ts.map +0 -1
- package/cjs/options/defaults.js +0 -39
- package/cjs/options/index.d.ts +0 -4
- package/cjs/options/index.d.ts.map +0 -1
- package/cjs/options/index.js +0 -38
- package/cjs/options/resolver.d.ts +0 -10
- package/cjs/options/resolver.d.ts.map +0 -1
- package/cjs/options/resolver.js +0 -120
- package/cjs/options/types.d.ts +0 -169
- package/cjs/options/types.d.ts.map +0 -1
- package/cjs/options/types.js +0 -5
- package/cjs/pathfinder/SortedSet.d.ts +0 -35
- package/cjs/pathfinder/SortedSet.d.ts.map +0 -1
- package/cjs/pathfinder/SortedSet.js +0 -95
- package/cjs/pathfinder/findRoute.d.ts +0 -8
- package/cjs/pathfinder/findRoute.d.ts.map +0 -1
- package/cjs/pathfinder/findRoute.js +0 -330
- package/cjs/pathfinder/index.d.ts +0 -3
- package/cjs/pathfinder/index.d.ts.map +0 -1
- package/cjs/pathfinder/index.js +0 -19
- package/cjs/svg/index.d.ts +0 -3
- package/cjs/svg/index.d.ts.map +0 -1
- package/cjs/svg/index.js +0 -31
- package/cjs/svg/pathConverter.d.ts +0 -10
- package/cjs/svg/pathConverter.d.ts.map +0 -1
- package/cjs/svg/pathConverter.js +0 -116
- package/cjs/svg/pathParser.d.ts +0 -11
- package/cjs/svg/pathParser.d.ts.map +0 -1
- package/cjs/svg/pathParser.js +0 -76
- package/cjs/utils/direction.d.ts +0 -24
- package/cjs/utils/direction.d.ts.map +0 -1
- package/cjs/utils/direction.js +0 -54
- package/cjs/utils/getAnchorPoints.d.ts +0 -15
- package/cjs/utils/getAnchorPoints.d.ts.map +0 -1
- package/cjs/utils/getAnchorPoints.js +0 -75
- package/cjs/utils/grid.d.ts +0 -27
- package/cjs/utils/grid.d.ts.map +0 -1
- package/cjs/utils/grid.js +0 -66
- package/cjs/utils/index.d.ts +0 -8
- package/cjs/utils/index.d.ts.map +0 -1
- package/cjs/utils/index.js +0 -82
- package/cjs/utils/node.d.ts +0 -27
- package/cjs/utils/node.d.ts.map +0 -1
- package/cjs/utils/node.js +0 -36
- package/cjs/utils/pathValidation.d.ts +0 -11
- package/cjs/utils/pathValidation.d.ts.map +0 -1
- package/cjs/utils/pathValidation.js +0 -130
- package/cjs/utils/rect.d.ts +0 -9
- package/cjs/utils/rect.d.ts.map +0 -1
- package/cjs/utils/rect.js +0 -103
- package/cjs/utils/route.d.ts +0 -19
- package/cjs/utils/route.d.ts.map +0 -1
- package/cjs/utils/route.js +0 -76
- package/esm/geometry/Line.d.ts +0 -21
- package/esm/geometry/Line.d.ts.map +0 -1
- package/esm/geometry/Point.d.ts +0 -49
- package/esm/geometry/Point.d.ts.map +0 -1
- package/esm/geometry/Rectangle.d.ts +0 -41
- package/esm/geometry/Rectangle.d.ts.map +0 -1
- package/esm/geometry/collision.d.ts +0 -15
- package/esm/geometry/collision.d.ts.map +0 -1
- package/esm/geometry/index.d.ts +0 -5
- package/esm/geometry/index.d.ts.map +0 -1
- package/esm/getManHattanPath.d.ts +0 -53
- package/esm/getManHattanPath.d.ts.map +0 -1
- package/esm/index.d.ts +0 -16
- package/esm/index.d.ts.map +0 -1
- package/esm/obstacle/ObstacleMap.d.ts +0 -34
- package/esm/obstacle/ObstacleMap.d.ts.map +0 -1
- package/esm/obstacle/index.d.ts +0 -2
- package/esm/obstacle/index.d.ts.map +0 -1
- package/esm/options/defaults.d.ts +0 -16
- package/esm/options/defaults.d.ts.map +0 -1
- package/esm/options/index.d.ts +0 -4
- package/esm/options/index.d.ts.map +0 -1
- package/esm/options/resolver.d.ts +0 -10
- package/esm/options/resolver.d.ts.map +0 -1
- package/esm/options/types.d.ts +0 -169
- package/esm/options/types.d.ts.map +0 -1
- package/esm/pathfinder/SortedSet.d.ts +0 -35
- package/esm/pathfinder/SortedSet.d.ts.map +0 -1
- package/esm/pathfinder/findRoute.d.ts +0 -8
- package/esm/pathfinder/findRoute.d.ts.map +0 -1
- package/esm/pathfinder/index.d.ts +0 -3
- package/esm/pathfinder/index.d.ts.map +0 -1
- package/esm/svg/index.d.ts +0 -3
- package/esm/svg/index.d.ts.map +0 -1
- package/esm/svg/pathConverter.d.ts +0 -10
- package/esm/svg/pathConverter.d.ts.map +0 -1
- package/esm/svg/pathParser.d.ts +0 -11
- package/esm/svg/pathParser.d.ts.map +0 -1
- package/esm/utils/direction.d.ts +0 -24
- package/esm/utils/direction.d.ts.map +0 -1
- package/esm/utils/getAnchorPoints.d.ts +0 -15
- package/esm/utils/getAnchorPoints.d.ts.map +0 -1
- package/esm/utils/grid.d.ts +0 -27
- package/esm/utils/grid.d.ts.map +0 -1
- package/esm/utils/index.d.ts +0 -8
- package/esm/utils/index.d.ts.map +0 -1
- package/esm/utils/node.d.ts +0 -27
- package/esm/utils/node.d.ts.map +0 -1
- package/esm/utils/pathValidation.d.ts +0 -11
- package/esm/utils/pathValidation.d.ts.map +0 -1
- package/esm/utils/rect.d.ts +0 -9
- package/esm/utils/rect.d.ts.map +0 -1
- package/esm/utils/route.d.ts +0 -19
- package/esm/utils/route.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -2,33 +2,237 @@
|
|
|
2
2
|
|
|
3
3
|
Manhattan 路由算法,用于 ReactFlow 生成正交路径,支持障碍物避让。
|
|
4
4
|
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- **A* 寻路算法** - 高效的路径搜索,支持早期终止优化
|
|
8
|
+
- **正交路径生成** - 生成符合 Manhattan 风格的直角路径
|
|
9
|
+
- **障碍物避让** - 自动绕过节点障碍物
|
|
10
|
+
- **四叉树优化** - 使用 QuadTree 加速空间查询
|
|
11
|
+
- **自适应步长** - 根据节点密度和距离自动调整网格步长
|
|
12
|
+
- **路径缓存** - LRU 缓存机制提升重复查询性能
|
|
13
|
+
- **全局网格对齐** - 确保所有路径点对齐到全局网格
|
|
14
|
+
- **圆角支持** - 可配置的路径转角圆角半径
|
|
15
|
+
- **性能监控** - 内置性能指标收集和日志
|
|
16
|
+
|
|
5
17
|
## 安装
|
|
6
18
|
|
|
7
19
|
```bash
|
|
8
20
|
npm install @rxflow/manhattan
|
|
9
21
|
# or
|
|
10
22
|
pnpm add @rxflow/manhattan
|
|
23
|
+
yarn add @rxflow/manhattan
|
|
11
24
|
```
|
|
12
25
|
|
|
13
|
-
##
|
|
26
|
+
## 基本使用
|
|
14
27
|
|
|
15
28
|
```tsx
|
|
16
29
|
import { getManHattanPath } from '@rxflow/manhattan';
|
|
30
|
+
import { useReactFlow } from '@xyflow/react';
|
|
31
|
+
|
|
32
|
+
function CustomEdge({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, source, target }) {
|
|
33
|
+
const { getNodes } = useReactFlow();
|
|
34
|
+
const nodeLookup = new Map(getNodes().map(n => [n.id, n]));
|
|
35
|
+
|
|
36
|
+
const path = getManHattanPath({
|
|
37
|
+
sourceNodeId: source,
|
|
38
|
+
targetNodeId: target,
|
|
39
|
+
sourceX,
|
|
40
|
+
sourceY,
|
|
41
|
+
targetX,
|
|
42
|
+
targetY,
|
|
43
|
+
sourcePosition,
|
|
44
|
+
targetPosition,
|
|
45
|
+
nodeLookup,
|
|
46
|
+
options: {
|
|
47
|
+
step: 10,
|
|
48
|
+
borderRadius: 5,
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return <path d={path} />;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API
|
|
57
|
+
|
|
58
|
+
### getManHattanPath(params)
|
|
59
|
+
|
|
60
|
+
生成 Manhattan 风格的 SVG 路径字符串。
|
|
61
|
+
|
|
62
|
+
#### 参数
|
|
63
|
+
|
|
64
|
+
| 参数 | 类型 | 必填 | 描述 |
|
|
65
|
+
|------|------|------|------|
|
|
66
|
+
| sourceNodeId | string | ✓ | 源节点 ID |
|
|
67
|
+
| targetNodeId | string | ✓ | 目标节点 ID |
|
|
68
|
+
| sourceX | number | ✓ | 源锚点 X 坐标 |
|
|
69
|
+
| sourceY | number | ✓ | 源锚点 Y 坐标 |
|
|
70
|
+
| targetX | number | ✓ | 目标锚点 X 坐标 |
|
|
71
|
+
| targetY | number | ✓ | 目标锚点 Y 坐标 |
|
|
72
|
+
| sourcePosition | Position | ✓ | 源锚点位置 (top/right/bottom/left) |
|
|
73
|
+
| targetPosition | Position | ✓ | 目标锚点位置 |
|
|
74
|
+
| nodeLookup | NodeLookup | ✓ | ReactFlow 节点查找 Map |
|
|
75
|
+
| options | ManhattanRouterOptions | - | 路由配置选项 |
|
|
76
|
+
|
|
77
|
+
#### 返回值
|
|
78
|
+
|
|
79
|
+
返回 SVG 路径字符串,可直接用于 `<path d={...} />` 元素。
|
|
17
80
|
|
|
81
|
+
### ManhattanRouterOptions
|
|
82
|
+
|
|
83
|
+
| 选项 | 类型 | 默认值 | 描述 |
|
|
84
|
+
|------|------|--------|------|
|
|
85
|
+
| step | number | 10 | 网格步长(像素) |
|
|
86
|
+
| maxLoopCount | number | 2000 | 最大迭代次数 |
|
|
87
|
+
| precision | number | 1 | 坐标精度(小数位数) |
|
|
88
|
+
| borderRadius | number | 5 | 转角圆角半径 |
|
|
89
|
+
| extensionDistance | number | 20 | 路径延伸距离 |
|
|
90
|
+
| padding | number \| object | 20 | 节点边界框内边距 |
|
|
91
|
+
| startDirections | Direction[] | ['top', 'right', 'bottom', 'left'] | 允许的起始方向 |
|
|
92
|
+
| endDirections | Direction[] | ['top', 'right', 'bottom', 'left'] | 允许的结束方向 |
|
|
93
|
+
| excludeNodes | string[] | [] | 排除的节点 ID |
|
|
94
|
+
| excludeShapes | string[] | [] | 排除的节点类型 |
|
|
95
|
+
| adaptiveStep | AdaptiveStepConfig | - | 自适应步长配置 |
|
|
96
|
+
| performance | PerformanceConfig | - | 性能优化配置 |
|
|
97
|
+
| debug | DebugConfig | - | 调试配置 |
|
|
98
|
+
|
|
99
|
+
### AdaptiveStepConfig
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
interface AdaptiveStepConfig {
|
|
103
|
+
enabled: boolean; // 是否启用自适应步长
|
|
104
|
+
minStep: number; // 最小步长 (默认: 5)
|
|
105
|
+
maxStep: number; // 最大步长 (默认: 50)
|
|
106
|
+
densityThreshold: number; // 密度阈值 (默认: 0.3)
|
|
107
|
+
distanceThreshold: number; // 距离阈值 (默认: 500)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### PerformanceConfig
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
interface PerformanceConfig {
|
|
115
|
+
enableCache: boolean; // 启用路径缓存
|
|
116
|
+
cacheSize: number; // 缓存大小 (默认: 100)
|
|
117
|
+
enableQuadTree: boolean; // 启用四叉树优化
|
|
118
|
+
earlyTermination: boolean; // 启用早期终止
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### DebugConfig
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
interface DebugConfig {
|
|
126
|
+
enableLogging: boolean; // 启用日志
|
|
127
|
+
enableMetrics: boolean; // 启用性能指标
|
|
128
|
+
logLevel: 'error' | 'warn' | 'info' | 'debug';
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 高级用法
|
|
133
|
+
|
|
134
|
+
### 自定义障碍物排除
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
18
137
|
const path = getManHattanPath({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
138
|
+
// ...基本参数
|
|
139
|
+
options: {
|
|
140
|
+
excludeNodes: ['node-3', 'node-4'], // 排除特定节点
|
|
141
|
+
excludeShapes: ['group'], // 排除特定类型
|
|
142
|
+
excludeTerminals: ['source'], // 排除源/目标节点
|
|
143
|
+
}
|
|
23
144
|
});
|
|
24
145
|
```
|
|
25
146
|
|
|
26
|
-
|
|
147
|
+
### 性能优化配置
|
|
27
148
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
149
|
+
```typescript
|
|
150
|
+
const path = getManHattanPath({
|
|
151
|
+
// ...基本参数
|
|
152
|
+
options: {
|
|
153
|
+
performance: {
|
|
154
|
+
enableCache: true,
|
|
155
|
+
cacheSize: 200,
|
|
156
|
+
enableQuadTree: true,
|
|
157
|
+
earlyTermination: true,
|
|
158
|
+
},
|
|
159
|
+
adaptiveStep: {
|
|
160
|
+
enabled: true,
|
|
161
|
+
minStep: 5,
|
|
162
|
+
maxStep: 30,
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 调试模式
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const path = getManHattanPath({
|
|
172
|
+
// ...基本参数
|
|
173
|
+
options: {
|
|
174
|
+
debug: {
|
|
175
|
+
enableLogging: true,
|
|
176
|
+
enableMetrics: true,
|
|
177
|
+
logLevel: 'debug',
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## 工具类
|
|
184
|
+
|
|
185
|
+
### GlobalGrid
|
|
186
|
+
|
|
187
|
+
全局网格对齐工具,确保所有路径点对齐到统一网格。
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { GlobalGrid } from '@rxflow/manhattan';
|
|
191
|
+
|
|
192
|
+
const grid = new GlobalGrid(10);
|
|
193
|
+
const snapped = grid.snapToGrid({ x: 15, y: 23 }); // { x: 20, y: 20 }
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### ObstacleMap
|
|
197
|
+
|
|
198
|
+
障碍物地图,用于空间查询和碰撞检测。
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { ObstacleMap } from '@rxflow/manhattan';
|
|
202
|
+
|
|
203
|
+
const map = new ObstacleMap(options);
|
|
204
|
+
map.build(nodeLookup, sourceId, targetId, sourceAnchor, targetAnchor);
|
|
205
|
+
const accessible = map.isAccessible(point);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### ErrorRecovery
|
|
209
|
+
|
|
210
|
+
错误恢复工具,提供回退路径生成。
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { ErrorRecovery } from '@rxflow/manhattan';
|
|
214
|
+
|
|
215
|
+
const fallback = ErrorRecovery.generateFallbackPath(start, end);
|
|
216
|
+
const validated = ErrorRecovery.validateAndFixConfig(config);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 架构
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
@rxflow/manhattan
|
|
223
|
+
├── getManHattanPath.ts # 主入口函数
|
|
224
|
+
├── geometry/ # 几何类 (Point, Rectangle)
|
|
225
|
+
├── obstacle/ # 障碍物处理 (ObstacleMap, QuadTree)
|
|
226
|
+
├── pathfinder/ # A* 寻路算法
|
|
227
|
+
├── options/ # 配置解析
|
|
228
|
+
├── svg/ # SVG 路径转换
|
|
229
|
+
└── utils/ # 工具函数
|
|
230
|
+
├── GlobalGrid.ts # 全局网格
|
|
231
|
+
├── AdaptiveStepCalculator.ts # 自适应步长
|
|
232
|
+
├── PerformanceMonitor.ts # 性能监控
|
|
233
|
+
├── ErrorRecovery.ts # 错误恢复
|
|
234
|
+
└── heuristics.ts # 启发式函数
|
|
235
|
+
```
|
|
32
236
|
|
|
33
237
|
## License
|
|
34
238
|
|
package/esm/getManHattanPath.js
CHANGED
|
@@ -14,13 +14,13 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
14
14
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
15
15
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
16
16
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
17
|
-
import { getSmoothStepPath } from '@xyflow/react';
|
|
17
|
+
import { getSmoothStepPath, Position } from '@xyflow/react';
|
|
18
18
|
import { Point, Rectangle } from "./geometry";
|
|
19
19
|
import { ObstacleMap } from "./obstacle";
|
|
20
20
|
import { resolveOptions } from "./options";
|
|
21
21
|
import { findRoute } from "./pathfinder";
|
|
22
22
|
import { pointsToPath, parseSVGPath } from "./svg";
|
|
23
|
-
import { getNodeDimensions, getNodePosition, pathIntersectsObstacles } from "./utils";
|
|
23
|
+
import { getNodeDimensions, getNodePosition, pathIntersectsObstacles, ErrorRecovery } from "./utils";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Parameters for getManHattanPath function
|
|
@@ -78,10 +78,12 @@ export function getManHattanPath(params) {
|
|
|
78
78
|
var targetNode = nodeLookup.get(targetNodeId);
|
|
79
79
|
if (!sourceNode || !targetNode) {
|
|
80
80
|
// Fallback to simple straight line if nodes not found
|
|
81
|
-
console.warn('Source or target node not found in nodeLookup');
|
|
81
|
+
console.warn('[getManHattanPath] Source or target node not found in nodeLookup');
|
|
82
82
|
var start = new Point(sourceX, sourceY);
|
|
83
83
|
var end = new Point(targetX, targetY);
|
|
84
|
-
|
|
84
|
+
// Use ErrorRecovery to generate a proper fallback path
|
|
85
|
+
var fallbackPath = ErrorRecovery.generateFallbackPath(start, end);
|
|
86
|
+
return pointsToPath(fallbackPath, options.precision, options.borderRadius);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
// Get node dimensions using ReactFlow's priority logic
|
|
@@ -118,10 +120,8 @@ export function getManHattanPath(params) {
|
|
|
118
120
|
|
|
119
121
|
// Check if smooth step path intersects with any obstacles
|
|
120
122
|
if (smoothStepPoints.length > 0 && !pathIntersectsObstacles(smoothStepPoints, nodeLookup)) {
|
|
121
|
-
console.log('[getManHattanPath] Using ReactFlow getSmoothStepPath (no obstacles)');
|
|
122
123
|
return smoothStepPath;
|
|
123
124
|
}
|
|
124
|
-
console.log('[getManHattanPath] SmoothStepPath intersects obstacles, using Manhattan routing');
|
|
125
125
|
|
|
126
126
|
// Build obstacle map with anchor information
|
|
127
127
|
var obstacleMap = new ObstacleMap(options).build(nodeLookup, sourceNodeId, targetNodeId, sourceAnchor, targetAnchor);
|
|
@@ -129,27 +129,80 @@ export function getManHattanPath(params) {
|
|
|
129
129
|
// Find route
|
|
130
130
|
var route = findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, obstacleMap, options);
|
|
131
131
|
|
|
132
|
-
// Fallback to
|
|
132
|
+
// Fallback to Z-shaped path if no route found
|
|
133
133
|
if (!route) {
|
|
134
|
-
console.warn('Unable to find Manhattan route, using
|
|
135
|
-
route =
|
|
134
|
+
console.warn('[getManHattanPath] Unable to find Manhattan route, using fallback path');
|
|
135
|
+
route = ErrorRecovery.generateFallbackPath(sourceAnchor, targetAnchor);
|
|
136
136
|
}
|
|
137
|
-
console.log('[getManHattanPath] Route from findRoute:', route.map(function (p) {
|
|
138
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
139
|
-
}));
|
|
140
|
-
console.log('[getManHattanPath] Source anchor:', "(".concat(sourceAnchor.x, ", ").concat(sourceAnchor.y, ")"));
|
|
141
|
-
console.log('[getManHattanPath] Target anchor:', "(".concat(targetAnchor.x, ", ").concat(targetAnchor.y, ")"));
|
|
142
137
|
|
|
143
138
|
// If using smart point generation (sourcePosition/targetPosition specified),
|
|
144
139
|
// the route already contains the correct extension points, so skip manual processing
|
|
145
140
|
var useSmartPoints = sourcePosition || targetPosition;
|
|
146
141
|
if (useSmartPoints) {
|
|
147
|
-
|
|
142
|
+
// Post-process route to fix X coordinates for horizontal anchors
|
|
143
|
+
// This ensures the extension distance is fixed and not affected by grid alignment
|
|
144
|
+
var isSourceHorizontal = sourcePosition === Position.Left || sourcePosition === Position.Right;
|
|
145
|
+
var isTargetHorizontal = targetPosition === Position.Left || targetPosition === Position.Right;
|
|
146
|
+
if (isSourceHorizontal && isTargetHorizontal && route.length > 0) {
|
|
147
|
+
// Calculate expected extension X coordinates
|
|
148
|
+
var sourceExtensionX = sourcePosition === Position.Right ? sourceAnchor.x + options.extensionDistance : sourceAnchor.x - options.extensionDistance;
|
|
149
|
+
var targetExtensionX = targetPosition === Position.Left ? targetAnchor.x - options.extensionDistance : targetAnchor.x + options.extensionDistance;
|
|
150
|
+
|
|
151
|
+
// Find the horizontal segment (where Y stays constant but X changes significantly)
|
|
152
|
+
// This divides the path into source-side and target-side
|
|
153
|
+
var horizontalSegmentStart = -1;
|
|
154
|
+
var horizontalSegmentEnd = -1;
|
|
155
|
+
for (var _i = 0; _i < route.length - 1; _i++) {
|
|
156
|
+
var curr = route[_i];
|
|
157
|
+
var next = route[_i + 1];
|
|
158
|
+
var isHorizontalMove = Math.abs(curr.y - next.y) < 1 && Math.abs(curr.x - next.x) > options.step;
|
|
159
|
+
if (isHorizontalMove) {
|
|
160
|
+
horizontalSegmentStart = _i;
|
|
161
|
+
horizontalSegmentEnd = _i + 1;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Fix route points
|
|
167
|
+
for (var _i2 = 0; _i2 < route.length; _i2++) {
|
|
168
|
+
var point = route[_i2];
|
|
169
|
+
if (horizontalSegmentStart >= 0) {
|
|
170
|
+
if (_i2 <= horizontalSegmentStart) {
|
|
171
|
+
// Source side - all vertical segment points should use sourceExtensionX
|
|
172
|
+
route[_i2] = new Point(sourceExtensionX, point.y);
|
|
173
|
+
} else if (_i2 >= horizontalSegmentEnd) {
|
|
174
|
+
// Target side - all vertical segment points should use targetExtensionX
|
|
175
|
+
route[_i2] = new Point(targetExtensionX, point.y);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Remove redundant points (consecutive points on same line)
|
|
181
|
+
var optimized = [];
|
|
182
|
+
for (var _i3 = 0; _i3 < route.length; _i3++) {
|
|
183
|
+
var _point = route[_i3];
|
|
184
|
+
if (optimized.length < 2) {
|
|
185
|
+
optimized.push(_point);
|
|
186
|
+
} else {
|
|
187
|
+
var prev = optimized[optimized.length - 1];
|
|
188
|
+
var prevPrev = optimized[optimized.length - 2];
|
|
189
|
+
|
|
190
|
+
// Check if prev is on the same line as prevPrev and point
|
|
191
|
+
var sameX = Math.abs(prevPrev.x - prev.x) < 1 && Math.abs(prev.x - _point.x) < 1;
|
|
192
|
+
var sameY = Math.abs(prevPrev.y - prev.y) < 1 && Math.abs(prev.y - _point.y) < 1;
|
|
193
|
+
if (sameX || sameY) {
|
|
194
|
+
// prev is redundant, replace it with current point
|
|
195
|
+
optimized[optimized.length - 1] = _point;
|
|
196
|
+
} else {
|
|
197
|
+
optimized.push(_point);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
route = optimized;
|
|
202
|
+
}
|
|
203
|
+
|
|
148
204
|
// Add source and target anchors to route
|
|
149
205
|
var _finalRoute = [sourceAnchor].concat(_toConsumableArray(route), [targetAnchor]);
|
|
150
|
-
console.log('[getManHattanPath] Final route:', _finalRoute.map(function (p) {
|
|
151
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
152
|
-
}));
|
|
153
206
|
return pointsToPath(_finalRoute, options.precision, options.borderRadius);
|
|
154
207
|
}
|
|
155
208
|
|
|
@@ -172,7 +225,6 @@ export function getManHattanPath(params) {
|
|
|
172
225
|
// This is likely an extension point, remove it
|
|
173
226
|
if (onRight && firstPoint.x > sourceAnchor.x || onLeft && firstPoint.x < sourceAnchor.x || onBottom && firstPoint.y > sourceAnchor.y || onTop && firstPoint.y < sourceAnchor.y) {
|
|
174
227
|
route.shift();
|
|
175
|
-
console.log('[getManHattanPath] Removed extension point from route start');
|
|
176
228
|
}
|
|
177
229
|
}
|
|
178
230
|
}
|
|
@@ -191,7 +243,6 @@ export function getManHattanPath(params) {
|
|
|
191
243
|
// This is likely an extension point, remove it
|
|
192
244
|
if (_onLeft && lastPoint.x < targetAnchor.x || _onRight && lastPoint.x > targetAnchor.x || _onTop && lastPoint.y < targetAnchor.y || _onBottom && lastPoint.y > targetAnchor.y) {
|
|
193
245
|
route.pop();
|
|
194
|
-
console.log('[getManHattanPath] Removed extension point from route end');
|
|
195
246
|
}
|
|
196
247
|
}
|
|
197
248
|
}
|
|
@@ -217,7 +268,6 @@ export function getManHattanPath(params) {
|
|
|
217
268
|
route.unshift(new Point(extendX, _firstPoint.y)); // Corner point
|
|
218
269
|
}
|
|
219
270
|
route.unshift(extensionPoint); // Extension point (fixed distance)
|
|
220
|
-
console.log('[getManHattanPath] Inserted source extension (right):', "(".concat(extendX, ", ").concat(sourceAnchor.y, ")"));
|
|
221
271
|
} else if (_onLeft2) {
|
|
222
272
|
// Anchor on left edge - extend left by step + borderRadius
|
|
223
273
|
var _extendX = sourceAnchor.x - extensionDistance;
|
|
@@ -226,7 +276,6 @@ export function getManHattanPath(params) {
|
|
|
226
276
|
route.unshift(new Point(_extendX, _firstPoint.y));
|
|
227
277
|
}
|
|
228
278
|
route.unshift(_extensionPoint);
|
|
229
|
-
console.log('[getManHattanPath] Inserted source extension (left):', "(".concat(_extendX, ", ").concat(sourceAnchor.y, ")"));
|
|
230
279
|
} else if (_onBottom2) {
|
|
231
280
|
// Anchor on bottom edge - extend down by step + borderRadius
|
|
232
281
|
var extendY = sourceAnchor.y + extensionDistance;
|
|
@@ -235,7 +284,6 @@ export function getManHattanPath(params) {
|
|
|
235
284
|
route.unshift(new Point(_firstPoint.x, extendY));
|
|
236
285
|
}
|
|
237
286
|
route.unshift(_extensionPoint2);
|
|
238
|
-
console.log('[getManHattanPath] Inserted source extension (down):', "(".concat(sourceAnchor.x, ", ").concat(extendY, ")"));
|
|
239
287
|
} else if (_onTop2) {
|
|
240
288
|
// Anchor on top edge - extend up by step + borderRadius
|
|
241
289
|
var _extendY = sourceAnchor.y - extensionDistance;
|
|
@@ -244,33 +292,25 @@ export function getManHattanPath(params) {
|
|
|
244
292
|
route.unshift(new Point(_firstPoint.x, _extendY));
|
|
245
293
|
}
|
|
246
294
|
route.unshift(_extensionPoint3);
|
|
247
|
-
console.log('[getManHattanPath] Inserted source extension (up):', "(".concat(sourceAnchor.x, ", ").concat(_extendY, ")"));
|
|
248
295
|
}
|
|
249
296
|
}
|
|
250
297
|
|
|
251
298
|
// Remove redundant points after source extension
|
|
252
299
|
// If the first route point has the same x or y coordinate as the source anchor, it's redundant
|
|
253
|
-
if (route.length >
|
|
254
|
-
var firstRoutePoint = route[0]; // Extension point
|
|
255
|
-
var secondRoutePoint = route[1]; // Corner point (if exists)
|
|
300
|
+
if (route.length > 3) {
|
|
256
301
|
var thirdRoutePoint = route[2]; // Original A* point
|
|
257
|
-
|
|
258
302
|
// Check if the third point (original A* point) is redundant
|
|
259
303
|
// It's redundant if it's on the same line as the corner point and can be skipped
|
|
260
|
-
var
|
|
261
|
-
var
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (cornerToThird && thirdToFourth) {
|
|
271
|
-
console.log('[getManHattanPath] Removing redundant point:', "(".concat(thirdRoutePoint.x, ", ").concat(thirdRoutePoint.y, ")"));
|
|
272
|
-
route.splice(2, 1); // Remove the third point
|
|
273
|
-
}
|
|
304
|
+
var _sameX = Math.abs(thirdRoutePoint.x - sourceAnchor.x) < tolerance;
|
|
305
|
+
var _sameY = Math.abs(thirdRoutePoint.y - sourceAnchor.y) < tolerance;
|
|
306
|
+
if (_sameX || _sameY) {
|
|
307
|
+
var secondRoutePoint = route[1]; // Corner point (if exists)
|
|
308
|
+
var fourthPoint = route[3];
|
|
309
|
+
// If corner point and fourth point form a straight line, remove the third point
|
|
310
|
+
var cornerToThird = Math.abs(secondRoutePoint.x - thirdRoutePoint.x) < tolerance || Math.abs(secondRoutePoint.y - thirdRoutePoint.y) < tolerance;
|
|
311
|
+
var thirdToFourth = Math.abs(thirdRoutePoint.x - fourthPoint.x) < tolerance || Math.abs(thirdRoutePoint.y - fourthPoint.y) < tolerance;
|
|
312
|
+
if (cornerToThird && thirdToFourth) {
|
|
313
|
+
route.splice(2, 1); // Remove the third point
|
|
274
314
|
}
|
|
275
315
|
}
|
|
276
316
|
}
|
|
@@ -280,16 +320,12 @@ export function getManHattanPath(params) {
|
|
|
280
320
|
// where the middle segment goes to target edge and then moves along it
|
|
281
321
|
// Use target bbox with padding (same as ObstacleMap)
|
|
282
322
|
var targetBBoxWithPadding = targetBBox.moveAndExpand(options.paddingBox);
|
|
283
|
-
console.log('[getManHattanPath] Route before zigzag check:', route.map(function (p) {
|
|
284
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
285
|
-
}));
|
|
286
|
-
console.log('[getManHattanPath] Target BBox with padding:', "x=".concat(targetBBoxWithPadding.x, ", y=").concat(targetBBoxWithPadding.y));
|
|
287
323
|
if (route.length >= 3) {
|
|
288
|
-
var
|
|
289
|
-
while (
|
|
290
|
-
var p1 = route[
|
|
291
|
-
var p2 = route[
|
|
292
|
-
var p3 = route[
|
|
324
|
+
var _i4 = 0;
|
|
325
|
+
while (_i4 < route.length - 2) {
|
|
326
|
+
var p1 = route[_i4];
|
|
327
|
+
var p2 = route[_i4 + 1];
|
|
328
|
+
var p3 = route[_i4 + 2];
|
|
293
329
|
|
|
294
330
|
// Check if p2 is on the target bbox edge (with padding)
|
|
295
331
|
var p2OnTargetLeftEdge = Math.abs(p2.x - targetBBoxWithPadding.x) < tolerance;
|
|
@@ -297,22 +333,19 @@ export function getManHattanPath(params) {
|
|
|
297
333
|
var p2OnTargetTopEdge = Math.abs(p2.y - targetBBoxWithPadding.y) < tolerance;
|
|
298
334
|
var p2OnTargetBottomEdge = Math.abs(p2.y - (targetBBoxWithPadding.y + targetBBoxWithPadding.height)) < tolerance;
|
|
299
335
|
var p2OnTargetEdge = p2OnTargetLeftEdge || p2OnTargetRightEdge || p2OnTargetTopEdge || p2OnTargetBottomEdge;
|
|
300
|
-
console.log("[getManHattanPath] Checking i=".concat(_i, ": p2=(").concat(p2.x, ", ").concat(p2.y, "), onEdge=").concat(p2OnTargetEdge));
|
|
301
336
|
if (p2OnTargetEdge) {
|
|
302
337
|
// Check if p1 -> p2 -> p3 forms a zigzag
|
|
303
338
|
var p1ToP2Horizontal = Math.abs(p1.y - p2.y) < tolerance;
|
|
304
339
|
var p2ToP3Vertical = Math.abs(p2.x - p3.x) < tolerance;
|
|
305
340
|
var p1ToP2Vertical = Math.abs(p1.x - p2.x) < tolerance;
|
|
306
341
|
var p2ToP3Horizontal = Math.abs(p2.y - p3.y) < tolerance;
|
|
307
|
-
console.log("[getManHattanPath] Zigzag pattern: H->V=".concat(p1ToP2Horizontal && p2ToP3Vertical, ", V->H=").concat(p1ToP2Vertical && p2ToP3Horizontal));
|
|
308
342
|
if (p1ToP2Horizontal && p2ToP3Vertical || p1ToP2Vertical && p2ToP3Horizontal) {
|
|
309
343
|
// We have a zigzag at target edge, remove p2 and p3
|
|
310
|
-
|
|
311
|
-
route.splice(_i + 1, 2); // Remove p2 and p3
|
|
344
|
+
route.splice(_i4 + 1, 2); // Remove p2 and p3
|
|
312
345
|
continue;
|
|
313
346
|
}
|
|
314
347
|
}
|
|
315
|
-
|
|
348
|
+
_i4++;
|
|
316
349
|
}
|
|
317
350
|
}
|
|
318
351
|
|
|
@@ -336,7 +369,6 @@ export function getManHattanPath(params) {
|
|
|
336
369
|
route.push(new Point(_extendX2, _lastPoint.y)); // Corner point
|
|
337
370
|
}
|
|
338
371
|
route.push(_extensionPoint4); // Extension point (fixed distance)
|
|
339
|
-
console.log('[getManHattanPath] Inserted target extension (left):', "(".concat(_extendX2, ", ").concat(targetAnchor.y, ")"));
|
|
340
372
|
} else if (_onRight3) {
|
|
341
373
|
// Anchor on right edge - extend right by step + borderRadius
|
|
342
374
|
var _extendX3 = targetAnchor.x + _extensionDistance;
|
|
@@ -345,7 +377,6 @@ export function getManHattanPath(params) {
|
|
|
345
377
|
route.push(new Point(_extendX3, _lastPoint.y));
|
|
346
378
|
}
|
|
347
379
|
route.push(_extensionPoint5);
|
|
348
|
-
console.log('[getManHattanPath] Inserted target extension (right):', "(".concat(_extendX3, ", ").concat(targetAnchor.y, ")"));
|
|
349
380
|
} else if (_onTop3) {
|
|
350
381
|
// Anchor on top edge - extend up by step + borderRadius
|
|
351
382
|
var _extendY2 = targetAnchor.y - _extensionDistance;
|
|
@@ -354,7 +385,6 @@ export function getManHattanPath(params) {
|
|
|
354
385
|
route.push(new Point(_lastPoint.x, _extendY2));
|
|
355
386
|
}
|
|
356
387
|
route.push(_extensionPoint6);
|
|
357
|
-
console.log('[getManHattanPath] Inserted target extension (up):', "(".concat(targetAnchor.x, ", ").concat(_extendY2, ")"));
|
|
358
388
|
} else if (_onBottom3) {
|
|
359
389
|
// Anchor on bottom edge - extend down by step + borderRadius
|
|
360
390
|
var _extendY3 = targetAnchor.y + _extensionDistance;
|
|
@@ -363,7 +393,6 @@ export function getManHattanPath(params) {
|
|
|
363
393
|
route.push(new Point(_lastPoint.x, _extendY3));
|
|
364
394
|
}
|
|
365
395
|
route.push(_extensionPoint7);
|
|
366
|
-
console.log('[getManHattanPath] Inserted target extension (down):', "(".concat(targetAnchor.x, ", ").concat(_extendY3, ")"));
|
|
367
396
|
}
|
|
368
397
|
}
|
|
369
398
|
|
|
@@ -371,20 +400,18 @@ export function getManHattanPath(params) {
|
|
|
371
400
|
// Similar logic for target side
|
|
372
401
|
if (route.length > 2) {
|
|
373
402
|
var lastIdx = route.length - 1;
|
|
374
|
-
var lastRoutePoint = route[lastIdx]; // Extension point
|
|
375
403
|
var secondLastPoint = route[lastIdx - 1]; // Corner point (if exists)
|
|
376
404
|
var thirdLastPoint = route[lastIdx - 2]; // Original A* point
|
|
377
405
|
|
|
378
406
|
// Check if the third-to-last point is redundant
|
|
379
|
-
var
|
|
380
|
-
var
|
|
381
|
-
if (
|
|
407
|
+
var _sameX2 = Math.abs(thirdLastPoint.x - targetAnchor.x) < tolerance;
|
|
408
|
+
var _sameY2 = Math.abs(thirdLastPoint.y - targetAnchor.y) < tolerance;
|
|
409
|
+
if (_sameX2 || _sameY2) {
|
|
382
410
|
if (route.length > 3) {
|
|
383
411
|
var fourthLastPoint = route[lastIdx - 3];
|
|
384
412
|
var fourthToThird = Math.abs(fourthLastPoint.x - thirdLastPoint.x) < tolerance || Math.abs(fourthLastPoint.y - thirdLastPoint.y) < tolerance;
|
|
385
413
|
var thirdToSecond = Math.abs(thirdLastPoint.x - secondLastPoint.x) < tolerance || Math.abs(thirdLastPoint.y - secondLastPoint.y) < tolerance;
|
|
386
414
|
if (fourthToThird && thirdToSecond) {
|
|
387
|
-
console.log('[getManHattanPath] Removing redundant point:', "(".concat(thirdLastPoint.x, ", ").concat(thirdLastPoint.y, ")"));
|
|
388
415
|
route.splice(lastIdx - 2, 1); // Remove the third-to-last point
|
|
389
416
|
}
|
|
390
417
|
}
|
|
@@ -417,7 +444,6 @@ export function getManHattanPath(params) {
|
|
|
417
444
|
var p3ToP4Horizontal = Math.abs(_p3.y - p4.y) < tolerance;
|
|
418
445
|
if (p3ToP4Horizontal) {
|
|
419
446
|
// Pattern: horizontal -> vertical -> horizontal (zigzag)
|
|
420
|
-
console.log('[getManHattanPath] Removing zigzag at target edge:', "(".concat(_p2.x, ", ").concat(_p2.y, ")"), "and (".concat(_p3.x, ", ").concat(_p3.y, ")"));
|
|
421
447
|
route.splice(i + 1, 2); // Remove p2 and p3
|
|
422
448
|
continue;
|
|
423
449
|
}
|
|
@@ -428,9 +454,6 @@ export function getManHattanPath(params) {
|
|
|
428
454
|
|
|
429
455
|
// Add source and target anchors to route
|
|
430
456
|
var finalRoute = [sourceAnchor].concat(_toConsumableArray(route), [targetAnchor]);
|
|
431
|
-
console.log('[getManHattanPath] Final route:', finalRoute.map(function (p) {
|
|
432
|
-
return "(".concat(p.x, ", ").concat(p.y, ")");
|
|
433
|
-
}));
|
|
434
457
|
|
|
435
458
|
// Convert to SVG path string
|
|
436
459
|
return pointsToPath(finalRoute, options.precision, options.borderRadius);
|