Fix for generateRoundedPath

This commit is contained in:
Knut Sveidqvist
2025-02-11 10:04:26 +01:00
parent fa51a37d8c
commit 2247bc583f
5 changed files with 247 additions and 125 deletions

View File

@@ -91,31 +91,29 @@
<pre id="diagram4" class="mermaid">
---
config:
look: neo
look: classic
theme: forest
layout: elk
---
flowchart LR
A["A"] --> C
B("B B B B B") --> C[/"C C C C C"/]
C@{ shape: circle }
%%C@{ shape: question }
%%C@{ shape: stadium }
</pre>
<pre id="diagram4" class="mermaid">
---
config:
look: classic
layout: elk
---
flowchart LR
n1["n1"] --- C
B("Continue") --> C(("Evaluate"))
B("Continue") --> C[/"Evaluate"/]
C -- One --> D["Option 1"]
C -- Two --> E["Option 2"]
C -- Three --> F["fa:fa-car Option 3"]
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart TB
%% swimlane 1 - A E
%% swimlane 2 - B
%% swimlane 3 - C D
A --> E & B
B --> C
B@{ shape: cyl, label: 'Cylinder'}
</pre>
<pre id="diagram4" class="mermaid2">
---

View File

@@ -1,103 +1,173 @@
<!doctype html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mindmap Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
<style>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
body {
/* background: rgb(221, 208, 208); */
/* background: #333; */
font-family: 'Arial';
/* color: white; */
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
/* background-color: #efefef;
background-image: radial-gradient(#fff 51%, transparent 91%),
radial-gradient(#fff 51%, transparent 91%);
background-size: 20px 20px;
background-position:
0 0,
10px 10px;
background-repeat: repeat; */
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
}
pre {
width: 100%;
}
/* tspan {
font-size: 6px !important;
} */
</style>
</head>
<body>
<h1>Mindmap diagram demo</h1>
<pre class="mermaid">
mindmap
root
child1((Circle))
grandchild 1
grandchild 2
child2(Round rectangle)
grandchild 3
grandchild 4
child3[Square]
grandchild 5
::icon(mdi mdi-fire)
gc6((grand<br/>child 6))
::icon(mdi mdi-fire)
gc7((grand<br/>grand<br/>child 8))
</pre>
<h2>Mindmap with root wrapping text and a shape</h2>
<pre class="mermaid">
mindmap
root[A root with a long text that wraps to keep the node size in check]
</pre>
<div class="flex gap-4">
<pre id="diagram4" class="mermaid">
---
config:
look: classic
theme: forest
layout: elk
---
flowchart LR
n1["n1"] --> C
n2("n2") --> C
C@{ shape: circle }
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
look: neo
theme: redux
layout: elk
---
flowchart LR
n1["n1"] --> C
n2("n2") --> C
C@{ shape: circle }
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
look: handDrawn
theme: forest
layout: elk
---
flowchart LR
n1["n1"] --> C
n2("n2") --> C
C@{ shape: circle }
</pre
>
</div>
<script type="module">
// import mermaid from './mermaid.esm.mjs';
import mermaid from '../../packages/mermaid/dist/mermaid.esm.mjs';
// import mermaidMindmap from './mermaid-mindmap.esm.mjs';
import mermaid from './mermaid.esm.mjs';
import layouts from './mermaid-layout-elk.esm.mjs';
// import mermaidMindmap from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-mindmap@9.3.0/+esm';
// await mermaid.registerExternalDiagrams([mermaidMindmap]);
const staticBellIconPack = {
prefix: 'fa6-regular',
icons: {
bell: {
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',
width: 448,
},
},
width: 512,
height: 512,
};
const ALLOWED_TAGS = [
'a',
'b',
'blockquote',
'br',
'dd',
'div',
'dl',
'dt',
'em',
'foreignObject',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'h7',
'h8',
'hr',
'i',
'li',
'ul',
'ol',
'p',
'pre',
'span',
'strike',
'strong',
'table',
'tbody',
'td',
'tfoot',
'th',
'thead',
'tr',
];
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos@1/icons.json').then((res) => res.json()),
},
{
name: 'fa',
loader: () => staticBellIconPack,
},
]);
mermaid.registerLayoutLoaders(layouts);
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
console.error('Mermaid error: ', err);
};
window.callback = function () {
alert('A callback was triggered');
};
mermaid.initialize({
theme: 'base',
startOnLoad: true,
flowchart: { titleTopMargin: 10, useMaxWidth: false },
fontSize: 12,
logLevel: 0,
flowchart: {
useMaxWidth: false,
htmlLabels: true,
},
gantt: {
useMaxWidth: false,
},
useMaxWidth: false,
securityLevel: 'loose',
});
function callback() {
alert('It worked');

View File

@@ -68,19 +68,25 @@ export const render = async (
// Add the element to the DOM
if (!node.isGroup) {
const child: NodeWithVertex = {
...node,
};
graph.children.push(child);
nodeDb[node.id] = child;
// const child: NodeWithVertex = {
// ...node,
// };
graph.children.push(node);
nodeDb[node.id] = node;
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
const boundingBox = childNodeEl.node()!.getBBox();
child.domId = childNodeEl;
child.calcIntersect = node.calcIntersect;
child.intersect = node.intersect;
child.width = boundingBox.width;
child.height = boundingBox.height;
node.domId = childNodeEl;
node.width = boundingBox.width;
node.height = boundingBox.height;
// child.calcIntersect = node.calcIntersect;
// child.intersect = node.intersect;
// child.width = boundingBox.width;
// child.height = boundingBox.height;
// child.updateIntersect = node.updateIntersect;
// if (child.updateIntersect) {
// child.updateIntersect();
// }
} else {
// A subgraph
const child: NodeWithVertex & { children: NodeWithVertex[] } = {
@@ -820,7 +826,7 @@ export const render = async (
'spacing.nodeNode': 20,
'spacing.nodeNodeBetweenLayers': 25,
'spacing.edgeNode': 20,
'spacing.edgeNodeBetweenLayers': 10,
'spacing.edgeNodeBetweenLayers': 20,
'spacing.edgeEdge': 10,
'spacing.edgeEdgeBetweenLayers': 20,
'spacing.nodeSelfLoop': 20,
@@ -845,7 +851,7 @@ export const render = async (
// 'elk.layered.considerModelOrder.strategy': 'PREFER_NODES',
// 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES
// 'elk.layered.wrapping.cutting.strategy': 'NODES_AND_EDGES',
'elk.alignment': 'BOTTOM',
// 'elk.alignment': 'BOTTOM',
// 'elk.layered.nodePlacement.bk.fixedAlignment': 'RIGHTDOWN',
// 'elk.edgeRouting': 'UNDEFINED',
'elk.layered.crossingMinimization.forceNodeModelOrder': false,
@@ -1046,12 +1052,14 @@ export const render = async (
const intersection = startNode.intersect(edge.points[0]);
if (distance(intersection2, edge.points[0]) > epsilon) {
// intersection.x = -startNode.offset.posX + intersection.x + startNode.width / 2;
// intersection.y = intersection.y + startNode.height / 2 + 5;
edge.points.unshift(intersection2);
}
}
if (endNode.intersect) {
// Remove the last point of the edge points
edge.points.pop();
// edge.points.pop();
const intersection2 = endNode.calcIntersect(
{
x: endNode.offset.posX + endNode.width / 2,
@@ -1073,9 +1081,27 @@ export const render = async (
console.log('APA13 intersection2.2', intersection, intersection2);
if (distance(intersection2, edge.points[edge.points.length - 1]) > epsilon) {
// console.log('APA13! distance ok\nintersection:', intersection);
console.log(
'APA13! distance ok\nintersection:',
intersection,
'\nstartNode:',
startNode.id,
'\nendNode:',
endNode.id,
'\n distance',
distance(intersection2, edge.points[edge.points.length - 1])
);
edge.points.push(intersection2);
// console.log('APA13! distance ok\npoints:', edge.points);
} else {
console.log(
'APA13! distance not ok\nintersection:',
intersection,
'\nstartNode:',
startNode.id,
'\nendNode:',
endNode.id
);
}
}

View File

@@ -15,6 +15,13 @@ import createLabel from './createLabel.js';
import { addEdgeMarkers } from './edgeMarker.ts';
import { isLabelStyle } from './shapes/handDrawnShapeStyles.js';
function distance(p1, p2) {
if (!p1 || !p2) {
return 0;
}
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
const edgeLabels = new Map();
const terminalLabels = new Map();
@@ -370,7 +377,6 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
}
edgeClassStyles.push(edge.cssCompiledStyles[key]);
}
console.log('APA13 edge.trim', edge.trim);
if (head.intersect && tail.intersect && edge.trim) {
points = points.slice(1, edge.points.length - 1);
points.unshift(tail.intersect(points[0]));
@@ -405,6 +411,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
}
let lineData = points.filter((p) => !Number.isNaN(p.y));
console.log('APA13 lineData ', lineData);
//lineData = fixCorners(lineData);
let curve = curveBasis;
curve = curveLinear;
@@ -460,7 +467,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
let svgPath;
let linePath =
edge.curve === 'rounded'
? generateRoundedPath(applyMarkerOffsetsToPoints(lineData, edge), 5)
? generateRoundedPath(applyMarkerOffsetsToPoints(lineData, edge), 3)
: lineFunction(lineData);
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
let animatedEdge = false;
@@ -590,6 +597,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
* @returns {String} - SVG path data string
*/
function generateRoundedPath(points, radius) {
console.log('APA13 points', points);
if (points.length < 2) {
return '';
}
@@ -597,7 +605,7 @@ function generateRoundedPath(points, radius) {
let path = '';
const size = points.length;
const epsilon = 1e-5;
const lastPoint = points[size - 1];
for (let i = 0; i < size; i++) {
const currPoint = points[i];
const prevPoint = points[i - 1];
@@ -639,6 +647,10 @@ function generateRoundedPath(points, radius) {
// Skip rounding if the angle is too small or too close to 180 degrees
if (angle < epsilon || Math.abs(Math.PI - angle) < epsilon) {
console.log(
'APA13 Skip rounding if the angle is too small',
`L${currPoint.x},${currPoint.y}`
);
path += `L${currPoint.x},${currPoint.y}`;
continue;
}
@@ -652,11 +664,21 @@ function generateRoundedPath(points, radius) {
const endX = currPoint.x + nx2 * cutLen;
const endY = currPoint.y + ny2 * cutLen;
// Draw the line to the start of the curve
path += `L${startX},${startY}`;
const d = distance(currPoint, lastPoint);
if (d > 3) {
// Draw the line to the start of the curve
path += `L${startX},${startY}`;
// Draw the quadratic Bezier curve
path += `Q${currPoint.x},${currPoint.y} ${endX},${endY}`;
// Draw the quadratic Bezier curve
path += `Q${currPoint.x},${currPoint.y} ${endX},${endY}`;
} else {
// // Draw the line to the start of the curve
// path += `L${startX},${startY}`;
// path += `Q${currPoint.x},${currPoint.y} ${currPoint.x},${currPoint.y}`;
// Draw the line to the start of the curve
// path += `L${startX},${startY}`;
path += `L${lastPoint.x},${lastPoint.y}`;
}
}
}

View File

@@ -54,6 +54,12 @@ export async function circle<T extends SVGGraphicsElement>(parent: D3Selection<T
log.info('Circle intersect', node, radius, point);
return intersect.circle(node, radius, point);
};
node.updateIntersect = function () {
node.intersect = function (point) {
log.info('Circle intersect', node, radius, point);
return intersect.circle(node, radius, point);
};
};
return shapeSvg;
}