Skip to content

Commit

Permalink
feat(routers.RightAngle): new approach to connect vertices
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinKanera committed Aug 7, 2023
1 parent 6fb58a5 commit e7ec7e5
Showing 1 changed file with 81 additions and 34 deletions.
115 changes: 81 additions & 34 deletions src/routers/rightAngle.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as g from '../g/index.mjs';
import * as util from '../util/index.mjs';

const Directions = {
AUTO: 'auto',
Expand Down Expand Up @@ -104,18 +105,18 @@ function pointDataFromAnchor(view, point, bbox, direction, isPort, fallBackAncho
};
}

function pointDataFromVertex({ x, y }, nextBBox) {
function pointDataFromVertex({ x, y }) {
const point = new g.Point(x, y);

return {
point,
x0: point.x,
y0: point.y,
view: null,
bbox: null,
bbox: new g.Rect(x, y, 0, 0),
width: 0,
height: 0,
direction: nextBBox.sideNearestToPoint(point)
direction: null
};
}

Expand Down Expand Up @@ -679,12 +680,17 @@ function routeBetweenPoints(source, target, margin) {
const x = toy < soy ? Math.min(smx0, tmx0) : smx0;
const y = Math.min(smy0, tmy0);

return [
{ x, y: soy },
const result = [
{ x, y },
{ x: tox, y }
];

if (y !== soy) {
result.unshift({ x, y: soy });
}

return result;

} else if (sourceSide === 'right' && targetSide === 'top') {
if (sox <= tox && soy < tmy0) {
return [{ x: tox, y: soy }];
Expand Down Expand Up @@ -712,11 +718,17 @@ function routeBetweenPoints(source, target, margin) {

const x = Math.max(smx1, tmx1);
const y = Math.min(smy0, tmy0);
return [
{ x, y: soy },

const result = [
{ x, y },
{ x: tox, y }
];

if (y !== soy) {
result.unshift({ x, y: soy });
}

return result;
} else if (sourceSide === 'right' && targetSide === 'bottom') {
if (sox < tox && soy >= tmy1) {
return [{ x: tox, y: soy }];
Expand Down Expand Up @@ -753,6 +765,14 @@ function routeBetweenPoints(source, target, margin) {
}
}

function routeOverlap(p1, p2, p3, p4) {

const angle1 = new g.Line(p1, p2).angle();
const angle2 = new g.Line(p3, p4).angle();

return Math.abs(angle1 - angle2) === 180;
}

function rightAngleRouter(vertices, opt, linkView) {
const { sourceDirection = Directions.AUTO, targetDirection = Directions.AUTO } = opt;
const margin = opt.margin || 20;
Expand All @@ -765,47 +785,74 @@ function rightAngleRouter(vertices, opt, linkView) {
const targetPoint = pointDataFromAnchor(linkView.targetView, linkView.targetAnchor, linkView.targetBBox, targetDirection, isTargetPort, linkView.targetAnchor);

let resultVertices = [];
let source = sourcePoint;

if (!useVertices) {
if (!useVertices || !vertices.length) {
return routeBetweenPoints(sourcePoint, targetPoint, margin);
}

for (let i = 0; i < vertices.length; i++) {
const current = vertices[i];
const verticesPoints = [sourcePoint, ...vertices.map((v) => pointDataFromVertex(v)), targetPoint];
verticesPoints[1].direction = verticesPoints[1].bbox.sideNearestToPoint(sourcePoint.point);

const next = vertices[i + 1] || targetPoint.point;
const nextBBox = new g.Rect(next.x, next.y, 0, 0);
const target = pointDataFromVertex(current, nextBBox);
for (let i = 0; i < verticesPoints.length - 1; i++) {
const from = verticesPoints[i];
const to = verticesPoints[i + 1];

if (new g.Point(current).equals(resultVertices[resultVertices.length - 1])) {
target.direction = OPPOSITE_DIRECTIONS[target.direction];
source = target;
continue;
}
const route = util.uniq(routeBetweenPoints(from, to, margin), (p) => new g.Point(p.x, p.y).serialize());

resultVertices.push(...routeBetweenPoints(source, target, margin));
if (new g.Point(resultVertices[resultVertices.length - 1]).equals(route[0])) {
route.shift();
}

target.direction = OPPOSITE_DIRECTIONS[target.direction];
source = target;
}
if (i > 0) {
let skip = false;

const targetPointRoute = routeBetweenPoints(source, targetPoint, margin);
// prevent infinite correction
if (from.originalDirection) {
from.direction = from.originalDirection;
skip = true;
}

const middlePoint = verticesPoints[i].point;
const lastPointOfPrevSegment = middlePoint.equals(resultVertices[resultVertices.length - 1]) ? resultVertices[resultVertices.length - 2] : resultVertices[resultVertices.length - 1];
const nextPoint = route[0];
const existingOverlap = routeOverlap(lastPointOfPrevSegment, middlePoint, middlePoint.clone(), nextPoint);

// possible correction for lines that might share the same segment
if (existingOverlap && !skip) {

const isHorizontal = lastPointOfPrevSegment.y === middlePoint.y;

from.originalDirection = from.direction;

if (isHorizontal) {
const isTargetBelow = to.point.y > middlePoint.y;
const direction = isTargetBelow ? Directions.BOTTOM : Directions.TOP;
from.direction = direction;
} else {
const isTargetRight = to.point.x > middlePoint.x;
const direction = isTargetRight ? Directions.RIGHT : Directions.LEFT;
from.direction = direction;
}

if (new g.Point(targetPointRoute[0]).equals(resultVertices[resultVertices.length - 1])) resultVertices.push(...targetPointRoute.slice(1));
else resultVertices.push(...targetPointRoute);
i--;
continue;
}
}

// Clean up - remove the first and the last point if they are the same as
// the source and target points because they would be redundant
if (!to.point.equals(route[route.length - 1]) && i < verticesPoints.length - 2) {
route.push(to.point);
}

if (sourcePoint.point.equals(resultVertices[0])) {
resultVertices.shift();
}
resultVertices.push(...route);

const lastIndex = resultVertices.length - 1;
// since the `verticesPoints` includes the source and target points, we don't want to change the direction of the last point
if (i < verticesPoints.length - 3) {
// modify the direction of the target for the upcoming segment
verticesPoints[i + 2].direction = to.direction;
}

if (targetPoint.point.equals(resultVertices[lastIndex])) {
resultVertices.pop();
to.direction = OPPOSITE_DIRECTIONS[to.direction];
}

return resultVertices;
Expand Down

0 comments on commit e7ec7e5

Please sign in to comment.