Browse Source

cleanup step

icestormikk 4 weeks ago
parent
commit
ce82cfb83c
2 changed files with 118 additions and 1 deletions
  1. 1 1
      package.json
  2. 117 0
      src/optimizer/steps/CleanupStep.ts

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 	"name": "bpmn-optimizer",
-	"version": "0.0.0",
+	"version": "0.0.1",
 	"description": "Optimizer for BPMN diagrams",
 	"keywords": [
 		"bpmn",

+ 117 - 0
src/optimizer/steps/CleanupStep.ts

@@ -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());
+		}
+	}
+}