This commit is contained in:
Dylan Vorster
2023-01-24 17:07:59 -07:00
parent ed7988f722
commit 7da7fa4cc9

View File

@@ -1,14 +1,13 @@
import { DiagramModel, PointModel } from '@projectstorm/react-diagrams-core';
import * as dagre from 'dagre';
import * as _ from 'lodash';
import { GraphLabel } from 'dagre';
import * as _ from 'lodash';
import { Point } from '@projectstorm/geometry';
import { link } from 'fs/promises';
export interface DagreEngineOptions {
graph?: GraphLabel;
/**
* Will also layout links
* Will also re-layout links
*/
includeLinks?: boolean;
nodeMargin?: number;
@@ -48,24 +47,9 @@ export class DagreEngine {
}
});
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
g.edges().forEach(function (e) {
console.log("Edge " + e.v + " -> " + e.w + ": " + JSON.stringify(g.edge(e)));
});
// layout the graph
dagre.layout(g);
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
g.edges().forEach(function (e) {
console.log("Edge " + e.v + " -> " + e.w + ": " + JSON.stringify(g.edge(e)));
});
g.nodes().forEach((v) => {
const node = g.node(v);
model.getNode(v).setPosition(node.x - node.width / 2, node.y - node.height / 2);
@@ -86,7 +70,10 @@ export class DagreEngine {
}
}
public refreshLinks(diagram: DiagramModel) {
/**
* TODO cleanup this method into smaller methods
*/
public refreshLinks(diagram: DiagramModel) {
const { nodeMargin } = this.options;
const nodes = diagram.getNodes();
const links = diagram.getLinks();
@@ -98,9 +85,11 @@ export class DagreEngine {
_.forEach(nodes, (node) => {
// find vertical lines. vertical lines go through maximum number of nodes located under each other.
const nodeColumnCenter = node.getX() + node.width / 2;
if (_.every(verticalLines, (vLine) => {
return Math.abs(nodeColumnCenter - vLine) > nodeMargin
})) {
if (
_.every(verticalLines, (vLine) => {
return Math.abs(nodeColumnCenter - vLine) > nodeMargin;
})
) {
verticalLines.push(nodeColumnCenter);
}
});
@@ -115,13 +104,13 @@ export class DagreEngine {
// set occupied chunks
_.forEach(nodes, (node) => {
const nodeColumnCenter = node.getX() + node.width / 2;
const startChunkIndex = Math.floor(node.getY() / (nodeMargin));
const endChunkIndex = Math.floor((node.getY() + node.height) / (nodeMargin));
const startChunkIndex = Math.floor(node.getY() / nodeMargin);
const endChunkIndex = Math.floor((node.getY() + node.height) / nodeMargin);
// find max ChunkRowIndex
if (endChunkIndex > maxChunkRowIndex) maxChunkRowIndex = endChunkIndex;
const nodeColumnIndex = _.findIndex(verticalLines, (vLine) => {
return Math.abs(nodeColumnCenter - vLine) <= nodeMargin;
})
});
_.forEach(_.range(startChunkIndex, endChunkIndex + 1), (chunkIndex) => {
chunks[nodeColumnIndex][chunkIndex] = true;
});
@@ -136,35 +125,35 @@ export class DagreEngine {
const sourceIndex = NodeXColumnIndexDictionary[source.getX()];
const targetIndex = NodeXColumnIndexDictionary[target.getX()];
return sourceIndex > targetIndex ? {
link,
sourceIndex,
sourceY: source.getY() + source.height / 2,
source,
targetIndex,
targetY: target.getY() + source.height / 2,
target
} : {
link,
sourceIndex: targetIndex,
sourceY: target.getY() + target.height / 2,
source: target,
targetIndex: sourceIndex,
targetY: source.getY() + source.height / 2,
target: source
};
return sourceIndex > targetIndex
? {
link,
sourceIndex,
sourceY: source.getY() + source.height / 2,
source,
targetIndex,
targetY: target.getY() + source.height / 2,
target
}
: {
link,
sourceIndex: targetIndex,
sourceY: target.getY() + target.height / 2,
source: target,
targetIndex: sourceIndex,
targetY: source.getY() + source.height / 2,
target: source
};
}
});
const sortedEdges = _.sortBy(edges, (link) => {
return Math.abs(link.targetIndex - link.sourceIndex);
})
});
// set link points
if (this.options.includeLinks) {
_.forEach(sortedEdges, (edge) => {
const link = diagram.getLink(edge.link.getID())
const link = diagram.getLink(edge.link.getID());
// re-draw
if (Math.abs(edge.sourceIndex - edge.targetIndex) > 1) {
// get the length of link in column
@@ -174,11 +163,18 @@ export class DagreEngine {
const targetChunkIndex = Math.floor(edge.targetY / nodeMargin);
// check upper paths
let northCost = 1; let aboveRowIndex = chunkIndex;
let northCost = 1;
let aboveRowIndex = chunkIndex;
for (; aboveRowIndex >= 0; aboveRowIndex--, northCost++) {
if (_.every(columns, (columnIndex) => {
return !(chunks[columnIndex][aboveRowIndex] || chunks[columnIndex + 0.5][aboveRowIndex] || chunks[columnIndex - 0.5][aboveRowIndex]) ;
})) {
if (
_.every(columns, (columnIndex) => {
return !(
chunks[columnIndex][aboveRowIndex] ||
chunks[columnIndex + 0.5][aboveRowIndex] ||
chunks[columnIndex - 0.5][aboveRowIndex]
);
})
) {
break;
}
}
@@ -187,41 +183,69 @@ export class DagreEngine {
let southCost = 0;
let belowRowIndex = chunkIndex;
for (; belowRowIndex <= maxChunkRowIndex; belowRowIndex++, southCost++) {
if (_.every(columns, (columnIndex) => {
return !(chunks[columnIndex][belowRowIndex] || chunks[columnIndex + 0.5][belowRowIndex] || chunks[columnIndex - 0.5][belowRowIndex]) ;
})) {
if (
_.every(columns, (columnIndex) => {
return !(
chunks[columnIndex][belowRowIndex] ||
chunks[columnIndex + 0.5][belowRowIndex] ||
chunks[columnIndex - 0.5][belowRowIndex]
);
})
) {
break;
}
}
// pick the cheapest path
const pathRowIndex = (southCost + (belowRowIndex - targetChunkIndex)) < (northCost + (targetChunkIndex - aboveRowIndex)) ? belowRowIndex + 1 : aboveRowIndex - 1;
const pathRowIndex =
southCost + (belowRowIndex - targetChunkIndex) < northCost + (targetChunkIndex - aboveRowIndex)
? belowRowIndex + 1
: aboveRowIndex - 1;
// Finally update the link points
const points = [link.getFirstPoint()];
points.push(new PointModel({ link: link, position: new Point((verticalLines[columns[0]] + verticalLines[columns[0] + 1]) / 2, (pathRowIndex + 0.5) * nodeMargin) }));
points.push(
new PointModel({
link: link,
position: new Point(
(verticalLines[columns[0]] + verticalLines[columns[0] + 1]) / 2,
(pathRowIndex + 0.5) * nodeMargin
)
})
);
_.forEach(columns, (column) => {
points.push(new PointModel({ link: link, position: new Point(verticalLines[column], (pathRowIndex + 0.5) * nodeMargin) }));
points.push(new PointModel({ link: link, position: new Point((verticalLines[column] + verticalLines[column - 1]) / 2, (pathRowIndex + 0.5) * nodeMargin) }));
points.push(
new PointModel({
link: link,
position: new Point(verticalLines[column], (pathRowIndex + 0.5) * nodeMargin)
})
);
points.push(
new PointModel({
link: link,
position: new Point(
(verticalLines[column] + verticalLines[column - 1]) / 2,
(pathRowIndex + 0.5) * nodeMargin
)
})
);
chunks[column][pathRowIndex] = true;
chunks[column][pathRowIndex + 1] = true;
chunks[column + 0.5][pathRowIndex] = true;
chunks[column + 0.5][pathRowIndex + 1] = true;
})
});
link.setPoints(points.concat(link.getLastPoint()));
} else { // refresh
} else {
// refresh
link.setPoints([link.getFirstPoint(), link.getLastPoint()]);
const columnIndex = (edge.sourceIndex + edge.targetIndex)/2;
const columnIndex = (edge.sourceIndex + edge.targetIndex) / 2;
if (!chunks[columnIndex]) {
chunks[columnIndex] = {};
}
const rowIndex = Math.floor(((edge.sourceY + edge.targetY)/2)/ nodeMargin);
chunks[columnIndex][rowIndex] = true
chunks[columnIndex][rowIndex + 1] = true
const rowIndex = Math.floor((edge.sourceY + edge.targetY) / 2 / nodeMargin);
chunks[columnIndex][rowIndex] = true;
chunks[columnIndex][rowIndex + 1] = true;
}
});
}
}