|
@@ -56,11 +56,11 @@ export default class LayerAssignmentStep extends AlgorithmStep<SiguiyamaContext>
|
|
|
const cache = new Map<string, number>();
|
|
const cache = new Map<string, number>();
|
|
|
|
|
|
|
|
const getLongestPath = (node: Node): number => {
|
|
const getLongestPath = (node: Node): number => {
|
|
|
- if (cache.has(node.getId()))
|
|
|
|
|
|
|
+ if(cache.has(node.getId()))
|
|
|
return cache.get(node.getId())!;
|
|
return cache.get(node.getId())!;
|
|
|
|
|
|
|
|
const successors = dag.getNodeOutputs(node);
|
|
const successors = dag.getNodeOutputs(node);
|
|
|
- if (successors.length === 0) {
|
|
|
|
|
|
|
+ if(successors.length === 0) {
|
|
|
cache.set(node.getId(), 0);
|
|
cache.set(node.getId(), 0);
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
@@ -71,7 +71,7 @@ export default class LayerAssignmentStep extends AlgorithmStep<SiguiyamaContext>
|
|
|
return dist;
|
|
return dist;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- for (const node of dag.getNodes()) {
|
|
|
|
|
|
|
+ for(const node of dag.getNodes()) {
|
|
|
const layer = getLongestPath(node);
|
|
const layer = getLongestPath(node);
|
|
|
layering.assign(node, layer);
|
|
layering.assign(node, layer);
|
|
|
}
|
|
}
|
|
@@ -87,7 +87,7 @@ export default class LayerAssignmentStep extends AlgorithmStep<SiguiyamaContext>
|
|
|
* @param edgeSubdivisions Массив для хранения информации о разбиениях рёбер.
|
|
* @param edgeSubdivisions Массив для хранения информации о разбиениях рёбер.
|
|
|
* @private
|
|
* @private
|
|
|
*/
|
|
*/
|
|
|
- private subdivideLongEdges(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, feedbackSet: FeedbackSet, edgeSubdivisions: EdgeSubdivision<Node>[]) : void {
|
|
|
|
|
|
|
+ private subdivideLongEdges(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, feedbackSet: FeedbackSet, edgeSubdivisions: NonNullable<SiguiyamaContext["edgeSubdivisions"]>) : void {
|
|
|
const edges = graph.getEdges();
|
|
const edges = graph.getEdges();
|
|
|
|
|
|
|
|
for(const edge of edges) {
|
|
for(const edge of edges) {
|
|
@@ -96,78 +96,67 @@ export default class LayerAssignmentStep extends AlgorithmStep<SiguiyamaContext>
|
|
|
if(edgeSpan <= 1)
|
|
if(edgeSpan <= 1)
|
|
|
continue;
|
|
continue;
|
|
|
|
|
|
|
|
- this.subdivideLongEdge(graph, layering, edge, feedbackSet, edgeSubdivisions, edgeSpan);
|
|
|
|
|
|
|
+ this.subdivideLongEdge(graph, layering, edge, feedbackSet, edgeSpan, edgeSubdivisions);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Разбивает одно длинное ребро на сегменты с dummy-вершинами.
|
|
|
|
|
- * @param graph Граф, в котором разбивается ребро.
|
|
|
|
|
- * @param layering Текущая структура слоёв.
|
|
|
|
|
- * @param edge Ребро для разбиения.
|
|
|
|
|
- * @param feedbackSet Набор рёбер обратной связи.
|
|
|
|
|
- * @param edgeSubdivisions Массив для хранения информации о разбиениях рёбер.
|
|
|
|
|
- * @param span Число слоёв, которые пересекает ребро.
|
|
|
|
|
- * @private
|
|
|
|
|
- */
|
|
|
|
|
- private subdivideLongEdge(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, edge: Edge<Node>, feedbackSet: FeedbackSet, edgeSubdivisions: EdgeSubdivision<Node>[], span: number) : void {
|
|
|
|
|
- if(span <= 1)
|
|
|
|
|
- return;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ * Разбивает одно длинное ребро на сегменты с dummy-вершинами.
|
|
|
|
|
+ * @param graph Граф, в котором разбивается ребро.
|
|
|
|
|
+ * @param layering Текущая структура слоёв.
|
|
|
|
|
+ * @param edge Ребро для разбиения.
|
|
|
|
|
+ * @param feedbackSet Набор рёбер обратной связи.
|
|
|
|
|
+ * @param span Число слоёв, которые пересекает ребро.
|
|
|
|
|
+ * @param subdivisions Массив для хранения информации о разбиениях рёбер.
|
|
|
|
|
+ * @private
|
|
|
|
|
+ */
|
|
|
|
|
+ private subdivideLongEdge(
|
|
|
|
|
+ graph: Graph<Node, Edge<Node>>,
|
|
|
|
|
+ layering: Layering<Node, Edge<Node>>,
|
|
|
|
|
+ edge: Edge<Node>,
|
|
|
|
|
+ feedbackSet: FeedbackSet,
|
|
|
|
|
+ span: number,
|
|
|
|
|
+ subdivisions: Map<string, EdgeSubdivision<Node>>
|
|
|
|
|
+ ): void {
|
|
|
const edgeId = edge.getId();
|
|
const edgeId = edge.getId();
|
|
|
const edgeFrom = edge.getFrom();
|
|
const edgeFrom = edge.getFrom();
|
|
|
const edgeTo = edge.getTo();
|
|
const edgeTo = edge.getTo();
|
|
|
|
|
|
|
|
- const edgeReversedIndex = feedbackSet.findIndex((e) => e.getId() == edgeId);
|
|
|
|
|
|
|
+ const edgeReversedIndex = feedbackSet.findIndex((e) => e.getId() === edgeId);
|
|
|
const isReversed = edgeReversedIndex !== -1;
|
|
const isReversed = edgeReversedIndex !== -1;
|
|
|
|
|
|
|
|
- if(isReversed)
|
|
|
|
|
- feedbackSet.splice(edgeReversedIndex, 1);
|
|
|
|
|
-
|
|
|
|
|
graph.removeEdge(edgeId);
|
|
graph.removeEdge(edgeId);
|
|
|
|
|
|
|
|
const fromLayer = layering.getLayerOf(edgeFrom)!;
|
|
const fromLayer = layering.getLayerOf(edgeFrom)!;
|
|
|
- const toLayer = layering.getLayerOf(edgeTo)!;
|
|
|
|
|
- const direction = Math.sign(toLayer - fromLayer);
|
|
|
|
|
- let splitTarget = edgeTo;
|
|
|
|
|
- let previousLeftEdge: Edge<Node> | null = null;
|
|
|
|
|
|
|
+ let previousNode = edgeFrom;
|
|
|
|
|
+
|
|
|
|
|
+ const segments: Edge<Node>[] = [];
|
|
|
|
|
+ const dummies: DummyNode[] = [];
|
|
|
|
|
|
|
|
for(let i = 1; i < span; i++) {
|
|
for(let i = 1; i < span; i++) {
|
|
|
- if(previousLeftEdge) {
|
|
|
|
|
- graph.removeEdge(previousLeftEdge.getId());
|
|
|
|
|
- if(isReversed) {
|
|
|
|
|
- const removedIndex = feedbackSet.findIndex((e) => e.getId() === previousLeftEdge!.getId());
|
|
|
|
|
- if(removedIndex !== -1)
|
|
|
|
|
- feedbackSet.splice(removedIndex, 1);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const dummy = new DummyNode(0, 0, edgeId, `dummy-${edgeId}-${i}`);
|
|
|
|
|
|
|
|
- const dummyId = `dummy-${edgeId}-${i}`;
|
|
|
|
|
- const node = new DummyNode(0, 0, edgeId, dummyId);
|
|
|
|
|
|
|
+ graph.addNode(dummy);
|
|
|
|
|
+ layering.assign(dummy, fromLayer - i);
|
|
|
|
|
+ dummies.push(dummy);
|
|
|
|
|
|
|
|
- const dummyLayerIndex = fromLayer + direction * i;
|
|
|
|
|
- graph.addNode(node);
|
|
|
|
|
- layering.assign(node, dummyLayerIndex);
|
|
|
|
|
|
|
+ const segment = new Edge(previousNode, dummy, [], `${edgeId}_segment_${i}`);
|
|
|
|
|
+ graph.addEdge(segment);
|
|
|
|
|
+ segments.push(segment);
|
|
|
|
|
|
|
|
- const leftEdge = new Edge(edgeFrom, node, [], `${edgeId}_segment_${i}_left`);
|
|
|
|
|
- const rightEdge = new Edge(node, splitTarget, [], `${edgeId}_segment_${i}_right`);
|
|
|
|
|
|
|
+ if(isReversed)
|
|
|
|
|
+ feedbackSet.push(segment);
|
|
|
|
|
|
|
|
- graph.addEdge(leftEdge);
|
|
|
|
|
- graph.addEdge(rightEdge);
|
|
|
|
|
|
|
+ previousNode = dummy;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if(isReversed) {
|
|
|
|
|
- feedbackSet.push(leftEdge);
|
|
|
|
|
- feedbackSet.push(rightEdge);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const lastSegment = new Edge(previousNode, edgeTo, [], `${edgeId}_segment_${span}`);
|
|
|
|
|
+ graph.addEdge(lastSegment);
|
|
|
|
|
+ segments.push(lastSegment);
|
|
|
|
|
|
|
|
- edgeSubdivisions.push({
|
|
|
|
|
- original: edge,
|
|
|
|
|
- left: leftEdge,
|
|
|
|
|
- right: rightEdge
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ if(isReversed)
|
|
|
|
|
+ feedbackSet.push(lastSegment);
|
|
|
|
|
|
|
|
- previousLeftEdge = leftEdge;
|
|
|
|
|
- splitTarget = node;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ subdivisions.set(edgeId, { originalEdge: edge, segments, dummies });
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|