import AlgorithmStep from "../AlgorithmStep.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 { public constructor() { super(EdgeRoutingStep.name); } public run(context: SiguiyamaContext) : void { 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, layering, edgeSubdivisions); } private routeEdges(graph: Graph>, layering: Layering>, edgeSubdivisions: EdgeSubdivisions>): 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); if(fromNodeIndex === -1 || toNodeIndex === -1 || fromNodeIndex === toNodeIndex) continue; const fromLayerIndex = layering.getNodeLayerIndex(from); if(fromLayerIndex === -1) continue; const targetRowIndex = fromNodeIndex > toNodeIndex ? fromNodeIndex - 1 : fromNodeIndex + 1; const neighborRowCenter = this.getRowCenter(layering, targetRowIndex); if(neighborRowCenter === null) continue; const fromCenterY = from.getY() + from.getHeight() / 2; const routedNode = new DummyNode( from.getX() + from.getWidth() / 2, (fromCenterY + neighborRowCenter) / 2 ); layering.addToLayer(fromLayerIndex, routedNode); graph.addNode(routedNode); const left = new DummyEdge(from, routedNode); const right = new DummyEdge(routedNode, to); graph.addEdge(left); graph.addEdge(right); graph.removeEdge(edge.getId()); edgeSubdivisions.set(edge, { left, right }); } } private getRowCenter(layering: Layering>, 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: Node) : number { const layerIndex = layering.getNodeLayerIndex(node); if(layerIndex === -1) return -1; return layering.getLayers()[layerIndex]!.getNodes().indexOf(node); } }