@rxflow/manhattan 0.0.1-alpha.0

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 (140) hide show
  1. package/README.md +35 -0
  2. package/cjs/geometry/Line.d.ts +21 -0
  3. package/cjs/geometry/Line.d.ts.map +1 -0
  4. package/cjs/geometry/Line.js +88 -0
  5. package/cjs/geometry/Point.d.ts +49 -0
  6. package/cjs/geometry/Point.d.ts.map +1 -0
  7. package/cjs/geometry/Point.js +94 -0
  8. package/cjs/geometry/Rectangle.d.ts +41 -0
  9. package/cjs/geometry/Rectangle.d.ts.map +1 -0
  10. package/cjs/geometry/Rectangle.js +65 -0
  11. package/cjs/geometry/index.d.ts +4 -0
  12. package/cjs/geometry/index.d.ts.map +1 -0
  13. package/cjs/geometry/index.js +26 -0
  14. package/cjs/getManHattanPath.d.ts +60 -0
  15. package/cjs/getManHattanPath.d.ts.map +1 -0
  16. package/cjs/getManHattanPath.js +285 -0
  17. package/cjs/index.d.ts +16 -0
  18. package/cjs/index.d.ts.map +1 -0
  19. package/cjs/index.js +117 -0
  20. package/cjs/obstacle/ObstacleMap.d.ts +28 -0
  21. package/cjs/obstacle/ObstacleMap.d.ts.map +1 -0
  22. package/cjs/obstacle/ObstacleMap.js +171 -0
  23. package/cjs/obstacle/index.d.ts +2 -0
  24. package/cjs/obstacle/index.d.ts.map +1 -0
  25. package/cjs/obstacle/index.js +12 -0
  26. package/cjs/options/defaults.d.ts +16 -0
  27. package/cjs/options/defaults.d.ts.map +1 -0
  28. package/cjs/options/defaults.js +39 -0
  29. package/cjs/options/index.d.ts +4 -0
  30. package/cjs/options/index.d.ts.map +1 -0
  31. package/cjs/options/index.js +38 -0
  32. package/cjs/options/resolver.d.ts +10 -0
  33. package/cjs/options/resolver.d.ts.map +1 -0
  34. package/cjs/options/resolver.js +118 -0
  35. package/cjs/options/types.d.ts +156 -0
  36. package/cjs/options/types.d.ts.map +1 -0
  37. package/cjs/options/types.js +5 -0
  38. package/cjs/pathfinder/SortedSet.d.ts +35 -0
  39. package/cjs/pathfinder/SortedSet.d.ts.map +1 -0
  40. package/cjs/pathfinder/SortedSet.js +95 -0
  41. package/cjs/pathfinder/findRoute.d.ts +8 -0
  42. package/cjs/pathfinder/findRoute.d.ts.map +1 -0
  43. package/cjs/pathfinder/findRoute.js +156 -0
  44. package/cjs/pathfinder/index.d.ts +3 -0
  45. package/cjs/pathfinder/index.d.ts.map +1 -0
  46. package/cjs/pathfinder/index.js +19 -0
  47. package/cjs/svg/index.d.ts +2 -0
  48. package/cjs/svg/index.d.ts.map +1 -0
  49. package/cjs/svg/index.js +18 -0
  50. package/cjs/svg/pathConverter.d.ts +10 -0
  51. package/cjs/svg/pathConverter.d.ts.map +1 -0
  52. package/cjs/svg/pathConverter.js +105 -0
  53. package/cjs/utils/direction.d.ts +24 -0
  54. package/cjs/utils/direction.d.ts.map +1 -0
  55. package/cjs/utils/direction.js +54 -0
  56. package/cjs/utils/grid.d.ts +26 -0
  57. package/cjs/utils/grid.d.ts.map +1 -0
  58. package/cjs/utils/grid.js +78 -0
  59. package/cjs/utils/index.d.ts +6 -0
  60. package/cjs/utils/index.d.ts.map +1 -0
  61. package/cjs/utils/index.js +60 -0
  62. package/cjs/utils/node.d.ts +27 -0
  63. package/cjs/utils/node.d.ts.map +1 -0
  64. package/cjs/utils/node.js +36 -0
  65. package/cjs/utils/rect.d.ts +9 -0
  66. package/cjs/utils/rect.d.ts.map +1 -0
  67. package/cjs/utils/rect.js +103 -0
  68. package/cjs/utils/route.d.ts +19 -0
  69. package/cjs/utils/route.d.ts.map +1 -0
  70. package/cjs/utils/route.js +76 -0
  71. package/esm/geometry/Line.d.ts +21 -0
  72. package/esm/geometry/Line.d.ts.map +1 -0
  73. package/esm/geometry/Line.js +96 -0
  74. package/esm/geometry/Point.d.ts +49 -0
  75. package/esm/geometry/Point.d.ts.map +1 -0
  76. package/esm/geometry/Point.js +117 -0
  77. package/esm/geometry/Rectangle.d.ts +41 -0
  78. package/esm/geometry/Rectangle.d.ts.map +1 -0
  79. package/esm/geometry/Rectangle.js +81 -0
  80. package/esm/geometry/index.d.ts +4 -0
  81. package/esm/geometry/index.d.ts.map +1 -0
  82. package/esm/geometry/index.js +3 -0
  83. package/esm/getManHattanPath.d.ts +60 -0
  84. package/esm/getManHattanPath.d.ts.map +1 -0
  85. package/esm/getManHattanPath.js +291 -0
  86. package/esm/index.d.ts +16 -0
  87. package/esm/index.d.ts.map +1 -0
  88. package/esm/index.js +24 -0
  89. package/esm/obstacle/ObstacleMap.d.ts +28 -0
  90. package/esm/obstacle/ObstacleMap.d.ts.map +1 -0
  91. package/esm/obstacle/ObstacleMap.js +183 -0
  92. package/esm/obstacle/index.d.ts +2 -0
  93. package/esm/obstacle/index.d.ts.map +1 -0
  94. package/esm/obstacle/index.js +1 -0
  95. package/esm/options/defaults.d.ts +16 -0
  96. package/esm/options/defaults.d.ts.map +1 -0
  97. package/esm/options/defaults.js +33 -0
  98. package/esm/options/index.d.ts +4 -0
  99. package/esm/options/index.d.ts.map +1 -0
  100. package/esm/options/index.js +3 -0
  101. package/esm/options/resolver.d.ts +10 -0
  102. package/esm/options/resolver.d.ts.map +1 -0
  103. package/esm/options/resolver.js +114 -0
  104. package/esm/options/types.d.ts +156 -0
  105. package/esm/options/types.d.ts.map +1 -0
  106. package/esm/options/types.js +1 -0
  107. package/esm/pathfinder/SortedSet.d.ts +35 -0
  108. package/esm/pathfinder/SortedSet.d.ts.map +1 -0
  109. package/esm/pathfinder/SortedSet.js +110 -0
  110. package/esm/pathfinder/findRoute.d.ts +8 -0
  111. package/esm/pathfinder/findRoute.d.ts.map +1 -0
  112. package/esm/pathfinder/findRoute.js +189 -0
  113. package/esm/pathfinder/index.d.ts +3 -0
  114. package/esm/pathfinder/index.d.ts.map +1 -0
  115. package/esm/pathfinder/index.js +2 -0
  116. package/esm/svg/index.d.ts +2 -0
  117. package/esm/svg/index.d.ts.map +1 -0
  118. package/esm/svg/index.js +1 -0
  119. package/esm/svg/pathConverter.d.ts +10 -0
  120. package/esm/svg/pathConverter.d.ts.map +1 -0
  121. package/esm/svg/pathConverter.js +107 -0
  122. package/esm/utils/direction.d.ts +24 -0
  123. package/esm/utils/direction.d.ts.map +1 -0
  124. package/esm/utils/direction.js +46 -0
  125. package/esm/utils/grid.d.ts +26 -0
  126. package/esm/utils/grid.d.ts.map +1 -0
  127. package/esm/utils/grid.js +70 -0
  128. package/esm/utils/index.d.ts +6 -0
  129. package/esm/utils/index.d.ts.map +1 -0
  130. package/esm/utils/index.js +5 -0
  131. package/esm/utils/node.d.ts +27 -0
  132. package/esm/utils/node.d.ts.map +1 -0
  133. package/esm/utils/node.js +30 -0
  134. package/esm/utils/rect.d.ts +9 -0
  135. package/esm/utils/rect.d.ts.map +1 -0
  136. package/esm/utils/rect.js +121 -0
  137. package/esm/utils/route.d.ts +19 -0
  138. package/esm/utils/route.d.ts.map +1 -0
  139. package/esm/utils/route.js +80 -0
  140. package/package.json +43 -0
@@ -0,0 +1,60 @@
1
+ import type { ManhattanRouterOptions, NodeLookup } from './options';
2
+ /**
3
+ * Parameters for getManHattanPath function
4
+ */
5
+ export interface GetManHattanPathParams {
6
+ /**
7
+ * Source node ID
8
+ */
9
+ sourceNodeId: string;
10
+ /**
11
+ * Target node ID
12
+ */
13
+ targetNodeId: string;
14
+ /**
15
+ * Source anchor position (where the edge starts)
16
+ */
17
+ sourcePosition: {
18
+ x: number;
19
+ y: number;
20
+ };
21
+ /**
22
+ * Target anchor position (where the edge ends)
23
+ */
24
+ targetPosition: {
25
+ x: number;
26
+ y: number;
27
+ };
28
+ /**
29
+ * ReactFlow node lookup map
30
+ */
31
+ nodeLookup: NodeLookup;
32
+ /**
33
+ * Router options
34
+ */
35
+ options?: ManhattanRouterOptions;
36
+ }
37
+ /**
38
+ * Generate Manhattan-routed path for ReactFlow edges
39
+ *
40
+ * @param params - Path generation parameters
41
+ * @returns SVG path string that can be used with ReactFlow's BaseEdge
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const path = getManHattanPath({
46
+ * sourceNodeId: 'node1',
47
+ * targetNodeId: 'node2',
48
+ * sourcePosition: { x: 100, y: 100 },
49
+ * targetPosition: { x: 300, y: 300 },
50
+ * nodeLookup: nodes,
51
+ * options: {
52
+ * step: 10,
53
+ * startDirections: ['bottom'],
54
+ * endDirections: ['top']
55
+ * }
56
+ * })
57
+ * ```
58
+ */
59
+ export declare function getManHattanPath(params: GetManHattanPathParams): string;
60
+ //# sourceMappingURL=getManHattanPath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getManHattanPath.d.ts","sourceRoot":"","sources":["../src/getManHattanPath.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAOnE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAExC;;OAEG;IACH,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAExC;;OAEG;IACH,UAAU,EAAE,UAAU,CAAA;IAEtB;;OAEG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAA;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CA4PvE"}
@@ -0,0 +1,291 @@
1
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
2
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
5
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
6
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
7
+ import { Point, Rectangle } from "./geometry";
8
+ import { resolveOptions } from "./options";
9
+ import { ObstacleMap } from "./obstacle";
10
+ import { findRoute } from "./pathfinder";
11
+ import { pointsToPath, snapPathToGrid } from "./svg";
12
+ import { getNodeDimensions, getNodePosition } from "./utils";
13
+
14
+ /**
15
+ * Parameters for getManHattanPath function
16
+ */
17
+
18
+ /**
19
+ * Generate Manhattan-routed path for ReactFlow edges
20
+ *
21
+ * @param params - Path generation parameters
22
+ * @returns SVG path string that can be used with ReactFlow's BaseEdge
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const path = getManHattanPath({
27
+ * sourceNodeId: 'node1',
28
+ * targetNodeId: 'node2',
29
+ * sourcePosition: { x: 100, y: 100 },
30
+ * targetPosition: { x: 300, y: 300 },
31
+ * nodeLookup: nodes,
32
+ * options: {
33
+ * step: 10,
34
+ * startDirections: ['bottom'],
35
+ * endDirections: ['top']
36
+ * }
37
+ * })
38
+ * ```
39
+ */
40
+ export function getManHattanPath(params) {
41
+ var sourceNodeId = params.sourceNodeId,
42
+ targetNodeId = params.targetNodeId,
43
+ sourcePosition = params.sourcePosition,
44
+ targetPosition = params.targetPosition,
45
+ nodeLookup = params.nodeLookup,
46
+ _params$options = params.options,
47
+ userOptions = _params$options === void 0 ? {} : _params$options;
48
+
49
+ // Resolve options
50
+ var options = resolveOptions(userOptions);
51
+
52
+ // Get source and target nodes
53
+ var sourceNode = nodeLookup.get(sourceNodeId);
54
+ var targetNode = nodeLookup.get(targetNodeId);
55
+ if (!sourceNode || !targetNode) {
56
+ // Fallback to simple straight line if nodes not found
57
+ console.warn('Source or target node not found in nodeLookup');
58
+ var start = new Point(sourcePosition.x, sourcePosition.y);
59
+ var end = new Point(targetPosition.x, targetPosition.y);
60
+ return pointsToPath([start, end], options.precision);
61
+ }
62
+
63
+ // Get node dimensions using ReactFlow's priority logic
64
+ var sourceDimensions = getNodeDimensions(sourceNode);
65
+ var targetDimensions = getNodeDimensions(targetNode);
66
+
67
+ // Get absolute positions from internals
68
+ var sourcePos = getNodePosition(sourceNode);
69
+ var targetPos = getNodePosition(targetNode);
70
+
71
+ // Calculate bounding boxes
72
+ var sourceBBox = new Rectangle(sourcePos.x, sourcePos.y, sourceDimensions.width, sourceDimensions.height);
73
+ var targetBBox = new Rectangle(targetPos.x, targetPos.y, targetDimensions.width, targetDimensions.height);
74
+
75
+ // Create anchor points
76
+ var sourceAnchor = new Point(sourcePosition.x, sourcePosition.y);
77
+ var targetAnchor = new Point(targetPosition.x, targetPosition.y);
78
+
79
+ // Build obstacle map with anchor information
80
+ var obstacleMap = new ObstacleMap(options).build(nodeLookup, sourceNodeId, targetNodeId, sourceAnchor, targetAnchor);
81
+
82
+ // Find route
83
+ var route = findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, obstacleMap, options);
84
+
85
+ // Fallback to straight line if no route found
86
+ if (!route) {
87
+ console.warn('Unable to find Manhattan route, using straight line fallback');
88
+ route = [sourceAnchor, targetAnchor];
89
+ }
90
+ console.log('[getManHattanPath] Route from findRoute:', route.map(function (p) {
91
+ return "(".concat(p.x, ", ").concat(p.y, ")");
92
+ }));
93
+ console.log('[getManHattanPath] Source anchor:', "(".concat(sourceAnchor.x, ", ").concat(sourceAnchor.y, ")"));
94
+ console.log('[getManHattanPath] Target anchor:', "(".concat(targetAnchor.x, ", ").concat(targetAnchor.y, ")"));
95
+
96
+ // Remove extension points from route that were added by getRectPoints
97
+ // We will add our own with fixed step distance
98
+ var step = options.step;
99
+ var tolerance = 1;
100
+
101
+ // Check if first point is an extension point from source
102
+ if (route.length > 0) {
103
+ var firstPoint = route[0];
104
+ var onLeft = Math.abs(sourceAnchor.x - sourceBBox.x) < tolerance;
105
+ var onRight = Math.abs(sourceAnchor.x - (sourceBBox.x + sourceBBox.width)) < tolerance;
106
+ var onTop = Math.abs(sourceAnchor.y - sourceBBox.y) < tolerance;
107
+ var onBottom = Math.abs(sourceAnchor.y - (sourceBBox.y + sourceBBox.height)) < tolerance;
108
+
109
+ // Check if firstPoint is close to source anchor (indicating it's an extension point)
110
+ var distToFirst = sourceAnchor.manhattanDistance(firstPoint);
111
+ if (distToFirst < step * 2) {
112
+ // This is likely an extension point, remove it
113
+ if (onRight && firstPoint.x > sourceAnchor.x || onLeft && firstPoint.x < sourceAnchor.x || onBottom && firstPoint.y > sourceAnchor.y || onTop && firstPoint.y < sourceAnchor.y) {
114
+ route.shift();
115
+ console.log('[getManHattanPath] Removed extension point from route start');
116
+ }
117
+ }
118
+ }
119
+
120
+ // Check if last point is an extension point from target
121
+ if (route.length > 0) {
122
+ var lastPoint = route[route.length - 1];
123
+ var _onLeft = Math.abs(targetAnchor.x - targetBBox.x) < tolerance;
124
+ var _onRight = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) < tolerance;
125
+ var _onTop = Math.abs(targetAnchor.y - targetBBox.y) < tolerance;
126
+ var _onBottom = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) < tolerance;
127
+
128
+ // Check if lastPoint is close to target anchor (indicating it's an extension point)
129
+ var distToLast = targetAnchor.manhattanDistance(lastPoint);
130
+ if (distToLast < step * 2) {
131
+ // This is likely an extension point, remove it
132
+ if (_onLeft && lastPoint.x < targetAnchor.x || _onRight && lastPoint.x > targetAnchor.x || _onTop && lastPoint.y < targetAnchor.y || _onBottom && lastPoint.y > targetAnchor.y) {
133
+ route.pop();
134
+ console.log('[getManHattanPath] Removed extension point from route end');
135
+ }
136
+ }
137
+ }
138
+
139
+ // Insert extension point at source - always extend away from node edge by fixed step distance
140
+ if (route.length > 0) {
141
+ var _firstPoint = route[0];
142
+
143
+ // Determine which edge the source anchor is on
144
+ var _onLeft2 = Math.abs(sourceAnchor.x - sourceBBox.x) < tolerance;
145
+ var _onRight2 = Math.abs(sourceAnchor.x - (sourceBBox.x + sourceBBox.width)) < tolerance;
146
+ var _onTop2 = Math.abs(sourceAnchor.y - sourceBBox.y) < tolerance;
147
+ var _onBottom2 = Math.abs(sourceAnchor.y - (sourceBBox.y + sourceBBox.height)) < tolerance;
148
+
149
+ // Insert extension point and corner point to ensure orthogonal path
150
+ if (_onRight2) {
151
+ // Anchor on right edge - extend right by fixed step
152
+ var extendX = sourceAnchor.x + step;
153
+ var extensionPoint = new Point(extendX, sourceAnchor.y);
154
+ // Check if we need a corner point
155
+ if (Math.abs(extensionPoint.y - _firstPoint.y) > tolerance) {
156
+ route.unshift(new Point(extendX, _firstPoint.y)); // Corner point
157
+ }
158
+ route.unshift(extensionPoint); // Extension point (fixed distance)
159
+ console.log('[getManHattanPath] Inserted source extension (right):', "(".concat(extendX, ", ").concat(sourceAnchor.y, ")"));
160
+ } else if (_onLeft2) {
161
+ // Anchor on left edge - extend left by fixed step
162
+ var _extendX = sourceAnchor.x - step;
163
+ var _extensionPoint = new Point(_extendX, sourceAnchor.y);
164
+ if (Math.abs(_extensionPoint.y - _firstPoint.y) > tolerance) {
165
+ route.unshift(new Point(_extendX, _firstPoint.y));
166
+ }
167
+ route.unshift(_extensionPoint);
168
+ console.log('[getManHattanPath] Inserted source extension (left):', "(".concat(_extendX, ", ").concat(sourceAnchor.y, ")"));
169
+ } else if (_onBottom2) {
170
+ // Anchor on bottom edge - extend down by fixed step
171
+ var extendY = sourceAnchor.y + step;
172
+ var _extensionPoint2 = new Point(sourceAnchor.x, extendY);
173
+ if (Math.abs(_extensionPoint2.x - _firstPoint.x) > tolerance) {
174
+ route.unshift(new Point(_firstPoint.x, extendY));
175
+ }
176
+ route.unshift(_extensionPoint2);
177
+ console.log('[getManHattanPath] Inserted source extension (down):', "(".concat(sourceAnchor.x, ", ").concat(extendY, ")"));
178
+ } else if (_onTop2) {
179
+ // Anchor on top edge - extend up by fixed step
180
+ var _extendY = sourceAnchor.y - step;
181
+ var _extensionPoint3 = new Point(sourceAnchor.x, _extendY);
182
+ if (Math.abs(_extensionPoint3.x - _firstPoint.x) > tolerance) {
183
+ route.unshift(new Point(_firstPoint.x, _extendY));
184
+ }
185
+ route.unshift(_extensionPoint3);
186
+ console.log('[getManHattanPath] Inserted source extension (up):', "(".concat(sourceAnchor.x, ", ").concat(_extendY, ")"));
187
+ }
188
+ }
189
+
190
+ // Insert extension point at target - always extend away from node edge by fixed step distance
191
+ if (route.length > 0) {
192
+ var _step = options.step;
193
+ var _tolerance = 1;
194
+ var _lastPoint = route[route.length - 1];
195
+
196
+ // Determine which edge the target anchor is on
197
+ var _onLeft3 = Math.abs(targetAnchor.x - targetBBox.x) < _tolerance;
198
+ var _onRight3 = Math.abs(targetAnchor.x - (targetBBox.x + targetBBox.width)) < _tolerance;
199
+ var _onTop3 = Math.abs(targetAnchor.y - targetBBox.y) < _tolerance;
200
+ var _onBottom3 = Math.abs(targetAnchor.y - (targetBBox.y + targetBBox.height)) < _tolerance;
201
+
202
+ // Insert extension point and corner point to ensure orthogonal path
203
+ if (_onLeft3) {
204
+ // Anchor on left edge - extend left by fixed step
205
+ var _extendX2 = targetAnchor.x - _step;
206
+ var _extensionPoint4 = new Point(_extendX2, targetAnchor.y);
207
+ if (Math.abs(_extensionPoint4.y - _lastPoint.y) > _tolerance) {
208
+ route.push(new Point(_extendX2, _lastPoint.y)); // Corner point
209
+ }
210
+ route.push(_extensionPoint4); // Extension point (fixed distance)
211
+ console.log('[getManHattanPath] Inserted target extension (left):', "(".concat(_extendX2, ", ").concat(targetAnchor.y, ")"));
212
+ } else if (_onRight3) {
213
+ // Anchor on right edge - extend right by fixed step
214
+ var _extendX3 = targetAnchor.x + _step;
215
+ var _extensionPoint5 = new Point(_extendX3, targetAnchor.y);
216
+ if (Math.abs(_extensionPoint5.y - _lastPoint.y) > _tolerance) {
217
+ route.push(new Point(_extendX3, _lastPoint.y));
218
+ }
219
+ route.push(_extensionPoint5);
220
+ console.log('[getManHattanPath] Inserted target extension (right):', "(".concat(_extendX3, ", ").concat(targetAnchor.y, ")"));
221
+ } else if (_onTop3) {
222
+ // Anchor on top edge - extend up by fixed step
223
+ var _extendY2 = targetAnchor.y - _step;
224
+ var _extensionPoint6 = new Point(targetAnchor.x, _extendY2);
225
+ if (Math.abs(_extensionPoint6.x - _lastPoint.x) > _tolerance) {
226
+ route.push(new Point(_lastPoint.x, _extendY2));
227
+ }
228
+ route.push(_extensionPoint6);
229
+ console.log('[getManHattanPath] Inserted target extension (up):', "(".concat(targetAnchor.x, ", ").concat(_extendY2, ")"));
230
+ } else if (_onBottom3) {
231
+ // Anchor on bottom edge - extend down by fixed step
232
+ var _extendY3 = targetAnchor.y + _step;
233
+ var _extensionPoint7 = new Point(targetAnchor.x, _extendY3);
234
+ if (Math.abs(_extensionPoint7.x - _lastPoint.x) > _tolerance) {
235
+ route.push(new Point(_lastPoint.x, _extendY3));
236
+ }
237
+ route.push(_extensionPoint7);
238
+ console.log('[getManHattanPath] Inserted target extension (down):', "(".concat(targetAnchor.x, ", ").concat(_extendY3, ")"));
239
+ }
240
+ }
241
+
242
+ // Add source and target anchors to route
243
+ var fullRoute = [sourceAnchor].concat(_toConsumableArray(route), [targetAnchor]);
244
+ console.log('[getManHattanPath] Full route:', fullRoute.map(function (p) {
245
+ return "(".concat(p.x, ", ").concat(p.y, ")");
246
+ }));
247
+
248
+ // Snap to grid if enabled
249
+ var finalRoute = fullRoute;
250
+ if (options.snapToGrid) {
251
+ // Use step size as grid size for snapping
252
+ finalRoute = snapPathToGrid(fullRoute, options.step);
253
+ }
254
+
255
+ // Simplify path by removing collinear points
256
+ finalRoute = simplifyPath(finalRoute);
257
+ console.log('[getManHattanPath] Simplified route:', finalRoute.map(function (p) {
258
+ return "(".concat(p.x, ", ").concat(p.y, ")");
259
+ }));
260
+
261
+ // Convert to SVG path string
262
+ return pointsToPath(finalRoute, options.precision, options.borderRadius);
263
+ }
264
+
265
+ /**
266
+ * Simplify path by removing collinear intermediate points
267
+ */
268
+ function simplifyPath(points) {
269
+ if (points.length <= 2) {
270
+ return points;
271
+ }
272
+ var simplified = [points[0]];
273
+ for (var i = 1; i < points.length - 1; i++) {
274
+ var prev = simplified[simplified.length - 1];
275
+ var current = points[i];
276
+ var next = points[i + 1];
277
+
278
+ // Check if current point is collinear with prev and next
279
+ var isHorizontalLine = prev.y === current.y && current.y === next.y;
280
+ var isVerticalLine = prev.x === current.x && current.x === next.x;
281
+
282
+ // Only keep the point if it's not collinear (i.e., it's a corner)
283
+ if (!isHorizontalLine && !isVerticalLine) {
284
+ simplified.push(current);
285
+ }
286
+ }
287
+
288
+ // Always add the last point
289
+ simplified.push(points[points.length - 1]);
290
+ return simplified;
291
+ }
package/esm/index.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ReactFlow Manhattan Router
3
+ *
4
+ * A Manhattan routing algorithm adapted for ReactFlow from AntV X6.
5
+ * Generates orthogonal (right-angle) paths that intelligently avoid obstacles.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { getManHattanPath } from './getManHattanPath';
10
+ export type { GetManHattanPathParams } from './getManHattanPath';
11
+ export type { Direction, InternalNode, NodeLookup, ManhattanRouterOptions, ResolvedOptions, } from './options';
12
+ export { Point, Rectangle, Line } from './geometry';
13
+ export { getGrid, align, snapToGrid, getDirectionAngle, getDirectionChange, getRectPoints, getCost, getKey, reconstructRoute, normalizePoint, getNodeDimensions, getNodePosition, } from './utils';
14
+ export type { NodeDimensions } from './utils';
15
+ export { pointsToPath, snapPathToGrid } from './svg';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAGhE,YAAY,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,sBAAsB,EACtB,eAAe,GAChB,MAAM,WAAW,CAAA;AAGlB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAGnD,OAAO,EACL,OAAO,EACP,KAAK,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,SAAS,CAAA;AAGhB,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG7C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA"}
package/esm/index.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ReactFlow Manhattan Router
3
+ *
4
+ * A Manhattan routing algorithm adapted for ReactFlow from AntV X6.
5
+ * Generates orthogonal (right-angle) paths that intelligently avoid obstacles.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ // Main function
11
+ export { getManHattanPath } from "./getManHattanPath";
12
+
13
+ // Types
14
+
15
+ // Geometry classes (for advanced usage)
16
+ export { Point, Rectangle, Line } from "./geometry";
17
+
18
+ // Utility functions (for advanced usage)
19
+ export { getGrid, align, snapToGrid, getDirectionAngle, getDirectionChange, getRectPoints, getCost, getKey, reconstructRoute, normalizePoint, getNodeDimensions, getNodePosition } from "./utils";
20
+
21
+ // Export types for utility functions
22
+
23
+ // SVG utilities (for advanced usage)
24
+ export { pointsToPath, snapPathToGrid } from "./svg";
@@ -0,0 +1,28 @@
1
+ import { Point } from '../geometry';
2
+ import type { ResolvedOptions, NodeLookup } from '../options';
3
+ /**
4
+ * ObstacleMap class for managing obstacles in pathfinding
5
+ * Uses a grid-based spatial partitioning for efficient queries
6
+ */
7
+ export declare class ObstacleMap {
8
+ private options;
9
+ private mapGridSize;
10
+ private map;
11
+ private sourceAnchor?;
12
+ private targetAnchor?;
13
+ constructor(options: ResolvedOptions);
14
+ /**
15
+ * Build obstacle map from node lookup
16
+ */
17
+ build(nodeLookup: NodeLookup, sourceNodeId: string, targetNodeId: string, sourceAnchor?: Point, targetAnchor?: Point): ObstacleMap;
18
+ /**
19
+ * Shrink bbox to exclude the area around the anchor point
20
+ * This allows paths to start/end at the anchor but prevents crossing the node
21
+ */
22
+ private shrinkBBoxAroundAnchor;
23
+ /**
24
+ * Check if a point is accessible (not inside any obstacle)
25
+ */
26
+ isAccessible(point: Point): boolean;
27
+ }
28
+ //# sourceMappingURL=ObstacleMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObstacleMap.d.ts","sourceRoot":"","sources":["../../src/obstacle/ObstacleMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG7D;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,YAAY,CAAC,CAAO;IAC5B,OAAO,CAAC,YAAY,CAAC,CAAO;gBAEhB,OAAO,EAAE,eAAe;IAMpC;;OAEG;IACH,KAAK,CACH,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,KAAK,EACpB,YAAY,CAAC,EAAE,KAAK,GACnB,WAAW;IAiEd;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA6D9B;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;CA2CpC"}
@@ -0,0 +1,183 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
3
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
4
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
5
+ 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; }
6
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
7
+ 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); }
8
+ import { Point, Rectangle } from "../geometry";
9
+ import { getNodePosition, getNodeDimensions } from "../utils";
10
+
11
+ /**
12
+ * ObstacleMap class for managing obstacles in pathfinding
13
+ * Uses a grid-based spatial partitioning for efficient queries
14
+ */
15
+ export var ObstacleMap = /*#__PURE__*/function () {
16
+ function ObstacleMap(options) {
17
+ _classCallCheck(this, ObstacleMap);
18
+ _defineProperty(this, "options", void 0);
19
+ _defineProperty(this, "mapGridSize", void 0);
20
+ _defineProperty(this, "map", void 0);
21
+ _defineProperty(this, "sourceAnchor", void 0);
22
+ _defineProperty(this, "targetAnchor", void 0);
23
+ this.options = options;
24
+ this.mapGridSize = 100;
25
+ this.map = new Map();
26
+ }
27
+
28
+ /**
29
+ * Build obstacle map from node lookup
30
+ */
31
+ _createClass(ObstacleMap, [{
32
+ key: "build",
33
+ value: function build(nodeLookup, sourceNodeId, targetNodeId, sourceAnchor, targetAnchor) {
34
+ var _this = this;
35
+ var _this$options = this.options,
36
+ excludeNodes = _this$options.excludeNodes,
37
+ excludeShapes = _this$options.excludeShapes,
38
+ excludeTerminals = _this$options.excludeTerminals,
39
+ paddingBox = _this$options.paddingBox;
40
+
41
+ // Store anchors for later use in isAccessible
42
+ this.sourceAnchor = sourceAnchor;
43
+ this.targetAnchor = targetAnchor;
44
+
45
+ // Iterate through all nodes
46
+ nodeLookup.forEach(function (node) {
47
+ // Check if node should be excluded
48
+ var isExcludedNode = excludeNodes.includes(node.id);
49
+ var isExcludedShape = node.type ? excludeShapes.includes(node.type) : false;
50
+ var isSourceTerminal = excludeTerminals.includes('source') && node.id === sourceNodeId;
51
+ var isTargetTerminal = excludeTerminals.includes('target') && node.id === targetNodeId;
52
+ var isSource = node.id === sourceNodeId;
53
+ var isTarget = node.id === targetNodeId;
54
+
55
+ // Skip if node should be excluded (but not source/target - we handle them specially)
56
+ if (isExcludedNode || isExcludedShape || isSourceTerminal || isTargetTerminal) {
57
+ return;
58
+ }
59
+
60
+ // Calculate node bounding box with padding
61
+ var position = getNodePosition(node);
62
+ var dimensions = getNodeDimensions(node);
63
+ var bbox = new Rectangle(position.x, position.y, dimensions.width, dimensions.height).moveAndExpand(paddingBox);
64
+
65
+ // For source and target nodes, add them as full obstacles
66
+ // We'll handle anchor accessibility in isAccessible() method
67
+ if (isSource) {
68
+ console.log('[ObstacleMap] Adding source node as obstacle:');
69
+ console.log(' Node ID:', node.id);
70
+ console.log(' BBox:', "(".concat(bbox.x, ", ").concat(bbox.y, ", ").concat(bbox.width, ", ").concat(bbox.height, ")"));
71
+ console.log(' Anchor:', sourceAnchor ? "(".concat(sourceAnchor.x, ", ").concat(sourceAnchor.y, ")") : 'none');
72
+ } else if (isTarget) {
73
+ console.log('[ObstacleMap] Adding target node as obstacle:');
74
+ console.log(' Node ID:', node.id);
75
+ console.log(' BBox:', "(".concat(bbox.x, ", ").concat(bbox.y, ", ").concat(bbox.width, ", ").concat(bbox.height, ")"));
76
+ console.log(' Anchor:', targetAnchor ? "(".concat(targetAnchor.x, ", ").concat(targetAnchor.y, ")") : 'none');
77
+ }
78
+
79
+ // Map bbox to grid cells
80
+ var origin = bbox.getOrigin().snapToGrid(_this.mapGridSize);
81
+ var corner = bbox.getCorner().snapToGrid(_this.mapGridSize);
82
+ for (var x = origin.x; x <= corner.x; x += _this.mapGridSize) {
83
+ for (var y = origin.y; y <= corner.y; y += _this.mapGridSize) {
84
+ var key = new Point(x, y).toString();
85
+ if (!_this.map.has(key)) {
86
+ _this.map.set(key, []);
87
+ }
88
+ _this.map.get(key).push(bbox);
89
+ }
90
+ }
91
+ });
92
+ return this;
93
+ }
94
+
95
+ /**
96
+ * Shrink bbox to exclude the area around the anchor point
97
+ * This allows paths to start/end at the anchor but prevents crossing the node
98
+ */
99
+ }, {
100
+ key: "shrinkBBoxAroundAnchor",
101
+ value: function shrinkBBoxAroundAnchor(bbox, anchor, nodePosition, nodeDimensions) {
102
+ var tolerance = 1;
103
+ var step = this.options.step || 10;
104
+ var margin = step * 3; // Increased margin for better clearance
105
+
106
+ // Determine which edge the anchor is on (relative to original node position)
107
+ var onLeft = Math.abs(anchor.x - nodePosition.x) < tolerance;
108
+ var onRight = Math.abs(anchor.x - (nodePosition.x + nodeDimensions.width)) < tolerance;
109
+ var onTop = Math.abs(anchor.y - nodePosition.y) < tolerance;
110
+ var onBottom = Math.abs(anchor.y - (nodePosition.y + nodeDimensions.height)) < tolerance;
111
+ console.log('[shrinkBBoxAroundAnchor] Edge detection:');
112
+ console.log(' anchor.x:', anchor.x, 'nodePosition.x:', nodePosition.x, 'diff:', Math.abs(anchor.x - nodePosition.x));
113
+ console.log(' anchor.x:', anchor.x, 'nodeRight:', nodePosition.x + nodeDimensions.width, 'diff:', Math.abs(anchor.x - (nodePosition.x + nodeDimensions.width)));
114
+ console.log(' onLeft:', onLeft, 'onRight:', onRight, 'onTop:', onTop, 'onBottom:', onBottom);
115
+ console.log(' margin:', margin);
116
+
117
+ // Shrink bbox based on anchor position
118
+ if (onLeft) {
119
+ // Anchor on left edge - exclude left portion
120
+ return new Rectangle(bbox.x + margin, bbox.y, Math.max(0, bbox.width - margin), bbox.height);
121
+ } else if (onRight) {
122
+ // Anchor on right edge - exclude right portion
123
+ return new Rectangle(bbox.x, bbox.y, Math.max(0, bbox.width - margin), bbox.height);
124
+ } else if (onTop) {
125
+ // Anchor on top edge - exclude top portion
126
+ return new Rectangle(bbox.x, bbox.y + margin, bbox.width, Math.max(0, bbox.height - margin));
127
+ } else if (onBottom) {
128
+ // Anchor on bottom edge - exclude bottom portion
129
+ return new Rectangle(bbox.x, bbox.y, bbox.width, Math.max(0, bbox.height - margin));
130
+ }
131
+
132
+ // If anchor is not on an edge, return original bbox
133
+ return bbox;
134
+ }
135
+
136
+ /**
137
+ * Check if a point is accessible (not inside any obstacle)
138
+ */
139
+ }, {
140
+ key: "isAccessible",
141
+ value: function isAccessible(point) {
142
+ var key = point.clone().snapToGrid(this.mapGridSize).toString();
143
+ var rects = this.map.get(key);
144
+ if (!rects) {
145
+ return true;
146
+ }
147
+
148
+ // Check if point is near an anchor - if so, allow it
149
+ var margin = (this.options.step || 10) * 3;
150
+ if (this.sourceAnchor) {
151
+ var distToSource = Math.abs(point.x - this.sourceAnchor.x) + Math.abs(point.y - this.sourceAnchor.y);
152
+ if (distToSource < margin) {
153
+ console.log("[isAccessible] Point (".concat(point.x, ", ").concat(point.y, "): accessible (near source anchor)"));
154
+ return true;
155
+ }
156
+ }
157
+ if (this.targetAnchor) {
158
+ var distToTarget = Math.abs(point.x - this.targetAnchor.x) + Math.abs(point.y - this.targetAnchor.y);
159
+ if (distToTarget < margin) {
160
+ console.log("[isAccessible] Point (".concat(point.x, ", ").concat(point.y, "): accessible (near target anchor)"));
161
+ return true;
162
+ }
163
+ }
164
+ var accessible = rects.every(function (rect) {
165
+ return !rect.containsPoint(point);
166
+ });
167
+
168
+ // Debug: log points on the direct path
169
+ if (point.y === 40 && point.x >= 0 && point.x <= 545) {
170
+ console.log("[isAccessible] Point (".concat(point.x, ", ").concat(point.y, "): ").concat(accessible ? 'accessible' : 'BLOCKED'));
171
+ if (!accessible) {
172
+ rects.forEach(function (rect, i) {
173
+ if (rect.containsPoint(point)) {
174
+ console.log(" Blocked by rect ".concat(i, ": (").concat(rect.x, ", ").concat(rect.y, ", ").concat(rect.width, ", ").concat(rect.height, ")"));
175
+ }
176
+ });
177
+ }
178
+ }
179
+ return accessible;
180
+ }
181
+ }]);
182
+ return ObstacleMap;
183
+ }();
@@ -0,0 +1,2 @@
1
+ export { ObstacleMap } from './ObstacleMap';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/obstacle/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1 @@
1
+ export { ObstacleMap } from "./ObstacleMap";
@@ -0,0 +1,16 @@
1
+ import { Point } from '../geometry';
2
+ import type { ManhattanRouterOptions } from './types';
3
+ /**
4
+ * Default configuration for Manhattan router
5
+ */
6
+ export declare const defaults: Required<Omit<ManhattanRouterOptions, 'fallbackRoute'>>;
7
+ /**
8
+ * Direction map - maps direction names to unit vectors
9
+ */
10
+ export declare const directionMap: {
11
+ top: Point;
12
+ right: Point;
13
+ bottom: Point;
14
+ left: Point;
15
+ };
16
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/options/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,EAAE,sBAAsB,EAAa,MAAM,SAAS,CAAA;AAEhE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,sBAAsB,EAAE,eAAe,CAAC,CAkB5E,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;CAKxB,CAAA"}