diff --git a/playground/testcases/flowchart.ts b/playground/testcases/flowchart.ts
index 211c031e..51ed4e8e 100644
--- a/playground/testcases/flowchart.ts
+++ b/playground/testcases/flowchart.ts
@@ -465,6 +465,61 @@ C -->|Two| E[Result two]
AA[Loop] --> AG[Subprocess]
AG[Subprocess] --> J[Task 1]
AG[Subprocess] --> B[Initialize]
+`,
+ type: "flowchart",
+ },
+ {
+ name: "Multiple Edges, Relations to a Single Entity",
+ definition: `flowchart LR
+ style Entity1 fill: gold, stroke:#333, stroke-width:4px
+
+ Entity1[Entity 1]
+ Entity2[Entity 2 fa:fa-suitcase]
+ Entity3[Entity 3 fa:fa-suitcase]
+ Entity4[Entity 4]
+ Entity5[Entity 5]
+ Entity6[Entity 6
Entity6]
+ Entity7[Entity 7]
+
+ Entity2 -..->|Relation1| Entity1
+ Entity3 -..->|Relation2| Entity1
+ Entity4 -..->|Relation3| Entity1
+ Entity3 -..->|Relation4| Entity1
+ Entity5 -..->|Relation5| Entity1
+ Entity5 -..->|Relation6| Entity1
+ Entity6 -..->|Relation7| Entity1
+ Entity7 -..->|Relation8| Entity1
+
+ Entity8[Entity 8
Entity8]
+ Entity1[Entity 1]
+ Entity9[Entity 9
Entity9]
+ Entity10[Entity 10]
+ Entity4[Entity 4]
+ Entity11[Entity 11]
+ Entity12[Entity 12]
+ Entity13[Entity 13
Entity13]
+ Entity14[Entity 14]
+ Entity15[Entity 15]
+ Entity16[Entity 16 fa:fa-suitcase]
+ Entity17[Entity 17 fa:fa-suitcase]
+ Entity18[Entity 18 fa:fa-suitcase]
+ Entity19[Entity 19 fa:fa-suitcase]
+
+ Entity1 -..->|Relation9| Entity8
+ Entity1 -..->|Relation10| Entity9
+ Entity1 -..->|Relation11| Entity10
+ Entity1 -..->|Relation12| Entity4
+ Entity1 ===>|fa:fa-link Relation13| Entity11
+ Entity1 -..->|Relation14| Entity12
+ Entity1 -..->|Relation15| Entity13
+ Entity1 -..->|Relation16| Entity14
+ Entity1 ===>|fa:fa-link Relation17| Entity15
+ Entity1 -..->|Relation18| Entity16
+ Entity1 -..->|Relation19| Entity17
+ Entity1 -..->|Relation20| Entity17
+ Entity1 -..->|Relation21| Entity17
+ Entity1 -..->|Relation22| Entity18
+ Entity1 -..->|Relation23| Entity19
`,
type: "flowchart",
},
diff --git a/src/parser/flowchart.ts b/src/parser/flowchart.ts
index 43849900..cae51d7c 100644
--- a/src/parser/flowchart.ts
+++ b/src/parser/flowchart.ts
@@ -141,18 +141,23 @@ const parseVertex = (data: any, containerEl: Element): Vertex | undefined => {
};
};
-const parseEdge = (data: any, containerEl: Element): Edge => {
+const parseEdge = (
+ data: any,
+ edgeIndex: number,
+ containerEl: Element
+): Edge => {
// Find edge element
- const el: SVGPathElement | null = containerEl.querySelector(
- `[id*="L-${data.start}-${data.end}"]`
+ const edge = containerEl.querySelector(
+ `[id*="L-${data.start}-${data.end}-${edgeIndex}"]`
);
- if (!el) {
+
+ if (!edge) {
throw new Error("Edge element not found");
}
// Compute edge position data
- const position = computeElementPosition(el, containerEl);
- const edgePositionData = computeEdgePositions(el, position);
+ const position = computeElementPosition(edge, containerEl);
+ const edgePositionData = computeEdgePositions(edge, position);
// Remove irrelevant properties
data.length = undefined;
@@ -228,13 +233,23 @@ export const parseMermaidFlowChartDiagram = (
Object.keys(vertices).forEach((id) => {
vertices[id] = parseVertex(vertices[id], containerEl);
});
+
+ // Track the count of edges based on the edge id
+ const edgeCountMap = new Map();
const edges = mermaidParser
.getEdges()
.filter((edge: any) => {
// Sometimes mermaid parser returns edges which are not present in the DOM hence this is a safety check to only consider edges present in the DOM, issue - https://github.com/mermaid-js/mermaid/issues/5516
return containerEl.querySelector(`[id*="L-${edge.start}-${edge.end}"]`);
})
- .map((edge: any) => parseEdge(edge, containerEl));
+ .map((data: any) => {
+ const edgeId = `${data.start}-${data.end}`;
+
+ const count = edgeCountMap.get(edgeId) || 0;
+ edgeCountMap.set(edgeId, count + 1);
+
+ return parseEdge(data, count, containerEl);
+ });
const subGraphs = mermaidParser
.getSubGraphs()
diff --git a/src/utils.ts b/src/utils.ts
index a1f038de..4cb1caf4 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -113,14 +113,36 @@ export const computeEdgePositions = (
})
.filter((point, index, array) => {
// Always include the last point
- if (index === array.length - 1) {
+ if (index === 0 || index === array.length - 1) {
return true;
}
- // Include the start point or if the current point if it's not the same as the previous point
- const prevPoint = array[index - 1];
- return (
- index === 0 || (point.x !== prevPoint.x && point.y !== prevPoint.y)
- );
+
+ // Exclude the points which are the same as the previous point
+ if (point.x === array[index - 1].x && point.y === array[index - 1].y) {
+ return false;
+ }
+
+ // The below check is exclusively for second last point
+ if (
+ index === array.length - 2 &&
+ (array[index - 1].x === point.x || array[index - 1].y === point.y)
+ ) {
+ const lastPoint = array[array.length - 1];
+
+ // Get the distance between the last point and second last point using Euclidean distance formula
+ const distance = Math.hypot(
+ lastPoint.x - point.x,
+ lastPoint.y - point.y
+ );
+ // Include the second last point if the distance between the
+ // last point and second last point is > 20.
+ // This is to ensure we have a distance for render the edge.
+ // 20 seems to be a good enough distance to render the edge
+ return distance > 20;
+ }
+
+ // Always include if the current point is not the same as the previous point
+ return point.x !== array[index - 1].x || point.y !== array[index - 1].y;
})
.map((p) => {
// Offset the point by the provided offset
@@ -129,6 +151,7 @@ export const computeEdgePositions = (
y: p.y + offset.y,
};
});
+
// Return the edge positions
return {
startX: startPosition[0] + offset.x,
diff --git a/tests/utils.test.ts b/tests/utils.test.ts
index 4aceb7e0..43c491ae 100644
--- a/tests/utils.test.ts
+++ b/tests/utils.test.ts
@@ -70,5 +70,42 @@ describe("Test Utils", () => {
{ x: 29.383, y: 83.2 },
]);
});
+
+ it("should include the second last point if the distance to the last point is greater than 20 and second last point is straight line", () => {
+ const commands = ["M29.383,38.5", "L29.383,83.2", "L90.383,83.2"];
+ pathElement.setAttribute("d", commands.join(""));
+
+ const result = computeEdgePositions(pathElement);
+
+ expect(result.reflectionPoints).toEqual([
+ { x: 29.383, y: 38.5 },
+ { x: 29.383, y: 83.2 },
+ { x: 90.383, y: 83.2 },
+ ]);
+ });
+
+ it("should exclude the second last point if the distance to the last point is less than 20 and second last point is straight line", () => {
+ const commands = ["M29.383,38.5", "L29.383,83.2", "L33.383,83.2"];
+ pathElement.setAttribute("d", commands.join(""));
+
+ const result = computeEdgePositions(pathElement);
+
+ expect(result.reflectionPoints).toEqual([
+ { x: 29.383, y: 38.5 },
+ { x: 33.383, y: 83.2 },
+ ]);
+ });
+
+ it("should filter out points that are the same as the previous point", () => {
+ const commands = ["M29.383,38.5", "L29.383,38.5", "L29.383,83.2"];
+ pathElement.setAttribute("d", commands.join(""));
+
+ const result = computeEdgePositions(pathElement);
+
+ expect(result.reflectionPoints).toEqual([
+ { x: 29.383, y: 38.5 },
+ { x: 29.383, y: 83.2 },
+ ]);
+ });
});
});