Open
Description
Context
Currently, the SVG output for state diagrams makes it difficult to programmatically identify, style, or manipulate starting and ending points. They are represented as undifferentiated <ellipse>
elements.
Specifically:
- Starting and ending points can only be distinguished by their radius, which is not robust for automated processing or accessibility.
- It's impossible to determine which composite state a start/end point belongs to (without complex graphical analysis of bounding boxes).
- This lack of structured identification hinders custom styling, JavaScript interaction, and integration with other tools that consume the generated SVG.
Proposed solution
- Group the two ellipses that form an ending point within a single
<g>
element. - Add distinct CSS classes, like
start
andend
. - Assign unique
id
to these elements, ideally the one already used in links (related to [SVG] Allow uniqueid
for SVGgroup
elements #2080).
Exemple
(click on the image to access the PlantUML editor)
Current SVG output
<path d="M74,7 L196,7 A12.5,12.5 0 0 1 208.5,19.5 L208.5,33.2969 L61.5,33.2969 L61.5,19.5 A12.5,12.5 0 0 1 74,7" fill="#F1F1F1"/>
<rect fill="none" height="98.2969" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="147" x="61.5" y="7"/>
<line style="stroke:#181818;stroke-width:0.5;" x1="61.5" x2="208.5" y1="33.2969" y2="33.2969"/>
<text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="9.5771" x="130.2114" y="24.9951">A</text>
<ellipse cx="82.5" cy="70.2969" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1;"/>
<g id="A.B">
<rect fill="#F1F1F1" height="50" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="50" x="110.5" y="45.2969"/>
<line style="stroke:#181818;stroke-width:0.5;" x1="110.5" x2="160.5" y1="71.5938" y2="71.5938"/>
<text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="9.6045" x="130.6978" y="63.292">B</text>
</g>
<ellipse cx="189.5" cy="70.2969" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1;"/>
<ellipse cx="189.5" cy="70.2969" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1;"/>
<!--link *start*A to B-->
<g class="link" data-entity-1="*start*A" data-entity-2="B" data-source-line="2" data-uid="lnk4" id="link_*start*A_B">
<path d="M92.85,70.2969 C98.63,70.2969 98.4,70.2969 104.18,70.2969" fill="none" id="*start*A-to-B" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="110.18,70.2969,101.18,66.2969,105.18,70.2969,101.18,74.2969,110.18,70.2969" style="stroke:#181818;stroke-width:1;"/>
</g>
<!--link B to *end*A-->
<g class="link" data-entity-1="B" data-entity-2="*end*A" data-source-line="3" data-uid="lnk6" id="link_B_*end*A">
<path d="M160.81,70.2969 C166.64,70.2969 166.47,70.2969 172.29,70.2969" fill="none" id="B-to-*end*A" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="178.29,70.2969,169.29,66.2969,173.29,70.2969,169.29,74.2969,178.29,70.2969" style="stroke:#181818;stroke-width:1;"/>
</g>
<ellipse cx="16" cy="56.15" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1;"/>
<ellipse cx="255" cy="56.15" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1;"/>
<ellipse cx="255" cy="56.15" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1;"/>
<!--link *start* to A-->
<g class="link" data-entity-1="*start*" data-entity-2="A" data-source-line="6" data-uid="lnk8" id="link_*start*_A">
<path d="M26.23,56.15 C37.84,56.15 43.45,56.15 55.07,56.15" fill="none" id="*start*-to-A" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="61.07,56.15,52.07,52.15,56.07,56.15,52.07,60.15,61.07,56.15" style="stroke:#181818;stroke-width:1;"/>
</g>
<!--link A to *end*-->
<g class="link" data-entity-1="A" data-entity-2="*end*" data-source-line="7" data-uid="lnk10" id="link_A_*end*">
<path d="M208.59,56.15 C220.32,56.15 226.04,56.15 237.76,56.15" fill="none" id="A-to-*end*" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="243.76,56.15,234.76,52.15,238.76,56.15,234.76,60.15,243.76,56.15" style="stroke:#181818;stroke-width:1;"/>
</g>
Suggested SVG output
<path d="M74,7 L196,7 A12.5,12.5 0 0 1 208.5,19.5 L208.5,33.2969 L61.5,33.2969 L61.5,19.5 A12.5,12.5 0 0 1 74,7" fill="#F1F1F1"/>
<rect fill="none" height="98.2969" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="147" x="61.5" y="7"/>
<line style="stroke:#181818;stroke-width:0.5;" x1="61.5" x2="208.5" y1="33.2969" y2="33.2969"/>
<text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="9.5771" x="130.2114" y="24.9951">A</text>
<ellipse id="*start*A" class="start" cx="82.5" cy="70.2969" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1;"/>
<g id="A.B">
<rect fill="#F1F1F1" height="50" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="50" x="110.5" y="45.2969"/>
<line style="stroke:#181818;stroke-width:0.5;" x1="110.5" x2="160.5" y1="71.5938" y2="71.5938"/>
<text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="9.6045" x="130.6978" y="63.292">B</text>
</g>
<g id="*end*A" class="end" style="stroke:#222222;stroke-width:1;">
<ellipse cx="189.5" cy="70.2969" fill="none" rx="11" ry="11" />
<ellipse cx="189.5" cy="70.2969" fill="#222222" rx="6" ry="6" />
</g>
<!--link *start*A to B-->
<g class="link" data-entity-1="*start*A" data-entity-2="B" data-source-line="2" data-uid="lnk4" id="link_*start*A_B">
<path d="M92.85,70.2969 C98.63,70.2969 98.4,70.2969 104.18,70.2969" fill="none" id="*start*A-to-B" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="110.18,70.2969,101.18,66.2969,105.18,70.2969,101.18,74.2969,110.18,70.2969" style="stroke:#181818;stroke-width:1;"/>
</g>
<!--link B to *end*A-->
<g class="link" data-entity-1="B" data-entity-2="*end*A" data-source-line="3" data-uid="lnk6" id="link_B_*end*A">
<path d="M160.81,70.2969 C166.64,70.2969 166.47,70.2969 172.29,70.2969" fill="none" id="B-to-*end*A" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="178.29,70.2969,169.29,66.2969,173.29,70.2969,169.29,74.2969,178.29,70.2969" style="stroke:#181818;stroke-width:1;"/>
</g>
<ellipse id="*start*" class="start" cx="16" cy="56.15" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1;"/>
<g id="*end*" class="end" style="stroke:#222222;stroke-width:1;">
<ellipse cx="255" cy="56.15" fill="none" rx="11" ry="11" />
<ellipse cx="255" cy="56.15" fill="#222222" rx="6" ry="6" />
</g>
<!--link *start* to A-->
<g class="link" data-entity-1="*start*" data-entity-2="A" data-source-line="6" data-uid="lnk8" id="link_*start*_A">
<path d="M26.23,56.15 C37.84,56.15 43.45,56.15 55.07,56.15" fill="none" id="*start*-to-A" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="61.07,56.15,52.07,52.15,56.07,56.15,52.07,60.15,61.07,56.15" style="stroke:#181818;stroke-width:1;"/>
</g>
<!--link A to *end*-->
<g class="link" data-entity-1="A" data-entity-2="*end*" data-source-line="7" data-uid="lnk10" id="link_A_*end*">
<path d="M208.59,56.15 C220.32,56.15 226.04,56.15 237.76,56.15" fill="none" id="A-to-*end*" style="stroke:#181818;stroke-width:1;"/>
<polygon fill="#181818" points="243.76,56.15,234.76,52.15,238.76,56.15,234.76,60.15,243.76,56.15" style="stroke:#181818;stroke-width:1;"/>
</g>