Forcefully draw edges on top of each other

Hello,

I came to working with graphviz over a tool called wireviz. The author uses graphviz to visualize cable connections and wire colors.

To make the resulting graph visually appealing he uses 2 colors (black, wire color, black) on edges. In reality there might also be 2 colored wires which now is solved by using 5 colors on a single edge (black, color1, color2, color1, black).

I would prefer to get a banded look on 2 colored wires, which can be achieved (in a way) by defining color weights but than there is no black border anymore.

color="#ffffff;0.1:#00ff00;0.1:#ffffff;0.1:#00ff00;0.1:#ffffff;0.1:#00ff00;0.1:#ffffff;0.1:#00ff00;0.1:#ffffff;
0.1:#00ff00;0.1"

While playing around with the graph description in the dot language I found a way to get graphviz to draw multiple edges exactly on top of each other, so I can use different penwidth and style to get a good result but I’m not sure if this behaviour is actually a bug in graphviz.

Here the dot code.

graph {
// Graph generated by WireViz
// https://github.com/formatc1702/WireViz
	graph [bgcolor="#ffffff" fontname=arial nodesep=0.33 rankdir=LR ranksep=2]
	
	node [fillcolor=white fontname=arial shape=record style=filled]
	X1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td balign="left">X1</td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td balign="left">Stewart Connector SS-37000-002</td><td balign="left">male</td><td balign="left">8-pin</td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td>DA+</td><td port="p1r">1</td></tr><tr><td>DA-</td><td port="p2r">2</td></tr><tr><td>DB+</td><td port="p3r">3</td></tr><tr><td>DC+</td><td port="p4r">4</td></tr><tr><td>DC-</td><td port="p5r">5</td></tr><tr><td>DB-</td><td port="p6r">6</td></tr><tr><td>DD+</td><td port="p7r">7</td></tr><tr><td>DD-</td><td port="p8r">8</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=none style=filled]
	X2 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td balign="left">X2</td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td balign="left">Stewart Connector SS-37000-002</td><td balign="left">male</td><td balign="left">8-pin</td></tr></table></td></tr><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td port="p1l">1</td><td>DB+</td></tr><tr><td port="p2l">2</td><td>DB-</td></tr><tr><td port="p3l">3</td><td>DA+</td></tr><tr><td port="p4l">4</td><td>DD+</td></tr><tr><td port="p5l">5</td><td>DD-</td></tr><tr><td port="p6l">6</td><td>DA-</td></tr><tr><td port="p7l">7</td><td>DC+</td></tr><tr><td port="p8l">8</td><td>DC-</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=none style=filled]
	W1 [label=<<table border="0" cellspacing="0" cellpadding="0"><tr><td><table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr><td colspan="4">W1</td></tr><tr><td balign="left">CAT5e</td><td balign="left">8x</td><td balign="left">24 AWG</td><td balign="left">1 m</td></tr></table></td></tr><tr><td>&nbsp;</td></tr><tr><td><table border="0" cellspacing="0" cellborder="0"><tr><td>X1:1</td><td>WHGN</td><td>X2:3</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w1" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#00ff00" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>X1:2</td><td>GN</td><td>X2:6</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w2" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#00ff00" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#00ff00" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#00ff00" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>X1:3</td><td>WHOG</td><td>X2:1</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w3" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ff8000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>X1:4</td><td>BU</td><td>X2:7</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w4" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#0066ff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#0066ff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#0066ff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>X1:5</td><td>WHBU</td><td>X2:8</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w5" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#0066ff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>X1:6</td><td>OG</td><td>X2:2</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w6" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ff8000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ff8000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ff8000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>X1:7</td><td>WHBN</td><td>X2:4</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w7" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#895956" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#ffffff" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>X1:8</td><td>BN</td><td>X2:5</td></tr><tr><td colspan="3" border="0" cellspacing="0" cellpadding="0" port="w8" height="10"><table cellspacing="0" cellborder="0" border = "0"><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#895956" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#895956" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#895956" border="0"></td></tr><tr><td colspan="3" cellpadding="0" height="2" bgcolor="#000000" border="0"></td></tr></table></td></tr><tr><td>&nbsp;</td></tr></table></td></tr></table>> fillcolor=white margin=0 shape=box style=""]
	

	edge [penwidth=10.0 color="#000000"]
	X1:p1r:e -- W1:w1:w
	W1:w1:e -- X2:p3l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#ffffff;0.1:#00ff00;0.1:#ffffff;0.1:#00ff00;0.1:#ffffff;0.1:#00ff00;0.1:#ffffff;0.1:#00ff00;0.1:#ffffff;0.1:#00ff00;0.1"]
	X1:p1r:e -- W1:w1:w
	W1:w1:e -- X2:p3l:w

	edge [penwidth=10.0 color="#000000"];
	X1:p2r:e -- W1:w2:w
	W1:w2:e -- X2:p6l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#00ff00"]
	X1:p2r:e -- W1:w2:w
	W1:w2:e -- X2:p6l:w

	edge [penwidth=10.0 color="#000000"];
	X1:p3r:e -- W1:w3:w
	W1:w3:e -- X2:p1l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#ffffff;0.1:#ff8000;0.1:#ffffff;0.1:#ff8000;0.1:#ffffff;0.1:#ff8000;0.1:#ffffff;0.1:#ff8000;0.1:#ffffff;0.1:#ff8000;0.1"]
	X1:p3r:e -- W1:w3:w
	W1:w3:e -- X2:p1l:w

	edge [penwidth=10.0 color="#000000"];
	X1:p4r:e -- W1:w4:w
	W1:w4:e -- X2:p7l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#0066ff"]
	X1:p4r:e -- W1:w4:w
	W1:w4:e -- X2:p7l:w

	edge [penwidth=10.0 color="#000000"];
	X1:p5r:e -- W1:w5:w
	W1:w5:e -- X2:p8l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#ffffff;0.1:#0066ff;0.1:#ffffff;0.1:#0066ff;0.1:#ffffff;0.1:#0066ff;0.1:#ffffff;0.1:#0066ff;0.1:#ffffff;0.1:#0066ff;0.1"]
	X1:p5r:e -- W1:w5:w
	W1:w5:e -- X2:p8l:w

	edge [penwidth=10.0 color="#000000"];
	X1:p6r:e -- W1:w6:w
	W1:w6:e -- X2:p2l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#ff8000"]
	X1:p6r:e -- W1:w6:w
	W1:w6:e -- X2:p2l:w
	
	edge [penwidth=10.0 color="#000000"];
	X1:p7r:e -- W1:w7:w
	W1:w7:e -- X2:p4l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#ffffff;0.1:#895956;0.1:#ffffff;0.1:#895956;0.1:#ffffff;0.1:#895956;0.1:#ffffff;0.1:#895956;0.1:#ffffff;0.1:#895956;0.1"]
	X1:p7r:e -- W1:w7:w
	W1:w7:e -- X2:p4l:w

	edge [penwidth=10.0 color="#000000"];
	X1:p8r:e -- W1:w8:w
	W1:w8:e -- X2:p5l:w
	edge [style="invis"]
	X1 -- W1
	W1 -- X2
	edge [penwidth=6.0 style="" color="#895956"]
	X1:p8r:e -- W1:w8:w
	W1:w8:e -- X2:p5l:w
}

I’m not sure why this is working so here my question: Is there any way to force graphviz to draw edges on top of each other? Or are there any more sophisticated possibilities to style edges?

1 Like

What specific part(s) of this file are iffy to you?
Could you provide a simpler graph with one set of edges?

1 Like

Sure here is a minimal example:

ex10.gv

graph {
	A:e -- X:w [penwidth=9.0 color="#000000"]
	//A -- X [color="#ff0000"]
	A:e -- X:w [penwidth=3.0 style="" color="#ffff00"]

	B:e -- Y:w [penwidth=9.0 color="#000000"]
	B -- Y [color="#ff0000"]
	B:e -- Y:w [penwidth=3.0 style="" color="#ffff00"]

	C -- Z [color="#ff0000"]
	C:e -- Z:w [penwidth=9.0 color="#000000"]
	C:e -- Z:w [penwidth=3.0 style="" color="#ffff00"]
}

without the red edge, the black and the yellow edge are drawn next to each other so that both edges are visible.
If I add the red edge in between black and yellow, yellow is drawn exactly on top of the black edge. I would expect that the output is the same, if the red would be drawn first or last. But this is not the case here. I haven’t found any documentation about how the order of the edge definition matters to the drawing algorithm. Can you confirm that this behavior is intended?

1 Like

[NOTE: I am not a Graphviz developer, just a “friend of the family”. I don’t speak for the developers]
Cool, I think you found a fascinating “accidental” result of a complex set of algorithms. Not documented that I know of, but not necessarily a bug. A gray area.
But can you depend on it, probably, but let me suggest a “more” legal alternative.
The basic idea is :

  1. you define the complete graph excluding all the “on-top” edges
  2. run it through dot with this command: dot -Tdot myfile.gv >myfile.dot
  3. you modify (edit) myfile.dot duplicating the “bottom” edges to create the “top” edges
  • change attribute values (color, penwidth, style, …) as needed
  • this process can be done manually (see below), with any programming language, or with the Graphviz GVPR language
    Then run the edited myfile.dot through this command: neato -n2 -Tpng myfie.dot >myfile.png (note the -n2 option)
    The FAQ has a section that helps explain, as does the documentation on dot as an output file format
    Here is an example of the result of step 3:
    graph {
    graph [bb=“0,0,252,108”];
    node [label="\N"];
    A [height=0.5,
    pos=“27,90”,
    width=0.75];
    X [height=0.5,
    pos=“81,18”,
    width=0.75];
    A:e – X:w [color="#000000",
    penwidth=9.0,
    pos=“55,90 71.006,90 36.994,18 53,18”];
    // duplicated by hand
    A:e – X:w [color="#ff00ff",
    penwidth=3.0,
    pos=“55,90 71.006,90 36.994,18 53,18”];
    B [height=0.5,
    pos=“99,90”,
    width=0.75];
    Y [height=0.5,
    pos=“153,18”,
    width=0.75];
    B:e – Y:w [color="#000000",
    penwidth=9.0,
    pos=“127,90 143.01,90 108.99,18 125,18”];
    // again
    B:e – Y:w [color="#ffff00",
    penwidth=3.0,
    pos=“127,90 143.01,90 108.99,18 125,18”];
    C [height=0.5,
    pos=“171,90”,
    width=0.75];
    Z [height=0.5,
    pos=“225,18”,
    width=0.75];
    C:e – Z:w [color="#000000",
    penwidth=9.0,
    pos=“199,90 215.01,90 180.99,18 197,18”];
    // number 3
    C:e – Z:w [color="#00ffff",
    penwidth=3.0,
    pos=“199,90 215.01,90 180.99,18 197,18”];
    }

Convoluted, yes. But definitely within the bounds of legal.
If you decide to try this, you could add “extra” attributes to the edges you wanted to duplicate like dup_pen=3 and dupcolor=blue (documented as legal). to give instructions to your script.

1 Like

Wow I just play around with your suggestion and this is SUPER helpful. This solves basically all my problems, since I can force graphviz to layout some things for me and add to the layout later without it messing everything up. Very nice solution indeed! Thank you very much. I will come around later and show some results when I’m done with the scripting. :+1:

1 Like