mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-10 10:59:51 +02:00
Compare commits
3 Commits
mindmaps-a
...
@mermaid-j
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9c85521e16 | ||
![]() |
8a565bce92 | ||
![]() |
baf510b935 |
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix: Render newlines as spaces in class diagrams
|
|
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix: Handle arrows correctly when auto number is enabled
|
|
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': minor
|
|
||||||
---
|
|
||||||
|
|
||||||
feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.
|
|
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': minor
|
|
||||||
'@mermaid-js/layout-tidy-tree': minor
|
|
||||||
'@mermaid-js/layout-elk': minor
|
|
||||||
---
|
|
||||||
|
|
||||||
feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
|
|
@@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
chore: revert marked dependency from ^15.0.7 to ^16.0.0
|
|
||||||
|
|
||||||
- Reverted marked package version to ^16.0.0 for better compatibility
|
|
||||||
- This is a dependency update that maintains API compatibility
|
|
||||||
- All tests pass with the updated version
|
|
@@ -8,7 +8,6 @@ compositTitleSize
|
|||||||
cose
|
cose
|
||||||
curv
|
curv
|
||||||
doublecircle
|
doublecircle
|
||||||
elem
|
|
||||||
elems
|
elems
|
||||||
gantt
|
gantt
|
||||||
gitgraph
|
gitgraph
|
||||||
|
@@ -32,8 +32,26 @@
|
|||||||
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
|
||||||
rel="stylesheet"
|
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=Recursive:wght@300..1000&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.recursive-mermaid {
|
||||||
|
font-family: 'Recursive', sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-variation-settings:
|
||||||
|
'slnt' 0,
|
||||||
|
'CASL' 0,
|
||||||
|
'CRSV' 0.5,
|
||||||
|
'MONO' 0;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
/* background: rgb(221, 208, 208); */
|
/* background: rgb(221, 208, 208); */
|
||||||
/* background: #333; */
|
/* background: #333; */
|
||||||
@@ -45,7 +63,9 @@
|
|||||||
h1 {
|
h1 {
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
|
.mermaid {
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
.mermaid2 {
|
.mermaid2 {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -83,6 +103,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.class2 {
|
||||||
|
fill: red;
|
||||||
|
fill-opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* tspan {
|
/* tspan {
|
||||||
font-size: 6px !important;
|
font-size: 6px !important;
|
||||||
} */
|
} */
|
||||||
@@ -105,61 +130,77 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: tidy-tree
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
|
||||||
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: tidy-tree
|
||||||
---
|
---
|
||||||
flowchart-elk TB
|
mindmap
|
||||||
internet
|
root((mindmap is a long thing))
|
||||||
nat
|
A
|
||||||
router
|
B
|
||||||
compute1
|
C
|
||||||
|
D
|
||||||
subgraph project
|
</pre
|
||||||
router
|
|
||||||
nat
|
|
||||||
subgraph subnet1
|
|
||||||
compute1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% router --> subnet1
|
|
||||||
subnet1 --> nat
|
|
||||||
%% nat --> internet
|
|
||||||
</pre
|
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: tidy-tree
|
||||||
---
|
---
|
||||||
flowchart-elk TB
|
mindmap
|
||||||
internet
|
root((mindmap))
|
||||||
nat
|
A
|
||||||
router
|
B
|
||||||
lb1
|
</pre
|
||||||
lb2
|
|
||||||
compute1
|
|
||||||
compute2
|
|
||||||
subgraph project
|
|
||||||
router
|
|
||||||
nat
|
|
||||||
subgraph subnet1
|
|
||||||
compute1
|
|
||||||
lb1
|
|
||||||
end
|
|
||||||
subgraph subnet2
|
|
||||||
compute2
|
|
||||||
lb2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
internet --> router
|
|
||||||
router --> subnet1 & subnet2
|
|
||||||
subnet1 & subnet2 --> nat --> internet
|
|
||||||
</pre
|
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: tidy-tree
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
A
|
||||||
|
a
|
||||||
|
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
b
|
||||||
|
c
|
||||||
|
d
|
||||||
|
B
|
||||||
|
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
D
|
||||||
|
apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -186,27 +227,106 @@ treemap
|
|||||||
"Leaf 2.2": 25
|
"Leaf 2.2": 25
|
||||||
"Leaf 2.3": 12
|
"Leaf 2.3": 12
|
||||||
|
|
||||||
</pre>
|
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||||
<pre id="diagram5" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
curve: rounded
|
|
||||||
---
|
|
||||||
flowchart LR
|
|
||||||
I["fa:fa-code Text"] -- Mermaid js --> D["Use<br/>the<br/>editor!"]
|
|
||||||
I --> D & D
|
|
||||||
D@{ shape: question}
|
|
||||||
I@{ shape: question}
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
treemap:
|
||||||
|
valueFormat: '$0,0'
|
||||||
|
---
|
||||||
|
treemap
|
||||||
|
"Budget"
|
||||||
|
"Operations"
|
||||||
|
"Salaries": 7000
|
||||||
|
"Equipment": 2000
|
||||||
|
"Supplies": 1000
|
||||||
|
"Marketing"
|
||||||
|
"Advertising": 4000
|
||||||
|
"Events": 1000
|
||||||
|
|
||||||
|
</pre
|
||||||
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
|
treemap
|
||||||
|
title Accessible Treemap Title
|
||||||
|
"Category A"
|
||||||
|
"Item A1": 10
|
||||||
|
"Item A2": 20
|
||||||
|
"Category B"
|
||||||
|
"Item B1": 15
|
||||||
|
"Item B2": 25
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: tidy-tree
|
layout: tidy-tree
|
||||||
---
|
---
|
||||||
mindmap
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
a
|
||||||
|
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
b
|
||||||
|
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: tidy-tree
|
||||||
|
---
|
||||||
|
flowchart TB
|
||||||
|
A --> n0["1"]
|
||||||
|
A --> n1["2"]
|
||||||
|
A --> n2["3"]
|
||||||
|
A --> n3["4"] --> Q & R & S & T
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart TB
|
||||||
|
A --> n0["1"]
|
||||||
|
A --> n1["2"]
|
||||||
|
A --> n2["3"]
|
||||||
|
A --> n3["4"] --> Q & R & S & T
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: dagre
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap is a long thing))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: cose-bilkent
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
root((mindmap))
|
root((mindmap))
|
||||||
Origins
|
Origins
|
||||||
Long history
|
Long history
|
||||||
@@ -214,7 +334,7 @@ treemap
|
|||||||
Popularisation
|
Popularisation
|
||||||
British popular psychology author Tony Buzan
|
British popular psychology author Tony Buzan
|
||||||
Research
|
Research
|
||||||
On effectiveness<br/>and features
|
On effectiveness<br/>and features
|
||||||
On Automatic creation
|
On Automatic creation
|
||||||
Uses
|
Uses
|
||||||
Creative techniques
|
Creative techniques
|
||||||
@@ -223,112 +343,105 @@ treemap
|
|||||||
Tools
|
Tools
|
||||||
Pen and paper
|
Pen and paper
|
||||||
Mermaid
|
Mermaid
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
flowchart:
|
---
|
||||||
curve: linear
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: cose-bilkent
|
||||||
---
|
---
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A[A] --> B[B]
|
root{mindmap} --- Origins --- Europe
|
||||||
A[A] --- B([C])
|
Origins --> Asia
|
||||||
A@{ shape: diamond}
|
root --- Background --- Rich
|
||||||
%%B@{ shape: diamond}
|
Background --- Poor
|
||||||
|
subgraph apa
|
||||||
|
Background
|
||||||
|
Poor
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
flowchart:
|
|
||||||
curve: linear
|
|
||||||
---
|
---
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A[A] -- Mermaid js --> B[B]
|
root{mindmap} --- Origins --- Europe
|
||||||
A[A] -- Mermaid js --- B[B]
|
Origins --> Asia
|
||||||
A@{ shape: diamond}
|
root --- Background --- Rich
|
||||||
B@{ shape: diamond}
|
Background --- Poor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
flowchart
|
||||||
config:
|
D(("for D"))
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
curve: rounded
|
|
||||||
---
|
|
||||||
flowchart LR
|
|
||||||
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
|
|
||||||
I --> D & D
|
|
||||||
D@{ shape: question}
|
|
||||||
I@{ shape: question}
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
curve: rounded
|
|
||||||
elk:
|
|
||||||
nodePlacementStrategy: NETWORK_SIMPLEX
|
|
||||||
---
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
|
A e1@==> B
|
||||||
D --> I & I
|
e1@{ animate: true}
|
||||||
a["a"]
|
|
||||||
D@{ shape: trap-b}
|
|
||||||
I@{ shape: lean-l}
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
|
|
||||||
---
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
%% subgraph s1["Untitled subgraph"]
|
A e1@--> B
|
||||||
C["Evaluate"]
|
classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round
|
||||||
%% end
|
class e1 animate
|
||||||
|
|
||||||
B --> C
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<h2>infinite</h2>
|
||||||
---
|
<pre id="diagram4" class="mermaid2">
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
//curve: linear
|
|
||||||
---
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
%% A ==> B
|
A e1@--> B
|
||||||
%% A2 --> B2
|
classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
|
||||||
A{A} --> B((Bo boo)) & B & B & B
|
class e1 animate
|
||||||
|
</pre>
|
||||||
|
<h2>Mermaid - edge-animation-slow</h2>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
A e1@--> B
|
||||||
|
e1@{ animation: fast}
|
||||||
|
</pre>
|
||||||
|
<h2>Mermaid - edge-animation-fast</h2>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
A e1@--> B
|
||||||
|
classDef animate stroke-dasharray: 1000,stroke-dashoffset: 1000,animation: dash 10s linear;
|
||||||
|
class e1 edge-animation-fast
|
||||||
|
</pre>
|
||||||
|
|
||||||
</pre>
|
<pre id="diagram4" class="mermaid2">
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
info </pre
|
||||||
config:
|
>
|
||||||
layout: elk
|
<pre id="diagram4" class="mermaid2">
|
||||||
theme: default
|
|
||||||
look: classic
|
|
||||||
---
|
|
||||||
flowchart LR
|
|
||||||
subgraph s1["APA"]
|
|
||||||
D{"Use the editor"}
|
|
||||||
end
|
|
||||||
subgraph S2["S2"]
|
|
||||||
s1
|
|
||||||
I>"fa:fa-code Text"]
|
|
||||||
E["E"]
|
|
||||||
end
|
|
||||||
D -- Mermaid js --> I
|
|
||||||
D --> I & E
|
|
||||||
E --> I
|
|
||||||
</pre>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -353,7 +466,7 @@ config:
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -366,7 +479,45 @@ config:
|
|||||||
D-->I
|
D-->I
|
||||||
D-->I
|
D-->I
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
a
|
||||||
|
subgraph s0["APA"]
|
||||||
|
subgraph s8["APA"]
|
||||||
|
subgraph s1["APA"]
|
||||||
|
D{"X"}
|
||||||
|
E[Q]
|
||||||
|
end
|
||||||
|
subgraph s3["BAPA"]
|
||||||
|
F[Q]
|
||||||
|
I
|
||||||
|
end
|
||||||
|
D --> I
|
||||||
|
D --> I
|
||||||
|
D --> I
|
||||||
|
|
||||||
|
I{"X"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
a
|
||||||
|
D{"Use the editor"}
|
||||||
|
|
||||||
|
D -- Mermaid js --> I{"fa:fa-code Text"}
|
||||||
|
D-->I
|
||||||
|
D-->I
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -405,7 +556,7 @@ flowchart LR
|
|||||||
n8@{ shape: rect}
|
n8@{ shape: rect}
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -421,7 +572,7 @@ flowchart LR
|
|||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -430,7 +581,7 @@ flowchart LR
|
|||||||
A{A} --> B & C
|
A{A} --> B & C
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -442,7 +593,7 @@ flowchart LR
|
|||||||
end
|
end
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -460,7 +611,7 @@ flowchart LR
|
|||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
kanban:
|
kanban:
|
||||||
@@ -479,81 +630,81 @@ kanban
|
|||||||
task3[💻 Develop login feature]@{ ticket: 103 }
|
task3[💻 Develop login feature]@{ ticket: 103 }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
|
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
kanban
|
kanban
|
||||||
id2[In progress]
|
id2[In progress]
|
||||||
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
kanban:
|
kanban:
|
||||||
@@ -617,22 +768,18 @@ kanban
|
|||||||
alert('It worked');
|
alert('It worked');
|
||||||
}
|
}
|
||||||
await mermaid.initialize({
|
await mermaid.initialize({
|
||||||
// theme: 'base',
|
// theme: 'forest',
|
||||||
// theme: 'default',
|
// theme: 'default',
|
||||||
// theme: 'forest',
|
// theme: 'forest',
|
||||||
// handDrawnSeed: 12,
|
// handDrawnSeed: 12,
|
||||||
// look: 'handDrawn',
|
// look: 'handDrawn',
|
||||||
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
||||||
// layout: 'dagre',
|
// layout: 'dagre',
|
||||||
layout: 'elk',
|
// layout: 'elk',
|
||||||
// layout: 'fixed',
|
// layout: 'fixed',
|
||||||
// htmlLabels: false,
|
// htmlLabels: false,
|
||||||
flowchart: { titleTopMargin: 10 },
|
flowchart: { titleTopMargin: 10 },
|
||||||
|
fontFamily: "'Recursive', sans-serif",
|
||||||
// fontFamily: 'Caveat',
|
|
||||||
// fontFamily: 'Kalam',
|
|
||||||
// fontFamily: 'courier',
|
|
||||||
fontFamily: 'arial',
|
|
||||||
sequence: {
|
sequence: {
|
||||||
actorFontFamily: 'courier',
|
actorFontFamily: 'courier',
|
||||||
noteFontFamily: 'courier',
|
noteFontFamily: 'courier',
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: LayoutLoaderDefinition
|
# Interface: LayoutLoaderDefinition
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:21](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L21)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.co
|
|||||||
|
|
||||||
> `optional` **algorithm**: `string`
|
> `optional` **algorithm**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:27](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L27)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:27](https://github.co
|
|||||||
|
|
||||||
> **loader**: `LayoutLoader`
|
> **loader**: `LayoutLoader`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:26](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L26)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:23](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L23)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:26](https://github.co
|
|||||||
|
|
||||||
> **name**: `string`
|
> **name**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:25](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L25)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:22](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L22)
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: RenderOptions
|
# Interface: RenderOptions
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L10)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L7)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,4 +18,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:10](https://github.co
|
|||||||
|
|
||||||
> `optional` **algorithm**: `string`
|
> `optional` **algorithm**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:11](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L11)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L8)
|
||||||
|
@@ -1,5 +1,16 @@
|
|||||||
# @mermaid-js/layout-elk
|
# @mermaid-js/layout-elk
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [[`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800), [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20), [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05), [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8)]:
|
||||||
|
- mermaid@11.11.0
|
||||||
|
|
||||||
## 0.1.9
|
## 0.1.9
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mermaid-js/layout-elk",
|
"name": "@mermaid-js/layout-elk",
|
||||||
"version": "0.1.9",
|
"version": "0.2.0",
|
||||||
"description": "ELK layout engine for mermaid",
|
"description": "ELK layout engine for mermaid",
|
||||||
"module": "dist/mermaid-layout-elk.core.mjs",
|
"module": "dist/mermaid-layout-elk.core.mjs",
|
||||||
"types": "dist/layouts.d.ts",
|
"types": "dist/layouts.d.ts",
|
||||||
|
@@ -1,67 +0,0 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import {
|
|
||||||
intersection,
|
|
||||||
ensureTrulyOutside,
|
|
||||||
makeInsidePoint,
|
|
||||||
tryNodeIntersect,
|
|
||||||
replaceEndpoint,
|
|
||||||
type RectLike,
|
|
||||||
type P,
|
|
||||||
} from '../geometry.js';
|
|
||||||
|
|
||||||
const approx = (a: number, b: number, eps = 1e-6) => Math.abs(a - b) < eps;
|
|
||||||
|
|
||||||
describe('geometry helpers', () => {
|
|
||||||
it('intersection: vertical approach hits bottom border', () => {
|
|
||||||
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
|
|
||||||
const h = rect.height / 2; // 25
|
|
||||||
const outside: P = { x: 0, y: 100 };
|
|
||||||
const inside: P = { x: 0, y: 0 };
|
|
||||||
const res = intersection(rect, outside, inside);
|
|
||||||
expect(approx(res.x, 0)).toBe(true);
|
|
||||||
expect(approx(res.y, h)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ensureTrulyOutside nudges near-boundary point outward', () => {
|
|
||||||
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
|
|
||||||
// near bottom boundary (y ~ h)
|
|
||||||
const near: P = { x: 0, y: rect.height / 2 - 0.2 };
|
|
||||||
const out = ensureTrulyOutside(rect, near, 10);
|
|
||||||
expect(out.y).toBeGreaterThan(rect.height / 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('makeInsidePoint keeps x for vertical and y from center', () => {
|
|
||||||
const rect: RectLike = { x: 10, y: 5, width: 100, height: 50 };
|
|
||||||
const outside: P = { x: 10, y: 40 };
|
|
||||||
const center: P = { x: 99, y: -123 }; // center y should be used
|
|
||||||
const inside = makeInsidePoint(rect, outside, center);
|
|
||||||
expect(inside.x).toBe(outside.x);
|
|
||||||
expect(inside.y).toBe(center.y);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tryNodeIntersect returns null for wrong-side intersections', () => {
|
|
||||||
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
|
|
||||||
const outside: P = { x: -50, y: 0 };
|
|
||||||
const node = { intersect: () => ({ x: 10, y: 0 }) } as any; // right side of center
|
|
||||||
const res = tryNodeIntersect(node, rect, outside);
|
|
||||||
expect(res).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('replaceEndpoint dedup removes end/start appropriately', () => {
|
|
||||||
const pts: P[] = [
|
|
||||||
{ x: 0, y: 0 },
|
|
||||||
{ x: 1, y: 1 },
|
|
||||||
];
|
|
||||||
// remove duplicate end
|
|
||||||
replaceEndpoint(pts, 'end', { x: 1, y: 1 });
|
|
||||||
expect(pts.length).toBe(1);
|
|
||||||
|
|
||||||
const pts2: P[] = [
|
|
||||||
{ x: 0, y: 0 },
|
|
||||||
{ x: 1, y: 1 },
|
|
||||||
];
|
|
||||||
// remove duplicate start
|
|
||||||
replaceEndpoint(pts2, 'start', { x: 0, y: 0 });
|
|
||||||
expect(pts2.length).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,194 +0,0 @@
|
|||||||
/* Geometry utilities extracted from render.ts for reuse and testing */
|
|
||||||
|
|
||||||
export interface P {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RectLike {
|
|
||||||
x: number; // center x
|
|
||||||
y: number; // center y
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
padding?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NodeLike {
|
|
||||||
intersect?: (p: P) => P | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EPS = 1;
|
|
||||||
export const PUSH_OUT = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute intersection between a rectangle (center x/y, width/height) and the line
|
|
||||||
* segment from insidePoint -\> outsidePoint. Returns the point on the rectangle border.
|
|
||||||
*
|
|
||||||
* This version avoids snapping to outsidePoint when certain variables evaluate to 0
|
|
||||||
* (previously caused vertical top/bottom cases to miss the border). It only enforces
|
|
||||||
* axis-constant behavior for purely vertical/horizontal approaches.
|
|
||||||
*/
|
|
||||||
export const intersection = (node: RectLike, outsidePoint: P, insidePoint: P): P => {
|
|
||||||
const x = node.x;
|
|
||||||
const y = node.y;
|
|
||||||
|
|
||||||
const dx = Math.abs(x - insidePoint.x);
|
|
||||||
const w = node.width / 2;
|
|
||||||
let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;
|
|
||||||
const h = node.height / 2;
|
|
||||||
|
|
||||||
const Q = Math.abs(outsidePoint.y - insidePoint.y);
|
|
||||||
const R = Math.abs(outsidePoint.x - insidePoint.x);
|
|
||||||
|
|
||||||
if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {
|
|
||||||
// Intersection is top or bottom of rect.
|
|
||||||
const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
|
|
||||||
r = (R * q) / Q;
|
|
||||||
const res = {
|
|
||||||
x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,
|
|
||||||
y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Keep axis-constant special-cases only
|
|
||||||
if (R === 0) {
|
|
||||||
res.x = outsidePoint.x;
|
|
||||||
}
|
|
||||||
if (Q === 0) {
|
|
||||||
res.y = outsidePoint.y;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
// Intersection on sides of rect
|
|
||||||
if (insidePoint.x < outsidePoint.x) {
|
|
||||||
r = outsidePoint.x - w - x;
|
|
||||||
} else {
|
|
||||||
r = x - w - outsidePoint.x;
|
|
||||||
}
|
|
||||||
const q = (Q * r) / R;
|
|
||||||
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
|
|
||||||
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
|
|
||||||
|
|
||||||
// Only handle axis-constant cases
|
|
||||||
if (R === 0) {
|
|
||||||
_x = outsidePoint.x;
|
|
||||||
}
|
|
||||||
if (Q === 0) {
|
|
||||||
_y = outsidePoint.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { x: _x, y: _y };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const outsideNode = (node: RectLike, point: P): boolean => {
|
|
||||||
const x = node.x;
|
|
||||||
const y = node.y;
|
|
||||||
const dx = Math.abs(point.x - x);
|
|
||||||
const dy = Math.abs(point.y - y);
|
|
||||||
const w = node.width / 2;
|
|
||||||
const h = node.height / 2;
|
|
||||||
return dx >= w || dy >= h;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ensureTrulyOutside = (bounds: RectLike, p: P, push = PUSH_OUT): P => {
|
|
||||||
const dx = Math.abs(p.x - bounds.x);
|
|
||||||
const dy = Math.abs(p.y - bounds.y);
|
|
||||||
const w = bounds.width / 2;
|
|
||||||
const h = bounds.height / 2;
|
|
||||||
if (Math.abs(dx - w) < EPS || Math.abs(dy - h) < EPS) {
|
|
||||||
const dirX = p.x - bounds.x;
|
|
||||||
const dirY = p.y - bounds.y;
|
|
||||||
const len = Math.sqrt(dirX * dirX + dirY * dirY);
|
|
||||||
if (len > 0) {
|
|
||||||
return {
|
|
||||||
x: bounds.x + (dirX / len) * (len + push),
|
|
||||||
y: bounds.y + (dirY / len) * (len + push),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeInsidePoint = (bounds: RectLike, outside: P, center: P): P => {
|
|
||||||
const isVertical = Math.abs(outside.x - bounds.x) < EPS;
|
|
||||||
const isHorizontal = Math.abs(outside.y - bounds.y) < EPS;
|
|
||||||
return {
|
|
||||||
x: isVertical
|
|
||||||
? outside.x
|
|
||||||
: outside.x < bounds.x
|
|
||||||
? bounds.x - bounds.width / 4
|
|
||||||
: bounds.x + bounds.width / 4,
|
|
||||||
y: isHorizontal ? outside.y : center.y,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tryNodeIntersect = (node: NodeLike, bounds: RectLike, outside: P): P | null => {
|
|
||||||
if (!node?.intersect) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const res = node.intersect(outside);
|
|
||||||
if (!res) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const wrongSide =
|
|
||||||
(outside.x < bounds.x && res.x > bounds.x) || (outside.x > bounds.x && res.x < bounds.x);
|
|
||||||
if (wrongSide) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const dist = Math.hypot(outside.x - res.x, outside.y - res.y);
|
|
||||||
if (dist <= EPS) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fallbackIntersection = (bounds: RectLike, outside: P, center: P): P => {
|
|
||||||
const inside = makeInsidePoint(bounds, outside, center);
|
|
||||||
return intersection(bounds, outside, inside);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const computeNodeIntersection = (
|
|
||||||
node: NodeLike,
|
|
||||||
bounds: RectLike,
|
|
||||||
outside: P,
|
|
||||||
center: P
|
|
||||||
): P => {
|
|
||||||
const outside2 = ensureTrulyOutside(bounds, outside);
|
|
||||||
return tryNodeIntersect(node, bounds, outside2) ?? fallbackIntersection(bounds, outside2, center);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const replaceEndpoint = (
|
|
||||||
points: P[],
|
|
||||||
which: 'start' | 'end',
|
|
||||||
value: P | null | undefined,
|
|
||||||
tol = 0.1
|
|
||||||
) => {
|
|
||||||
if (!value || points.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (which === 'start') {
|
|
||||||
if (
|
|
||||||
points.length > 0 &&
|
|
||||||
Math.abs(points[0].x - value.x) < tol &&
|
|
||||||
Math.abs(points[0].y - value.y) < tol
|
|
||||||
) {
|
|
||||||
// duplicate start remove it
|
|
||||||
points.shift();
|
|
||||||
} else {
|
|
||||||
points[0] = value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const last = points.length - 1;
|
|
||||||
if (
|
|
||||||
points.length > 0 &&
|
|
||||||
Math.abs(points[last].x - value.x) < tol &&
|
|
||||||
Math.abs(points[last].y - value.y) < tol
|
|
||||||
) {
|
|
||||||
// duplicate end remove it
|
|
||||||
points.pop();
|
|
||||||
} else {
|
|
||||||
points[last] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,25 +1,11 @@
|
|||||||
import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid';
|
|
||||||
// @ts-ignore TODO: Investigate D3 issue
|
|
||||||
import { curveLinear } from 'd3';
|
import { curveLinear } from 'd3';
|
||||||
import ELK from 'elkjs/lib/elk.bundled.js';
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
||||||
|
import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid';
|
||||||
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
|
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
|
||||||
|
|
||||||
import {
|
|
||||||
type P,
|
|
||||||
type RectLike,
|
|
||||||
outsideNode,
|
|
||||||
computeNodeIntersection,
|
|
||||||
replaceEndpoint,
|
|
||||||
} from './geometry.js';
|
|
||||||
|
|
||||||
type Node = LayoutData['nodes'][number];
|
type Node = LayoutData['nodes'][number];
|
||||||
|
// Used to calculate distances in order to avoid floating number rounding issues when comparing floating numbers
|
||||||
// Minimal structural type to avoid depending on d3 Selection typings
|
const epsilon = 0.0001;
|
||||||
interface D3Selection<T extends Element> {
|
|
||||||
node(): T | null;
|
|
||||||
attr(name: string, value: string): D3Selection<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LabelData {
|
interface LabelData {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
@@ -30,9 +16,18 @@ interface LabelData {
|
|||||||
interface NodeWithVertex extends Omit<Node, 'domId'> {
|
interface NodeWithVertex extends Omit<Node, 'domId'> {
|
||||||
children?: LayoutData['nodes'];
|
children?: LayoutData['nodes'];
|
||||||
labelData?: LabelData;
|
labelData?: LabelData;
|
||||||
domId?: D3Selection<SVGAElement | SVGGElement>;
|
domId?: Node['domId'] | SVGGroup | d3.Selection<SVGAElement, unknown, Element | null, unknown>;
|
||||||
|
}
|
||||||
|
interface Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
function distance(p1?: Point, p2?: Point): number {
|
||||||
|
if (!p1 || !p2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const render = async (
|
export const render = async (
|
||||||
data4Layout: LayoutData,
|
data4Layout: LayoutData,
|
||||||
svg: SVG,
|
svg: SVG,
|
||||||
@@ -66,26 +61,39 @@ export const render = async (
|
|||||||
|
|
||||||
// Add the element to the DOM
|
// Add the element to the DOM
|
||||||
if (!node.isGroup) {
|
if (!node.isGroup) {
|
||||||
const child = node as NodeWithVertex;
|
// Create a clean node object for ELK with only the properties it expects
|
||||||
|
const child: NodeWithVertex = {
|
||||||
|
id: node.id,
|
||||||
|
width: node.width,
|
||||||
|
height: node.height,
|
||||||
|
// Store the original node data for later use
|
||||||
|
label: node.label,
|
||||||
|
isGroup: node.isGroup,
|
||||||
|
shape: node.shape,
|
||||||
|
padding: node.padding,
|
||||||
|
cssClasses: node.cssClasses,
|
||||||
|
cssStyles: node.cssStyles,
|
||||||
|
look: node.look,
|
||||||
|
// Include parentId for subgraph processing
|
||||||
|
parentId: node.parentId,
|
||||||
|
};
|
||||||
graph.children.push(child);
|
graph.children.push(child);
|
||||||
nodeDb[node.id] = node;
|
nodeDb[node.id] = child;
|
||||||
|
|
||||||
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
|
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
|
||||||
const boundingBox = childNodeEl.node()!.getBBox();
|
const boundingBox = childNodeEl.node()!.getBBox();
|
||||||
// Store the domId separately for rendering, not in the ELK graph
|
// Store the domId separately for rendering, not in the ELK graph
|
||||||
child.domId = childNodeEl;
|
child.domId = childNodeEl;
|
||||||
|
child.calcIntersect = node.calcIntersect;
|
||||||
child.width = boundingBox.width;
|
child.width = boundingBox.width;
|
||||||
child.height = boundingBox.height;
|
child.height = boundingBox.height;
|
||||||
} else {
|
} else {
|
||||||
// A subgraph
|
// A subgraph
|
||||||
const child: NodeWithVertex & { children: NodeWithVertex[] } = {
|
const child: NodeWithVertex & { children: NodeWithVertex[] } = {
|
||||||
...node,
|
...node,
|
||||||
domId: undefined,
|
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
// Let elk render with the copy
|
|
||||||
graph.children.push(child);
|
graph.children.push(child);
|
||||||
// Save the original containing the intersection function
|
|
||||||
nodeDb[node.id] = child;
|
nodeDb[node.id] = child;
|
||||||
await addVertices(nodeEl, nodeArr, child, node.id);
|
await addVertices(nodeEl, nodeArr, child, node.id);
|
||||||
|
|
||||||
@@ -160,7 +168,7 @@ export const render = async (
|
|||||||
height: node.height,
|
height: node.height,
|
||||||
};
|
};
|
||||||
if (node.isGroup) {
|
if (node.isGroup) {
|
||||||
log.debug('Id abc88 subgraph = ', node.id, node.x, node.y, node.labelData);
|
log.debug('id abc88 subgraph = ', node.id, node.x, node.y, node.labelData);
|
||||||
const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph');
|
const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph');
|
||||||
// TODO use faster way of cloning
|
// TODO use faster way of cloning
|
||||||
const clusterNode = JSON.parse(JSON.stringify(node));
|
const clusterNode = JSON.parse(JSON.stringify(node));
|
||||||
@@ -169,10 +177,10 @@ export const render = async (
|
|||||||
clusterNode.width = Math.max(clusterNode.width, node.labelData.width);
|
clusterNode.width = Math.max(clusterNode.width, node.labelData.width);
|
||||||
await insertCluster(subgraphEl, clusterNode);
|
await insertCluster(subgraphEl, clusterNode);
|
||||||
|
|
||||||
log.debug('Id (UIO)= ', node.id, node.width, node.shape, node.labels);
|
log.debug('id (UIO)= ', node.id, node.width, node.shape, node.labels);
|
||||||
} else {
|
} else {
|
||||||
log.info(
|
log.info(
|
||||||
'Id NODE = ',
|
'id NODE = ',
|
||||||
node.id,
|
node.id,
|
||||||
node.x,
|
node.x,
|
||||||
node.y,
|
node.y,
|
||||||
@@ -214,19 +222,25 @@ export const render = async (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
subgraphs.forEach(function (subgraph: { id: string | number }) {
|
||||||
|
const data: any = { id: subgraph.id };
|
||||||
|
if (parentLookupDb.parentById[subgraph.id] !== undefined) {
|
||||||
|
data.parent = parentLookupDb.parentById[subgraph.id];
|
||||||
|
}
|
||||||
|
});
|
||||||
return parentLookupDb;
|
return parentLookupDb;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getEdgeStartEndPoint = (edge: any) => {
|
const getEdgeStartEndPoint = (edge: any) => {
|
||||||
// edge.start and edge.end are IDs (string/number) in our layout data
|
const source: any = edge.start;
|
||||||
const sourceId: string | number = edge.start;
|
const target: any = edge.end;
|
||||||
const targetId: string | number = edge.end;
|
|
||||||
|
|
||||||
const source = sourceId;
|
// Save the original source and target
|
||||||
const target = targetId;
|
const sourceId = source;
|
||||||
|
const targetId = target;
|
||||||
|
|
||||||
const startNode = nodeDb[sourceId];
|
const startNode = nodeDb[edge.start.id];
|
||||||
const endNode = nodeDb[targetId];
|
const endNode = nodeDb[edge.end.id];
|
||||||
|
|
||||||
if (!startNode || !endNode) {
|
if (!startNode || !endNode) {
|
||||||
return { source, target };
|
return { source, target };
|
||||||
@@ -249,112 +263,6 @@ export const render = async (
|
|||||||
/**
|
/**
|
||||||
* Add edges to graph based on parsed graph definition
|
* Add edges to graph based on parsed graph definition
|
||||||
*/
|
*/
|
||||||
// Edge helper maps and utilities (de-duplicated)
|
|
||||||
const ARROW_MAP: Record<string, [string, string]> = {
|
|
||||||
arrow_open: ['arrow_open', 'arrow_open'],
|
|
||||||
arrow_cross: ['arrow_open', 'arrow_cross'],
|
|
||||||
double_arrow_cross: ['arrow_cross', 'arrow_cross'],
|
|
||||||
arrow_point: ['arrow_open', 'arrow_point'],
|
|
||||||
double_arrow_point: ['arrow_point', 'arrow_point'],
|
|
||||||
arrow_circle: ['arrow_open', 'arrow_circle'],
|
|
||||||
double_arrow_circle: ['arrow_circle', 'arrow_circle'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const computeStroke = (
|
|
||||||
stroke: string | undefined,
|
|
||||||
defaultStyle?: string,
|
|
||||||
defaultLabelStyle?: string
|
|
||||||
) => {
|
|
||||||
// Defaults correspond to 'normal'
|
|
||||||
let thickness = 'normal';
|
|
||||||
let pattern = 'solid';
|
|
||||||
let style = '';
|
|
||||||
let labelStyle = '';
|
|
||||||
|
|
||||||
if (stroke === 'dotted') {
|
|
||||||
pattern = 'dotted';
|
|
||||||
style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
|
|
||||||
} else if (stroke === 'thick') {
|
|
||||||
thickness = 'thick';
|
|
||||||
style = 'stroke-width: 3.5px;fill:none;';
|
|
||||||
} else {
|
|
||||||
// normal
|
|
||||||
style = defaultStyle ?? 'fill:none;';
|
|
||||||
if (defaultLabelStyle !== undefined) {
|
|
||||||
labelStyle = defaultLabelStyle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { thickness, pattern, style, labelStyle };
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurve = (edgeInterpolate: any, edgesDefaultInterpolate: any, confCurve: any) => {
|
|
||||||
if (edgeInterpolate !== undefined) {
|
|
||||||
return interpolateToCurve(edgeInterpolate, curveLinear);
|
|
||||||
}
|
|
||||||
if (edgesDefaultInterpolate !== undefined) {
|
|
||||||
return interpolateToCurve(edgesDefaultInterpolate, curveLinear);
|
|
||||||
}
|
|
||||||
// @ts-ignore TODO: fix this
|
|
||||||
return interpolateToCurve(confCurve, curveLinear);
|
|
||||||
};
|
|
||||||
const buildEdgeData = (
|
|
||||||
edge: any,
|
|
||||||
defaults: {
|
|
||||||
defaultStyle?: string;
|
|
||||||
defaultLabelStyle?: string;
|
|
||||||
defaultInterpolate?: any;
|
|
||||||
confCurve: any;
|
|
||||||
},
|
|
||||||
common: any
|
|
||||||
) => {
|
|
||||||
const edgeData: any = { style: '', labelStyle: '' };
|
|
||||||
edgeData.minlen = edge.length || 1;
|
|
||||||
// maintain legacy behavior
|
|
||||||
edge.text = edge.label;
|
|
||||||
|
|
||||||
// Arrowhead fill vs none
|
|
||||||
edgeData.arrowhead = edge.type === 'arrow_open' ? 'none' : 'normal';
|
|
||||||
|
|
||||||
// Arrow types
|
|
||||||
const arrowMap = ARROW_MAP[edge.type] ?? ARROW_MAP.arrow_open;
|
|
||||||
edgeData.arrowTypeStart = arrowMap[0];
|
|
||||||
edgeData.arrowTypeEnd = arrowMap[1];
|
|
||||||
|
|
||||||
// Optional edge label positioning flags
|
|
||||||
edgeData.startLabelRight = edge.startLabelRight;
|
|
||||||
edgeData.endLabelLeft = edge.endLabelLeft;
|
|
||||||
|
|
||||||
// Stroke
|
|
||||||
const strokeRes = computeStroke(edge.stroke, defaults.defaultStyle, defaults.defaultLabelStyle);
|
|
||||||
edgeData.thickness = strokeRes.thickness;
|
|
||||||
edgeData.pattern = strokeRes.pattern;
|
|
||||||
edgeData.style = (edgeData.style || '') + (strokeRes.style || '');
|
|
||||||
edgeData.labelStyle = (edgeData.labelStyle || '') + (strokeRes.labelStyle || '');
|
|
||||||
|
|
||||||
// Curve
|
|
||||||
// @ts-ignore - defaults.confCurve is present at runtime but missing in type
|
|
||||||
edgeData.curve = getCurve(edge.interpolate, defaults.defaultInterpolate, defaults.confCurve);
|
|
||||||
|
|
||||||
// Arrowhead style + labelpos when we have label text
|
|
||||||
const hasText = (edge?.text ?? '') !== '';
|
|
||||||
if (hasText) {
|
|
||||||
edgeData.arrowheadStyle = 'fill: #333';
|
|
||||||
edgeData.labelpos = 'c';
|
|
||||||
} else if (edge.style !== undefined) {
|
|
||||||
edgeData.arrowheadStyle = 'fill: #333';
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeData.labelType = edge.labelType;
|
|
||||||
edgeData.label = (edge?.text ?? '').replace(common.lineBreakRegex, '\n');
|
|
||||||
|
|
||||||
if (edge.style === undefined) {
|
|
||||||
edgeData.style = edgeData.style ?? 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
|
|
||||||
return edgeData;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addEdges = async function (
|
const addEdges = async function (
|
||||||
dataForLayout: { edges: any; direction?: string },
|
dataForLayout: { edges: any; direction?: string },
|
||||||
graph: {
|
graph: {
|
||||||
@@ -376,6 +284,7 @@ export const render = async (
|
|||||||
const edges = dataForLayout.edges;
|
const edges = dataForLayout.edges;
|
||||||
const labelsEl = svg.insert('g').attr('class', 'edgeLabels');
|
const labelsEl = svg.insert('g').attr('class', 'edgeLabels');
|
||||||
const linkIdCnt: any = {};
|
const linkIdCnt: any = {};
|
||||||
|
const dir = dataForLayout.direction || 'DOWN';
|
||||||
let defaultStyle: string | undefined;
|
let defaultStyle: string | undefined;
|
||||||
let defaultLabelStyle: string | undefined;
|
let defaultLabelStyle: string | undefined;
|
||||||
|
|
||||||
@@ -405,24 +314,105 @@ export const render = async (
|
|||||||
linkIdCnt[linkIdBase]++;
|
linkIdCnt[linkIdBase]++;
|
||||||
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
|
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
|
||||||
}
|
}
|
||||||
const linkId = linkIdBase; // + '_' + linkIdCnt[linkIdBase];
|
const linkId = linkIdBase + '_' + linkIdCnt[linkIdBase];
|
||||||
edge.id = linkId;
|
edge.id = linkId;
|
||||||
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
|
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
|
||||||
const linkNameStart = 'LS_' + edge.start;
|
const linkNameStart = 'LS_' + edge.start;
|
||||||
const linkNameEnd = 'LE_' + edge.end;
|
const linkNameEnd = 'LE_' + edge.end;
|
||||||
|
|
||||||
|
const edgeData: any = { style: '', labelStyle: '' };
|
||||||
|
edgeData.minlen = edge.length || 1;
|
||||||
|
edge.text = edge.label;
|
||||||
|
// Set link type for rendering
|
||||||
|
if (edge.type === 'arrow_open') {
|
||||||
|
edgeData.arrowhead = 'none';
|
||||||
|
} else {
|
||||||
|
edgeData.arrowhead = 'normal';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check of arrow types, placed here in order not to break old rendering
|
||||||
|
edgeData.arrowTypeStart = 'arrow_open';
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_open';
|
||||||
|
|
||||||
|
/* eslint-disable no-fallthrough */
|
||||||
|
switch (edge.type) {
|
||||||
|
case 'double_arrow_cross':
|
||||||
|
edgeData.arrowTypeStart = 'arrow_cross';
|
||||||
|
case 'arrow_cross':
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_cross';
|
||||||
|
break;
|
||||||
|
case 'double_arrow_point':
|
||||||
|
edgeData.arrowTypeStart = 'arrow_point';
|
||||||
|
case 'arrow_point':
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_point';
|
||||||
|
break;
|
||||||
|
case 'double_arrow_circle':
|
||||||
|
edgeData.arrowTypeStart = 'arrow_circle';
|
||||||
|
case 'arrow_circle':
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_circle';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = '';
|
||||||
|
let labelStyle = '';
|
||||||
|
|
||||||
|
edgeData.startLabelRight = edge.startLabelRight;
|
||||||
|
edgeData.endLabelLeft = edge.endLabelLeft;
|
||||||
|
|
||||||
|
switch (edge.stroke) {
|
||||||
|
case 'normal':
|
||||||
|
style = 'fill:none;';
|
||||||
|
if (defaultStyle !== undefined) {
|
||||||
|
style = defaultStyle;
|
||||||
|
}
|
||||||
|
if (defaultLabelStyle !== undefined) {
|
||||||
|
labelStyle = defaultLabelStyle;
|
||||||
|
}
|
||||||
|
edgeData.thickness = 'normal';
|
||||||
|
edgeData.pattern = 'solid';
|
||||||
|
break;
|
||||||
|
case 'dotted':
|
||||||
|
edgeData.thickness = 'normal';
|
||||||
|
edgeData.pattern = 'dotted';
|
||||||
|
edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
|
||||||
|
break;
|
||||||
|
case 'thick':
|
||||||
|
edgeData.thickness = 'thick';
|
||||||
|
edgeData.pattern = 'solid';
|
||||||
|
edgeData.style = 'stroke-width: 3.5px;fill:none;';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeData.style = edgeData.style += style;
|
||||||
|
edgeData.labelStyle = edgeData.labelStyle += labelStyle;
|
||||||
|
|
||||||
const conf = getConfig();
|
const conf = getConfig();
|
||||||
const edgeData = buildEdgeData(
|
if (edge.interpolate !== undefined) {
|
||||||
edge,
|
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
|
||||||
{
|
} else if (edges.defaultInterpolate !== undefined) {
|
||||||
defaultStyle,
|
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
|
||||||
defaultLabelStyle,
|
} else {
|
||||||
defaultInterpolate: edges.defaultInterpolate,
|
// @ts-ignore TODO: fix this
|
||||||
// @ts-ignore - conf.curve exists at runtime but is missing from typing
|
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
|
||||||
confCurve: conf.curve,
|
}
|
||||||
},
|
|
||||||
common
|
if (edge.text === undefined) {
|
||||||
);
|
if (edge.style !== undefined) {
|
||||||
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
|
edgeData.labelpos = 'c';
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeData.labelType = edge.labelType;
|
||||||
|
edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n');
|
||||||
|
|
||||||
|
if (edge.style === undefined) {
|
||||||
|
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
|
||||||
|
|
||||||
edgeData.id = linkId;
|
edgeData.id = linkId;
|
||||||
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
|
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
|
||||||
@@ -431,11 +421,13 @@ export const render = async (
|
|||||||
|
|
||||||
// calculate start and end points of the edge, note that the source and target
|
// calculate start and end points of the edge, note that the source and target
|
||||||
// can be modified for shapes that have ports
|
// can be modified for shapes that have ports
|
||||||
|
// @ts-ignore TODO: fix this
|
||||||
const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge);
|
const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, dir);
|
||||||
log.debug('abc78 source and target', source, target);
|
log.debug('abc78 source and target', source, target);
|
||||||
// Add the edge to the graph
|
// Add the edge to the graph
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
|
// @ts-ignore TODO: fix this
|
||||||
|
id: 'e' + edge.start + edge.end,
|
||||||
...edge,
|
...edge,
|
||||||
sources: [source],
|
sources: [source],
|
||||||
targets: [target],
|
targets: [target],
|
||||||
@@ -469,7 +461,6 @@ export const render = async (
|
|||||||
case 'RL':
|
case 'RL':
|
||||||
return 'LEFT';
|
return 'LEFT';
|
||||||
case 'TB':
|
case 'TB':
|
||||||
case 'TD': // TD is an alias for TB in Mermaid
|
|
||||||
return 'DOWN';
|
return 'DOWN';
|
||||||
case 'BT':
|
case 'BT':
|
||||||
return 'UP';
|
return 'UP';
|
||||||
@@ -493,118 +484,6 @@ export const render = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node bounds helpers (global)
|
|
||||||
const getEffectiveGroupWidth = (node: any): number => {
|
|
||||||
const labelW = node?.labels?.[0]?.width ?? 0;
|
|
||||||
const padding = node?.padding ?? 0;
|
|
||||||
return Math.max(node.width ?? 0, labelW + padding);
|
|
||||||
};
|
|
||||||
|
|
||||||
const boundsFor = (node: any): RectLike => {
|
|
||||||
const width = node?.isGroup ? getEffectiveGroupWidth(node) : node.width;
|
|
||||||
return {
|
|
||||||
x: node.offset.posX + node.width / 2,
|
|
||||||
y: node.offset.posY + node.height / 2,
|
|
||||||
width,
|
|
||||||
height: node.height,
|
|
||||||
padding: node.padding,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const cutter2 = (startNode: any, endNode: any, _points: any[]) => {
|
|
||||||
const startBounds = boundsFor(startNode);
|
|
||||||
const endBounds = boundsFor(endNode);
|
|
||||||
|
|
||||||
if (_points.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the original points array
|
|
||||||
const points: P[] = [..._points] as P[];
|
|
||||||
|
|
||||||
// The first point is the center of sNode, the last point is the center of eNode
|
|
||||||
const startCenter = points[0];
|
|
||||||
const endCenter = points[points.length - 1];
|
|
||||||
|
|
||||||
// Minimal, structured logging for diagnostics
|
|
||||||
log.debug('UIO cutter2: bounds', { startBounds, endBounds });
|
|
||||||
log.debug('UIO cutter2: original points', _points);
|
|
||||||
|
|
||||||
let firstOutsideStartIndex = -1;
|
|
||||||
|
|
||||||
// Single iteration through the array
|
|
||||||
for (const [i, point] of points.entries()) {
|
|
||||||
if (firstOutsideStartIndex === -1 && outsideNode(startBounds, point)) {
|
|
||||||
firstOutsideStartIndex = i;
|
|
||||||
}
|
|
||||||
if (outsideNode(endBounds, point)) {
|
|
||||||
// keep scanning; we'll also scan from the end for the last outside point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate intersection with start node if we found a point outside it
|
|
||||||
if (firstOutsideStartIndex !== -1) {
|
|
||||||
const outsidePointForStart = points[firstOutsideStartIndex];
|
|
||||||
const startIntersection = computeNodeIntersection(
|
|
||||||
startNode,
|
|
||||||
startBounds,
|
|
||||||
outsidePointForStart,
|
|
||||||
startCenter
|
|
||||||
);
|
|
||||||
log.debug('UIO cutter2: start intersection', startIntersection);
|
|
||||||
replaceEndpoint(points, 'start', startIntersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate intersection with end node
|
|
||||||
let outsidePointForEnd = null;
|
|
||||||
let outsideIndexForEnd = -1;
|
|
||||||
|
|
||||||
for (let i = points.length - 1; i >= 0; i--) {
|
|
||||||
if (outsideNode(endBounds, points[i])) {
|
|
||||||
outsidePointForEnd = points[i];
|
|
||||||
outsideIndexForEnd = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!outsidePointForEnd && points.length > 1) {
|
|
||||||
outsidePointForEnd = points[points.length - 2];
|
|
||||||
outsideIndexForEnd = points.length - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outsidePointForEnd) {
|
|
||||||
const endIntersection = computeNodeIntersection(
|
|
||||||
endNode,
|
|
||||||
endBounds,
|
|
||||||
outsidePointForEnd,
|
|
||||||
endCenter
|
|
||||||
);
|
|
||||||
log.debug('UIO cutter2: end intersection', { endIntersection, outsideIndexForEnd });
|
|
||||||
replaceEndpoint(points, 'end', endIntersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final cleanup: Check if the last point is too close to the previous point
|
|
||||||
if (points.length > 1) {
|
|
||||||
const lastPoint = points[points.length - 1];
|
|
||||||
const secondLastPoint = points[points.length - 2];
|
|
||||||
const distance = Math.sqrt(
|
|
||||||
(lastPoint.x - secondLastPoint.x) ** 2 + (lastPoint.y - secondLastPoint.y) ** 2
|
|
||||||
);
|
|
||||||
if (distance < 2) {
|
|
||||||
log.debug('UIO cutter2: trimming tail point (too close)', {
|
|
||||||
distance,
|
|
||||||
lastPoint,
|
|
||||||
secondLastPoint,
|
|
||||||
});
|
|
||||||
points.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug('UIO cutter2: final points', points);
|
|
||||||
|
|
||||||
return points;
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-ignore - ELK is not typed
|
// @ts-ignore - ELK is not typed
|
||||||
const elk = new ELK();
|
const elk = new ELK();
|
||||||
const element = svg.select('g');
|
const element = svg.select('g');
|
||||||
@@ -616,19 +495,17 @@ export const render = async (
|
|||||||
id: 'root',
|
id: 'root',
|
||||||
layoutOptions: {
|
layoutOptions: {
|
||||||
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
|
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
|
||||||
|
'elk.layered.crossingMinimization.forceNodeModelOrder':
|
||||||
|
data4Layout.config.elk?.forceNodeModelOrder,
|
||||||
|
'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,
|
||||||
|
|
||||||
'elk.algorithm': algorithm,
|
'elk.algorithm': algorithm,
|
||||||
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
|
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
|
||||||
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
|
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
|
||||||
'elk.direction': 'DOWN',
|
'elk.direction': 'DOWN',
|
||||||
'spacing.baseValue': 40,
|
'spacing.baseValue': 35,
|
||||||
'elk.layered.crossingMinimization.forceNodeModelOrder':
|
|
||||||
data4Layout.config.elk?.forceNodeModelOrder,
|
|
||||||
'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,
|
|
||||||
'elk.layered.unnecessaryBendpoints': true,
|
'elk.layered.unnecessaryBendpoints': true,
|
||||||
'elk.layered.cycleBreaking.strategy': data4Layout.config.elk?.cycleBreakingStrategy,
|
'elk.layered.cycleBreaking.strategy': data4Layout.config.elk?.cycleBreakingStrategy,
|
||||||
|
|
||||||
// 'elk.layered.cycleBreaking.strategy': 'GREEDY_MODEL_ORDER',
|
|
||||||
// 'elk.layered.cycleBreaking.strategy': 'MODEL_ORDER',
|
|
||||||
// 'spacing.nodeNode': 20,
|
// 'spacing.nodeNode': 20,
|
||||||
// 'spacing.nodeNodeBetweenLayers': 25,
|
// 'spacing.nodeNodeBetweenLayers': 25,
|
||||||
// 'spacing.edgeNode': 20,
|
// 'spacing.edgeNode': 20,
|
||||||
@@ -636,28 +513,22 @@ export const render = async (
|
|||||||
// 'spacing.edgeEdge': 10,
|
// 'spacing.edgeEdge': 10,
|
||||||
// 'spacing.edgeEdgeBetweenLayers': 20,
|
// 'spacing.edgeEdgeBetweenLayers': 20,
|
||||||
// 'spacing.nodeSelfLoop': 20,
|
// 'spacing.nodeSelfLoop': 20,
|
||||||
|
|
||||||
// Tweaking options
|
// Tweaking options
|
||||||
// 'nodePlacement.favorStraightEdges': true,
|
|
||||||
// 'elk.layered.nodePlacement.favorStraightEdges': true,
|
// 'elk.layered.nodePlacement.favorStraightEdges': true,
|
||||||
// 'nodePlacement.feedbackEdges': true,
|
// 'nodePlacement.feedbackEdges': true,
|
||||||
'elk.layered.wrapping.multiEdge.improveCuts': true,
|
// 'elk.layered.wrapping.multiEdge.improveCuts': true,
|
||||||
'elk.layered.wrapping.multiEdge.improveWrappedEdges': true,
|
// 'elk.layered.wrapping.multiEdge.improveWrappedEdges': true,
|
||||||
// 'elk.layered.wrapping.strategy': 'MULTI_EDGE',
|
// 'elk.layered.wrapping.strategy': 'MULTI_EDGE',
|
||||||
// 'elk.layered.wrapping.strategy': 'SINGLE_EDGE',
|
// 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY',
|
||||||
'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY',
|
// 'elk.layered.mergeHierarchyEdges': true,
|
||||||
'elk.layered.mergeHierarchyEdges': true,
|
|
||||||
|
|
||||||
// 'elk.layered.feedbackEdges': true,
|
// 'elk.layered.feedbackEdges': true,
|
||||||
// 'elk.layered.crossingMinimization.semiInteractive': true,
|
// 'elk.layered.crossingMinimization.semiInteractive': true,
|
||||||
// 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1,
|
// 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1,
|
||||||
// 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0,
|
// 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0,
|
||||||
// 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK',
|
// 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK',
|
||||||
// 'elk.insideSelfLoops.activate': true,
|
// 'elk.insideSelfLoops.activate': true,
|
||||||
// 'elk.separateConnectedComponents': true,
|
|
||||||
// 'elk.alg.layered.options.EdgeStraighteningStrategy': 'NONE',
|
// 'elk.alg.layered.options.EdgeStraighteningStrategy': 'NONE',
|
||||||
// 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES', // NODES_AND_EDGES
|
// 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES', // NODES_AND_EDGES
|
||||||
// 'elk.layered.considerModelOrder.strategy': 'EDGES', // NODES_AND_EDGES
|
|
||||||
// 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES
|
// 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES
|
||||||
},
|
},
|
||||||
children: [],
|
children: [],
|
||||||
@@ -667,7 +538,7 @@ export const render = async (
|
|||||||
log.info('Drawing flowchart using v4 renderer', elk);
|
log.info('Drawing flowchart using v4 renderer', elk);
|
||||||
|
|
||||||
// Set the direction of the graph based on the parsed information
|
// Set the direction of the graph based on the parsed information
|
||||||
const dir = data4Layout.direction ?? 'DOWN';
|
const dir = data4Layout.direction || 'DOWN';
|
||||||
elkGraph.layoutOptions['elk.direction'] = dir2ElkDirection(dir);
|
elkGraph.layoutOptions['elk.direction'] = dir2ElkDirection(dir);
|
||||||
|
|
||||||
// Create the lookup db for the subgraphs and their children to used when creating
|
// Create the lookup db for the subgraphs and their children to used when creating
|
||||||
@@ -698,16 +569,15 @@ export const render = async (
|
|||||||
|
|
||||||
// Subgraph
|
// Subgraph
|
||||||
if (parentLookupDb.childrenById[node.id] !== undefined) {
|
if (parentLookupDb.childrenById[node.id] !== undefined) {
|
||||||
// Set label and adjust node width separately (avoid side effects in labels array)
|
|
||||||
node.labels = [
|
node.labels = [
|
||||||
{
|
{
|
||||||
text: node.label,
|
text: node.label,
|
||||||
width: node?.labelData?.width ?? 50,
|
width: node?.labelData?.width || 50,
|
||||||
height: node?.labelData?.height ?? 50,
|
height: node?.labelData?.height || 50,
|
||||||
},
|
},
|
||||||
|
(node.width = node.width + 2 * node.padding),
|
||||||
|
log.debug('UIO node label', node?.labelData?.width, node.padding),
|
||||||
];
|
];
|
||||||
node.width = node.width + 2 * node.padding;
|
|
||||||
log.debug('UIO node label', node?.labelData?.width, node.padding);
|
|
||||||
node.layoutOptions = {
|
node.layoutOptions = {
|
||||||
'spacing.baseValue': 30,
|
'spacing.baseValue': 30,
|
||||||
'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]',
|
'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]',
|
||||||
@@ -771,7 +641,7 @@ export const render = async (
|
|||||||
try {
|
try {
|
||||||
g = await elk.layout(elkGraph);
|
g = await elk.layout(elkGraph);
|
||||||
log.debug('APA01 after - success');
|
log.debug('APA01 after - success');
|
||||||
log.info('APA01 layout result:', JSON.stringify(g, null, 2));
|
log.debug('APA01 layout result:', JSON.stringify(g, null, 2));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('APA01 ELK layout error:', error);
|
log.error('APA01 ELK layout error:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -832,10 +702,10 @@ export const render = async (
|
|||||||
// sw = Math.max(bbox.width, startNode.width, startNode.labels[0].width);
|
// sw = Math.max(bbox.width, startNode.width, startNode.labels[0].width);
|
||||||
sw = Math.max(startNode.width, startNode.labels[0].width + startNode.padding);
|
sw = Math.max(startNode.width, startNode.labels[0].width + startNode.padding);
|
||||||
// sw = startNode.width;
|
// sw = startNode.width;
|
||||||
log.info(
|
log.debug(
|
||||||
'UIO width',
|
'UIO width',
|
||||||
startNode.id,
|
startNode.id,
|
||||||
startNode.width,
|
startNode.with,
|
||||||
'bbox.width=',
|
'bbox.width=',
|
||||||
bbox.width,
|
bbox.width,
|
||||||
'lw=',
|
'lw=',
|
||||||
@@ -855,7 +725,7 @@ export const render = async (
|
|||||||
log.debug(
|
log.debug(
|
||||||
'UIO width',
|
'UIO width',
|
||||||
startNode.id,
|
startNode.id,
|
||||||
startNode.width,
|
startNode.with,
|
||||||
bbox.width,
|
bbox.width,
|
||||||
'EW = ',
|
'EW = ',
|
||||||
ew,
|
ew,
|
||||||
@@ -863,56 +733,38 @@ export const render = async (
|
|||||||
startNode.innerHTML
|
startNode.innerHTML
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
startNode.x = startNode.offset.posX + startNode.width / 2;
|
|
||||||
startNode.y = startNode.offset.posY + startNode.height / 2;
|
if (startNode.calcIntersect) {
|
||||||
endNode.x = endNode.offset.posX + endNode.width / 2;
|
const intersection = startNode.calcIntersect(
|
||||||
endNode.y = endNode.offset.posY + endNode.height / 2;
|
{
|
||||||
if (startNode.shape !== 'rect33') {
|
x: startNode.offset.posX + startNode.width / 2,
|
||||||
edge.points.unshift({
|
y: startNode.offset.posY + startNode.height / 2,
|
||||||
x: startNode.x,
|
width: startNode.width,
|
||||||
y: startNode.y,
|
height: startNode.height,
|
||||||
});
|
},
|
||||||
|
edge.points[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distance(intersection, edge.points[0]) > epsilon) {
|
||||||
|
edge.points.unshift(intersection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (endNode.shape !== 'rect33') {
|
if (endNode.calcIntersect) {
|
||||||
edge.points.push({
|
const intersection = endNode.calcIntersect(
|
||||||
x: endNode.x,
|
{
|
||||||
y: endNode.y,
|
x: endNode.offset.posX + endNode.width / 2,
|
||||||
});
|
y: endNode.offset.posY + endNode.height / 2,
|
||||||
|
width: endNode.width,
|
||||||
|
height: endNode.height,
|
||||||
|
},
|
||||||
|
edge.points[edge.points.length - 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distance(intersection, edge.points[edge.points.length - 1]) > epsilon) {
|
||||||
|
edge.points.push(intersection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug and sanitize points around cutter2
|
|
||||||
const prevPoints = Array.isArray(edge.points) ? [...edge.points] : [];
|
|
||||||
log.debug('UIO cutter2: Points before cutter2:', prevPoints);
|
|
||||||
edge.points = cutter2(startNode, endNode, prevPoints);
|
|
||||||
const hasNaN = (pts: { x: number; y: number }[]) =>
|
|
||||||
pts?.some((p) => !Number.isFinite(p?.x) || !Number.isFinite(p?.y));
|
|
||||||
if (!Array.isArray(edge.points) || edge.points.length < 2 || hasNaN(edge.points)) {
|
|
||||||
log.warn(
|
|
||||||
'UIO cutter2: Invalid points from cutter2, falling back to prevPoints',
|
|
||||||
edge.points
|
|
||||||
);
|
|
||||||
// Fallback to previous points and strip any invalid ones just in case
|
|
||||||
const cleaned = prevPoints.filter((p) => Number.isFinite(p?.x) && Number.isFinite(p?.y));
|
|
||||||
edge.points = cleaned.length >= 2 ? cleaned : prevPoints;
|
|
||||||
}
|
|
||||||
log.debug('UIO cutter2: Points after cutter2 (sanitized):', edge.points);
|
|
||||||
// Remove consecutive duplicate points to avoid zero-length segments in path builders
|
|
||||||
const deduped = edge.points.filter(
|
|
||||||
(p: { x: number; y: number }, i: number, arr: { x: number; y: number }[]) => {
|
|
||||||
if (i === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const prev = arr[i - 1];
|
|
||||||
return Math.abs(p.x - prev.x) > 1e-6 || Math.abs(p.y - prev.y) > 1e-6;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (deduped.length !== edge.points.length) {
|
|
||||||
log.debug('UIO cutter2: removed consecutive duplicate points', {
|
|
||||||
before: edge.points,
|
|
||||||
after: deduped,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
edge.points = deduped;
|
|
||||||
const paths = insertEdge(
|
const paths = insertEdge(
|
||||||
edgesEl,
|
edgesEl,
|
||||||
edge,
|
edge,
|
||||||
@@ -920,10 +772,8 @@ export const render = async (
|
|||||||
data4Layout.type,
|
data4Layout.type,
|
||||||
startNode,
|
startNode,
|
||||||
endNode,
|
endNode,
|
||||||
data4Layout.diagramId,
|
data4Layout.diagramId
|
||||||
true
|
|
||||||
);
|
);
|
||||||
log.info('APA12 edge points after insert', JSON.stringify(edge.points));
|
|
||||||
|
|
||||||
edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
|
edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
|
||||||
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
|
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
|
||||||
|
12
packages/mermaid-layout-tidy-tree/CHANGELOG.md
Normal file
12
packages/mermaid-layout-tidy-tree/CHANGELOG.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# @mermaid-js/layout-tidy-tree
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [[`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800), [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20), [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05), [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8)]:
|
||||||
|
- mermaid@11.11.0
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mermaid-js/layout-tidy-tree",
|
"name": "@mermaid-js/layout-tidy-tree",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "Tidy-tree layout engine for mermaid",
|
"description": "Tidy-tree layout engine for mermaid",
|
||||||
"module": "dist/mermaid-layout-tidy-tree.core.mjs",
|
"module": "dist/mermaid-layout-tidy-tree.core.mjs",
|
||||||
"types": "dist/layouts.d.ts",
|
"types": "dist/layouts.d.ts",
|
||||||
|
@@ -1,5 +1,19 @@
|
|||||||
# mermaid
|
# mermaid
|
||||||
|
|
||||||
|
## 11.11.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#6704](https://github.com/mermaid-js/mermaid/pull/6704) [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05) Thanks [@omkarht](https://github.com/omkarht)! - feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.
|
||||||
|
|
||||||
|
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#6905](https://github.com/mermaid-js/mermaid/pull/6905) [`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Render newlines as spaces in class diagrams
|
||||||
|
|
||||||
|
- [#6886](https://github.com/mermaid-js/mermaid/pull/6886) [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Handle arrows correctly when auto number is enabled
|
||||||
|
|
||||||
## 11.10.0
|
## 11.10.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid",
|
"name": "mermaid",
|
||||||
"version": "11.10.0",
|
"version": "11.11.0",
|
||||||
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
|
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "./dist/mermaid.core.mjs",
|
"module": "./dist/mermaid.core.mjs",
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
"katex": "^0.16.22",
|
"katex": "^0.16.22",
|
||||||
"khroma": "^2.1.0",
|
"khroma": "^2.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"marked": "^16.0.0",
|
"marked": "^15.0.7",
|
||||||
"roughjs": "^4.6.6",
|
"roughjs": "^4.6.6",
|
||||||
"stylis": "^4.3.6",
|
"stylis": "^4.3.6",
|
||||||
"ts-dedent": "^2.2.0",
|
"ts-dedent": "^2.2.0",
|
||||||
|
@@ -4,9 +4,6 @@ import { internalHelpers } from '../internals.js';
|
|||||||
import { log } from '../logger.js';
|
import { log } from '../logger.js';
|
||||||
import type { LayoutData } from './types.js';
|
import type { LayoutData } from './types.js';
|
||||||
|
|
||||||
// console.log('MUST be removed, this only for keeping dev server working');
|
|
||||||
// import tmp from './layout-algorithms/dagre/index.js';
|
|
||||||
|
|
||||||
export interface RenderOptions {
|
export interface RenderOptions {
|
||||||
algorithm?: string;
|
algorithm?: string;
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
import { evaluate } from '../../diagrams/common/common.js';
|
import { evaluate, getUrl } from '../../diagrams/common/common.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import { createText } from '../createText.js';
|
import { createText } from '../createText.js';
|
||||||
import utils from '../../utils.js';
|
import utils from '../../utils.js';
|
||||||
import {
|
import { getLineFunctionsWithOffset } from '../../utils/lineWithOffset.js';
|
||||||
getLineFunctionsWithOffset,
|
|
||||||
markerOffsets,
|
|
||||||
markerOffsets2,
|
|
||||||
} from '../../utils/lineWithOffset.js';
|
|
||||||
import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js';
|
import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -31,8 +27,8 @@ import createLabel from './createLabel.js';
|
|||||||
import { addEdgeMarkers } from './edgeMarker.ts';
|
import { addEdgeMarkers } from './edgeMarker.ts';
|
||||||
import { isLabelStyle } from './shapes/handDrawnShapeStyles.js';
|
import { isLabelStyle } from './shapes/handDrawnShapeStyles.js';
|
||||||
|
|
||||||
export const edgeLabels = new Map();
|
const edgeLabels = new Map();
|
||||||
export const terminalLabels = new Map();
|
const terminalLabels = new Map();
|
||||||
|
|
||||||
export const clear = () => {
|
export const clear = () => {
|
||||||
edgeLabels.clear();
|
edgeLabels.clear();
|
||||||
@@ -59,7 +55,7 @@ export const insertEdgeLabel = async (elem, edge) => {
|
|||||||
const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
|
const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
|
||||||
|
|
||||||
// Create inner g, label, this will be positioned now for centering the text
|
// Create inner g, label, this will be positioned now for centering the text
|
||||||
const label = edgeLabel.insert('g').attr('class', 'label').attr('data-id', edge.id);
|
const label = edgeLabel.insert('g').attr('class', 'label');
|
||||||
label.node().appendChild(labelElement);
|
label.node().appendChild(labelElement);
|
||||||
|
|
||||||
// Center the label
|
// Center the label
|
||||||
@@ -442,33 +438,7 @@ const fixCorners = function (lineData) {
|
|||||||
}
|
}
|
||||||
return newLineData;
|
return newLineData;
|
||||||
};
|
};
|
||||||
const generateDashArray = (len, oValueS, oValueE) => {
|
export const insertEdge = function (elem, edge, clusterDb, diagramType, startNode, endNode, id) {
|
||||||
const middleLength = len - oValueS - oValueE;
|
|
||||||
const dashLength = 2; // Length of each dash
|
|
||||||
const gapLength = 2; // Length of each gap
|
|
||||||
const dashGapPairLength = dashLength + gapLength;
|
|
||||||
|
|
||||||
// Calculate number of complete dash-gap pairs that can fit
|
|
||||||
const numberOfPairs = Math.floor(middleLength / dashGapPairLength);
|
|
||||||
|
|
||||||
// Generate the middle pattern array
|
|
||||||
const middlePattern = Array(numberOfPairs).fill(`${dashLength} ${gapLength}`).join(' ');
|
|
||||||
|
|
||||||
// Combine all parts
|
|
||||||
const dashArray = `0 ${oValueS} ${middlePattern} ${oValueE}`;
|
|
||||||
|
|
||||||
return dashArray;
|
|
||||||
};
|
|
||||||
export const insertEdge = function (
|
|
||||||
elem,
|
|
||||||
edge,
|
|
||||||
clusterDb,
|
|
||||||
diagramType,
|
|
||||||
startNode,
|
|
||||||
endNode,
|
|
||||||
id,
|
|
||||||
skipIntersect = false
|
|
||||||
) {
|
|
||||||
const { handDrawnSeed } = getConfig();
|
const { handDrawnSeed } = getConfig();
|
||||||
let points = edge.points;
|
let points = edge.points;
|
||||||
let pointsHasChanged = false;
|
let pointsHasChanged = false;
|
||||||
@@ -482,12 +452,11 @@ export const insertEdge = function (
|
|||||||
edgeClassStyles.push(edge.cssCompiledStyles[key]);
|
edgeClassStyles.push(edge.cssCompiledStyles[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug('UIO intersect check', edge.points, head.x, tail.x);
|
if (head.intersect && tail.intersect) {
|
||||||
if (head.intersect && tail.intersect && !skipIntersect) {
|
|
||||||
points = points.slice(1, edge.points.length - 1);
|
points = points.slice(1, edge.points.length - 1);
|
||||||
points.unshift(tail.intersect(points[0]));
|
points.unshift(tail.intersect(points[0]));
|
||||||
log.debug(
|
log.debug(
|
||||||
'Last point UIO',
|
'Last point APA12',
|
||||||
edge.start,
|
edge.start,
|
||||||
'-->',
|
'-->',
|
||||||
edge.end,
|
edge.end,
|
||||||
@@ -497,7 +466,6 @@ export const insertEdge = function (
|
|||||||
);
|
);
|
||||||
points.push(head.intersect(points[points.length - 1]));
|
points.push(head.intersect(points[points.length - 1]));
|
||||||
}
|
}
|
||||||
const pointsStr = btoa(JSON.stringify(points));
|
|
||||||
if (edge.toCluster) {
|
if (edge.toCluster) {
|
||||||
log.info('to cluster abc88', clusterDb.get(edge.toCluster));
|
log.info('to cluster abc88', clusterDb.get(edge.toCluster));
|
||||||
points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node);
|
points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node);
|
||||||
@@ -561,10 +529,6 @@ export const insertEdge = function (
|
|||||||
curve = curveBasis;
|
curve = curveBasis;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (edge.curve) {
|
|
||||||
// curve = edge.curve;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const { x, y } = getLineFunctionsWithOffset(edge);
|
const { x, y } = getLineFunctionsWithOffset(edge);
|
||||||
const lineFunction = line().x(x).y(y).curve(curve);
|
const lineFunction = line().x(x).y(y).curve(curve);
|
||||||
|
|
||||||
@@ -596,14 +560,10 @@ export const insertEdge = function (
|
|||||||
strokeClasses += ' edge-pattern-solid';
|
strokeClasses += ' edge-pattern-solid';
|
||||||
}
|
}
|
||||||
let svgPath;
|
let svgPath;
|
||||||
let linePath =
|
let linePath = lineFunction(lineData);
|
||||||
edge.curve === 'rounded'
|
const edgeStyles = Array.isArray(edge.style) ? edge.style : edge.style ? [edge.style] : [];
|
||||||
? generateRoundedPath(applyMarkerOffsetsToPoints(lineData, edge), 5)
|
|
||||||
: lineFunction(lineData);
|
|
||||||
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
|
||||||
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
|
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
|
||||||
|
|
||||||
let animatedEdge = false;
|
|
||||||
if (edge.look === 'handDrawn') {
|
if (edge.look === 'handDrawn') {
|
||||||
const rc = rough.svg(elem);
|
const rc = rough.svg(elem);
|
||||||
Object.assign([], lineData);
|
Object.assign([], lineData);
|
||||||
@@ -634,10 +594,7 @@ export const insertEdge = function (
|
|||||||
animationClass = ' edge-animation-' + edge.animation;
|
animationClass = ' edge-animation-' + edge.animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathStyle =
|
const pathStyle = stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles;
|
||||||
(stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) +
|
|
||||||
';' +
|
|
||||||
(edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
|
||||||
svgPath = elem
|
svgPath = elem
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', linePath)
|
.attr('d', linePath)
|
||||||
@@ -647,39 +604,11 @@ export const insertEdge = function (
|
|||||||
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '')
|
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '')
|
||||||
)
|
)
|
||||||
.attr('style', pathStyle);
|
.attr('style', pathStyle);
|
||||||
|
|
||||||
//eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
|
|
||||||
strokeColor = pathStyle.match(/stroke:([^;]+)/)?.[1];
|
strokeColor = pathStyle.match(/stroke:([^;]+)/)?.[1];
|
||||||
|
|
||||||
// Possible fix to remove eslint-disable-next-line
|
|
||||||
//strokeColor = /stroke:([^;]+)/.exec(pathStyle)?.[1];
|
|
||||||
|
|
||||||
animatedEdge =
|
|
||||||
edge.animate === true || !!edge.animation || stylesFromClasses.includes('animation');
|
|
||||||
const pathNode = svgPath.node();
|
|
||||||
const len = typeof pathNode.getTotalLength === 'function' ? pathNode.getTotalLength() : 0;
|
|
||||||
const oValueS = markerOffsets2[edge.arrowTypeStart] || 0;
|
|
||||||
const oValueE = markerOffsets2[edge.arrowTypeEnd] || 0;
|
|
||||||
|
|
||||||
if (edge.look === 'neo' && !animatedEdge) {
|
|
||||||
const dashArray =
|
|
||||||
edge.pattern === 'dotted' || edge.pattern === 'dashed'
|
|
||||||
? generateDashArray(len, oValueS, oValueE)
|
|
||||||
: `0 ${oValueS} ${len - oValueS - oValueE} ${oValueE}`;
|
|
||||||
|
|
||||||
// No offset needed because we already start with a zero-length dash that effectively sets us up for a gap at the start.
|
|
||||||
const mOffset = `stroke-dasharray: ${dashArray}; stroke-dashoffset: 0;`;
|
|
||||||
svgPath.attr('style', mOffset + svgPath.attr('style'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MC Special
|
// DEBUG code, DO NOT REMOVE
|
||||||
svgPath.attr('data-edge', true);
|
// adds a red circle at each edge coordinate
|
||||||
svgPath.attr('data-et', 'edge');
|
|
||||||
svgPath.attr('data-id', edge.id);
|
|
||||||
svgPath.attr('data-points', pointsStr);
|
|
||||||
|
|
||||||
// DEBUG code, adds a red circle at each edge coordinate
|
|
||||||
// cornerPoints.forEach((point) => {
|
// cornerPoints.forEach((point) => {
|
||||||
// elem
|
// elem
|
||||||
// .append('circle')
|
// .append('circle')
|
||||||
@@ -689,33 +618,24 @@ export const insertEdge = function (
|
|||||||
// .attr('cx', point.x)
|
// .attr('cx', point.x)
|
||||||
// .attr('cy', point.y);
|
// .attr('cy', point.y);
|
||||||
// });
|
// });
|
||||||
if (edge.showPoints) {
|
// lineData.forEach((point) => {
|
||||||
lineData.forEach((point) => {
|
// elem
|
||||||
elem
|
// .append('circle')
|
||||||
.append('circle')
|
// .style('stroke', 'red')
|
||||||
.style('stroke', 'red')
|
// .style('fill', 'red')
|
||||||
.style('fill', 'red')
|
// .attr('r', 1)
|
||||||
.attr('r', 1)
|
// .attr('cx', point.x)
|
||||||
.attr('cx', point.x)
|
// .attr('cy', point.y);
|
||||||
.attr('cy', point.y);
|
// });
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = '';
|
let url = '';
|
||||||
if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
|
if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
|
||||||
url =
|
url = getUrl(true);
|
||||||
window.location.protocol +
|
|
||||||
'//' +
|
|
||||||
window.location.host +
|
|
||||||
window.location.pathname +
|
|
||||||
window.location.search;
|
|
||||||
url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
|
||||||
}
|
}
|
||||||
log.info('arrowTypeStart', edge.arrowTypeStart);
|
log.info('arrowTypeStart', edge.arrowTypeStart);
|
||||||
log.info('arrowTypeEnd', edge.arrowTypeEnd);
|
log.info('arrowTypeEnd', edge.arrowTypeEnd);
|
||||||
|
|
||||||
const useMargin = !animatedEdge && edge?.look === 'neo';
|
addEdgeMarkers(svgPath, edge, url, id, diagramType, strokeColor);
|
||||||
addEdgeMarkers(svgPath, edge, url, id, diagramType, useMargin, strokeColor);
|
|
||||||
const midIndex = Math.floor(points.length / 2);
|
const midIndex = Math.floor(points.length / 2);
|
||||||
const point = points[midIndex];
|
const point = points[midIndex];
|
||||||
if (!utils.isLabelCoordinateInPath(point, svgPath.attr('d'))) {
|
if (!utils.isLabelCoordinateInPath(point, svgPath.attr('d'))) {
|
||||||
@@ -729,134 +649,3 @@ export const insertEdge = function (
|
|||||||
paths.originalPath = edge.points;
|
paths.originalPath = edge.points;
|
||||||
return paths;
|
return paths;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates SVG path data with rounded corners from an array of points.
|
|
||||||
* @param {Array} points - Array of points in the format [{x: Number, y: Number}, ...]
|
|
||||||
* @param {Number} radius - The radius of the rounded corners
|
|
||||||
* @returns {String} - SVG path data string
|
|
||||||
*/
|
|
||||||
function generateRoundedPath(points, radius) {
|
|
||||||
if (points.length < 2) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = '';
|
|
||||||
const size = points.length;
|
|
||||||
const epsilon = 1e-5;
|
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
const currPoint = points[i];
|
|
||||||
const prevPoint = points[i - 1];
|
|
||||||
const nextPoint = points[i + 1];
|
|
||||||
|
|
||||||
if (i === 0) {
|
|
||||||
// Move to the first point
|
|
||||||
path += `M${currPoint.x},${currPoint.y}`;
|
|
||||||
} else if (i === size - 1) {
|
|
||||||
// Last point, draw a straight line to the final point
|
|
||||||
path += `L${currPoint.x},${currPoint.y}`;
|
|
||||||
} else {
|
|
||||||
// Calculate vectors for incoming and outgoing segments
|
|
||||||
const dx1 = currPoint.x - prevPoint.x;
|
|
||||||
const dy1 = currPoint.y - prevPoint.y;
|
|
||||||
const dx2 = nextPoint.x - currPoint.x;
|
|
||||||
const dy2 = nextPoint.y - currPoint.y;
|
|
||||||
|
|
||||||
const len1 = Math.hypot(dx1, dy1);
|
|
||||||
const len2 = Math.hypot(dx2, dy2);
|
|
||||||
|
|
||||||
// Prevent division by zero
|
|
||||||
if (len1 < epsilon || len2 < epsilon) {
|
|
||||||
path += `L${currPoint.x},${currPoint.y}`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the vectors
|
|
||||||
const nx1 = dx1 / len1;
|
|
||||||
const ny1 = dy1 / len1;
|
|
||||||
const nx2 = dx2 / len2;
|
|
||||||
const ny2 = dy2 / len2;
|
|
||||||
|
|
||||||
// Calculate the angle between the vectors
|
|
||||||
const dot = nx1 * nx2 + ny1 * ny2;
|
|
||||||
// Clamp the dot product to avoid numerical issues with acos
|
|
||||||
const clampedDot = Math.max(-1, Math.min(1, dot));
|
|
||||||
const angle = Math.acos(clampedDot);
|
|
||||||
|
|
||||||
// Skip rounding if the angle is too small or too close to 180 degrees
|
|
||||||
if (angle < epsilon || Math.abs(Math.PI - angle) < epsilon) {
|
|
||||||
path += `L${currPoint.x},${currPoint.y}`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the distance to offset the control point
|
|
||||||
const cutLen = Math.min(radius / Math.sin(angle / 2), len1 / 2, len2 / 2);
|
|
||||||
|
|
||||||
// Calculate the start and end points of the curve
|
|
||||||
const startX = currPoint.x - nx1 * cutLen;
|
|
||||||
const startY = currPoint.y - ny1 * cutLen;
|
|
||||||
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}`;
|
|
||||||
|
|
||||||
// Draw the quadratic Bezier curve
|
|
||||||
path += `Q${currPoint.x},${currPoint.y} ${endX},${endY}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
// Helper function to calculate delta and angle between two points
|
|
||||||
function calculateDeltaAndAngle(point1, point2) {
|
|
||||||
if (!point1 || !point2) {
|
|
||||||
return { angle: 0, deltaX: 0, deltaY: 0 };
|
|
||||||
}
|
|
||||||
const deltaX = point2.x - point1.x;
|
|
||||||
const deltaY = point2.y - point1.y;
|
|
||||||
const angle = Math.atan2(deltaY, deltaX);
|
|
||||||
return { angle, deltaX, deltaY };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to adjust the first and last points of the points array
|
|
||||||
function applyMarkerOffsetsToPoints(points, edge) {
|
|
||||||
// Copy the points array to avoid mutating the original data
|
|
||||||
const newPoints = points.map((point) => ({ ...point }));
|
|
||||||
|
|
||||||
// Handle the first point (start of the edge)
|
|
||||||
if (points.length >= 2 && markerOffsets[edge.arrowTypeStart]) {
|
|
||||||
const offsetValue = markerOffsets[edge.arrowTypeStart];
|
|
||||||
|
|
||||||
const point1 = points[0];
|
|
||||||
const point2 = points[1];
|
|
||||||
|
|
||||||
const { angle } = calculateDeltaAndAngle(point1, point2);
|
|
||||||
|
|
||||||
const offsetX = offsetValue * Math.cos(angle);
|
|
||||||
const offsetY = offsetValue * Math.sin(angle);
|
|
||||||
|
|
||||||
newPoints[0].x = point1.x + offsetX;
|
|
||||||
newPoints[0].y = point1.y + offsetY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the last point (end of the edge)
|
|
||||||
const n = points.length;
|
|
||||||
if (n >= 2 && markerOffsets[edge.arrowTypeEnd]) {
|
|
||||||
const offsetValue = markerOffsets[edge.arrowTypeEnd];
|
|
||||||
|
|
||||||
const point1 = points[n - 1];
|
|
||||||
const point2 = points[n - 2];
|
|
||||||
|
|
||||||
const { angle } = calculateDeltaAndAngle(point2, point1);
|
|
||||||
|
|
||||||
const offsetX = offsetValue * Math.cos(angle);
|
|
||||||
const offsetY = offsetValue * Math.sin(angle);
|
|
||||||
|
|
||||||
newPoints[n - 1].x = point1.x - offsetX;
|
|
||||||
newPoints[n - 1].y = point1.y - offsetY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPoints;
|
|
||||||
}
|
|
||||||
|
@@ -4,22 +4,12 @@ import type { EdgeData, Point } from '../types.js';
|
|||||||
// under any transparent markers.
|
// under any transparent markers.
|
||||||
// The offsets are calculated from the markers' dimensions.
|
// The offsets are calculated from the markers' dimensions.
|
||||||
export const markerOffsets = {
|
export const markerOffsets = {
|
||||||
aggregation: 17.25,
|
aggregation: 18,
|
||||||
extension: 17.25,
|
extension: 18,
|
||||||
composition: 17.25,
|
composition: 18,
|
||||||
dependency: 6,
|
dependency: 6,
|
||||||
lollipop: 13.5,
|
lollipop: 13.5,
|
||||||
arrow_point: 4,
|
arrow_point: 4,
|
||||||
//arrow_cross: 24,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// We need to draw the lines a bit shorter to avoid drawing
|
|
||||||
// under any transparent markers.
|
|
||||||
// The offsets are calculated from the markers' dimensions.
|
|
||||||
export const markerOffsets2 = {
|
|
||||||
arrow_point: 9,
|
|
||||||
arrow_cross: 12.5,
|
|
||||||
arrow_circle: 12.5,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,7 +104,6 @@ export const getLineFunctionsWithOffset = (
|
|||||||
adjustment *= DIRECTION === 'right' ? -1 : 1;
|
adjustment *= DIRECTION === 'right' ? -1 : 1;
|
||||||
offset += adjustment;
|
offset += adjustment;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pointTransformer(d).x + offset;
|
return pointTransformer(d).x + offset;
|
||||||
},
|
},
|
||||||
y: function (
|
y: function (
|
||||||
|
@@ -1,5 +1,19 @@
|
|||||||
# mermaid
|
# mermaid
|
||||||
|
|
||||||
|
## 11.11.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#6704](https://github.com/mermaid-js/mermaid/pull/6704) [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05) Thanks [@omkarht](https://github.com/omkarht)! - feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.
|
||||||
|
|
||||||
|
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#6905](https://github.com/mermaid-js/mermaid/pull/6905) [`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Render newlines as spaces in class diagrams
|
||||||
|
|
||||||
|
- [#6886](https://github.com/mermaid-js/mermaid/pull/6886) [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Handle arrows correctly when auto number is enabled
|
||||||
|
|
||||||
## 11.10.0
|
## 11.10.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mermaid-js/tiny",
|
"name": "@mermaid-js/tiny",
|
||||||
"version": "11.10.0",
|
"version": "11.11.0",
|
||||||
"description": "Tiny version of mermaid",
|
"description": "Tiny version of mermaid",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"main": "./dist/mermaid.tiny.js",
|
"main": "./dist/mermaid.tiny.js",
|
||||||
|
81
pnpm-lock.yaml
generated
81
pnpm-lock.yaml
generated
@@ -269,8 +269,8 @@ importers:
|
|||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
marked:
|
marked:
|
||||||
specifier: ^16.0.0
|
specifier: ^15.0.7
|
||||||
version: 16.2.1
|
version: 15.0.12
|
||||||
roughjs:
|
roughjs:
|
||||||
specifier: ^4.6.6
|
specifier: ^4.6.6
|
||||||
version: 4.6.6(patch_hash=3543d47108cb41b68ec6a671c0e1f9d0cfe2ce524fea5b0992511ae84c3c6b64)
|
version: 4.6.6(patch_hash=3543d47108cb41b68ec6a671c0e1f9d0cfe2ce524fea5b0992511ae84c3c6b64)
|
||||||
@@ -7445,9 +7445,9 @@ packages:
|
|||||||
markdown-table@3.0.4:
|
markdown-table@3.0.4:
|
||||||
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||||
|
|
||||||
marked@16.2.1:
|
marked@15.0.12:
|
||||||
resolution: {integrity: sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==}
|
resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==}
|
||||||
engines: {node: '>= 20'}
|
engines: {node: '>= 18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
marked@4.3.0:
|
marked@4.3.0:
|
||||||
@@ -14430,6 +14430,14 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
|
|
||||||
|
'@unocss/astro@66.4.2(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))':
|
||||||
|
dependencies:
|
||||||
|
'@unocss/core': 66.4.2
|
||||||
|
'@unocss/reset': 66.4.2
|
||||||
|
'@unocss/vite': 66.4.2(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
|
|
||||||
'@unocss/cli@66.4.2':
|
'@unocss/cli@66.4.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
@@ -14574,6 +14582,19 @@ snapshots:
|
|||||||
unplugin-utils: 0.2.4
|
unplugin-utils: 0.2.4
|
||||||
vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
|
|
||||||
|
'@unocss/vite@66.4.2(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))':
|
||||||
|
dependencies:
|
||||||
|
'@ampproject/remapping': 2.3.0
|
||||||
|
'@unocss/config': 66.4.2
|
||||||
|
'@unocss/core': 66.4.2
|
||||||
|
'@unocss/inspector': 66.4.2
|
||||||
|
chokidar: 3.6.0
|
||||||
|
magic-string: 0.30.17
|
||||||
|
pathe: 2.0.3
|
||||||
|
tinyglobby: 0.2.14
|
||||||
|
unplugin-utils: 0.2.4
|
||||||
|
vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
|
|
||||||
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
|
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -14637,6 +14658,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
vite-plugin-pwa: 1.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0)
|
vite-plugin-pwa: 1.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0)
|
||||||
|
|
||||||
|
'@vite-pwa/vitepress@1.0.0(vite-plugin-pwa@1.0.0(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0))':
|
||||||
|
dependencies:
|
||||||
|
vite-plugin-pwa: 1.0.0(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0)
|
||||||
|
|
||||||
'@vitejs/plugin-vue@5.2.1(vite@5.4.19(@types/node@22.13.5)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))':
|
'@vitejs/plugin-vue@5.2.1(vite@5.4.19(@types/node@22.13.5)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 5.4.19(@types/node@22.13.5)(terser@5.39.0)
|
vite: 5.4.19(@types/node@22.13.5)(terser@5.39.0)
|
||||||
@@ -14648,6 +14673,12 @@ snapshots:
|
|||||||
vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
vue: 3.5.13(typescript@5.7.3)
|
vue: 3.5.13(typescript@5.7.3)
|
||||||
|
|
||||||
|
'@vitejs/plugin-vue@6.0.0(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
'@rolldown/pluginutils': 1.0.0-beta.19
|
||||||
|
vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
|
vue: 3.5.13(typescript@5.7.3)
|
||||||
|
|
||||||
'@vitest/coverage-v8@3.0.6(vitest@3.0.6)':
|
'@vitest/coverage-v8@3.0.6(vitest@3.0.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
@@ -19145,7 +19176,7 @@ snapshots:
|
|||||||
|
|
||||||
markdown-table@3.0.4: {}
|
markdown-table@3.0.4: {}
|
||||||
|
|
||||||
marked@16.2.1: {}
|
marked@15.0.12: {}
|
||||||
|
|
||||||
marked@4.3.0: {}
|
marked@4.3.0: {}
|
||||||
|
|
||||||
@@ -21812,6 +21843,33 @@ snapshots:
|
|||||||
- postcss
|
- postcss
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
unocss@66.4.2(postcss@8.5.6)(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)):
|
||||||
|
dependencies:
|
||||||
|
'@unocss/astro': 66.4.2(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))
|
||||||
|
'@unocss/cli': 66.4.2
|
||||||
|
'@unocss/core': 66.4.2
|
||||||
|
'@unocss/postcss': 66.4.2(postcss@8.5.6)
|
||||||
|
'@unocss/preset-attributify': 66.4.2
|
||||||
|
'@unocss/preset-icons': 66.4.2
|
||||||
|
'@unocss/preset-mini': 66.4.2
|
||||||
|
'@unocss/preset-tagify': 66.4.2
|
||||||
|
'@unocss/preset-typography': 66.4.2
|
||||||
|
'@unocss/preset-uno': 66.4.2
|
||||||
|
'@unocss/preset-web-fonts': 66.4.2
|
||||||
|
'@unocss/preset-wind': 66.4.2
|
||||||
|
'@unocss/preset-wind3': 66.4.2
|
||||||
|
'@unocss/preset-wind4': 66.4.2
|
||||||
|
'@unocss/transformer-attributify-jsx': 66.4.2
|
||||||
|
'@unocss/transformer-compile-class': 66.4.2
|
||||||
|
'@unocss/transformer-directives': 66.4.2
|
||||||
|
'@unocss/transformer-variant-group': 66.4.2
|
||||||
|
'@unocss/vite': 66.4.2(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- postcss
|
||||||
|
- supports-color
|
||||||
|
|
||||||
unpipe@1.0.0: {}
|
unpipe@1.0.0: {}
|
||||||
|
|
||||||
unplugin-utils@0.2.4:
|
unplugin-utils@0.2.4:
|
||||||
@@ -21966,6 +22024,17 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
vite-plugin-pwa@1.0.0(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0):
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.0
|
||||||
|
pretty-bytes: 6.1.1
|
||||||
|
tinyglobby: 0.2.12
|
||||||
|
vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)
|
||||||
|
workbox-build: 7.1.1(@types/babel__core@7.20.5)
|
||||||
|
workbox-window: 7.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
vite@5.4.19(@types/node@22.13.5)(terser@5.39.0):
|
vite@5.4.19(@types/node@22.13.5)(terser@5.39.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.21.5
|
esbuild: 0.21.5
|
||||||
|
Reference in New Issue
Block a user