|
|
@@ -4,38 +4,37 @@ 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";
|
|
|
+import CleanupStepError from "../../errors/optimizer/CleanupStepError.js";
|
|
|
|
|
|
/**
|
|
|
* Шаг восстановления исходного графа, удаления лишних конструкций, возникших в процессе работы алгоритма Сигуямы
|
|
|
*/
|
|
|
export default class CleanupStep extends AlgorithmStep<SiguiyamaContext> {
|
|
|
+ public constructor() {
|
|
|
+ super(CleanupStep.name);
|
|
|
+ }
|
|
|
+
|
|
|
public run(context: SiguiyamaContext): void {
|
|
|
const { graph, feedbackSet, edgeSubdivisions } = context;
|
|
|
|
|
|
if(!graph)
|
|
|
- throw new Error("Graph is null or undefined");
|
|
|
+ throw new CleanupStepError("Graph is null or undefined");
|
|
|
if(!feedbackSet)
|
|
|
- throw new Error("Feedback set is null or undefined");
|
|
|
+ throw new CleanupStepError("Feedback set is null or undefined");
|
|
|
if(!edgeSubdivisions)
|
|
|
- throw new Error("Edge Subdivisions information is null or undefined");
|
|
|
+ throw new CleanupStepError("Edge Subdivisions information is null or undefined");
|
|
|
|
|
|
this.restoreEdges(graph, edgeSubdivisions);
|
|
|
- this.applyFeedbackSet(graph, feedbackSet, edgeSubdivisions);
|
|
|
+ this.applyFeedbackSet(feedbackSet);
|
|
|
}
|
|
|
|
|
|
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))
|
|
|
+ if(edge instanceof Edge)
|
|
|
roots.push(edge);
|
|
|
|
|
|
const collectSegments = (edge: Edge<Node>): Edge<Node>[] => {
|
|
|
@@ -46,26 +45,25 @@ export default class CleanupStep extends AlgorithmStep<SiguiyamaContext> {
|
|
|
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 node = segments[i]!.getTo();
|
|
|
+ if(node instanceof DummyNode)
|
|
|
+ waypoints.push({ x: node.getX(), y: node.getY() });
|
|
|
+ // dummyNodeIds.add(joinNode.getId());
|
|
|
}
|
|
|
|
|
|
const firstNode = segments[0]?.getFrom();
|
|
|
- const lastNode = segments[segments.length - 1]?.getTo();
|
|
|
if(firstNode)
|
|
|
root.setFrom(firstNode);
|
|
|
+
|
|
|
+ const lastNode = segments[segments.length - 1]?.getTo();
|
|
|
if(lastNode)
|
|
|
root.setTo(lastNode);
|
|
|
+
|
|
|
root.setWaypoints(waypoints);
|
|
|
|
|
|
for(const segment of segments)
|
|
|
@@ -75,43 +73,18 @@ export default class CleanupStep extends AlgorithmStep<SiguiyamaContext> {
|
|
|
graph.addEdge(root);
|
|
|
}
|
|
|
|
|
|
- for(const node of graph.getNodes()) {
|
|
|
+ for(const node of graph.getNodes())
|
|
|
if(node instanceof DummyNode)
|
|
|
- dummyNodeIds.add(node.getId());
|
|
|
- }
|
|
|
-
|
|
|
- for(const dummyNodeId of dummyNodeIds)
|
|
|
- graph.removeNode(dummyNodeId);
|
|
|
+ graph.removeEdge(node.getId());
|
|
|
}
|
|
|
|
|
|
- private applyFeedbackSet(graph: Graph<Node, Edge<Node>>, feedbackSet: FeedbackSet, edgeSubdivisions: EdgeSubdivisions<Node, Edge<Node>>) : void {
|
|
|
+ private applyFeedbackSet(feedbackSet: FeedbackSet) : 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());
|
|
|
+ for(const edge of feedbackSet) {
|
|
|
+ const from = edge.getFrom(), to = edge.getTo();
|
|
|
+ edge.setFrom(to).setTo(from);
|
|
|
}
|
|
|
}
|
|
|
}
|