mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-09 15:24:13 +01:00
Handing edges for edges leaving subgraphs
This commit is contained in:
@@ -105,6 +105,49 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<pre id="diagram4" class="mermaid">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart-elk TB
|
||||||
|
c1-->a2
|
||||||
|
subgraph one
|
||||||
|
a1-->a2
|
||||||
|
end
|
||||||
|
subgraph two
|
||||||
|
b1-->b2
|
||||||
|
end
|
||||||
|
subgraph three
|
||||||
|
c1-->c2
|
||||||
|
end
|
||||||
|
one --> two
|
||||||
|
three --> two
|
||||||
|
two --> c2
|
||||||
|
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram4" class="mermaid">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart TB
|
||||||
|
|
||||||
|
process_C
|
||||||
|
subgraph container_Alpha
|
||||||
|
subgraph process_B
|
||||||
|
pppB
|
||||||
|
end
|
||||||
|
subgraph process_A
|
||||||
|
pppA
|
||||||
|
end
|
||||||
|
process_B-->|via_AWSBatch|container_Beta
|
||||||
|
process_A-->|messages|container_Beta
|
||||||
|
end
|
||||||
|
|
||||||
|
</pre
|
||||||
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
|
|||||||
@@ -511,6 +511,91 @@ export const render = async (
|
|||||||
padding: node.padding,
|
padding: node.padding,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// Helper utilities for endpoint handling around cutter2
|
||||||
|
type Side = 'start' | 'end';
|
||||||
|
const approxEq = (a: number, b: number, eps = 1e-6) => Math.abs(a - b) < eps;
|
||||||
|
const isCenterApprox = (pt: P, node: { x: number; y: number }) =>
|
||||||
|
approxEq(pt.x, node.x) && approxEq(pt.y, node.y);
|
||||||
|
|
||||||
|
const getCandidateBorderPoint = (
|
||||||
|
points: P[],
|
||||||
|
node: any,
|
||||||
|
side: Side
|
||||||
|
): { candidate: P; centerApprox: boolean } => {
|
||||||
|
if (!points?.length) {
|
||||||
|
return { candidate: { x: node.x, y: node.y } as P, centerApprox: true };
|
||||||
|
}
|
||||||
|
if (side === 'start') {
|
||||||
|
const first = points[0];
|
||||||
|
const centerApprox = isCenterApprox(first, node);
|
||||||
|
const candidate = centerApprox && points.length > 1 ? points[1] : first;
|
||||||
|
return { candidate, centerApprox };
|
||||||
|
} else {
|
||||||
|
const last = points[points.length - 1];
|
||||||
|
const centerApprox = isCenterApprox(last, node);
|
||||||
|
const candidate = centerApprox && points.length > 1 ? points[points.length - 2] : last;
|
||||||
|
return { candidate, centerApprox };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dropAutoCenterPoint = (points: P[], side: Side, doDrop: boolean) => {
|
||||||
|
if (!doDrop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (side === 'start') {
|
||||||
|
if (points.length > 0) {
|
||||||
|
points.shift();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (points.length > 0) {
|
||||||
|
points.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyStartIntersectionIfNeeded = (points: P[], startNode: any, startBounds: RectLike) => {
|
||||||
|
let firstOutsideStartIndex = -1;
|
||||||
|
for (const [i, p] of points.entries()) {
|
||||||
|
if (outsideNode(startBounds, p)) {
|
||||||
|
firstOutsideStartIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (firstOutsideStartIndex !== -1) {
|
||||||
|
const outsidePointForStart = points[firstOutsideStartIndex];
|
||||||
|
const startCenter = points[0];
|
||||||
|
const startIntersection = computeNodeIntersection(
|
||||||
|
startNode,
|
||||||
|
startBounds,
|
||||||
|
outsidePointForStart,
|
||||||
|
startCenter
|
||||||
|
);
|
||||||
|
replaceEndpoint(points, 'start', startIntersection);
|
||||||
|
log.debug('UIO cutter2: start-only intersection applied', { startIntersection });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyEndIntersectionIfNeeded = (points: P[], endNode: any, endBounds: RectLike) => {
|
||||||
|
let outsideIndexForEnd = -1;
|
||||||
|
for (let i = points.length - 1; i >= 0; i--) {
|
||||||
|
if (outsideNode(endBounds, points[i])) {
|
||||||
|
outsideIndexForEnd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (outsideIndexForEnd !== -1) {
|
||||||
|
const outsidePointForEnd = points[outsideIndexForEnd];
|
||||||
|
const endCenter = points[points.length - 1];
|
||||||
|
const endIntersection = computeNodeIntersection(
|
||||||
|
endNode,
|
||||||
|
endBounds,
|
||||||
|
outsidePointForEnd,
|
||||||
|
endCenter
|
||||||
|
);
|
||||||
|
replaceEndpoint(points, 'end', endIntersection);
|
||||||
|
log.debug('UIO cutter2: end-only intersection applied', { endIntersection });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const cutter2 = (startNode: any, endNode: any, _points: any[]) => {
|
const cutter2 = (startNode: any, endNode: any, _points: any[]) => {
|
||||||
const startBounds = boundsFor(startNode);
|
const startBounds = boundsFor(startNode);
|
||||||
@@ -897,45 +982,41 @@ export const render = async (
|
|||||||
endBounds,
|
endBounds,
|
||||||
onBorder(endBounds, edge.points[edge.points.length - 1])
|
onBorder(endBounds, edge.points[edge.points.length - 1])
|
||||||
);
|
);
|
||||||
|
// Block for reducing variable scope and guardrails for the cutter function
|
||||||
{
|
{
|
||||||
|
const startBounds = boundsFor(startNode);
|
||||||
|
const endBounds = boundsFor(endNode);
|
||||||
|
|
||||||
|
const startIsGroup = !!startNode?.isGroup;
|
||||||
const endIsGroup = !!endNode?.isGroup;
|
const endIsGroup = !!endNode?.isGroup;
|
||||||
const lastIdx = prevPoints.length - 1;
|
|
||||||
const lastPt = prevPoints[lastIdx];
|
const { candidate: startCandidate, centerApprox: startCenterApprox } =
|
||||||
const endCenterApprox =
|
getCandidateBorderPoint(prevPoints as P[], startNode, 'start');
|
||||||
Math.abs(lastPt.x - endNode.x) < 1e-6 && Math.abs(lastPt.y - endNode.y) < 1e-6;
|
const { candidate: endCandidate, centerApprox: endCenterApprox } =
|
||||||
const candidatePt =
|
getCandidateBorderPoint(prevPoints as P[], endNode, 'end');
|
||||||
endCenterApprox && prevPoints.length > 1 ? prevPoints[lastIdx - 1] : lastPt;
|
|
||||||
const lastOnEndBorder = onBorder(endBounds, candidatePt);
|
const skipStart = startIsGroup && onBorder(startBounds, startCandidate);
|
||||||
if (endIsGroup && lastOnEndBorder) {
|
const skipEnd = endIsGroup && onBorder(endBounds, endCandidate);
|
||||||
if (endCenterApprox) {
|
|
||||||
// drop the appended end-center so the path truly ends at the border
|
dropAutoCenterPoint(prevPoints as P[], 'start', skipStart && startCenterApprox);
|
||||||
prevPoints.pop();
|
dropAutoCenterPoint(prevPoints as P[], 'end', skipEnd && endCenterApprox);
|
||||||
|
|
||||||
|
if (skipStart || skipEnd) {
|
||||||
|
if (!skipStart) {
|
||||||
|
applyStartIntersectionIfNeeded(prevPoints as P[], startNode, startBounds);
|
||||||
}
|
}
|
||||||
// still compute start intersection to avoid starting at center
|
if (!skipEnd) {
|
||||||
const startBounds = boundsFor(startNode);
|
applyEndIntersectionIfNeeded(prevPoints as P[], endNode, endBounds);
|
||||||
let firstOutsideStartIndex = -1;
|
|
||||||
for (const [i, prevPoint] of prevPoints.entries()) {
|
|
||||||
if (outsideNode(startBounds, prevPoint)) {
|
|
||||||
firstOutsideStartIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (firstOutsideStartIndex !== -1) {
|
|
||||||
const outsidePointForStart = prevPoints[firstOutsideStartIndex];
|
log.debug('PPP cutter2: skipping cutter2 due to on-border group endpoint(s)', {
|
||||||
const startCenter = prevPoints[0];
|
skipStart,
|
||||||
const startIntersection = computeNodeIntersection(
|
skipEnd,
|
||||||
startNode,
|
startCenterApprox,
|
||||||
startBounds,
|
endCenterApprox,
|
||||||
outsidePointForStart,
|
startCandidate,
|
||||||
startCenter
|
endCandidate,
|
||||||
);
|
});
|
||||||
replaceEndpoint(prevPoints, 'start', startIntersection);
|
|
||||||
log.debug('UIO cutter2: start-only intersection applied', { startIntersection });
|
|
||||||
}
|
|
||||||
log.debug(
|
|
||||||
'PPP cutter2: skipping cutter2 because last point on end border and end is group',
|
|
||||||
{ endCenterApprox, candidatePt }
|
|
||||||
);
|
|
||||||
edge.points = prevPoints;
|
edge.points = prevPoints;
|
||||||
} else {
|
} else {
|
||||||
edge.points = cutter2(startNode, endNode, prevPoints);
|
edge.points = cutter2(startNode, endNode, prevPoints);
|
||||||
|
|||||||
Reference in New Issue
Block a user