Skip to content

Commit

Permalink
Update dagre.js
Browse files Browse the repository at this point in the history
  • Loading branch information
lutzroeder committed Jun 16, 2024
1 parent 69957d0 commit 417628a
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 25 deletions.
89 changes: 68 additions & 21 deletions source/dagre.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const dagre = {};
// https://github.com/dagrejs/dagre
// https://github.com/dagrejs/graphlib

dagre.layout = (identifier, nodes, edges, layout, state) => {
dagre.layout = (nodes, edges, layout, state) => {

let uniqueIdCounter = 0;
const uniqueId = (prefix) => {
Expand Down Expand Up @@ -617,6 +617,7 @@ dagre.layout = (identifier, nodes, edges, layout, state) => {
const top = addDummyNode(g, 'border', { width: 0, height: 0 }, '_bt');
const bottom = addDummyNode(g, 'border', { width: 0, height: 0 }, '_bb');
const label = g.node(v).label;
g.hasBorder = true;
g.setParent(top, v);
label.borderTop = top;
g.setParent(bottom, v);
Expand Down Expand Up @@ -679,12 +680,14 @@ dagre.layout = (identifier, nodes, edges, layout, state) => {
}
}
let maxRank = 0;
for (const node of g.nodes.values()) {
const label = node.label;
if (label.borderTop) {
label.minRank = g.node(label.borderTop).label.rank;
label.maxRank = g.node(label.borderBottom).label.rank;
maxRank = Math.max(maxRank, label.maxRank);
if (g.hasBorder) {
for (const node of g.nodes.values()) {
const label = node.label;
if (label.borderTop) {
label.minRank = g.node(label.borderTop).label.rank;
label.maxRank = g.node(label.borderBottom).label.rank;
maxRank = Math.max(maxRank, label.maxRank);
}
}
}
state.maxRank = maxRank;
Expand Down Expand Up @@ -1247,7 +1250,7 @@ dagre.layout = (identifier, nodes, edges, layout, state) => {
// relationship parameter, are included in the graph (without hierarchy).
// 4. Edges incident on movable nodes, selected by the relationship parameter, are added to the output graph.
// 5. The weights for copied edges are aggregated as need, since the output graph is not a multi-graph.
const buildLayerGraph = (g, nodes, rank, relationship) => {
const buildLayerGraph = (g, nodes, rankIndexes, rank, relationship) => {
let root = '';
while (g.hasNode((root = uniqueId('_root')))) {
// continue
Expand All @@ -1259,11 +1262,44 @@ dagre.layout = (identifier, nodes, edges, layout, state) => {
return node ? node.label : undefined;
});
const length = nodes.length;
let i = 0;
while (i < length) {
const node = nodes[i++];
const label = node.label;
if (label.rank === rank || 'minRank' in label && 'maxRank' in label && label.minRank <= rank && rank <= label.maxRank) {
if (g.hasBorder) {
let i = 0;
while (i < length) {
const node = nodes[i++];
const label = node.label;
if (label.rank === rank || 'minRank' in label && 'maxRank' in label && label.minRank <= rank && rank <= label.maxRank) {
const v = node.v;
graph.setNode(v);
const parent = g.parent(v);
graph.setParent(v, parent || root);
// This assumes we have only short edges!
if (relationship) {
for (const e of node.in) {
graph.setEdge(e.v, v, { weight: e.label.weight });
}
} else {
for (const e of node.out) {
graph.setEdge(e.w, v, { weight: e.label.weight });
}
}
if ('minRank' in label) {
graph.setNode(v, {
borderLeft: label.borderLeft[rank],
borderRight: label.borderRight[rank]
});
}
}
}
} else {
// When label.borderTop isn't set, labels don't have minRank and maxRank properties so checking them can be skipped.
// And in that case, iterating nodes can be sped up by presorting nodes and using start indexes of each rank.
let i = rankIndexes.get(rank);
while (i < length) {
const node = nodes[i++];
const label = node.label;
if (label.rank !== rank) {
break;
}
const v = node.v;
graph.setNode(v);
const parent = g.parent(v);
Expand All @@ -1278,12 +1314,6 @@ dagre.layout = (identifier, nodes, edges, layout, state) => {
graph.setEdge(e.w, v, { weight: e.label.weight });
}
}
if ('minRank' in label) {
graph.setNode(v, {
borderLeft: label.borderLeft[rank],
borderRight: label.borderRight[rank]
});
}
}
}
return graph;
Expand All @@ -1301,9 +1331,23 @@ dagre.layout = (identifier, nodes, edges, layout, state) => {
const downLayerGraphs = new Array(rank);
const upLayerGraphs = new Array(rank);
const nodes = Array.from(g.nodes.values());
let rankIndexes = null;
if (!g.hasBorder) {
nodes.sort((a, b) => {
return a.label.rank - b.label.rank;
});
rankIndexes = new Map();
for (let i = 0; i < nodes.length; ++i) {
const node = nodes[i];
const rank = node.label.rank;
if (!rankIndexes.has(rank)) {
rankIndexes.set(rank, i);
}
}
}
for (let i = 0; i < rank; i++) {
downLayerGraphs[i] = buildLayerGraph(g, nodes, i + 1, true);
upLayerGraphs[i] = buildLayerGraph(g, nodes, rank - i - 1, false);
downLayerGraphs[i] = buildLayerGraph(g, nodes, rankIndexes, i + 1, true);
upLayerGraphs[i] = buildLayerGraph(g, nodes, rankIndexes, rank - i - 1, false);
}
let bestCC = Number.POSITIVE_INFINITY;
let best = [];
Expand Down Expand Up @@ -2098,6 +2142,9 @@ dagre.layout = (identifier, nodes, edges, layout, state) => {
edge.y = label.y;
}
}
if (state.log) {
state.log = g.toString();
}
};

dagre.Graph = class {
Expand Down
11 changes: 8 additions & 3 deletions source/grapher.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,18 +195,23 @@ grapher.Graph = class {
labelpos: edge.label.labelpos || 'r'
});
}
const identifier = this.identifier;
const state = { /* log: true */ };
const layout = this._layout;
if (worker) {
const message = await worker.request({ type: 'dagre.layout', identifier, nodes, edges, layout }, 2500, 'This large graph layout might take a very long time to complete.');
const message = await worker.request({ type: 'dagre.layout', nodes, edges, layout, state }, 2500, 'This large graph layout might take a very long time to complete.');
if (message.type === 'cancel') {
return 'graph-layout-cancelled';
}
nodes = message.nodes;
edges = message.edges;
state.log = message.state.log;
} else {
const dagre = await import('./dagre.js');
dagre.layout(identifier, nodes, edges, layout, {});
dagre.layout(nodes, edges, layout, state);
}
if (state.log) {
const fs = await import('fs');
fs.writeFileSync(`dist/test/${this.identifier}.log`, state.log);
}
for (const node of nodes) {
const label = this.node(node.v).label;
Expand Down
2 changes: 1 addition & 1 deletion source/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require().then((self) => {
case 'dagre.layout': {
try {
const dagre = await import('./dagre.js');
dagre.layout(message.identifier, message.nodes, message.edges, message.layout, {});
dagre.layout(message.nodes, message.edges, message.layout, message.state);
self.postMessage(message);
} catch (error) {
self.postMessage({ type: 'error', message: error.message });
Expand Down

0 comments on commit 417628a

Please sign in to comment.