UNPKG

@rxflow/manhattan

Version:

Manhattan routing algorithm for ReactFlow - generates orthogonal paths with obstacle avoidance

103 lines (90 loc) 4.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getRectPoints = getRectPoints; var _geometry = require("../geometry"); var _grid = require("./grid"); /** * Check if a point is on the edge of a rectangle */ function isPointOnRectangleEdge(point, bbox, tolerance = 0.01) { const onLeft = Math.abs(point.x - bbox.x) < tolerance; const onRight = Math.abs(point.x - (bbox.x + bbox.width)) < tolerance; const onTop = Math.abs(point.y - bbox.y) < tolerance; const onBottom = Math.abs(point.y - (bbox.y + bbox.height)) < tolerance; const withinVerticalBounds = point.y >= bbox.y - tolerance && point.y <= bbox.y + bbox.height + tolerance; const withinHorizontalBounds = point.x >= bbox.x - tolerance && point.x <= bbox.x + bbox.width + tolerance; return (onLeft || onRight) && withinVerticalBounds || (onTop || onBottom) && withinHorizontalBounds; } /** * Check if a direction points outward from the rectangle edge where the anchor is located */ function isDirectionOutward(anchor, bbox, direction, tolerance = 0.01) { const onLeft = Math.abs(anchor.x - bbox.x) < tolerance; const onRight = Math.abs(anchor.x - (bbox.x + bbox.width)) < tolerance; const onTop = Math.abs(anchor.y - bbox.y) < tolerance; const onBottom = Math.abs(anchor.y - (bbox.y + bbox.height)) < tolerance; // Only allow outward directions from the edge if (onLeft && direction.x < 0) return true; if (onRight && direction.x > 0) return true; if (onTop && direction.y < 0) return true; if (onBottom && direction.y > 0) return true; // For corners, allow both perpendicular directions if ((onLeft || onRight) && direction.x === 0) return true; if ((onTop || onBottom) && direction.y === 0) return true; return false; } /** * Get points around a rectangle taking given directions into account * Lines are drawn from anchor in given directions, intersections recorded */ function getRectPoints(anchor, bbox, directionList, grid, options) { const precision = options.precision; const directionMap = options.directionMap; const centerVector = anchor.diff(bbox.getCenter()); const rectPoints = []; // Check if anchor is on the edge of the bbox const isOnEdge = isPointOnRectangleEdge(anchor, bbox); // Check each allowed direction for (const key of directionList) { const direction = directionMap[key]; // If anchor is on edge, only consider outward directions if (isOnEdge) { const isOutward = isDirectionOutward(anchor, bbox, direction); if (!isOutward) { continue; // Skip inward directions } } // Create a line that is guaranteed to intersect the bbox if bbox // is in the direction even if anchor lies outside of bbox const ending = new _geometry.Point(anchor.x + direction.x * (Math.abs(centerVector.x) + bbox.width), anchor.y + direction.y * (Math.abs(centerVector.y) + bbox.height)); const intersectionLine = new _geometry.Line(anchor, ending); // Get the farther intersection, in case there are two const intersections = intersectionLine.intersect(bbox); let farthestIntersectionDistance; let farthestIntersection = null; for (const intersection of intersections) { const distance = anchor.squaredDistance(intersection); if (farthestIntersectionDistance === undefined || distance > farthestIntersectionDistance) { farthestIntersectionDistance = distance; farthestIntersection = intersection; } } // If an intersection was found in this direction, it is our rectPoint if (farthestIntersection) { let target = (0, _grid.align)(farthestIntersection, grid, precision); // If the rectPoint lies inside the bbox, offset it by one more step if (bbox.containsPoint(target)) { target = (0, _grid.align)(target.translate(direction.x * grid.x, direction.y * grid.y), grid, precision); } rectPoints.push(target); } } // If anchor lies outside of bbox, add it to the array of points // If anchor is on edge, don't add it - force path to start from extended points if (!bbox.containsPoint(anchor) && !isOnEdge) { rectPoints.push((0, _grid.align)(anchor, grid, precision)); } return rectPoints; }