neo4j-labs / arrows.app

A graph drawing application
https://arrows.app
Apache License 2.0
107 stars 16 forks source link

for SVG export, please group svg elements by node and edge #45

Closed dobbs closed 2 years ago

dobbs commented 2 years ago

Please improve your SVG export to semantically group the svg elements within <g class="node"> and <g class="edge"> elements. It would also be helpful to semantically group labels and properties in the exported SVG.

Our specific use case follows.

In federated wiki, we have instrumented graphviz diagrams with click behavior that allows readers to navigate their site through visual graphs. We love graphviz, but some graphs want to be laid-out by hand and we find Arrows.app to be fantastic for this.

The example SVG below is exported by graphviz and shows the semantic grouping. Please notice that each node in the graph is represented in SVG as a <g class="node"> containing a <path> and one or more <text> elements. Edges are similarly grouped as a <g class="edge"> with <path> elements for the arc and arrowhead, and <text> elements of label.

Those group tags make it very easy to create a general-purpose click handler for our navigation behavior. From any clicked element we can find the closest parent <g> that is a node or an edge and then search for all the <text> nodes within to determine the title of a wiki page to open. It is worth noting that graphviz exports can use many different shapes for the nodes besides <path>. But our click handler only needs to look for the <g> and <text> elements.

The absence of those tags in the SVG exported from Arrows.app make it prohibitively difficult to distinguish a node label from a node name or from an edge label.

<svg width="201pt" height="44pt" viewBox="0.00 0.00 200.98 44.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 40)">
<title>%0</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-40 196.979,-40 196.979,4 -4,4"/>
<!-- A -->
<g id="node1" class="node">
  <title>A</title>
  <path fill="#d3d3d3" stroke="#000000" d="M42,-36C42,-36 12,-36 12,-36 6,-36 0,-30 0,-24 0,-24 0,-12 0,-12 0,-6 6,0 12,0 12,0 42,0 42,0 48,0 54,-6 54,-12 54,-12 54,-24 54,-24 54,-30 48,-36 42,-36"/>
  <text text-anchor="middle" x="27" y="-13.8" font-family="Times,serif" font-size="14.00" fill="#000000">A</text>
</g>
<!-- B -->
<g id="node2" class="node">
<title>B</title>
  <path fill="#d3d3d3" stroke="#000000" d="M180.979,-36C180.979,-36 150.979,-36 150.979,-36 144.979,-36 138.979,-30 138.979,-24 138.979,-24 138.979,-12 138.979,-12 138.979,-6 144.979,0 150.979,0 150.979,0 180.979,0 180.979,0 186.979,0 192.979,-6 192.979,-12 192.979,-12 192.979,-24 192.979,-24 192.979,-30 186.979,-36 180.979,-36"/>
  <text text-anchor="middle" x="165.979" y="-13.8" font-family="Times,serif" font-size="14.00" fill="#000000">B</text>
</g>
<!-- A&#45;&gt;B -->
<g id="edge1" class="edge">
  <title>A-&gt;B</title>
  <path fill="none" stroke="#000000" d="M54.1265,-18C75.3625,-18 105.1342,-18 128.6237,-18"/>
  <polygon fill="#000000" stroke="#000000" points="128.921,-21.5001 138.9209,-18 128.9209,-14.5001 128.921,-21.5001"/>
  <text text-anchor="middle" x="96.4895" y="-22.2" font-family="Times,serif" font-size="14.00" fill="#000000">connects</text>
</g>
</g>
</svg>
apcj commented 2 years ago

Hi @dobbs thanks for the suggestion!

I got round to adding the groups and class attributes as you suggest. There is also some deeper nesting to reduce duplication.

Here is an example in the new format:

<svg width="470" height="108" viewBox="0 0 470 108">
    <defs>
        <style type="text/css"></style>
    </defs>
    <g transform="translate(106 54) scale(1)">
        <g class="relationship">
            <g transform="translate(-52 0) rotate(0)" stroke-width="5" stroke="#000000">
                <path d="M 59 0 L 277.7113392878259 0"></path>
                <polygon
                        points="-24.8621506177483,0 -27.62461179749811,9.20820393249937 0,0 -27.62461179749811,-9.20820393249937"
                        fill="#000000" transform="translate(302.5734899055742 0) rotate(0)" stroke="none"></polygon>
            </g>
            <g transform="translate(116.35566964391296 0) rotate(0) translate(0 -13)">
                <g transform="translate(0 0)">
                    <g transform="translate(-35.22265625 0)" fill="#ffffff" stroke="#000000" stroke-width="0">
                        <rect x="0" y="0" width="70.4453125" height="26" rx="5" ry="5" stroke="none"></rect>
                        <text xml:space="preserve" x="5" y="17.45703125" stroke="none" text-anchor="left"
                              font-family="sans-serif" font-size="16" font-weight="normal" fill="#000000">KNOWS</text>
                    </g>
                </g>
            </g>
        </g>
        <g class="node">
            <g fill="#ffffff" stroke="#000000" stroke-width="4">
                <circle cx="-52" cy="0" r="52"></circle>
            </g>
            <g transform="translate(-52 0)">
                <g transform="scale(1) translate(0 0)">
                    <g transform="translate(0 0)">
                        <g font-family="sans-serif" font-size="50" font-weight="normal" fill="#000000"
                           text-anchor="middle">
                            <text xml:space="preserve" x="0" y="13.92822265625" stroke="none">a</text>
                        </g>
                    </g>
                </g>
            </g>
        </g>
        <g class="node">
            <g fill="#ffffff" stroke="#000000" stroke-width="4">
                <circle cx="309.5734899055742" cy="0" r="52"></circle>
            </g>
            <g transform="translate(309.5734899055742 0)">
                <g transform="scale(1) translate(0 0)">
                    <g transform="translate(0 0)">
                        <g font-family="sans-serif" font-size="50" font-weight="normal" fill="#000000"
                           text-anchor="middle">
                            <text xml:space="preserve" x="0" y="13.92822265625" stroke="none">b</text>
                        </g>
                    </g>
                </g>
            </g>
        </g>
    </g>
</svg>

I know this has take a long time to respond to, but hopefully still useful for you?

dobbs commented 2 years ago

@apcj, many thanks for adding this and no worries about the wait. I'll see about building a click handler around this new structure—should be quite straightforward now. Thank you, again.