VertexRenderingFunction (or EdgeRenderingFunction) with movable nodes?

In GraphPlot, I'd like to use VertexRenderingFunction (VRF) and have the resulting object behave the same way under dragging verticies as it does when I do not use VRF. The same issue occurs for EdgeRenderingFunction (ERF) and dragging edges.

Here's a MWE:

g = {1 -> 2, 2 -> 3, 3 -> 4, 4 -> 1};
{
GraphPlot[g]
,
GraphPlot[g, VertexRenderingFunction -> ({Blue, Disk[#1, 0.01]} &)]
}


Without VRF, I can click (sufficiently many times) on a node, drag it around, and the graph deforms without losing connections between the node and edges - see element one in the picture below. With VRF, if I click on a node, it behaves like a distinct graphics object that does not maintain connectivity to the appropriate edges - element two in the picture.

I'm guessing this has to do with how Mathematica generates the graphs since their InputForms are different:

 InputForm[GraphPlot[g]]

(*Graphics[Annotation[GraphicsComplex[{{0., 0.9997532360813222},
{0.9993931236462025, 1.0258160108662504}, {1.0286626995939243,
0.026431169015735057}, {0.02872413637035287, 0.}},
{{RGBColor[0.5, 0., 0.], Line[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]},
{RGBColor[0, 0, 0.7], Tooltip[Point[1], 1], Tooltip[Point[2], 2],
Tooltip[Point[3], 3], Tooltip[Point[4], 4]}}, {}],
VertexCoordinateRules -> {{0., 0.9997532360813222},
{0.9993931236462025, 1.0258160108662504}, {1.0286626995939243,
0.026431169015735057}, {0.02872413637035287, 0.}}],
FrameTicks -> None, PlotRange -> All, PlotRangePadding ->
Scaled[0.1], AspectRatio -> Automatic]*)


vs.

In[159]:= InputForm[GraphPlot[g, VertexRenderingFunction -> ({Blue, Disk[#1, 0.01]} &)]]

(* Out[159]//InputForm=
Graphics[Annotation[GraphicsGroup[
{GraphicsComplex[{{0., 0.9997532360813222}, {0.9993931236462025,
1.0258160108662504}, {1.0286626995939243, 0.026431169015735057},
{0.02872413637035287, 0.}}, {RGBColor[0.5, 0., 0.],
Line[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]}, {}],
{{RGBColor[0, 0, 1], Disk[{0., 0.9997532360813222}, 0.01]},
{RGBColor[0, 0, 1], Disk[{0.9993931236462025,
1.0258160108662504}, 0.01]}, {RGBColor[0, 0, 1],
Disk[{1.0286626995939243, 0.026431169015735057}, 0.01]},
{RGBColor[0, 0, 1], Disk[{0.02872413637035287, 0.}, 0.01]}}},
ContentSelectable -> True], VertexCoordinateRules -> {{0.,
0.9997532360813222}, {0.9993931236462025, 1.0258160108662504},
{1.0286626995939243, 0.026431169015735057}, {0.02872413637035287,
0.}}], FrameTicks -> None, PlotRange -> All,
PlotRangePadding -> Scaled[0.1], AspectRatio -> Automatic]*)


I'm interested in this because I want to draw more complicated graphs with a subset of vertices styled one way, another subset styled a different way, and so on. In this post, someone suggested just dragging around the rendered vertices after moving the edges, but:

• This option is not feasible for more than a handful of verticies, especially if the verticies are supposed to be labeled in special ways.
• If I want to use both ERF and VRF, then I have to move every piece of the graph individually.
• It seems if Mathematica creates the non-VRF graphs in a way I like, then I should be able to duplicate the behavior on my own VRF, but I couldn't work out how from the InputForms.
• The ability to click and drag edges is important because Mathematica does not render graphs the way I want them to appear (even under various "Methods->___"). So any solutions with different graph options (i.e. using Graph) should also offer a way to arbitrarily deform the output graph.
• Please see the abstraction withVRF that I added. Feb 23, 2015 at 6:42
• I edited the title in an effort to make it more concise and descriptive; I hope you do not mind. Feb 23, 2015 at 6:48
• Fine with me. And your solution worked perfectly, thanks! Feb 23, 2015 at 22:04

1 Answer

For the vertices to be correctly movable their primitives must be positioned using the indexes of GraphicsComplex rather than direct Graphics coordinates. This way all primitives with the same index move together. I don't know of a way to make VertexRenderingFunction use these indexes natively, though I have not explored it. However since the default vertex points are created within the GraphicsComplex we can apply the vertex function in post-processing to achieve your goal:

g = {1 -> 2, 2 -> 3, 3 -> 4, 4 -> 1};

GraphPlot[g] /. Point[n_Integer] :> {Blue, Disk[n, 0.1]}


If you wish to use vertex labels you will need a slightly different replacement:

GraphPlot[g] /.
Tooltip[Point[n_Integer], label_] :>
{Blue, Disk[n, 0.1], White, Text[label ~Style~ 18, n]}


If you already have your VertexRenderingFunction written here is an abstraction:

withVRF[VRF_][args__] := GraphPlot[args] /.
Tooltip[Point[n_Integer], label_] :> VRF[n, label]


Example:

vertex = {White, EdgeForm[Black], Disk[#, .1], Black, Text[#2, #1]} &;

withVRF[vertex][g, DirectedEdges -> True]