mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-02 07:06:43 +02:00
Merge pull request #4830 from mermaid-js/fix/flowchartElkArrow
Fix: flowchartElk Arrow overlap
This commit is contained in:
@@ -5,6 +5,7 @@ import { line, curveBasis, select } from 'd3';
|
|||||||
import { getConfig } from '../config.js';
|
import { getConfig } from '../config.js';
|
||||||
import utils from '../utils.js';
|
import utils from '../utils.js';
|
||||||
import { evaluate } from '../diagrams/common/common.js';
|
import { evaluate } from '../diagrams/common/common.js';
|
||||||
|
import { getLineFunctionsWithOffset } from '../utils/lineWithOffset.js';
|
||||||
|
|
||||||
let edgeLabels = {};
|
let edgeLabels = {};
|
||||||
let terminalLabels = {};
|
let terminalLabels = {};
|
||||||
@@ -368,20 +369,6 @@ const cutPathAtIntersect = (_points, boundryNode) => {
|
|||||||
return points;
|
return points;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the deltas and angle between two points
|
|
||||||
* @param {{x: number, y:number}} point1
|
|
||||||
* @param {{x: number, y:number}} point2
|
|
||||||
* @returns {{angle: number, deltaX: number, deltaY: number}}
|
|
||||||
*/
|
|
||||||
function calculateDeltaAndAngle(point1, point2) {
|
|
||||||
const [x1, y1] = [point1.x, point1.y];
|
|
||||||
const [x2, y2] = [point2.x, point2.y];
|
|
||||||
const deltaX = x2 - x1;
|
|
||||||
const deltaY = y2 - y1;
|
|
||||||
return { angle: Math.atan(deltaY / deltaX), deltaX, deltaY };
|
|
||||||
}
|
|
||||||
|
|
||||||
export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph) {
|
export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph) {
|
||||||
let points = edge.points;
|
let points = edge.points;
|
||||||
let pointsHasChanged = false;
|
let pointsHasChanged = false;
|
||||||
@@ -456,56 +443,8 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
|
|||||||
curve = edge.curve;
|
curve = edge.curve;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to draw the lines a bit shorter to avoid drawing
|
const { x, y } = getLineFunctionsWithOffset(edge);
|
||||||
// under any transparent markers.
|
const lineFunction = line().x(x).y(y).curve(curve);
|
||||||
// The offsets are calculated from the markers' dimensions.
|
|
||||||
const markerOffsets = {
|
|
||||||
aggregation: 18,
|
|
||||||
extension: 18,
|
|
||||||
composition: 18,
|
|
||||||
dependency: 6,
|
|
||||||
lollipop: 13.5,
|
|
||||||
arrow_point: 5.3,
|
|
||||||
};
|
|
||||||
|
|
||||||
const lineFunction = line()
|
|
||||||
.x(function (d, i, data) {
|
|
||||||
let offset = 0;
|
|
||||||
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
|
|
||||||
// Handle first point
|
|
||||||
// Calculate the angle and delta between the first two points
|
|
||||||
const { angle, deltaX } = calculateDeltaAndAngle(data[0], data[1]);
|
|
||||||
// Calculate the offset based on the angle and the marker's dimensions
|
|
||||||
offset = markerOffsets[edge.arrowTypeStart] * Math.cos(angle) * (deltaX >= 0 ? 1 : -1) || 0;
|
|
||||||
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
|
|
||||||
// Handle last point
|
|
||||||
// Calculate the angle and delta between the last two points
|
|
||||||
const { angle, deltaX } = calculateDeltaAndAngle(
|
|
||||||
data[data.length - 1],
|
|
||||||
data[data.length - 2]
|
|
||||||
);
|
|
||||||
offset = markerOffsets[edge.arrowTypeEnd] * Math.cos(angle) * (deltaX >= 0 ? 1 : -1) || 0;
|
|
||||||
}
|
|
||||||
return d.x + offset;
|
|
||||||
})
|
|
||||||
.y(function (d, i, data) {
|
|
||||||
// Same handling as X above
|
|
||||||
let offset = 0;
|
|
||||||
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
|
|
||||||
const { angle, deltaY } = calculateDeltaAndAngle(data[0], data[1]);
|
|
||||||
offset =
|
|
||||||
markerOffsets[edge.arrowTypeStart] * Math.abs(Math.sin(angle)) * (deltaY >= 0 ? 1 : -1);
|
|
||||||
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
|
|
||||||
const { angle, deltaY } = calculateDeltaAndAngle(
|
|
||||||
data[data.length - 1],
|
|
||||||
data[data.length - 2]
|
|
||||||
);
|
|
||||||
offset =
|
|
||||||
markerOffsets[edge.arrowTypeEnd] * Math.abs(Math.sin(angle)) * (deltaY >= 0 ? 1 : -1);
|
|
||||||
}
|
|
||||||
return d.y + offset;
|
|
||||||
})
|
|
||||||
.curve(curve);
|
|
||||||
|
|
||||||
// Construct stroke classes based on properties
|
// Construct stroke classes based on properties
|
||||||
let strokeClasses;
|
let strokeClasses;
|
||||||
|
@@ -176,7 +176,7 @@ const point = (elem, type) => {
|
|||||||
.attr('id', type + '-pointStart')
|
.attr('id', type + '-pointStart')
|
||||||
.attr('class', 'marker ' + type)
|
.attr('class', 'marker ' + type)
|
||||||
.attr('viewBox', '0 0 10 10')
|
.attr('viewBox', '0 0 10 10')
|
||||||
.attr('refX', 0)
|
.attr('refX', 4.5)
|
||||||
.attr('refY', 5)
|
.attr('refY', 5)
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
.attr('markerUnits', 'userSpaceOnUse')
|
||||||
.attr('markerWidth', 12)
|
.attr('markerWidth', 12)
|
||||||
|
@@ -8,7 +8,8 @@ import utils from '../../utils.js';
|
|||||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||||
import common from '../common/common.js';
|
import common from '../common/common.js';
|
||||||
import type { ClassRelation, ClassNote, ClassMap, EdgeData, NamespaceMap } from './classTypes.js';
|
import type { ClassRelation, ClassNote, ClassMap, NamespaceMap } from './classTypes.js';
|
||||||
|
import type { EdgeData } from '../../types.js';
|
||||||
|
|
||||||
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());
|
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());
|
||||||
|
|
||||||
|
@@ -137,24 +137,6 @@ export interface ClassNote {
|
|||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EdgeData {
|
|
||||||
arrowheadStyle?: string;
|
|
||||||
labelpos?: string;
|
|
||||||
labelType?: string;
|
|
||||||
label?: string;
|
|
||||||
classes: string;
|
|
||||||
pattern: string;
|
|
||||||
id: string;
|
|
||||||
arrowhead: string;
|
|
||||||
startLabelRight: string;
|
|
||||||
endLabelLeft: string;
|
|
||||||
arrowTypeStart: string;
|
|
||||||
arrowTypeEnd: string;
|
|
||||||
style: string;
|
|
||||||
labelStyle: string;
|
|
||||||
curve: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ClassRelation = {
|
export type ClassRelation = {
|
||||||
id1: string;
|
id1: string;
|
||||||
id2: string;
|
id2: string;
|
||||||
|
@@ -10,6 +10,8 @@ import { setupGraphViewbox } from '../../../setupGraphViewbox.js';
|
|||||||
import common from '../../common/common.js';
|
import common from '../../common/common.js';
|
||||||
import { interpolateToCurve, getStylesFromArray } from '../../../utils.js';
|
import { interpolateToCurve, getStylesFromArray } from '../../../utils.js';
|
||||||
import ELK from 'elkjs/lib/elk.bundled.js';
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
||||||
|
import { getLineFunctionsWithOffset } from '../../../utils/lineWithOffset.js';
|
||||||
|
|
||||||
const elk = new ELK();
|
const elk = new ELK();
|
||||||
|
|
||||||
let portPos = {};
|
let portPos = {};
|
||||||
@@ -704,8 +706,8 @@ const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
|
|||||||
[dest.x + offset.x, dest.y + offset.y],
|
[dest.x + offset.x, dest.y + offset.y],
|
||||||
];
|
];
|
||||||
|
|
||||||
// const curve = line().curve(curveBasis);
|
const { x, y } = getLineFunctionsWithOffset(edge.edgeData);
|
||||||
const curve = line().curve(curveLinear);
|
const curve = line().x(x).y(y).curve(curveLinear);
|
||||||
const edgePath = edgesEl
|
const edgePath = edgesEl
|
||||||
.insert('path')
|
.insert('path')
|
||||||
.attr('d', curve(points))
|
.attr('d', curve(points))
|
||||||
|
@@ -14,3 +14,21 @@ export interface TextDimensions {
|
|||||||
height: number;
|
height: number;
|
||||||
lineHeight?: number;
|
lineHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EdgeData {
|
||||||
|
arrowheadStyle?: string;
|
||||||
|
labelpos?: string;
|
||||||
|
labelType?: string;
|
||||||
|
label?: string;
|
||||||
|
classes: string;
|
||||||
|
pattern: string;
|
||||||
|
id: string;
|
||||||
|
arrowhead: string;
|
||||||
|
startLabelRight: string;
|
||||||
|
endLabelLeft: string;
|
||||||
|
arrowTypeStart: string;
|
||||||
|
arrowTypeEnd: string;
|
||||||
|
style: string;
|
||||||
|
labelStyle: string;
|
||||||
|
curve: any;
|
||||||
|
}
|
||||||
|
90
packages/mermaid/src/utils/lineWithOffset.ts
Normal file
90
packages/mermaid/src/utils/lineWithOffset.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import type { EdgeData, Point } from '../types.js';
|
||||||
|
|
||||||
|
// We need to draw the lines a bit shorter to avoid drawing
|
||||||
|
// under any transparent markers.
|
||||||
|
// The offsets are calculated from the markers' dimensions.
|
||||||
|
const markerOffsets = {
|
||||||
|
aggregation: 18,
|
||||||
|
extension: 18,
|
||||||
|
composition: 18,
|
||||||
|
dependency: 6,
|
||||||
|
lollipop: 13.5,
|
||||||
|
arrow_point: 5.3,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the deltas and angle between two points
|
||||||
|
* @param point1 - First point
|
||||||
|
* @param point2 - Second point
|
||||||
|
* @returns The angle, deltaX and deltaY
|
||||||
|
*/
|
||||||
|
function calculateDeltaAndAngle(
|
||||||
|
point1: Point | [number, number],
|
||||||
|
point2: Point | [number, number]
|
||||||
|
): { angle: number; deltaX: number; deltaY: number } {
|
||||||
|
point1 = pointTransformer(point1);
|
||||||
|
point2 = pointTransformer(point2);
|
||||||
|
const [x1, y1] = [point1.x, point1.y];
|
||||||
|
const [x2, y2] = [point2.x, point2.y];
|
||||||
|
const deltaX = x2 - x1;
|
||||||
|
const deltaY = y2 - y1;
|
||||||
|
return { angle: Math.atan(deltaY / deltaX), deltaX, deltaY };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pointTransformer = (data: Point | [number, number]) => {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return { x: data[0], y: data[1] };
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLineFunctionsWithOffset = (edge: EdgeData) => {
|
||||||
|
return {
|
||||||
|
x: function (d: Point | [number, number], i: number, data: (Point | [number, number])[]) {
|
||||||
|
let offset = 0;
|
||||||
|
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
|
||||||
|
// Handle first point
|
||||||
|
// Calculate the angle and delta between the first two points
|
||||||
|
const { angle, deltaX } = calculateDeltaAndAngle(data[0], data[1]);
|
||||||
|
// Calculate the offset based on the angle and the marker's dimensions
|
||||||
|
offset =
|
||||||
|
markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets] *
|
||||||
|
Math.cos(angle) *
|
||||||
|
(deltaX >= 0 ? 1 : -1);
|
||||||
|
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
|
||||||
|
// Handle last point
|
||||||
|
// Calculate the angle and delta between the last two points
|
||||||
|
const { angle, deltaX } = calculateDeltaAndAngle(
|
||||||
|
data[data.length - 1],
|
||||||
|
data[data.length - 2]
|
||||||
|
);
|
||||||
|
offset =
|
||||||
|
markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets] *
|
||||||
|
Math.cos(angle) *
|
||||||
|
(deltaX >= 0 ? 1 : -1);
|
||||||
|
}
|
||||||
|
return pointTransformer(d).x + offset;
|
||||||
|
},
|
||||||
|
y: function (d: Point | [number, number], i: number, data: (Point | [number, number])[]) {
|
||||||
|
// Same handling as X above
|
||||||
|
let offset = 0;
|
||||||
|
if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) {
|
||||||
|
const { angle, deltaY } = calculateDeltaAndAngle(data[0], data[1]);
|
||||||
|
offset =
|
||||||
|
markerOffsets[edge.arrowTypeStart as keyof typeof markerOffsets] *
|
||||||
|
Math.abs(Math.sin(angle)) *
|
||||||
|
(deltaY >= 0 ? 1 : -1);
|
||||||
|
} else if (i === data.length - 1 && Object.hasOwn(markerOffsets, edge.arrowTypeEnd)) {
|
||||||
|
const { angle, deltaY } = calculateDeltaAndAngle(
|
||||||
|
data[data.length - 1],
|
||||||
|
data[data.length - 2]
|
||||||
|
);
|
||||||
|
offset =
|
||||||
|
markerOffsets[edge.arrowTypeEnd as keyof typeof markerOffsets] *
|
||||||
|
Math.abs(Math.sin(angle)) *
|
||||||
|
(deltaY >= 0 ? 1 : -1);
|
||||||
|
}
|
||||||
|
return pointTransformer(d).y + offset;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
Reference in New Issue
Block a user