@rxflow/manhattan
Version:
Manhattan routing algorithm for ReactFlow - generates orthogonal paths with obstacle avoidance
103 lines (90 loc) • 4.24 kB
JavaScript
;
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;
}