|
|
@@ -1,10 +1,12 @@
|
|
|
import AlgorithmStep from "../AlgorithmStep.js";
|
|
|
-import {SiguiyamaContext} from "../siguiyama/SiguiyamaContext.js";
|
|
|
+import {EdgeSubdivisions, SiguiyamaContext} from "../siguiyama/SiguiyamaContext.js";
|
|
|
import EdgeRoutingStepError from "../../errors/optimizer/EdgeRoutingStepError.js";
|
|
|
import Node from "../../graph/node/Node.js";
|
|
|
import Edge from "../../graph/edge/Edge.js";
|
|
|
import DummyNode from "../../graph/node/DummyNode.js";
|
|
|
import DummyEdge from "../../graph/edge/DummyEdge.js";
|
|
|
+import Graph from "../../graph/Graph.js";
|
|
|
+import Layering from "../../graph/layering/Layering.js";
|
|
|
|
|
|
export default class EdgeRoutingStep extends AlgorithmStep<SiguiyamaContext> {
|
|
|
public constructor() {
|
|
|
@@ -12,60 +14,77 @@ export default class EdgeRoutingStep extends AlgorithmStep<SiguiyamaContext> {
|
|
|
}
|
|
|
|
|
|
public run(context: SiguiyamaContext) : void {
|
|
|
- const { layering, graph } = context;
|
|
|
+ const { layering, graph, edgeSubdivisions } = context;
|
|
|
|
|
|
if(!layering)
|
|
|
throw new EdgeRoutingStepError("Layering is null or undefined");
|
|
|
if(!graph)
|
|
|
throw new EdgeRoutingStepError("Graph is null or undefined");
|
|
|
+ if(!edgeSubdivisions)
|
|
|
+ throw new EdgeRoutingStepError("Edge Subdivisions are null or undefined");
|
|
|
|
|
|
- this.routeEdges(graph.getEdges(), layering, graph);
|
|
|
+ this.routeEdges(graph, layering, edgeSubdivisions);
|
|
|
}
|
|
|
|
|
|
- private routeEdges(edges: Edge<Node>[], layering: NonNullable<SiguiyamaContext["layering"]>, graph: NonNullable<SiguiyamaContext["graph"]>): void {
|
|
|
- const layers = layering.getLayers();
|
|
|
+ private routeEdges(graph: Graph<Node, Edge<Node>>, layering: Layering<Node, Edge<Node>>, edgeSubdivisions: EdgeSubdivisions<Node, Edge<Node>>): void {
|
|
|
+ const edges = graph.getEdges();
|
|
|
|
|
|
for(const edge of edges) {
|
|
|
const from = edge.getFrom();
|
|
|
const to = edge.getTo();
|
|
|
+ const fromNodeIndex = this.getNodeIndex(layering, from);
|
|
|
+ const toNodeIndex = this.getNodeIndex(layering, to);
|
|
|
|
|
|
- const fromLayerIndex = layering.getNodeLayerIndex(from);
|
|
|
- const toLayerIndex = layering.getNodeLayerIndex(to);
|
|
|
-
|
|
|
- if(fromLayerIndex < 0 || toLayerIndex < 0)
|
|
|
+ if(fromNodeIndex === -1 || toNodeIndex === -1 || fromNodeIndex === toNodeIndex)
|
|
|
continue;
|
|
|
|
|
|
- const fromRowIndex = layers[fromLayerIndex]?.getNodes().findIndex(node => node === from) ?? -1;
|
|
|
- const toRowIndex = layers[toLayerIndex]?.getNodes().findIndex(node => node === to) ?? -1;
|
|
|
-
|
|
|
- if(fromRowIndex < 0 || toRowIndex < 0 || fromRowIndex === toRowIndex)
|
|
|
- continue;
|
|
|
-
|
|
|
- const rightLayer = layers[fromLayerIndex - 1];
|
|
|
- if(!rightLayer)
|
|
|
+ const fromLayerIndex = layering.getNodeLayerIndex(from);
|
|
|
+ if(fromLayerIndex === -1)
|
|
|
continue;
|
|
|
|
|
|
- const rightLayerNodes = rightLayer.getNodes();
|
|
|
- if(rightLayerNodes.length === 0)
|
|
|
+ const targetRowIndex = fromNodeIndex > toNodeIndex ? fromNodeIndex - 1 : fromNodeIndex + 1;
|
|
|
+ const neighborRowCenter = this.getRowCenter(layering, targetRowIndex);
|
|
|
+ if(neighborRowCenter === null)
|
|
|
continue;
|
|
|
|
|
|
- const anchorNode = rightLayerNodes[0]!;
|
|
|
+ const fromCenterY = from.getY() + from.getHeight() / 2;
|
|
|
const routedNode = new DummyNode(
|
|
|
- anchorNode.getX() + anchorNode.getWidth() / 2,
|
|
|
- from.getY() + from.getHeight() / 2,
|
|
|
- edge.getId()
|
|
|
+ from.getX() + from.getWidth() / 2,
|
|
|
+ (fromCenterY + neighborRowCenter) / 2
|
|
|
);
|
|
|
|
|
|
- layering.addToLayer(fromLayerIndex - 1, routedNode);
|
|
|
+ layering.addToLayer(fromLayerIndex, routedNode);
|
|
|
graph.addNode(routedNode);
|
|
|
|
|
|
- const firstSegment = new DummyEdge(from, routedNode, []);
|
|
|
- const secondSegment = new DummyEdge(routedNode, to, []);
|
|
|
+ const left = new DummyEdge(from, routedNode);
|
|
|
+ const right = new DummyEdge(routedNode, to);
|
|
|
|
|
|
- graph.addEdge(firstSegment);
|
|
|
- graph.addEdge(secondSegment);
|
|
|
+ graph.addEdge(left);
|
|
|
+ graph.addEdge(right);
|
|
|
graph.removeEdge(edge.getId());
|
|
|
+
|
|
|
+ edgeSubdivisions.set(edge, { left, right });
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ private getRowCenter(layering: Layering<Node, Edge<Node>>, rowIndex: number): number | null {
|
|
|
+ if(rowIndex < 0)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ for(const layer of layering.getLayers()) {
|
|
|
+ const node = layer.getNodes().at(rowIndex);
|
|
|
+ if(node)
|
|
|
+ return node.getY() + node.getHeight() / 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private getNodeIndex(layering: Layering<Node, Edge<Node>>, node: Node) : number {
|
|
|
+ const layerIndex = layering.getNodeLayerIndex(node);
|
|
|
+ if(layerIndex === -1)
|
|
|
+ return -1;
|
|
|
|
|
|
+ return layering.getLayers()[layerIndex]!.getNodes().indexOf(node);
|
|
|
}
|
|
|
}
|