@rxflow/manhattan 0.0.1-alpha.11 → 0.0.1-alpha.13

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 (49) hide show
  1. package/cjs/geometry/Rectangle.d.ts +1 -1
  2. package/cjs/geometry/Rectangle.js +2 -2
  3. package/cjs/getManHattanPath.d.ts.map +1 -1
  4. package/cjs/getManHattanPath.js +153 -20
  5. package/cjs/obstacle/ObstacleMap.d.ts +7 -1
  6. package/cjs/obstacle/ObstacleMap.d.ts.map +1 -1
  7. package/cjs/obstacle/ObstacleMap.js +53 -1
  8. package/cjs/options/defaults.d.ts +1 -1
  9. package/cjs/options/defaults.d.ts.map +1 -1
  10. package/cjs/options/defaults.js +1 -1
  11. package/cjs/options/resolver.d.ts.map +1 -1
  12. package/cjs/options/resolver.js +4 -2
  13. package/cjs/options/types.d.ts +19 -6
  14. package/cjs/options/types.d.ts.map +1 -1
  15. package/cjs/pathfinder/findRoute.d.ts.map +1 -1
  16. package/cjs/pathfinder/findRoute.js +239 -15
  17. package/cjs/svg/pathConverter.d.ts.map +1 -1
  18. package/cjs/svg/pathConverter.js +23 -12
  19. package/cjs/utils/getAnchorPoints.d.ts +15 -0
  20. package/cjs/utils/getAnchorPoints.d.ts.map +1 -0
  21. package/cjs/utils/getAnchorPoints.js +75 -0
  22. package/cjs/utils/index.d.ts +1 -0
  23. package/cjs/utils/index.d.ts.map +1 -1
  24. package/cjs/utils/index.js +11 -0
  25. package/esm/geometry/Rectangle.d.ts +1 -1
  26. package/esm/geometry/Rectangle.js +2 -2
  27. package/esm/getManHattanPath.d.ts.map +1 -1
  28. package/esm/getManHattanPath.js +162 -22
  29. package/esm/obstacle/ObstacleMap.d.ts +7 -1
  30. package/esm/obstacle/ObstacleMap.d.ts.map +1 -1
  31. package/esm/obstacle/ObstacleMap.js +78 -0
  32. package/esm/options/defaults.d.ts +1 -1
  33. package/esm/options/defaults.d.ts.map +1 -1
  34. package/esm/options/defaults.js +1 -1
  35. package/esm/options/resolver.d.ts.map +1 -1
  36. package/esm/options/resolver.js +5 -3
  37. package/esm/options/types.d.ts +19 -6
  38. package/esm/options/types.d.ts.map +1 -1
  39. package/esm/pathfinder/findRoute.d.ts.map +1 -1
  40. package/esm/pathfinder/findRoute.js +262 -23
  41. package/esm/svg/pathConverter.d.ts.map +1 -1
  42. package/esm/svg/pathConverter.js +23 -12
  43. package/esm/utils/getAnchorPoints.d.ts +15 -0
  44. package/esm/utils/getAnchorPoints.d.ts.map +1 -0
  45. package/esm/utils/getAnchorPoints.js +69 -0
  46. package/esm/utils/index.d.ts +1 -0
  47. package/esm/utils/index.d.ts.map +1 -1
  48. package/esm/utils/index.js +2 -1
  49. package/package.json +1 -1
@@ -1,8 +1,164 @@
1
1
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
2
2
  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); }
3
3
  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; }
4
+ import { Point } from "../geometry";
4
5
  import { SortedSet } from "./SortedSet";
5
- import { getGrid, align, round, getDirectionAngle, getDirectionChange, getGridOffsets, getRectPoints, getCost, getKey, reconstructRoute } from "../utils";
6
+ import { getGrid, round, getDirectionAngle, getDirectionChange, getGridOffsets, getRectPoints, getCost, getKey, reconstructRoute } from "../utils";
7
+
8
+ /**
9
+ * Find the closest accessible point in a given direction using step-based search with linear refinement
10
+ *
11
+ * Algorithm:
12
+ * 1. Try moving by step increments (step, 2*step, 3*step...) until finding an accessible point
13
+ * 2. Once found, linearly search backward from (n-1)*step+1 to n*step to find the closest accessible point
14
+ * 3. Returns null if no accessible point found within maxSteps
15
+ */
16
+ function findAccessibleNeighbor(from, directionX, directionY, step, map, precision) {
17
+ var maxSteps = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 20;
18
+ // Normalize direction
19
+ var length = Math.sqrt(directionX * directionX + directionY * directionY);
20
+ var ndx = directionX / length;
21
+ var ndy = directionY / length;
22
+
23
+ // 1. Try the first step directly (most common case)
24
+ var firstStepPoint = new Point(from.x + ndx * step, from.y + ndy * step).round(precision);
25
+ if (map.isAccessible(firstStepPoint)) {
26
+ // First step is accessible, no need for refinement
27
+ return firstStepPoint;
28
+ }
29
+
30
+ // 2. First step blocked, search outward by step increments
31
+ var stepMultiplier = 2;
32
+ var foundAccessibleDistance = -1;
33
+ while (stepMultiplier <= maxSteps) {
34
+ var distance = stepMultiplier * step;
35
+ var testPoint = new Point(from.x + ndx * distance, from.y + ndy * distance).round(precision);
36
+ if (map.isAccessible(testPoint)) {
37
+ foundAccessibleDistance = distance;
38
+ break;
39
+ }
40
+ stepMultiplier++;
41
+ }
42
+ if (foundAccessibleDistance < 0) {
43
+ return null; // No accessible point found
44
+ }
45
+
46
+ // 3. Linear search within the last step interval to find closest accessible point
47
+ // Search from (n-1)*step+1 to n*step
48
+ var outerDistance = foundAccessibleDistance;
49
+ var innerDistance = foundAccessibleDistance - step;
50
+
51
+ // Search backward from outer to inner to find the closest accessible point
52
+ for (var dist = innerDistance + 1; dist < outerDistance; dist++) {
53
+ var _testPoint = new Point(from.x + ndx * dist, from.y + ndy * dist).round(precision);
54
+ if (map.isAccessible(_testPoint)) {
55
+ return _testPoint;
56
+ }
57
+ }
58
+
59
+ // If no closer point found, return the outer distance point
60
+ return new Point(from.x + ndx * outerDistance, from.y + ndy * outerDistance).round(precision);
61
+ }
62
+
63
+ /**
64
+ * Generate smart points based on position using extensionDistance and binary search
65
+ *
66
+ * Algorithm:
67
+ * 1. First try extensionDistance in the specified direction
68
+ * 2. If blocked, use binary search starting from step, halving until finding accessible point
69
+ * 3. For target points, approach from opposite direction
70
+ */
71
+ function generateSmartPoints(anchor, bbox, position, grid, map, options) {
72
+ var isTarget = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
73
+ var directionMap = {
74
+ 'right': {
75
+ x: 1,
76
+ y: 0
77
+ },
78
+ 'left': {
79
+ x: -1,
80
+ y: 0
81
+ },
82
+ 'top': {
83
+ x: 0,
84
+ y: -1
85
+ },
86
+ 'bottom': {
87
+ x: 0,
88
+ y: 1
89
+ }
90
+ };
91
+ var direction = directionMap[position];
92
+ if (!direction) {
93
+ console.warn("[generateSmartPoints] Unknown position: ".concat(position, ", falling back to anchor"));
94
+ return [anchor];
95
+ }
96
+
97
+ // Both source and target extend in the specified direction
98
+ // - Source: extends away from node in sourcePosition direction
99
+ // - Target: extends away from node in targetPosition direction (path approaches from this direction)
100
+ var actualDirection = direction;
101
+ var points = [];
102
+
103
+ // 1. First try extensionDistance
104
+ var extensionPoint = new Point(anchor.x + actualDirection.x * options.extensionDistance, anchor.y + actualDirection.y * options.extensionDistance).round(options.precision);
105
+ console.log("[generateSmartPoints] ".concat(isTarget ? 'Target' : 'Source', " position=").concat(position, ", trying extension point: (").concat(extensionPoint.x, ", ").concat(extensionPoint.y, ")"));
106
+ if (map.isAccessible(extensionPoint)) {
107
+ points.push(extensionPoint);
108
+ console.log("[generateSmartPoints] Extension point is accessible");
109
+ return points;
110
+ }
111
+ console.log("[generateSmartPoints] Extension point blocked, using step-based search");
112
+
113
+ // 2. Step-based search with binary refinement
114
+ // First, extend outward by step increments until we find an accessible point
115
+ var stepMultiplier = 1;
116
+ var maxSteps = 20; // Prevent infinite loop
117
+ var foundAccessibleDistance = -1;
118
+ console.log("[generateSmartPoints] Starting outward search with step=".concat(options.step));
119
+ while (stepMultiplier <= maxSteps) {
120
+ var distance = stepMultiplier * options.step;
121
+ var testPoint = new Point(anchor.x + actualDirection.x * distance, anchor.y + actualDirection.y * distance).round(options.precision);
122
+ console.log("[generateSmartPoints] Testing ".concat(stepMultiplier, "*step (distance=").concat(distance, "): (").concat(testPoint.x, ", ").concat(testPoint.y, ")"));
123
+ if (map.isAccessible(testPoint)) {
124
+ foundAccessibleDistance = distance;
125
+ console.log("[generateSmartPoints] Found accessible point at ".concat(stepMultiplier, "*step (distance=").concat(distance, ")"));
126
+ break;
127
+ }
128
+ stepMultiplier++;
129
+ }
130
+
131
+ // 3. If we found an accessible point, refine by linear search within the last step interval
132
+ if (foundAccessibleDistance > 0) {
133
+ var outerDistance = foundAccessibleDistance;
134
+ var innerDistance = foundAccessibleDistance - options.step;
135
+ console.log("[generateSmartPoints] Refining between ".concat(innerDistance, " and ").concat(outerDistance));
136
+
137
+ // Linear search from innerDistance+1 to outerDistance to find the closest accessible point
138
+ var bestDistance = outerDistance;
139
+
140
+ // Search forward from inner to outer to find the first accessible point
141
+ for (var dist = innerDistance + 1; dist < outerDistance; dist++) {
142
+ var _testPoint2 = new Point(anchor.x + actualDirection.x * dist, anchor.y + actualDirection.y * dist).round(options.precision);
143
+ console.log("[generateSmartPoints] Linear search testing distance ".concat(dist, ": (").concat(_testPoint2.x, ", ").concat(_testPoint2.y, ")"));
144
+ if (map.isAccessible(_testPoint2)) {
145
+ bestDistance = dist;
146
+ console.log("[generateSmartPoints] Found closest accessible point at distance ".concat(dist));
147
+ break;
148
+ }
149
+ }
150
+
151
+ // Use the best distance found
152
+ var finalPoint = new Point(anchor.x + actualDirection.x * bestDistance, anchor.y + actualDirection.y * bestDistance).round(options.precision);
153
+ points.push(finalPoint);
154
+ console.log("[generateSmartPoints] Final point at distance ".concat(bestDistance, ": (").concat(finalPoint.x, ", ").concat(finalPoint.y, ")"));
155
+ } else {
156
+ // 4. If no accessible point found after maxSteps, use anchor as fallback
157
+ console.log("[generateSmartPoints] No accessible point found after ".concat(maxSteps, " steps, using anchor: (").concat(anchor.x, ", ").concat(anchor.y, ")"));
158
+ points.push(anchor);
159
+ }
160
+ return points;
161
+ }
6
162
 
7
163
  /**
8
164
  * Find route between two points using A* algorithm
@@ -22,22 +178,43 @@ export function findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, ma
22
178
  var endPoint = targetEndpoint;
23
179
 
24
180
  // Get start and end points around rectangles
25
- var startPoints = getRectPoints(startPoint, sourceBBox, options.startDirections, grid, options);
26
- var endPoints = getRectPoints(targetEndpoint, targetBBox, options.endDirections, grid, options);
27
- console.log('[findRoute] Start points from getRectPoints:', startPoints.map(function (p) {
28
- return "(".concat(p.x, ", ").concat(p.y, ")");
29
- }));
30
- console.log('[findRoute] End points from getRectPoints:', endPoints.map(function (p) {
31
- return "(".concat(p.x, ", ").concat(p.y, ")");
32
- }));
181
+ // Use smart point generation based on position if available
182
+ var startPoints;
183
+ var endPoints;
33
184
 
34
- // Take into account only accessible rect points
35
- startPoints = startPoints.filter(function (p) {
36
- return map.isAccessible(p);
37
- });
38
- endPoints = endPoints.filter(function (p) {
39
- return map.isAccessible(p);
40
- });
185
+ // Generate smart start points based on sourcePosition
186
+ if (options.sourcePosition) {
187
+ startPoints = generateSmartPoints(startPoint, sourceBBox, options.sourcePosition, grid, map, options, false);
188
+ console.log('[findRoute] Start points from smart generation:', startPoints.map(function (p) {
189
+ return "(".concat(p.x, ", ").concat(p.y, ")");
190
+ }));
191
+ } else {
192
+ startPoints = getRectPoints(startPoint, sourceBBox, options.startDirections, grid, options);
193
+ console.log('[findRoute] Start points from getRectPoints:', startPoints.map(function (p) {
194
+ return "(".concat(p.x, ", ").concat(p.y, ")");
195
+ }));
196
+ // Take into account only accessible rect points
197
+ startPoints = startPoints.filter(function (p) {
198
+ return map.isAccessible(p);
199
+ });
200
+ }
201
+
202
+ // Generate smart end points based on targetPosition
203
+ if (options.targetPosition) {
204
+ endPoints = generateSmartPoints(targetEndpoint, targetBBox, options.targetPosition, grid, map, options, true);
205
+ console.log('[findRoute] End points from smart generation:', endPoints.map(function (p) {
206
+ return "(".concat(p.x, ", ").concat(p.y, ")");
207
+ }));
208
+ } else {
209
+ endPoints = getRectPoints(targetEndpoint, targetBBox, options.endDirections, grid, options);
210
+ console.log('[findRoute] End points from getRectPoints:', endPoints.map(function (p) {
211
+ return "(".concat(p.x, ", ").concat(p.y, ")");
212
+ }));
213
+ // Take into account only accessible rect points
214
+ endPoints = endPoints.filter(function (p) {
215
+ return map.isAccessible(p);
216
+ });
217
+ }
41
218
  console.log('[findRoute] Start points after filter:', startPoints.map(function (p) {
42
219
  return "(".concat(p.x, ", ").concat(p.y, ")");
43
220
  }));
@@ -48,10 +225,10 @@ export function findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, ma
48
225
  // Ensure we always have at least the anchor points
49
226
  // This handles edge cases where anchor is on the node boundary
50
227
  if (startPoints.length === 0) {
51
- startPoints = [align(startPoint, grid, precision)];
228
+ startPoints = [round(startPoint, precision)];
52
229
  }
53
230
  if (endPoints.length === 0) {
54
- endPoints = [align(endPoint, grid, precision)];
231
+ endPoints = [round(endPoint, precision)];
55
232
  }
56
233
 
57
234
  // Initialize A* data structures
@@ -139,16 +316,78 @@ export function findRoute(sourceBBox, targetBBox, sourceAnchor, targetAnchor, ma
139
316
  continue;
140
317
  }
141
318
 
142
- // Calculate neighbor point
143
- var neighborPoint = align(currentPoint.clone().translate(direction.gridOffsetX || 0, direction.gridOffsetY || 0), grid, precision);
319
+ // Find the closest accessible neighbor in this direction using binary search
320
+ var neighborPoint = findAccessibleNeighbor(currentPoint, direction.offsetX, direction.offsetY, options.step, map, precision);
321
+
322
+ // Skip if no accessible neighbor found in this direction
323
+ if (!neighborPoint) {
324
+ continue;
325
+ }
144
326
  var neighborKey = getKey(neighborPoint);
145
327
 
146
- // Skip if closed or not accessible
147
- if (openSet.isClose(neighborKey) || !map.isAccessible(neighborPoint)) {
328
+ // Skip if already closed
329
+ if (openSet.isClose(neighborKey)) {
148
330
  continue;
149
331
  }
150
332
 
151
- // Check if neighbor is an end point
333
+ // Check if we can reach any end point directly from this neighbor
334
+ // This allows connecting to end points that are not on the grid
335
+ var canReachEndPoint = false;
336
+ var reachableEndPoint = null;
337
+ var _iterator3 = _createForOfIteratorHelper(endPoints),
338
+ _step3;
339
+ try {
340
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
341
+ var endPt = _step3.value;
342
+ var distanceToEnd = neighborPoint.manhattanDistance(endPt);
343
+
344
+ // If close enough to end point (within step distance), try direct connection
345
+ if (distanceToEnd < options.step * 1.5) {
346
+ // Check if we can move directly to the end point
347
+ var dx = endPt.x - neighborPoint.x;
348
+ var dy = endPt.y - neighborPoint.y;
349
+
350
+ // Allow direct connection if it's orthogonal or close to orthogonal
351
+ var isOrthogonal = Math.abs(dx) < 0.1 || Math.abs(dy) < 0.1;
352
+ if (isOrthogonal && map.isAccessible(endPt)) {
353
+ canReachEndPoint = true;
354
+ reachableEndPoint = endPt;
355
+ break;
356
+ }
357
+ }
358
+ }
359
+
360
+ // If we can reach an end point directly, add it as the final step
361
+ } catch (err) {
362
+ _iterator3.e(err);
363
+ } finally {
364
+ _iterator3.f();
365
+ }
366
+ if (canReachEndPoint && reachableEndPoint) {
367
+ var endKey = getKey(reachableEndPoint);
368
+ var endCost = neighborPoint.manhattanDistance(reachableEndPoint);
369
+ var totalCost = currentCost + direction.cost + endCost;
370
+ if (!openSet.isOpen(endKey) || totalCost < (costs.get(endKey) || Infinity)) {
371
+ points.set(endKey, reachableEndPoint);
372
+ parents.set(endKey, neighborPoint);
373
+ costs.set(endKey, totalCost);
374
+
375
+ // Also add the neighbor point if not already added
376
+ if (!points.has(neighborKey)) {
377
+ points.set(neighborKey, neighborPoint);
378
+ parents.set(neighborKey, currentPoint);
379
+ costs.set(neighborKey, currentCost + direction.cost);
380
+ }
381
+
382
+ // Check if this is our target end point
383
+ if (endPointsKeys.has(endKey)) {
384
+ options.previousDirectionAngle = directionAngle;
385
+ return reconstructRoute(parents, points, reachableEndPoint, startPoint, endPoint);
386
+ }
387
+ }
388
+ }
389
+
390
+ // Check if neighbor is an end point (exact match)
152
391
  if (endPointsKeys.has(neighborKey)) {
153
392
  var isEndPoint = neighborPoint.equals(endPoint);
154
393
  if (!isEndPoint) {
@@ -1 +1 @@
1
- {"version":3,"file":"pathConverter.d.ts","sourceRoot":"","sources":["../../src/svg/pathConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,GAAG,MAAM,CAgEjG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,CAgCzE"}
1
+ {"version":3,"file":"pathConverter.d.ts","sourceRoot":"","sources":["../../src/svg/pathConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,GAAG,MAAM,CA2EjG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,CAgCzE"}
@@ -38,31 +38,42 @@ export function pointsToPath(points, precision) {
38
38
  var current = points[_i];
39
39
  var next = points[_i + 1];
40
40
 
41
- // Calculate the distance from prev to current and current to next
42
- var distToPrev = Math.sqrt(Math.pow(current.x - prev.x, 2) + Math.pow(current.y - prev.y, 2));
43
- var distToNext = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
41
+ // Calculate direction vectors
42
+ var dx1 = current.x - prev.x;
43
+ var dy1 = current.y - prev.y;
44
+ var dx2 = next.x - current.x;
45
+ var dy2 = next.y - current.y;
46
+
47
+ // Calculate distances
48
+ var dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49
+ var dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44
50
 
45
51
  // Use the smaller of borderRadius or half the segment length
46
- var radius = Math.min(borderRadius, distToPrev / 2, distToNext / 2);
52
+ var radius = Math.min(borderRadius, dist1 / 2, dist2 / 2);
53
+
54
+ // Normalize direction vectors
55
+ var ndx1 = dx1 / dist1;
56
+ var ndy1 = dy1 / dist1;
57
+ var ndx2 = dx2 / dist2;
58
+ var ndy2 = dy2 / dist2;
47
59
 
48
- // Calculate the point before the corner (on the line from prev to current)
49
- var beforeCornerRatio = (distToPrev - radius) / distToPrev;
60
+ // Calculate the point before the corner
50
61
  var beforeCorner = {
51
- x: prev.x + (current.x - prev.x) * beforeCornerRatio,
52
- y: prev.y + (current.y - prev.y) * beforeCornerRatio
62
+ x: current.x - ndx1 * radius,
63
+ y: current.y - ndy1 * radius
53
64
  };
54
65
 
55
- // Calculate the point after the corner (on the line from current to next)
56
- var afterCornerRatio = radius / distToNext;
66
+ // Calculate the point after the corner
57
67
  var afterCorner = {
58
- x: current.x + (next.x - current.x) * afterCornerRatio,
59
- y: current.y + (next.y - current.y) * afterCornerRatio
68
+ x: current.x + ndx2 * radius,
69
+ y: current.y + ndy2 * radius
60
70
  };
61
71
 
62
72
  // Draw line to the point before corner
63
73
  path += " L ".concat(roundCoord(beforeCorner.x), " ").concat(roundCoord(beforeCorner.y));
64
74
 
65
75
  // Draw quadratic bezier curve for the rounded corner
76
+ // The control point is the actual corner point
66
77
  path += " Q ".concat(roundCoord(current.x), " ").concat(roundCoord(current.y), " ").concat(roundCoord(afterCorner.x), " ").concat(roundCoord(afterCorner.y));
67
78
  }
68
79
 
@@ -0,0 +1,15 @@
1
+ import { Point } from '../geometry';
2
+ import type { ObstacleMap } from '../obstacle';
3
+ import type { Direction } from '../options';
4
+ /**
5
+ * Get accessible anchor points using binary search optimization
6
+ *
7
+ * @param anchor - The anchor point (on node edge)
8
+ * @param position - The position/direction (right, left, top, bottom)
9
+ * @param extensionDistance - The preferred extension distance
10
+ * @param step - The step size for binary search
11
+ * @param obstacleMap - The obstacle map for accessibility checking
12
+ * @returns Array of accessible points, prioritized by distance
13
+ */
14
+ export declare function getAnchorPoints(anchor: Point, position: Direction, extensionDistance: number, step: number, obstacleMap: ObstacleMap): Point[];
15
+ //# sourceMappingURL=getAnchorPoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getAnchorPoints.d.ts","sourceRoot":"","sources":["../../src/utils/getAnchorPoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,KAAK,EACb,QAAQ,EAAE,SAAS,EACnB,iBAAiB,EAAE,MAAM,EACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,GACvB,KAAK,EAAE,CA0DT"}
@@ -0,0 +1,69 @@
1
+ import { Point } from "../geometry";
2
+ /**
3
+ * Get accessible anchor points using binary search optimization
4
+ *
5
+ * @param anchor - The anchor point (on node edge)
6
+ * @param position - The position/direction (right, left, top, bottom)
7
+ * @param extensionDistance - The preferred extension distance
8
+ * @param step - The step size for binary search
9
+ * @param obstacleMap - The obstacle map for accessibility checking
10
+ * @returns Array of accessible points, prioritized by distance
11
+ */
12
+ export function getAnchorPoints(anchor, position, extensionDistance, step, obstacleMap) {
13
+ var points = [];
14
+
15
+ // Determine direction vector based on position
16
+ var directionMap = {
17
+ 'right': {
18
+ dx: 1,
19
+ dy: 0
20
+ },
21
+ 'left': {
22
+ dx: -1,
23
+ dy: 0
24
+ },
25
+ 'top': {
26
+ dx: 0,
27
+ dy: -1
28
+ },
29
+ 'bottom': {
30
+ dx: 0,
31
+ dy: 1
32
+ }
33
+ };
34
+ var dir = directionMap[position];
35
+ if (!dir) {
36
+ console.warn("[getAnchorPoints] Invalid position: ".concat(position));
37
+ return [anchor];
38
+ }
39
+ console.log("[getAnchorPoints] Finding points for position '".concat(position, "' from (").concat(anchor.x, ", ").concat(anchor.y, ")"));
40
+
41
+ // 1. First try extensionDistance
42
+ var extensionPoint = new Point(anchor.x + dir.dx * extensionDistance, anchor.y + dir.dy * extensionDistance);
43
+ if (obstacleMap.isAccessible(extensionPoint)) {
44
+ console.log("[getAnchorPoints] Extension point (".concat(extensionPoint.x, ", ").concat(extensionPoint.y, ") is accessible"));
45
+ points.push(extensionPoint);
46
+ return points;
47
+ }
48
+ console.log("[getAnchorPoints] Extension point (".concat(extensionPoint.x, ", ").concat(extensionPoint.y, ") is blocked, trying binary search"));
49
+
50
+ // 2. If extensionDistance point is blocked, use binary search with step
51
+ // Try: step -> step/2 -> step/4 -> ... -> 1px
52
+ var distance = step;
53
+ while (distance >= 1) {
54
+ var testPoint = new Point(anchor.x + dir.dx * distance, anchor.y + dir.dy * distance);
55
+ if (obstacleMap.isAccessible(testPoint)) {
56
+ console.log("[getAnchorPoints] Found accessible point at distance ".concat(distance, "px: (").concat(testPoint.x, ", ").concat(testPoint.y, ")"));
57
+ points.push(testPoint);
58
+ return points;
59
+ }
60
+
61
+ // Halve the distance for next iteration
62
+ distance = Math.floor(distance / 2);
63
+ }
64
+
65
+ // 3. If still no accessible point found, return the anchor itself
66
+ console.warn("[getAnchorPoints] No accessible point found, using anchor itself: (".concat(anchor.x, ", ").concat(anchor.y, ")"));
67
+ points.push(anchor);
68
+ return points;
69
+ }
@@ -4,4 +4,5 @@ export * from './rect';
4
4
  export * from './route';
5
5
  export * from './node';
6
6
  export * from './pathValidation';
7
+ export * from './getAnchorPoints';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AACtB,cAAc,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AACtB,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA"}
@@ -3,4 +3,5 @@ export * from "./direction";
3
3
  export * from "./rect";
4
4
  export * from "./route";
5
5
  export * from "./node";
6
- export * from "./pathValidation";
6
+ export * from "./pathValidation";
7
+ export * from "./getAnchorPoints";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rxflow/manhattan",
3
- "version": "0.0.1-alpha.11",
3
+ "version": "0.0.1-alpha.13",
4
4
  "description": "Manhattan routing algorithm for ReactFlow - generates orthogonal paths with obstacle avoidance",
5
5
  "keywords": [
6
6
  "reactflow",