# How to use the edge routing of LayeredDigraphEmbedding when specifying explicit vertex coordinates?

The "LayeredDigraphEmbedding" GraphLayout uses an edge routing algorithm which produces nice and readable diagrams and curved edges.

g = Graph[{1 -> 2, 2 -> 3, 1 -> 4, 4 -> 3, 4 -> 2, 4 -> 5},
EdgeStyle -> Arrowheads[{{Medium, 0.5}}], VertexLabels -> "Name",
GraphLayout -> "LayeredDigraphEmbedding"]


How can I access this edge routing algorithm without also letting it position vertices? It seems that as soon as I fix vertex coordinates, it ceases working:

coord = GraphEmbedding[g];

SetProperty[g, VertexCoordinates -> coord]


Why do I need this? First I would like to plot an acyclic directed graph like the one above, then I would like to add additional edges (some of which introduce cycles) without disturbing either the layout of the vertices or the layout of the already existing edges. For example, I might want to add an additional edge 1 -> 3 to the above graph (without overlapping with 1 -> 2 -> 3), then yet another one from 5 -> 1 which makes it acyclic. All this time I want to keep the exiting vertex and edge locations untouched.

Update: A more convenient way than the original post is to generate a seperate graph with only the new edges and combine the GraphicsGroupBoxes of the two graphs:

ClearAll[graphAddF]
GraphicsGroupBox[x_] :> x[[1]], {0, Infinity}][[1]]},
Replace[ToBoxes[#], GraphicsGroupBox[{x_, y_}] :>
GraphicsGroupBox[{{x, gg2}, y}], {0, Infinity}]]] &;


Examples:

g = Graph[{1 -> 2, 2 -> 3, 1 -> 4, 4 -> 3, 4 -> 2, 4 -> 5},
EdgeStyle -> Arrowheads[{{Medium, 0.5}}], BaseStyle -> Thick,
VertexLabels -> "Name", GraphLayout -> "LayeredDigraphEmbedding",
ImagePadding -> 10, ImageSize -> 300];

{newedgesa, newedgesb} = {{1 -> 3, 5 -> 1}, {1 -> 3, 5 -> 1, 3 -> 5}};
{curvaturesa, curvaturesb} = {{-0.5, 0}, {-0.5, 0, 0}};

g2 = Graph[newedgesa,  VertexCoordinates -> GraphEmbedding[g][[{1, 3, 5}]],
ImageSize -> 200, BaseStyle -> {Thick, Red},
EdgeShapeFunction -> Thread[newedgesa -> (curvedArcF[{{Large, .75}}] /@ curvaturesa)]];
g3 = Graph[newedgesb,  VertexCoordinates -> GraphEmbedding[g][[{1, 3, 5}]],
ImageSize -> 200, BaseStyle -> {Thick, Red},
EdgeShapeFunction -> Thread[newedgesb -> (curvedArcF[{{Large, .75}}] /@ curvaturesb)]];



Original post:

A work-around until someone posts a more direct answer on the inner workings of edge routing in various embeddings:

to keep the existing vertex and edge locations untouched

We can construct EdgeShapeFunctions extracting the edge primitives from the box expression of a graph:

ClearAll[edgeRoutesF, curvedArcF]
edgeRoutesF[g_Graph] := Module[{grgrp = Cases[ToBoxes[g],
GraphicsGroupBox[x_] :> (x[[1]] /. {dirs___, sb : StyleBox[_, __] ..} :>
{StyleBox[#, ## & @@Flatten[{dirs, #2}]] & @@@ {sb}} /.
{DynamicLocation[v1_, ___], mid___, DynamicLocation[v2_, ___]} :> {v1, mid, v2}),
{0, Infinity}][[1, 1]], edges},
edges = Cases[grgrp, {v1_String, mid___, v2_String} :>
(DirectedEdge @@ (ToExpression /@ StringSplit[{v1, v2}, "\$"][[All, -1]])),
{0, Infinity}];
Thread[edges -> (Function /@ (grgrp /. {v1_String, mid___, v2_String} :>
{#[[1]], mid, #[[2]]}))]]


To construct BezierCurves for the newly added edges we can use the built-in (but undocumented) EdgeShapeFunction "CurvedArc":

curvedArcF[ah_: {{Medium, .5}}][curv_: .5] := Composition[Style[#, Arrowheads[ah]] &,
Arrow, GraphElementData[{"CurvedArc", "Curvature" -> curv}]]


Examples:

g = Graph[{1 -> 2, 2 -> 3, 1 -> 4, 4 -> 3, 4 -> 2, 4 -> 5},
EdgeStyle -> Arrowheads[{{Medium, 0.5}}], VertexLabels -> "Name",
GraphLayout -> "LayeredDigraphEmbedding", ImagePadding -> 10, ImageSize -> 300];

newedgesa = DirectedEdge @@@ {{1, 3}, {5, 1}};
curvaturesa = {-.5, -1.5};
edgeshapesa = Join[edgeRoutesF@g,
Thread[newedgesa -> (curvedArcF[{{Large, .75}}] /@ curvaturesa)]];

{EdgeShapeFunction -> edgeshapesa, VertexCoordinates -> GraphEmbedding[g],
ImageSize -> 400}];

newedgesb = Join[newedgesa, {DirectedEdge[3, 5]}];
curvaturesb = Join[curvaturesa, {0}];
edgeshapesb = Join[edgeRoutesF@g,
Thread[newedgesb -> (curvedArcF[{{Large, .75}}] /@ curvaturesb)]];


With GraphLayout -> "SpringElectricalEmbedding" and curvaturesa = {1.5, .5} we get
Row[{g, ga, gb}]