|
|
@@ -0,0 +1,117 @@
|
|
|
+import AlgorithmStep from "../AlgorithmStep.js";
|
|
|
+import {EdgeSubdivisions, FeedbackSet, SiguiyamaContext} from "../siguiyama/SiguiyamaContext.js";
|
|
|
+import Graph from "../../graph/Graph.js";
|
|
|
+import Node from "../../graph/node/Node.js";
|
|
|
+import Edge from "../../graph/edge/Edge.js";
|
|
|
+import DummyNode from "../../graph/node/DummyNode.js";
|
|
|
+
|
|
|
+/**
|
|
|
+ * Шаг восстановления исходного графа, удаления лишних конструкций, возникших в процессе работы алгоритма Сигуямы
|
|
|
+ */
|
|
|
+export default class CleanupStep extends AlgorithmStep<SiguiyamaContext> {
|
|
|
+ public run(context: SiguiyamaContext): void {
|
|
|
+ const { graph, feedbackSet, edgeSubdivisions } = context;
|
|
|
+
|
|
|
+ if(!graph)
|
|
|
+ throw new Error("Graph is null or undefined");
|
|
|
+ if(!feedbackSet)
|
|
|
+ throw new Error("Feedback set is null or undefined");
|
|
|
+ if(!edgeSubdivisions)
|
|
|
+ throw new Error("Edge Subdivisions information is null or undefined");
|
|
|
+
|
|
|
+ this.restoreEdges(graph, edgeSubdivisions);
|
|
|
+ this.applyFeedbackSet(graph, feedbackSet, edgeSubdivisions);
|
|
|
+ }
|
|
|
+
|
|
|
+ private restoreEdges(graph: Graph<Node, Edge<Node>>, edgeSubdivisions: EdgeSubdivisions<Node, Edge<Node>>) : void {
|
|
|
+ if(edgeSubdivisions.size === 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ const childEdges = new Set<Edge<Node>>();
|
|
|
+ for(const subdivision of edgeSubdivisions.values()) {
|
|
|
+ childEdges.add(subdivision.left);
|
|
|
+ childEdges.add(subdivision.right);
|
|
|
+ }
|
|
|
+
|
|
|
+ const roots: Edge<Node>[] = [];
|
|
|
+ for(const edge of edgeSubdivisions.keys())
|
|
|
+ if(!childEdges.has(edge))
|
|
|
+ roots.push(edge);
|
|
|
+
|
|
|
+ const collectSegments = (edge: Edge<Node>): Edge<Node>[] => {
|
|
|
+ const subdivision = edgeSubdivisions.get(edge);
|
|
|
+ if(!subdivision)
|
|
|
+ return [edge];
|
|
|
+
|
|
|
+ return [...collectSegments(subdivision.left), ...collectSegments(subdivision.right)];
|
|
|
+ };
|
|
|
+
|
|
|
+ const dummyNodeIds = new Set<string>();
|
|
|
+
|
|
|
+ for(const root of roots) {
|
|
|
+ const segments = collectSegments(root);
|
|
|
+ const waypoints: Array<{ x: number, y: number }> = [];
|
|
|
+
|
|
|
+ for(let i = 0; i < segments.length - 1; i++) {
|
|
|
+ const joinNode = segments[i]!.getTo();
|
|
|
+ if(joinNode instanceof DummyNode) {
|
|
|
+ waypoints.push({ x: joinNode.getX(), y: joinNode.getY() });
|
|
|
+ dummyNodeIds.add(joinNode.getId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const firstNode = segments[0]?.getFrom();
|
|
|
+ const lastNode = segments[segments.length - 1]?.getTo();
|
|
|
+ if(firstNode)
|
|
|
+ root.setFrom(firstNode);
|
|
|
+ if(lastNode)
|
|
|
+ root.setTo(lastNode);
|
|
|
+ root.setWaypoints(waypoints);
|
|
|
+
|
|
|
+ for(const segment of segments)
|
|
|
+ graph.removeEdge(segment.getId());
|
|
|
+
|
|
|
+ if(!graph.getEdge(root.getId()))
|
|
|
+ graph.addEdge(root);
|
|
|
+ }
|
|
|
+
|
|
|
+ for(const node of graph.getNodes()) {
|
|
|
+ if(node instanceof DummyNode)
|
|
|
+ dummyNodeIds.add(node.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ for(const dummyNodeId of dummyNodeIds)
|
|
|
+ graph.removeNode(dummyNodeId);
|
|
|
+ }
|
|
|
+
|
|
|
+ private applyFeedbackSet(graph: Graph<Node, Edge<Node>>, feedbackSet: FeedbackSet, edgeSubdivisions: EdgeSubdivisions<Node, Edge<Node>>) : void {
|
|
|
+ if(feedbackSet.length === 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ const feedbackEdgeIds = new Set(feedbackSet.map((edge) => edge.getId()));
|
|
|
+ const reversedEdgeIds = new Set<string>();
|
|
|
+
|
|
|
+ const hasFeedbackDescendant = (edge: Edge<Node>): boolean => {
|
|
|
+ if(feedbackEdgeIds.has(edge.getId()))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ const subdivision = edgeSubdivisions.get(edge);
|
|
|
+ if(!subdivision)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return hasFeedbackDescendant(subdivision.left) || hasFeedbackDescendant(subdivision.right);
|
|
|
+ };
|
|
|
+
|
|
|
+ for(const edge of graph.getEdges()) {
|
|
|
+ if(reversedEdgeIds.has(edge.getId()))
|
|
|
+ continue;
|
|
|
+ if(!hasFeedbackDescendant(edge))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ const from = edge.getFrom();
|
|
|
+ edge.setFrom(edge.getTo());
|
|
|
+ edge.setTo(from);
|
|
|
+ reversedEdgeIds.add(edge.getId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|