|
@@ -1,8 +1,63 @@
|
|
|
-import SugiyamaOptimizerStep, { Context } from "../../abstract/sugiyama-based/SugiyamaOptimizerStep.js";
|
|
|
|
|
|
|
+import SugiyamaOptimizerStep, {Context} from "../../abstract/sugiyama-based/SugiyamaOptimizerStep.js";
|
|
|
|
|
+import XDirectedGraph from "../../../graphs/types/XDirectedGraph.js";
|
|
|
|
|
+import XDirectedEdge from "../../../graphs/types/XDirectedEdge.js";
|
|
|
|
|
+import XVertex from "../../../graphs/types/XVertex.js";
|
|
|
|
|
+import SimpleGraphStore from "../../../graphs/types/SimpleGraphStore.js";
|
|
|
|
|
+
|
|
|
|
|
+type CycleRemoverStepOutput = {
|
|
|
|
|
+ reversedEdges: NonNullable<Context["reversedEdges"]>,
|
|
|
|
|
+ acyclicGraph: NonNullable<Context["acyclicGraph"]>
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
export default class CycleRemoverStep extends SugiyamaOptimizerStep {
|
|
export default class CycleRemoverStep extends SugiyamaOptimizerStep {
|
|
|
public process(context: Context): Context {
|
|
public process(context: Context): Context {
|
|
|
- //TODO
|
|
|
|
|
|
|
+ const { graph } = context;
|
|
|
|
|
+
|
|
|
|
|
+ const { reversedEdges, acyclicGraph } = this.cycleRemover(graph);
|
|
|
|
|
+
|
|
|
|
|
+ context.acyclicGraph = acyclicGraph;
|
|
|
|
|
+ context.reversedEdges = reversedEdges;
|
|
|
|
|
+
|
|
|
return super.process(context);
|
|
return super.process(context);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ protected cycleRemover(graph: XDirectedGraph) : CycleRemoverStepOutput {
|
|
|
|
|
+ return this.dfs(graph);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private dfs(graph: XDirectedGraph) : CycleRemoverStepOutput {
|
|
|
|
|
+ const stack: { incidentEdge?: XDirectedEdge, vertex: XVertex }[] = [];
|
|
|
|
|
+ const visited = new Set<XVertex>();
|
|
|
|
|
+ const reversedEdges = new Set<XDirectedEdge>();
|
|
|
|
|
+
|
|
|
|
|
+ const startVertex = graph.vertices[Math.floor(Math.random() * graph.vertices.length)]!;
|
|
|
|
|
+
|
|
|
|
|
+ stack.push({ vertex: startVertex });
|
|
|
|
|
+
|
|
|
|
|
+ while(stack.length > 0) {
|
|
|
|
|
+ const current = stack.pop();
|
|
|
|
|
+
|
|
|
|
|
+ if(!current)
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ const currVertex = current.vertex;
|
|
|
|
|
+
|
|
|
|
|
+ if(current.incidentEdge && visited.has(currVertex)) {
|
|
|
|
|
+ reversedEdges.add(current.incidentEdge)
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ visited.add(currVertex);
|
|
|
|
|
+
|
|
|
|
|
+ for(const edge of graph.edges.filter((edge) => edge.start.id == currVertex.id))
|
|
|
|
|
+ stack.push({ incidentEdge: edge, vertex: edge.target });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const acyclicGraph = new XDirectedGraph(new SimpleGraphStore(
|
|
|
|
|
+ graph.vertices,
|
|
|
|
|
+ graph.edges.map((edge) => reversedEdges.has(edge) ? new XDirectedEdge(edge.id, edge.target, edge.start) : edge)
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ return { acyclicGraph: acyclicGraph, reversedEdges: Array.from(reversedEdges) }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|