@rxflow/manhattan 0.0.2-alpha.8 → 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.
Files changed (116) hide show
  1. package/README.md +214 -10
  2. package/cjs/getManHattanPath.d.ts.map +1 -1
  3. package/cjs/getManHattanPath.js +77 -46
  4. package/cjs/obstacle/ObstacleMap.d.ts +41 -9
  5. package/cjs/obstacle/ObstacleMap.d.ts.map +1 -1
  6. package/cjs/obstacle/ObstacleMap.js +201 -96
  7. package/cjs/obstacle/QuadTree.d.ts +119 -0
  8. package/cjs/obstacle/QuadTree.d.ts.map +1 -0
  9. package/cjs/obstacle/QuadTree.js +334 -0
  10. package/cjs/options/defaults.d.ts +1 -1
  11. package/cjs/options/defaults.d.ts.map +1 -1
  12. package/cjs/options/resolver.d.ts.map +1 -1
  13. package/cjs/options/resolver.js +145 -17
  14. package/cjs/options/types.d.ts +41 -0
  15. package/cjs/options/types.d.ts.map +1 -1
  16. package/cjs/pathfinder/PathCache.d.ts +92 -0
  17. package/cjs/pathfinder/PathCache.d.ts.map +1 -0
  18. package/cjs/pathfinder/PathCache.js +249 -0
  19. package/cjs/pathfinder/findRoute.d.ts.map +1 -1
  20. package/cjs/pathfinder/findRoute.js +96 -31
  21. package/cjs/pathfinder/index.d.ts +1 -0
  22. package/cjs/pathfinder/index.d.ts.map +1 -1
  23. package/cjs/pathfinder/index.js +26 -1
  24. package/cjs/svg/pathConverter.d.ts +13 -0
  25. package/cjs/svg/pathConverter.d.ts.map +1 -1
  26. package/cjs/svg/pathConverter.js +170 -1
  27. package/cjs/utils/AdaptiveStepCalculator.d.ts +90 -0
  28. package/cjs/utils/AdaptiveStepCalculator.d.ts.map +1 -0
  29. package/cjs/utils/AdaptiveStepCalculator.js +224 -0
  30. package/cjs/utils/ErrorRecovery.d.ts +182 -0
  31. package/cjs/utils/ErrorRecovery.d.ts.map +1 -0
  32. package/cjs/utils/ErrorRecovery.js +413 -0
  33. package/cjs/utils/GlobalGrid.d.ts +99 -0
  34. package/cjs/utils/GlobalGrid.d.ts.map +1 -0
  35. package/cjs/utils/GlobalGrid.js +224 -0
  36. package/cjs/utils/PerformanceMonitor.d.ts +139 -0
  37. package/cjs/utils/PerformanceMonitor.d.ts.map +1 -0
  38. package/cjs/utils/PerformanceMonitor.js +305 -0
  39. package/cjs/utils/getAnchorPoints.d.ts.map +1 -1
  40. package/cjs/utils/getAnchorPoints.js +0 -4
  41. package/cjs/utils/grid.d.ts +15 -0
  42. package/cjs/utils/grid.d.ts.map +1 -1
  43. package/cjs/utils/grid.js +19 -12
  44. package/cjs/utils/heuristics.d.ts +61 -0
  45. package/cjs/utils/heuristics.d.ts.map +1 -0
  46. package/cjs/utils/heuristics.js +141 -0
  47. package/cjs/utils/index.d.ts +6 -0
  48. package/cjs/utils/index.d.ts.map +1 -1
  49. package/cjs/utils/index.js +66 -0
  50. package/cjs/utils/pathProcessing.d.ts +45 -0
  51. package/cjs/utils/pathProcessing.d.ts.map +1 -0
  52. package/cjs/utils/pathProcessing.js +270 -0
  53. package/cjs/utils/pathValidation.d.ts.map +1 -1
  54. package/cjs/utils/pathValidation.js +0 -1
  55. package/cjs/utils/rect.d.ts.map +1 -1
  56. package/cjs/utils/rect.js +7 -0
  57. package/cjs/utils/route.d.ts.map +1 -1
  58. package/cjs/utils/route.js +18 -2
  59. package/esm/getManHattanPath.d.ts.map +1 -1
  60. package/esm/getManHattanPath.js +92 -69
  61. package/esm/obstacle/ObstacleMap.d.ts +41 -9
  62. package/esm/obstacle/ObstacleMap.d.ts.map +1 -1
  63. package/esm/obstacle/ObstacleMap.js +218 -99
  64. package/esm/obstacle/QuadTree.d.ts +119 -0
  65. package/esm/obstacle/QuadTree.d.ts.map +1 -0
  66. package/esm/obstacle/QuadTree.js +488 -0
  67. package/esm/options/defaults.d.ts +1 -1
  68. package/esm/options/defaults.d.ts.map +1 -1
  69. package/esm/options/resolver.d.ts.map +1 -1
  70. package/esm/options/resolver.js +147 -18
  71. package/esm/options/types.d.ts +41 -0
  72. package/esm/options/types.d.ts.map +1 -1
  73. package/esm/pathfinder/PathCache.d.ts +92 -0
  74. package/esm/pathfinder/PathCache.d.ts.map +1 -0
  75. package/esm/pathfinder/PathCache.js +278 -0
  76. package/esm/pathfinder/findRoute.d.ts.map +1 -1
  77. package/esm/pathfinder/findRoute.js +98 -44
  78. package/esm/pathfinder/index.d.ts +1 -0
  79. package/esm/pathfinder/index.d.ts.map +1 -1
  80. package/esm/pathfinder/index.js +2 -1
  81. package/esm/svg/pathConverter.d.ts +13 -0
  82. package/esm/svg/pathConverter.d.ts.map +1 -1
  83. package/esm/svg/pathConverter.js +170 -1
  84. package/esm/utils/AdaptiveStepCalculator.d.ts +90 -0
  85. package/esm/utils/AdaptiveStepCalculator.d.ts.map +1 -0
  86. package/esm/utils/AdaptiveStepCalculator.js +252 -0
  87. package/esm/utils/ErrorRecovery.d.ts +182 -0
  88. package/esm/utils/ErrorRecovery.d.ts.map +1 -0
  89. package/esm/utils/ErrorRecovery.js +499 -0
  90. package/esm/utils/GlobalGrid.d.ts +99 -0
  91. package/esm/utils/GlobalGrid.d.ts.map +1 -0
  92. package/esm/utils/GlobalGrid.js +259 -0
  93. package/esm/utils/PerformanceMonitor.d.ts +139 -0
  94. package/esm/utils/PerformanceMonitor.d.ts.map +1 -0
  95. package/esm/utils/PerformanceMonitor.js +360 -0
  96. package/esm/utils/getAnchorPoints.d.ts.map +1 -1
  97. package/esm/utils/getAnchorPoints.js +0 -4
  98. package/esm/utils/grid.d.ts +15 -0
  99. package/esm/utils/grid.d.ts.map +1 -1
  100. package/esm/utils/grid.js +18 -13
  101. package/esm/utils/heuristics.d.ts +61 -0
  102. package/esm/utils/heuristics.d.ts.map +1 -0
  103. package/esm/utils/heuristics.js +144 -0
  104. package/esm/utils/index.d.ts +6 -0
  105. package/esm/utils/index.d.ts.map +1 -1
  106. package/esm/utils/index.js +7 -1
  107. package/esm/utils/pathProcessing.d.ts +45 -0
  108. package/esm/utils/pathProcessing.d.ts.map +1 -0
  109. package/esm/utils/pathProcessing.js +270 -0
  110. package/esm/utils/pathValidation.d.ts.map +1 -1
  111. package/esm/utils/pathValidation.js +0 -1
  112. package/esm/utils/rect.d.ts.map +1 -1
  113. package/esm/utils/rect.js +11 -4
  114. package/esm/utils/route.d.ts.map +1 -1
  115. package/esm/utils/route.js +18 -2
  116. package/package.json +10 -2
@@ -3,8 +3,47 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.countCorners = countCorners;
7
+ exports.hasRoundedCorners = hasRoundedCorners;
6
8
  exports.pointsToPath = pointsToPath;
9
+ exports.pointsToPathCubic = pointsToPathCubic;
7
10
  exports.snapPathToGrid = snapPathToGrid;
11
+ /**
12
+ * Check if three points form a corner (direction change)
13
+ */
14
+ function isCorner(prev, current, next) {
15
+ const dx1 = current.x - prev.x;
16
+ const dy1 = current.y - prev.y;
17
+ const dx2 = next.x - current.x;
18
+ const dy2 = next.y - current.y;
19
+
20
+ // Check if direction changes (not collinear)
21
+ // For Manhattan paths, this means one segment is horizontal and the other is vertical
22
+ const isHorizontal1 = Math.abs(dy1) < 0.001;
23
+ const isVertical1 = Math.abs(dx1) < 0.001;
24
+ const isHorizontal2 = Math.abs(dy2) < 0.001;
25
+ const isVertical2 = Math.abs(dx2) < 0.001;
26
+
27
+ // Corner exists if one segment is horizontal and the other is vertical
28
+ return isHorizontal1 && isVertical2 || isVertical1 && isHorizontal2;
29
+ }
30
+
31
+ /**
32
+ * Calculate optimal border radius for a corner
33
+ * Ensures the radius doesn't exceed half the length of adjacent segments
34
+ */
35
+ function calculateOptimalRadius(prev, current, next, maxRadius) {
36
+ const dx1 = current.x - prev.x;
37
+ const dy1 = current.y - prev.y;
38
+ const dx2 = next.x - current.x;
39
+ const dy2 = next.y - current.y;
40
+ const dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
41
+ const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42
+
43
+ // Use the smaller of maxRadius or half the segment length
44
+ return Math.min(maxRadius, dist1 / 2, dist2 / 2);
45
+ }
46
+
8
47
  /**
9
48
  * Convert array of points to SVG path string
10
49
  */
@@ -36,6 +75,13 @@ function pointsToPath(points, precision, borderRadius = 0) {
36
75
  const current = points[i];
37
76
  const next = points[i + 1];
38
77
 
78
+ // Check if this is actually a corner
79
+ if (!isCorner(prev, current, next)) {
80
+ // Not a corner, just draw a line to current point
81
+ path += ` L ${roundCoord(current.x)} ${roundCoord(current.y)}`;
82
+ continue;
83
+ }
84
+
39
85
  // Calculate direction vectors
40
86
  const dx1 = current.x - prev.x;
41
87
  const dy1 = current.y - prev.y;
@@ -47,7 +93,13 @@ function pointsToPath(points, precision, borderRadius = 0) {
47
93
  const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
48
94
 
49
95
  // Use the smaller of borderRadius or half the segment length
50
- const radius = Math.min(borderRadius, dist1 / 2, dist2 / 2);
96
+ const radius = calculateOptimalRadius(prev, current, next, borderRadius);
97
+
98
+ // Skip if radius is too small
99
+ if (radius < 0.5) {
100
+ path += ` L ${roundCoord(current.x)} ${roundCoord(current.y)}`;
101
+ continue;
102
+ }
51
103
 
52
104
  // Normalize direction vectors
53
105
  const ndx1 = dx1 / dist1;
@@ -81,6 +133,103 @@ function pointsToPath(points, precision, borderRadius = 0) {
81
133
  return path;
82
134
  }
83
135
 
136
+ /**
137
+ * Convert array of points to SVG path string with cubic bezier curves
138
+ * Provides smoother corners than quadratic bezier
139
+ */
140
+ function pointsToPathCubic(points, precision, borderRadius = 0) {
141
+ if (points.length === 0) {
142
+ return '';
143
+ }
144
+ const factor = Math.pow(10, precision);
145
+ const roundCoord = value => Math.round(value * factor) / factor;
146
+
147
+ // If no border radius or only 2 points, use simple line path
148
+ if (borderRadius === 0 || points.length <= 2) {
149
+ const firstPoint = points[0];
150
+ let path = `M ${roundCoord(firstPoint.x)} ${roundCoord(firstPoint.y)}`;
151
+ for (let i = 1; i < points.length; i++) {
152
+ const point = points[i];
153
+ path += ` L ${roundCoord(point.x)} ${roundCoord(point.y)}`;
154
+ }
155
+ return path;
156
+ }
157
+
158
+ // Start with M (moveTo) command for first point
159
+ const firstPoint = points[0];
160
+ let path = `M ${roundCoord(firstPoint.x)} ${roundCoord(firstPoint.y)}`;
161
+
162
+ // Process each segment with rounded corners using cubic bezier
163
+ for (let i = 1; i < points.length - 1; i++) {
164
+ const prev = points[i - 1];
165
+ const current = points[i];
166
+ const next = points[i + 1];
167
+
168
+ // Check if this is actually a corner
169
+ if (!isCorner(prev, current, next)) {
170
+ path += ` L ${roundCoord(current.x)} ${roundCoord(current.y)}`;
171
+ continue;
172
+ }
173
+
174
+ // Calculate direction vectors
175
+ const dx1 = current.x - prev.x;
176
+ const dy1 = current.y - prev.y;
177
+ const dx2 = next.x - current.x;
178
+ const dy2 = next.y - current.y;
179
+
180
+ // Calculate distances
181
+ const dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
182
+ const dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
183
+
184
+ // Use the smaller of borderRadius or half the segment length
185
+ const radius = calculateOptimalRadius(prev, current, next, borderRadius);
186
+
187
+ // Skip if radius is too small
188
+ if (radius < 0.5) {
189
+ path += ` L ${roundCoord(current.x)} ${roundCoord(current.y)}`;
190
+ continue;
191
+ }
192
+
193
+ // Normalize direction vectors
194
+ const ndx1 = dx1 / dist1;
195
+ const ndy1 = dy1 / dist1;
196
+ const ndx2 = dx2 / dist2;
197
+ const ndy2 = dy2 / dist2;
198
+
199
+ // Calculate the point before the corner
200
+ const beforeCorner = {
201
+ x: current.x - ndx1 * radius,
202
+ y: current.y - ndy1 * radius
203
+ };
204
+
205
+ // Calculate the point after the corner
206
+ const afterCorner = {
207
+ x: current.x + ndx2 * radius,
208
+ y: current.y + ndy2 * radius
209
+ };
210
+
211
+ // Draw line to the point before corner
212
+ path += ` L ${roundCoord(beforeCorner.x)} ${roundCoord(beforeCorner.y)}`;
213
+
214
+ // Draw cubic bezier curve for smoother rounded corner
215
+ // Control points are positioned to create a smooth arc
216
+ const cp1 = {
217
+ x: beforeCorner.x + ndx1 * radius * 0.55,
218
+ y: beforeCorner.y + ndy1 * radius * 0.55
219
+ };
220
+ const cp2 = {
221
+ x: afterCorner.x - ndx2 * radius * 0.55,
222
+ y: afterCorner.y - ndy2 * radius * 0.55
223
+ };
224
+ path += ` C ${roundCoord(cp1.x)} ${roundCoord(cp1.y)} ${roundCoord(cp2.x)} ${roundCoord(cp2.y)} ${roundCoord(afterCorner.x)} ${roundCoord(afterCorner.y)}`;
225
+ }
226
+
227
+ // Draw line to the last point
228
+ const lastPoint = points[points.length - 1];
229
+ path += ` L ${roundCoord(lastPoint.x)} ${roundCoord(lastPoint.y)}`;
230
+ return path;
231
+ }
232
+
84
233
  /**
85
234
  * Snap path to grid by aligning consecutive points
86
235
  */
@@ -113,4 +262,24 @@ function snapPathToGrid(points, gridSize) {
113
262
  }
114
263
  }
115
264
  return snappedPoints;
265
+ }
266
+
267
+ /**
268
+ * Check if an SVG path contains rounded corners (Q or C commands)
269
+ */
270
+ function hasRoundedCorners(path) {
271
+ return /[QC]\s/.test(path);
272
+ }
273
+
274
+ /**
275
+ * Count the number of corners in a path
276
+ */
277
+ function countCorners(points) {
278
+ let corners = 0;
279
+ for (let i = 1; i < points.length - 1; i++) {
280
+ if (isCorner(points[i - 1], points[i], points[i + 1])) {
281
+ corners++;
282
+ }
283
+ }
284
+ return corners;
116
285
  }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * AdaptiveStepCalculator - 自适应步长计算器
3
+ *
4
+ * 根据场景自动调整网格步长,平衡性能和路径质量
5
+ * - 高密度区域使用小步长,提高路径精度
6
+ * - 长距离路径使用大步长,提高计算性能
7
+ *
8
+ * Feature: manhattan-optimization
9
+ * Requirements: 2.3, 2.4
10
+ */
11
+ import type { InternalNode, NodeLookup } from '../options/types';
12
+ import { Point } from '../geometry';
13
+ /**
14
+ * 自适应步长配置
15
+ */
16
+ export interface AdaptiveStepConfig {
17
+ /** 是否启用自适应步长 */
18
+ enabled: boolean;
19
+ /** 最小步长 */
20
+ minStep: number;
21
+ /** 最大步长 */
22
+ maxStep: number;
23
+ /** 基于密度的调整配置 */
24
+ densityBased: {
25
+ enabled: boolean;
26
+ /** 节点密度阈值(节点数/10000平方像素) */
27
+ threshold: number;
28
+ };
29
+ /** 基于距离的调整配置 */
30
+ distanceBased: {
31
+ enabled: boolean;
32
+ /** 短路径阈值(像素) */
33
+ shortPathThreshold: number;
34
+ /** 长路径阈值(像素) */
35
+ longPathThreshold: number;
36
+ };
37
+ }
38
+ /**
39
+ * 默认自适应步长配置
40
+ */
41
+ export declare const DEFAULT_ADAPTIVE_STEP_CONFIG: AdaptiveStepConfig;
42
+ /**
43
+ * 自适应步长计算器
44
+ */
45
+ export declare class AdaptiveStepCalculator {
46
+ private config;
47
+ constructor(config?: Partial<AdaptiveStepConfig>);
48
+ /**
49
+ * 合并配置与默认值
50
+ */
51
+ private mergeConfig;
52
+ /**
53
+ * 计算节点密度
54
+ * @param nodeLookup 节点查找表
55
+ * @returns 密度值(节点数/10000平方像素)
56
+ */
57
+ calculateNodeDensity(nodeLookup: NodeLookup): number;
58
+ /**
59
+ * 计算两点之间的曼哈顿距离
60
+ */
61
+ calculateDistance(source: Point | {
62
+ x: number;
63
+ y: number;
64
+ }, target: Point | {
65
+ x: number;
66
+ y: number;
67
+ }): number;
68
+ /**
69
+ * 基于距离计算步长
70
+ */
71
+ calculateDistanceBasedStep(distance: number): number;
72
+ /**
73
+ * 基于密度计算步长
74
+ */
75
+ calculateDensityBasedStep(density: number): number;
76
+ /**
77
+ * 计算自适应步长
78
+ * 综合考虑距离和密度因素
79
+ */
80
+ calculateStep(sourceNode: InternalNode, targetNode: InternalNode, nodeLookup: NodeLookup): number;
81
+ /**
82
+ * 获取当前配置
83
+ */
84
+ getConfig(): AdaptiveStepConfig;
85
+ /**
86
+ * 更新配置
87
+ */
88
+ updateConfig(config: Partial<AdaptiveStepConfig>): void;
89
+ }
90
+ //# sourceMappingURL=AdaptiveStepCalculator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdaptiveStepCalculator.d.ts","sourceRoot":"","sources":["../../src/utils/AdaptiveStepCalculator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gBAAgB;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW;IACX,OAAO,EAAE,MAAM,CAAA;IACf,WAAW;IACX,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB;IAChB,YAAY,EAAE;QACZ,OAAO,EAAE,OAAO,CAAA;QAChB,4BAA4B;QAC5B,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,gBAAgB;IAChB,aAAa,EAAE;QACb,OAAO,EAAE,OAAO,CAAA;QAChB,gBAAgB;QAChB,kBAAkB,EAAE,MAAM,CAAA;QAC1B,gBAAgB;QAChB,iBAAiB,EAAE,MAAM,CAAA;KAC1B,CAAA;CACF;AAED;;GAEG;AACH,eAAO,MAAM,4BAA4B,EAAE,kBAa1C,CAAA;AAED;;GAEG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAoB;gBAEtB,MAAM,GAAE,OAAO,CAAC,kBAAkB,CAAM;IAIpD;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;;;OAIG;IACH,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM;IA2BpD;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,KAAK,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,MAAM,EAAE,KAAK,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAI7G;;OAEG;IACH,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAsBpD;;OAEG;IACH,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAsBlD;;;OAGG;IACH,aAAa,CACX,UAAU,EAAE,YAAY,EACxB,UAAU,EAAE,YAAY,EACxB,UAAU,EAAE,UAAU,GACrB,MAAM;IAyCT;;OAEG;IACH,SAAS,IAAI,kBAAkB;IAI/B;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI;CAGxD"}
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DEFAULT_ADAPTIVE_STEP_CONFIG = exports.AdaptiveStepCalculator = void 0;
7
+ /**
8
+ * AdaptiveStepCalculator - 自适应步长计算器
9
+ *
10
+ * 根据场景自动调整网格步长,平衡性能和路径质量
11
+ * - 高密度区域使用小步长,提高路径精度
12
+ * - 长距离路径使用大步长,提高计算性能
13
+ *
14
+ * Feature: manhattan-optimization
15
+ * Requirements: 2.3, 2.4
16
+ */
17
+
18
+ /**
19
+ * 自适应步长配置
20
+ */
21
+
22
+ /**
23
+ * 默认自适应步长配置
24
+ */
25
+ const DEFAULT_ADAPTIVE_STEP_CONFIG = exports.DEFAULT_ADAPTIVE_STEP_CONFIG = {
26
+ enabled: false,
27
+ minStep: 5,
28
+ maxStep: 20,
29
+ densityBased: {
30
+ enabled: true,
31
+ threshold: 0.5 // 每10000平方像素0.5个节点
32
+ },
33
+ distanceBased: {
34
+ enabled: true,
35
+ shortPathThreshold: 200,
36
+ longPathThreshold: 800
37
+ }
38
+ };
39
+
40
+ /**
41
+ * 自适应步长计算器
42
+ */
43
+ class AdaptiveStepCalculator {
44
+ config;
45
+ constructor(config = {}) {
46
+ this.config = this.mergeConfig(config);
47
+ }
48
+
49
+ /**
50
+ * 合并配置与默认值
51
+ */
52
+ mergeConfig(config) {
53
+ return {
54
+ enabled: config.enabled ?? DEFAULT_ADAPTIVE_STEP_CONFIG.enabled,
55
+ minStep: config.minStep ?? DEFAULT_ADAPTIVE_STEP_CONFIG.minStep,
56
+ maxStep: config.maxStep ?? DEFAULT_ADAPTIVE_STEP_CONFIG.maxStep,
57
+ densityBased: {
58
+ enabled: config.densityBased?.enabled ?? DEFAULT_ADAPTIVE_STEP_CONFIG.densityBased.enabled,
59
+ threshold: config.densityBased?.threshold ?? DEFAULT_ADAPTIVE_STEP_CONFIG.densityBased.threshold
60
+ },
61
+ distanceBased: {
62
+ enabled: config.distanceBased?.enabled ?? DEFAULT_ADAPTIVE_STEP_CONFIG.distanceBased.enabled,
63
+ shortPathThreshold: config.distanceBased?.shortPathThreshold ?? DEFAULT_ADAPTIVE_STEP_CONFIG.distanceBased.shortPathThreshold,
64
+ longPathThreshold: config.distanceBased?.longPathThreshold ?? DEFAULT_ADAPTIVE_STEP_CONFIG.distanceBased.longPathThreshold
65
+ }
66
+ };
67
+ }
68
+
69
+ /**
70
+ * 计算节点密度
71
+ * @param nodeLookup 节点查找表
72
+ * @returns 密度值(节点数/10000平方像素)
73
+ */
74
+ calculateNodeDensity(nodeLookup) {
75
+ if (nodeLookup.size === 0) return 0;
76
+ let minX = Infinity,
77
+ minY = Infinity;
78
+ let maxX = -Infinity,
79
+ maxY = -Infinity;
80
+ for (const node of nodeLookup.values()) {
81
+ const pos = node.internals?.positionAbsolute ?? node.position;
82
+ const width = node.measured?.width ?? node.width ?? 100;
83
+ const height = node.measured?.height ?? node.height ?? 50;
84
+ minX = Math.min(minX, pos.x);
85
+ minY = Math.min(minY, pos.y);
86
+ maxX = Math.max(maxX, pos.x + width);
87
+ maxY = Math.max(maxY, pos.y + height);
88
+ }
89
+
90
+ // 计算边界框面积(以10000平方像素为单位)
91
+ const width = maxX - minX;
92
+ const height = maxY - minY;
93
+ const area = width * height / 10000;
94
+ if (area <= 0) return 0;
95
+ return nodeLookup.size / area;
96
+ }
97
+
98
+ /**
99
+ * 计算两点之间的曼哈顿距离
100
+ */
101
+ calculateDistance(source, target) {
102
+ return Math.abs(target.x - source.x) + Math.abs(target.y - source.y);
103
+ }
104
+
105
+ /**
106
+ * 基于距离计算步长
107
+ */
108
+ calculateDistanceBasedStep(distance) {
109
+ const {
110
+ minStep,
111
+ maxStep,
112
+ distanceBased
113
+ } = this.config;
114
+ if (!distanceBased.enabled) {
115
+ return minStep;
116
+ }
117
+ const {
118
+ shortPathThreshold,
119
+ longPathThreshold
120
+ } = distanceBased;
121
+ if (distance <= shortPathThreshold) {
122
+ return minStep;
123
+ }
124
+ if (distance >= longPathThreshold) {
125
+ return maxStep;
126
+ }
127
+
128
+ // 线性插值
129
+ const ratio = (distance - shortPathThreshold) / (longPathThreshold - shortPathThreshold);
130
+ return Math.round(minStep + ratio * (maxStep - minStep));
131
+ }
132
+
133
+ /**
134
+ * 基于密度计算步长
135
+ */
136
+ calculateDensityBasedStep(density) {
137
+ const {
138
+ minStep,
139
+ maxStep,
140
+ densityBased
141
+ } = this.config;
142
+ if (!densityBased.enabled) {
143
+ return maxStep;
144
+ }
145
+
146
+ // 高密度使用小步长
147
+ if (density >= densityBased.threshold) {
148
+ return minStep;
149
+ }
150
+
151
+ // 低密度使用大步长
152
+ if (density <= densityBased.threshold / 2) {
153
+ return maxStep;
154
+ }
155
+
156
+ // 线性插值
157
+ const ratio = (densityBased.threshold - density) / (densityBased.threshold / 2);
158
+ return Math.round(minStep + ratio * (maxStep - minStep));
159
+ }
160
+
161
+ /**
162
+ * 计算自适应步长
163
+ * 综合考虑距离和密度因素
164
+ */
165
+ calculateStep(sourceNode, targetNode, nodeLookup) {
166
+ if (!this.config.enabled) {
167
+ return this.config.minStep;
168
+ }
169
+
170
+ // 获取源和目标位置
171
+ const sourcePos = sourceNode.internals?.positionAbsolute ?? sourceNode.position;
172
+ const targetPos = targetNode.internals?.positionAbsolute ?? targetNode.position;
173
+
174
+ // 计算节点中心点
175
+ const sourceWidth = sourceNode.measured?.width ?? sourceNode.width ?? 100;
176
+ const sourceHeight = sourceNode.measured?.height ?? sourceNode.height ?? 50;
177
+ const targetWidth = targetNode.measured?.width ?? targetNode.width ?? 100;
178
+ const targetHeight = targetNode.measured?.height ?? targetNode.height ?? 50;
179
+ const sourceCenter = {
180
+ x: sourcePos.x + sourceWidth / 2,
181
+ y: sourcePos.y + sourceHeight / 2
182
+ };
183
+ const targetCenter = {
184
+ x: targetPos.x + targetWidth / 2,
185
+ y: targetPos.y + targetHeight / 2
186
+ };
187
+
188
+ // 计算距离
189
+ const distance = this.calculateDistance(sourceCenter, targetCenter);
190
+
191
+ // 计算密度
192
+ const density = this.calculateNodeDensity(nodeLookup);
193
+
194
+ // 获取基于距离和密度的步长
195
+ const distanceStep = this.calculateDistanceBasedStep(distance);
196
+ const densityStep = this.calculateDensityBasedStep(density);
197
+
198
+ // 取两者中较小的值(更保守的选择)
199
+ const step = Math.min(distanceStep, densityStep);
200
+
201
+ // 确保步长在有效范围内
202
+ return Math.max(this.config.minStep, Math.min(this.config.maxStep, step));
203
+ }
204
+
205
+ /**
206
+ * 获取当前配置
207
+ */
208
+ getConfig() {
209
+ return {
210
+ ...this.config
211
+ };
212
+ }
213
+
214
+ /**
215
+ * 更新配置
216
+ */
217
+ updateConfig(config) {
218
+ this.config = this.mergeConfig({
219
+ ...this.config,
220
+ ...config
221
+ });
222
+ }
223
+ }
224
+ exports.AdaptiveStepCalculator = AdaptiveStepCalculator;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * ErrorRecovery - 错误恢复和降级策略
3
+ *
4
+ * 提供路径计算失败时的恢复机制,包括:
5
+ * - 超时检测和处理
6
+ * - 降级路径生成
7
+ * - 错误分类和报告
8
+ *
9
+ * Implements: Requirements 4.2, 3.6
10
+ */
11
+ import { Point, Rectangle } from '../geometry';
12
+ /**
13
+ * 错误类型枚举
14
+ */
15
+ export declare enum PathfindingErrorType {
16
+ /** 超时错误 - 计算时间超过限制 */
17
+ TIMEOUT = "TIMEOUT",
18
+ /** 无路径错误 - 无法找到有效路径 */
19
+ NO_PATH = "NO_PATH",
20
+ /** 无效输入错误 - 输入参数无效 */
21
+ INVALID_INPUT = "INVALID_INPUT",
22
+ /** 障碍物阻塞错误 - 起点或终点被障碍物阻塞 */
23
+ BLOCKED = "BLOCKED",
24
+ /** 内部错误 - 算法内部错误 */
25
+ INTERNAL = "INTERNAL"
26
+ }
27
+ /**
28
+ * 路径计算错误
29
+ */
30
+ export declare class PathfindingError extends Error {
31
+ readonly type: PathfindingErrorType;
32
+ readonly details?: Record<string, unknown> | undefined;
33
+ constructor(type: PathfindingErrorType, message: string, details?: Record<string, unknown> | undefined);
34
+ }
35
+ /**
36
+ * 恢复策略类型
37
+ */
38
+ export declare enum RecoveryStrategy {
39
+ /** 直线连接 */
40
+ DIRECT_LINE = "DIRECT_LINE",
41
+ /** L形路径 */
42
+ L_SHAPE = "L_SHAPE",
43
+ /** Z形路径 */
44
+ Z_SHAPE = "Z_SHAPE",
45
+ /** 简化曼哈顿路径 */
46
+ SIMPLE_MANHATTAN = "SIMPLE_MANHATTAN",
47
+ /** 无恢复 */
48
+ NONE = "NONE"
49
+ }
50
+ /**
51
+ * 恢复结果
52
+ */
53
+ export interface RecoveryResult {
54
+ /** 是否成功恢复 */
55
+ success: boolean;
56
+ /** 恢复后的路径 */
57
+ path: Point[];
58
+ /** 使用的恢复策略 */
59
+ strategy: RecoveryStrategy;
60
+ /** 原始错误 */
61
+ originalError?: PathfindingError;
62
+ /** 恢复耗时(毫秒) */
63
+ recoveryTime: number;
64
+ }
65
+ /**
66
+ * 错误恢复配置
67
+ */
68
+ export interface ErrorRecoveryConfig {
69
+ /** 是否启用自动恢复 */
70
+ enabled: boolean;
71
+ /** 最大恢复尝试次数 */
72
+ maxAttempts: number;
73
+ /** 恢复策略优先级 */
74
+ strategyPriority: RecoveryStrategy[];
75
+ /** 是否记录恢复日志 */
76
+ logRecovery: boolean;
77
+ }
78
+ /**
79
+ * 默认恢复配置
80
+ */
81
+ export declare const DEFAULT_RECOVERY_CONFIG: ErrorRecoveryConfig;
82
+ /**
83
+ * 错误恢复类
84
+ */
85
+ export declare class ErrorRecovery {
86
+ private config;
87
+ private recoveryAttempts;
88
+ constructor(config?: Partial<ErrorRecoveryConfig>);
89
+ /**
90
+ * 尝试恢复路径计算
91
+ */
92
+ recover(from: Point, to: Point, error: PathfindingError, obstacles?: Rectangle[]): RecoveryResult;
93
+ /**
94
+ * 应用恢复策略
95
+ */
96
+ private applyStrategy;
97
+ /**
98
+ * 直线连接策略
99
+ */
100
+ private directLine;
101
+ /**
102
+ * L形路径策略
103
+ */
104
+ private lShape;
105
+ /**
106
+ * Z形路径策略
107
+ */
108
+ private zShape;
109
+ /**
110
+ * 简化曼哈顿路径策略
111
+ */
112
+ private simpleManhattan;
113
+ /**
114
+ * 检查路径是否与障碍物相交
115
+ */
116
+ private pathIntersectsObstacles;
117
+ /**
118
+ * 检查线段是否与矩形相交
119
+ */
120
+ private lineIntersectsRect;
121
+ /**
122
+ * 检查点是否在矩形内部
123
+ */
124
+ private pointInRect;
125
+ /**
126
+ * 检查两条线段是否相交
127
+ */
128
+ private lineIntersectsLine;
129
+ /**
130
+ * 计算叉积方向
131
+ */
132
+ private direction;
133
+ /**
134
+ * 检查点是否在线段上
135
+ */
136
+ private onSegment;
137
+ /**
138
+ * 创建超时错误
139
+ */
140
+ static createTimeoutError(iterations: number, maxIterations: number): PathfindingError;
141
+ /**
142
+ * 创建无路径错误
143
+ */
144
+ static createNoPathError(from: Point, to: Point): PathfindingError;
145
+ /**
146
+ * 创建无效输入错误
147
+ */
148
+ static createInvalidInputError(message: string): PathfindingError;
149
+ /**
150
+ * 创建阻塞错误
151
+ */
152
+ static createBlockedError(point: Point, type: 'source' | 'target'): PathfindingError;
153
+ /**
154
+ * 创建内部错误
155
+ */
156
+ static createInternalError(message: string, cause?: Error): PathfindingError;
157
+ /**
158
+ * 生成回退路径(静态方法)
159
+ */
160
+ static generateFallbackPath(source: Point, target: Point): Point[];
161
+ /**
162
+ * 验证并修复配置选项
163
+ */
164
+ static validateAndFixOptions<T extends Record<string, unknown>>(options: T, defaults: T): T;
165
+ /**
166
+ * 获取恢复尝试次数
167
+ */
168
+ getRecoveryAttempts(): number;
169
+ /**
170
+ * 重置恢复状态
171
+ */
172
+ reset(): void;
173
+ /**
174
+ * 更新配置
175
+ */
176
+ updateConfig(config: Partial<ErrorRecoveryConfig>): void;
177
+ /**
178
+ * 获取当前配置
179
+ */
180
+ getConfig(): ErrorRecoveryConfig;
181
+ }
182
+ //# sourceMappingURL=ErrorRecovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorRecovery.d.ts","sourceRoot":"","sources":["../../src/utils/ErrorRecovery.ts"],"names":[],"mappings":"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"}