111 lines
2.7 KiB
JavaScript
111 lines
2.7 KiB
JavaScript
/**
|
|
* TypeScript type imports:
|
|
*
|
|
* @import { Graph } from '../graphlib/graph.js';
|
|
*/
|
|
import * as _ from 'lodash-es';
|
|
import * as util from './util.js';
|
|
|
|
export { run, undo };
|
|
|
|
/*
|
|
* Breaks any long edges in the graph into short segments that span 1 layer
|
|
* each. This operation is undoable with the denormalize function.
|
|
*
|
|
* Pre-conditions:
|
|
*
|
|
* 1. The input graph is a DAG.
|
|
* 2. Each node in the graph has a "rank" property.
|
|
*
|
|
* Post-condition:
|
|
*
|
|
* 1. All edges in the graph have a length of 1.
|
|
* 2. Dummy nodes are added where edges have been split into segments.
|
|
* 3. The graph is augmented with a "dummyChains" attribute which contains
|
|
* the first dummy in each chain of dummy nodes produced.
|
|
*/
|
|
function run(g) {
|
|
g.graph().dummyChains = [];
|
|
_.forEach(g.edges(), function (edge) {
|
|
normalizeEdge(g, edge);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {Graph} g
|
|
*/
|
|
function normalizeEdge(g, e) {
|
|
var v = e.v;
|
|
var vRank = g.node(v).rank;
|
|
var w = e.w;
|
|
var wRank = g.node(w).rank;
|
|
var name = e.name;
|
|
var edgeLabel = g.edge(e);
|
|
var labelRank = edgeLabel.labelRank;
|
|
|
|
if (wRank === vRank + 1) return;
|
|
|
|
g.removeEdge(e);
|
|
|
|
/**
|
|
* @typedef {Object} Attrs
|
|
* @property {number} width
|
|
* @property {number} height
|
|
* @property {ReturnType<Graph["node"]>} edgeLabel
|
|
* @property {any} edgeObj
|
|
* @property {ReturnType<Graph["node"]>["rank"]} rank
|
|
* @property {string} [dummy]
|
|
* @property {ReturnType<Graph["node"]>["labelpos"]} [labelpos]
|
|
*/
|
|
|
|
/** @type {Attrs | undefined} */
|
|
var attrs = undefined;
|
|
var dummy, i;
|
|
for (i = 0, ++vRank; vRank < wRank; ++i, ++vRank) {
|
|
edgeLabel.points = [];
|
|
attrs = {
|
|
width: 0,
|
|
height: 0,
|
|
edgeLabel: edgeLabel,
|
|
edgeObj: e,
|
|
rank: vRank,
|
|
};
|
|
dummy = util.addDummyNode(g, 'edge', attrs, '_d');
|
|
if (vRank === labelRank) {
|
|
attrs.width = edgeLabel.width;
|
|
attrs.height = edgeLabel.height;
|
|
attrs.dummy = 'edge-label';
|
|
attrs.labelpos = edgeLabel.labelpos;
|
|
}
|
|
g.setEdge(v, dummy, { weight: edgeLabel.weight }, name);
|
|
if (i === 0) {
|
|
g.graph().dummyChains.push(dummy);
|
|
}
|
|
v = dummy;
|
|
}
|
|
|
|
g.setEdge(v, w, { weight: edgeLabel.weight }, name);
|
|
}
|
|
|
|
function undo(g) {
|
|
_.forEach(g.graph().dummyChains, function (v) {
|
|
var node = g.node(v);
|
|
var origLabel = node.edgeLabel;
|
|
var w;
|
|
g.setEdge(node.edgeObj, origLabel);
|
|
while (node.dummy) {
|
|
w = g.successors(v)[0];
|
|
g.removeNode(v);
|
|
origLabel.points.push({ x: node.x, y: node.y });
|
|
if (node.dummy === 'edge-label') {
|
|
origLabel.x = node.x;
|
|
origLabel.y = node.y;
|
|
origLabel.width = node.width;
|
|
origLabel.height = node.height;
|
|
}
|
|
v = w;
|
|
node = g.node(v);
|
|
}
|
|
});
|
|
}
|