Pavel Zhigalov 1 month ago
parent
commit
a70f39c4ee

+ 4 - 2
package.json

@@ -12,8 +12,10 @@
 	"license": "ISC",
 	"author": "icestormikk",
 	"type": "module",
-	"main": "index.js",
-	"scripts": {
+	"main": "dist/index.js",
+    "types": "dist/index.d.ts",
+    "files": ["dist"],
+    "scripts": {
 		"build": "tsc",
 		"start": "node dist/index.js",
 		"dev": "npm run build && node dist/index.js",

+ 7 - 15
src/index.ts

@@ -3,16 +3,15 @@ import BPMNError from "./v1/errors/BPMNError.js";
 import Edge from "./v1/graph/edge/Edge.js";
 import Graph from "./v1/graph/Graph.js";
 import Node from "./v1/graph/node/Node.js";
-import Deserializer from "./v1/io/deserialize/Deserializer.js";
-import DefaultJsonDeserializer from "./v1/io/deserialize/json/DefaultJsonDeserializer.js";
-import Serializer from "./v1/io/serialize/Serializer.js";
-import DefaultJsonSerializer from "./v1/io/serialize/json/DefaultJsonSerializer.js";
 import CoordinateAssignmentStep from "./v1/optimizer/steps/CoordinateAssignmentStep.js";
 import CycleRemoveStep from "./v1/optimizer/steps/CycleRemoveStep.js";
 import LayerAssignmentStep from "./v1/optimizer/steps/LayerAssignmentStep.js";
 import NodeOrderingStep from "./v1/optimizer/steps/NodeOrderingStep.js";
 import SiguiyamaAlgorithm from "./v1/optimizer/siguiyama/SiguiyamaAlgorithm.js";
 import { SiguiyamaContext } from "./v1/optimizer/siguiyama/SiguiyamaContext.js";
+import DzwfJsonDeserializer from "./v1/io/deserialize/json/DzwfJsonDeserializer.js";
+import DzwfJsonSerializer from "./v1/io/serialize/json/DzwfJsonSerializer.js";
+import EdgeRoutingStep from "./v1/optimizer/steps/EdgeRoutingStep.js";
 
 export class BPMNOptimizer {
 	private _optimizationAlgorithm?: SiguiyamaAlgorithm;
@@ -29,23 +28,16 @@ export class BPMNOptimizer {
 	}
 }
 
-function getDeserializer() : Deserializer<string, Graph<Node, Edge<Node>>> {
-	return new DefaultJsonDeserializer();
-}
-
-function getSerializer() : Serializer<Graph<Node, Edge<Node>>, string> {
-	return new DefaultJsonSerializer();
-}
-
 const algorithm = new SiguiyamaAlgorithm()
 	.addStep(new CycleRemoveStep())
 	.addStep(new LayerAssignmentStep())
 	.addStep(new NodeOrderingStep())
 	.addStep(new CoordinateAssignmentStep(60, 100))
+	.addStep(new EdgeRoutingStep());
 const optimizer = new BPMNOptimizer().setOptimizationAlgorithm(algorithm);
 
-const data = readFileSync("./data/graph.json").toString();
-const graph = getDeserializer().deserialize(data);
+const data = readFileSync("./data/bpmn-graph.json").toString();
+const { dzwfData, graph } = new DzwfJsonDeserializer().deserialize(data);
 const result = optimizer.run(graph);
 
-writeFileSync(`./data/graph-${Date.now()}.json`, getSerializer().serialize(result.graph));
+writeFileSync(`./data/document.dzwf`, new DzwfJsonSerializer().serialize({ dzwfData, graph: result.graph }));

+ 7 - 0
src/v1/errors/SerializerError.ts

@@ -0,0 +1,7 @@
+import BPMNError from "./BPMNError.js";
+
+export default class SerializerError extends BPMNError {
+	public constructor(message: string) {
+		super(message);
+	}
+}

+ 4 - 0
src/v1/graph/Graph.ts

@@ -62,6 +62,10 @@ export default class Graph<TNode extends Node, TEdge extends Edge<TNode>> {
 		return [...this._nodeMap.values()];
 	}
 
+	public getNode(id: string) : Node | null {
+		return this._nodeMap.get(id) || null;
+	}
+
 	public addNode(node: TNode) : this {
 		const id = node.getId();
 

+ 21 - 3
src/v1/io/deserialize/json/DzwfJsonDeserializer.ts

@@ -2,9 +2,27 @@ import {JsonDeserializer} from "./JsonDeserializer.js";
 import Graph from "../../../graph/Graph.js";
 import Node from "../../../graph/node/Node.js";
 import Edge from "../../../graph/edge/Edge.js";
+import {DZWFData} from "../../dzwf/DZWFData.js";
+import DeserializerError from "../../../errors/DeserializerError.js";
 
-export default class DzwfJsonDeserializer implements JsonDeserializer<Graph<Node, Edge<Node>>> {
-	deserialize(input: string): Graph<Node, Edge<Node>> {
-		throw new Error("Method not implemented.");
+export default class DzwfJsonDeserializer implements JsonDeserializer<{ dzwfData: DZWFData, graph: Graph<Node, Edge<Node>> }> {
+	deserialize(input: string): { dzwfData: DZWFData, graph: Graph<Node, Edge<Node>> } {
+		const json = JSON.parse(input) as DZWFData;
+
+		const nodes = json.elements.map(({ type, diagram, id }) => {
+			return new Node(diagram.position.x, diagram.position.y, 100, 100, type, id);
+		})
+
+		const edges = json.links.map(({ sourceId, targetId, id }) => {
+			const source = nodes.find((n) => n.getId() === sourceId);
+			const target = nodes.find((n) => n.getId() === targetId);
+
+			if(!source || !target)
+				throw new DeserializerError(`Can't find source or target node for edge ${id}`)
+
+			return new Edge(source, target);
+		})
+
+		return { dzwfData: json, graph: new Graph(nodes, edges) };
 	}
 }

+ 0 - 22
src/v1/io/dzwf/DZWFData.ts

@@ -1,22 +0,0 @@
-export type DZWFData = { 
-	elements: { 
-		diagram: { 
-			position: { x: number, y: number }
-		},
-		outs: unknown[],
-		ins: unknown[],
-		variables: unknown[],
-		stages: unknown[],
-		name: string,
-		type: string,
-		id: string
-	}[], 
-	links: {
-		sourceId: string,
-		targetId: string,
-		id: string,
-		diagram?: {
-			vertices: { x: number, y: number }[]
-		}
-	}[] 
-};

+ 27 - 0
src/v1/io/dzwf/DzwfData.ts

@@ -0,0 +1,27 @@
+export type DzwfElement = {
+	diagram: {
+		position: { x: number, y: number },
+		size: { width: number, height: number }
+	},
+	outs: unknown[],
+	ins: unknown[],
+	variables: unknown[],
+	stages: unknown[],
+	name: string,
+	type: string,
+	id: string
+}
+
+export type DzwfLink = {
+	sourceId: string,
+	targetId: string,
+	id: string,
+	diagram?: {
+		vertices: { x: number, y: number }[]
+	}
+}
+
+export type DzwfData = object & {
+	elements: DzwfElement[],
+	links: DzwfLink[]
+};

+ 16 - 3
src/v1/io/serialize/json/DzwfJsonSerializer.ts

@@ -2,9 +2,22 @@ import {JsonSerializer} from "./JsonSerializer.js";
 import Node from "../../../graph/node/Node.js";
 import Edge from "../../../graph/edge/Edge.js";
 import Graph from "../../../graph/Graph.js";
+import {DZWFData} from "../../dzwf/DZWFData.js";
+import SerializerError from "../../../errors/SerializerError.js";
 
-export default class DzwfJsonSerializer implements JsonSerializer<Graph<Node, Edge<Node>>> {
-	serialize(data: Graph<Node, Edge<Node>>): string {
-		throw new Error("Method not implemented.");
+export default class DzwfJsonSerializer implements JsonSerializer<{ dzwfData: DZWFData, graph: Graph<Node, Edge<Node>> }> {
+	serialize(data: { dzwfData: DZWFData, graph: Graph<Node, Edge<Node>> }): string {
+		const { dzwfData, graph } = data;
+
+		for(const element of dzwfData.elements) {
+			const node = graph.getNode(element.id);
+			if(!node)
+				throw new SerializerError(`Can't find node with id ${element.id} in result graph`);
+
+			element.diagram.position.x = node.getX();
+			element.diagram.position.y = node.getY();
+		}
+
+		return JSON.stringify(dzwfData);
 	}
 }

+ 71 - 0
src/v1/optimizer/steps/EdgeRoutingStep.ts

@@ -0,0 +1,71 @@
+import AlgorithmStep from "../AlgorithmStep.js";
+import {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";
+
+export default class EdgeRoutingStep extends AlgorithmStep<SiguiyamaContext> {
+	public constructor() {
+		super(EdgeRoutingStep.name);
+	}
+
+	public run(context: SiguiyamaContext) : void {
+		const { layering, graph } = context;
+
+		if(!layering)
+			throw new EdgeRoutingStepError("Layering is null or undefined");
+		if(!graph)
+			throw new EdgeRoutingStepError("Graph is null or undefined");
+
+		this.routeEdges(graph.getEdges(), layering, graph);
+	}
+
+	private routeEdges(edges: Edge<Node>[], layering: NonNullable<SiguiyamaContext["layering"]>, graph: NonNullable<SiguiyamaContext["graph"]>): void {
+		const layers = layering.getLayers();
+
+		for(const edge of edges) {
+			const from = edge.getFrom();
+			const to = edge.getTo();
+
+			const fromLayerIndex = layering.getNodeLayerIndex(from);
+			const toLayerIndex = layering.getNodeLayerIndex(to);
+
+			if(fromLayerIndex < 0 || toLayerIndex < 0)
+				continue;
+
+			const fromRowIndex = layers[fromLayerIndex]?.getNodes().findIndex(node => node === from) ?? -1;
+			const toRowIndex = layers[toLayerIndex]?.getNodes().findIndex(node => node === to) ?? -1;
+
+			if(fromRowIndex < 0 || toRowIndex < 0 || fromRowIndex === toRowIndex)
+				continue;
+
+			const rightLayer = layers[fromLayerIndex - 1];
+			if(!rightLayer)
+				continue;
+
+			const rightLayerNodes = rightLayer.getNodes();
+			if(rightLayerNodes.length === 0)
+				continue;
+
+			const anchorNode = rightLayerNodes[0]!;
+			const routedNode = new DummyNode(
+				anchorNode.getX() + anchorNode.getWidth() / 2,
+				from.getY() + from.getHeight() / 2,
+				edge.getId()
+			);
+
+			layering.addToLayer(fromLayerIndex - 1, routedNode);
+			graph.addNode(routedNode);
+
+			const firstSegment = new DummyEdge(from, routedNode, []);
+			const secondSegment = new DummyEdge(routedNode, to, []);
+
+			graph.addEdge(firstSegment);
+			graph.addEdge(secondSegment);
+			graph.removeEdge(edge.getId());
+		}
+
+	}
+}

+ 1 - 1
tsconfig.json

@@ -12,10 +12,10 @@
 		"sourceMap": true,
 		"declaration": true,
 		"declarationMap": true,
+        "declarationDir": "dist",
 		"noUncheckedIndexedAccess": true,
 		"exactOptionalPropertyTypes": true,
 		"strict": true,
-		"jsx": "react-jsx",
 		"verbatimModuleSyntax": false,
 		"isolatedModules": true,
 		"noUncheckedSideEffectImports": true,