Skip to content

Commit ed9159c

Browse files
authored
Merge pull request #4830 from mermaid-js/fix/flowchartElkArrow
Fix: flowchartElk Arrow overlap
2 parents 6219aa5 + f208631 commit ed9159c

File tree

7 files changed

+118
-86
lines changed

7 files changed

+118
-86
lines changed

packages/mermaid/src/dagre-wrapper/edges.js

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { line, curveBasis, select } from 'd3';
55
import { getConfig } from '../config.js';
66
import utils from '../utils.js';
77
import { evaluate } from '../diagrams/common/common.js';
8+
import { getLineFunctionsWithOffset } from '../utils/lineWithOffset.js';
89

910
let edgeLabels = {};
1011
let terminalLabels = {};
@@ -368,20 +369,6 @@ const cutPathAtIntersect = (_points, boundryNode) => {
368369
return points;
369370
};
370371

371-
/**
372-
* Calculate the deltas and angle between two points
373-
* @param {{x: number, y:number}} point1
374-
* @param {{x: number, y:number}} point2
375-
* @returns {{angle: number, deltaX: number, deltaY: number}}
376-
*/
377-
function calculateDeltaAndAngle(point1, point2) {
378-
const [x1, y1] = [point1.x, point1.y];
379-
const [x2, y2] = [point2.x, point2.y];
380-
const deltaX = x2 - x1;
381-
const deltaY = y2 - y1;
382-
return { angle: Math.atan(deltaY / deltaX), deltaX, deltaY };
383-
}
384-
385372
export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph) {
386373
let points = edge.points;
387374
let pointsHasChanged = false;
@@ -456,56 +443,8 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
456443
curve = edge.curve;
457444
}
458445

459-
// We need to draw the lines a bit shorter to avoid drawing
460-
// under any transparent markers.
461-
// The offsets are calculated from the markers' dimensions.
462-
const markerOffsets = {
463-
aggregation: 18,
464-
extension: 18,
465-
composition: 18,
466-
dependency: 6,
467-
lollipop: 13.5,
468-
arrow_point: 5.3,
469-
};
470-
471-
const lineFunction = line()
472-
.x(function (d, i, data) {
473-
let offset = 0;
474-
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
475-
// Handle first point
476-
// Calculate the angle and delta between the first two points
477-
const { angle, deltaX } = calculateDeltaAndAngle(data[0], data[1]);
478-
// Calculate the offset based on the angle and the marker's dimensions
479-
offset = markerOffsets[edge.arrowTypeStart] * Math.cos(angle) * (deltaX >= 0 ? 1 : -1) || 0;
480-
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
481-
// Handle last point
482-
// Calculate the angle and delta between the last two points
483-
const { angle, deltaX } = calculateDeltaAndAngle(
484-
data[data.length - 1],
485-
data[data.length - 2]
486-
);
487-
offset = markerOffsets[edge.arrowTypeEnd] * Math.cos(angle) * (deltaX >= 0 ? 1 : -1) || 0;
488-
}
489-
return d.x + offset;
490-
})
491-
.y(function (d, i, data) {
492-
// Same handling as X above
493-
let offset = 0;
494-
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
495-
const { angle, deltaY } = calculateDeltaAndAngle(data[0], data[1]);
496-
offset =
497-
markerOffsets[edge.arrowTypeStart] * Math.abs(Math.sin(angle)) * (deltaY >= 0 ? 1 : -1);
498-
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
499-
const { angle, deltaY } = calculateDeltaAndAngle(
500-
data[data.length - 1],
501-
data[data.length - 2]
502-
);
503-
offset =
504-
markerOffsets[edge.arrowTypeEnd] * Math.abs(Math.sin(angle)) * (deltaY >= 0 ? 1 : -1);
505-
}
506-
return d.y + offset;
507-
})
508-
.curve(curve);
446+
const { x, y } = getLineFunctionsWithOffset(edge);
447+
const lineFunction = line().x(x).y(y).curve(curve);
509448

510449
// Construct stroke classes based on properties
511450
let strokeClasses;

packages/mermaid/src/dagre-wrapper/markers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ const point = (elem, type) => {
176176
.attr('id', type + '-pointStart')
177177
.attr('class', 'marker ' + type)
178178
.attr('viewBox', '0 0 10 10')
179-
.attr('refX', 0)
179+
.attr('refX', 4.5)
180180
.attr('refY', 5)
181181
.attr('markerUnits', 'userSpaceOnUse')
182182
.attr('markerWidth', 12)

packages/mermaid/src/diagrams/class/classRenderer-v2.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import utils from '../../utils.js';
88
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
99
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
1010
import common from '../common/common.js';
11-
import type { ClassRelation, ClassNote, ClassMap, EdgeData, NamespaceMap } from './classTypes.js';
11+
import type { ClassRelation, ClassNote, ClassMap, NamespaceMap } from './classTypes.js';
12+
import type { EdgeData } from '../../types.js';
1213

1314
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());
1415

packages/mermaid/src/diagrams/class/classTypes.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -137,24 +137,6 @@ export interface ClassNote {
137137
text: string;
138138
}
139139

140-
export interface EdgeData {
141-
arrowheadStyle?: string;
142-
labelpos?: string;
143-
labelType?: string;
144-
label?: string;
145-
classes: string;
146-
pattern: string;
147-
id: string;
148-
arrowhead: string;
149-
startLabelRight: string;
150-
endLabelLeft: string;
151-
arrowTypeStart: string;
152-
arrowTypeEnd: string;
153-
style: string;
154-
labelStyle: string;
155-
curve: any;
156-
}
157-
158140
export type ClassRelation = {
159141
id1: string;
160142
id2: string;

packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { setupGraphViewbox } from '../../../setupGraphViewbox.js';
1010
import common from '../../common/common.js';
1111
import { interpolateToCurve, getStylesFromArray } from '../../../utils.js';
1212
import ELK from 'elkjs/lib/elk.bundled.js';
13+
import { getLineFunctionsWithOffset } from '../../../utils/lineWithOffset.js';
14+
1315
const elk = new ELK();
1416

1517
let portPos = {};
@@ -704,8 +706,8 @@ const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
704706
[dest.x + offset.x, dest.y + offset.y],
705707
];
706708

707-
// const curve = line().curve(curveBasis);
708-
const curve = line().curve(curveLinear);
709+
const { x, y } = getLineFunctionsWithOffset(edge.edgeData);
710+
const curve = line().x(x).y(y).curve(curveLinear);
709711
const edgePath = edgesEl
710712
.insert('path')
711713
.attr('d', curve(points))

packages/mermaid/src/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,21 @@ export interface TextDimensions {
1414
height: number;
1515
lineHeight?: number;
1616
}
17+
18+
export interface EdgeData {
19+
arrowheadStyle?: string;
20+
labelpos?: string;
21+
labelType?: string;
22+
label?: string;
23+
classes: string;
24+
pattern: string;
25+
id: string;
26+
arrowhead: string;
27+
startLabelRight: string;
28+
endLabelLeft: string;
29+
arrowTypeStart: string;
30+
arrowTypeEnd: string;
31+
style: string;
32+
labelStyle: string;
33+
curve: any;
34+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import type { EdgeData, Point } from '../types.js';
2+
3+
// We need to draw the lines a bit shorter to avoid drawing
4+
// under any transparent markers.
5+
// The offsets are calculated from the markers' dimensions.
6+
const markerOffsets = {
7+
aggregation: 18,
8+
extension: 18,
9+
composition: 18,
10+
dependency: 6,
11+
lollipop: 13.5,
12+
arrow_point: 5.3,
13+
} as const;
14+
15+
/**
16+
* Calculate the deltas and angle between two points
17+
* @param point1 - First point
18+
* @param point2 - Second point
19+
* @returns The angle, deltaX and deltaY
20+
*/
21+
function calculateDeltaAndAngle(
22+
point1: Point | [number, number],
23+
point2: Point | [number, number]
24+
): { angle: number; deltaX: number; deltaY: number } {
25+
point1 = pointTransformer(point1);
26+
point2 = pointTransformer(point2);
27+
const [x1, y1] = [point1.x, point1.y];
28+
const [x2, y2] = [point2.x, point2.y];
29+
const deltaX = x2 - x1;
30+
const deltaY = y2 - y1;
31+
return { angle: Math.atan(deltaY / deltaX), deltaX, deltaY };
32+
}
33+
34+
const pointTransformer = (data: Point | [number, number]) => {
35+
if (Array.isArray(data)) {
36+
return { x: data[0], y: data[1] };
37+
}
38+
return data;
39+
};
40+
41+
export const getLineFunctionsWithOffset = (edge: EdgeData) => {
42+
return {
43+
x: function (d: Point | [number, number], i: number, data: (Point | [number, number])[]) {
44+
let offset = 0;
45+
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
46+
// Handle first point
47+
// Calculate the angle and delta between the first two points
48+
const { angle, deltaX } = calculateDeltaAndAngle(data[0], data[1]);
49+
// Calculate the offset based on the angle and the marker's dimensions
50+
offset =
51+
markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets] *
52+
Math.cos(angle) *
53+
(deltaX >= 0 ? 1 : -1);
54+
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
55+
// Handle last point
56+
// Calculate the angle and delta between the last two points
57+
const { angle, deltaX } = calculateDeltaAndAngle(
58+
data[data.length - 1],
59+
data[data.length - 2]
60+
);
61+
offset =
62+
markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets] *
63+
Math.cos(angle) *
64+
(deltaX >= 0 ? 1 : -1);
65+
}
66+
return pointTransformer(d).x + offset;
67+
},
68+
y: function (d: Point | [number, number], i: number, data: (Point | [number, number])[]) {
69+
// Same handling as X above
70+
let offset = 0;
71+
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
72+
const { angle, deltaY } = calculateDeltaAndAngle(data[0], data[1]);
73+
offset =
74+
markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets] *
75+
Math.abs(Math.sin(angle)) *
76+
(deltaY >= 0 ? 1 : -1);
77+
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
78+
const { angle, deltaY } = calculateDeltaAndAngle(
79+
data[data.length - 1],
80+
data[data.length - 2]
81+
);
82+
offset =
83+
markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets] *
84+
Math.abs(Math.sin(angle)) *
85+
(deltaY >= 0 ? 1 : -1);
86+
}
87+
return pointTransformer(d).y + offset;
88+
},
89+
};
90+
};

0 commit comments

Comments
 (0)