7
$\begingroup$

I am trying to program a method to manually move around graph vertices, and then output the new coordinates. I'd like to turn this into a function that can be called with a variable number of vertices, but can't figure out how to get manipulate to work, despite looking at a variety of examples, including https://mathematica.stackexchange.com/questions/59826/variable-number-of-controls-in-manipulate.

The code to set up the manipulate parameters and control specifications is:

Clear[vc0, el0, verts0, controls0, gr0];
gr0 = RandomGraph[{10, 21}];
vc0 = VertexCoordinates /. AbsoluteOptions[gr0, VertexCoordinates];
el0 = EdgeList[gr0];
verts0 = Table[makeVar["v", i], {i, 10}]
controls0 = Table[{{makeVar["v", i], vc0[[i]]}, Locator}, {i, 10}]

This gives me a nice list of vertex variables and Locatorspecifications with initial coordinates for the graph:

{v1, v2, v3, v4, v5, v6, v7, v8, v9, v10}

{{{v1, {0.589112, 1.61609}}, Locator}, {{v2, {1.45239, 1.55683}}, Locator}, 
 {{v3, {0.914629, 0.269689}}, Locator}, {{v4, {1.55473, 0.213739}}, Locator}, 
 {{v5, {0., 0.845834}}, Locator}, {{v6, {2.13173, 0.}}, Locator}, 
 {{v7, {2.43365, 1.79731}}, Locator}, {{v8, {1.01283, 0.998538}}, Locator}, 
 {{v9, {2.21884, 0.899871}}, Locator}, {{v10, {1.70324, 1.0327}}, Locator}}

If I cut and paste these into Manipulate the code looks like:

Manipulate[{Graph[el0, EdgeStyle -> LightGray, 
  VertexCoordinates -> {v1, v2, v3, v4, v5, v6, v7, v8, v9, v10}, 
  VertexLabels -> "Name", ImageSize -> Large];
  coords = Grid[Partition[Flatten[Riffle[Table["v" <> ToString[i], {i, 10}], 
    VertexCoordinates /. AbsoluteOptions[gr5, VertexCoordinates]]], 3], 
  Alignment -> Left];
  Row[{gr5, coords}]}[[1]],
 {{v1, {0.589112, 1.61609}}, Locator}, {{v2, {1.45239, 1.55683}}, Locator}, 
 {{v3, {0.914629, 0.269689}}, Locator}, {{v4, {1.55473, 0.213739}}, Locator}, 
 {{v5, {0., 0.845834}}, Locator}, {{v6, {2.13173, 0.}}, Locator}, 
 {{v7, {2.43365, 1.79731}}, Locator}, {{v8, {1.01283, 0.998538}}, Locator}, 
 {{v9, {2.21884, 0.899871}}, Locator}, {{v10, {1.70324, 1.0327}}, Locator}}]

Coordinate Rearrangement

What I really want is a more general solution which looks like:

Manipulate[{Graph[el0, EdgeStyle -> LightGray, 

 (* vertex variables here *) 
 VertexCoordinates -> pts, 

 VertexLabels -> "Name", ImageSize -> Large];
 coords = Grid[Partition[Flatten[Riffle[Table["v" <> ToString[i], {i, 10}], 
    VertexCoordinates /. AbsoluteOptions[gr5, VertexCoordinates]]], 3], 
 Alignment -> Left];
 Row[{gr5, coords}]}[[1]],

 (* controllers specified here *)
 controls0]

I keep getting the error message: Manipulate::vsform: Manipulate argument controls0 does not have the correct form for a variable specification. >>

I have tried HoldAll, Sequence, etc., but cannot seem to figure out how to generate the proper form for the controller specifications.

Apologies for the long post, and thanks in advance!

$\endgroup$
6
  • $\begingroup$ You can't do this. Mathematica parses Manipulate expression for correct syntax/form, but it does not evaluate until it gets to Initialization. This is one big problem I always had with it, since all control code have to be written in-place with lots of duplicate boiler plate code, since one can't make functions to call that will build the controls on the fly. Basically, you have to write the controls code directly, so it parses correctly. $\endgroup$
    – Nasser
    Commented Nov 24, 2014 at 0:05
  • 1
    $\begingroup$ The closest you can get is this: How to define part of a Manipulate control variable definition to reduce code duplication $\endgroup$
    – Nasser
    Commented Nov 24, 2014 at 0:09
  • $\begingroup$ @Nasser why not? using With a lot is possible. Of course the code gets hard to read and to maintain once a bit bigger, but that's also doable in Workbench or IntelliJ plugin. $\endgroup$ Commented Nov 24, 2014 at 0:34
  • $\begingroup$ @RolfMertig using With a lot is possible but that is what I said? The link I pointed to shows the With macro method shown by Leonid in the answer there. This With method helps, but it is still not easy to manage. Having the ability to call a function to build a control would be the ideal solution, but it is not possible. $\endgroup$
    – Nasser
    Commented Nov 24, 2014 at 0:41
  • $\begingroup$ @Nasser: I did not look at the link, just at your claim it would not be possible. Sorry. Clearly there should be a preprocessor or way better meta-programming features for the Wolfram Language. With is nice up to a point, but one can easily create quite unreadable code. Let's hope the language will actually evolve to something more usable for non-one-line-twitterable programs.BTW: I nearly always prefer DynamicModule over Manipulate. $\endgroup$ Commented Nov 24, 2014 at 0:53

2 Answers 2

6
$\begingroup$

It's much simpler

f[g1_Graph] := 
 DynamicModule[{pts = PropertyValue[{g1, #}, VertexCoordinates] & /@ VertexList@g1, g2 = g1}, 
  {Dynamic@Column@pts, 
   LocatorPane[Dynamic[pts], Dynamic[g2 = SetProperty[g2, VertexCoordinates -> pts]]]}]

g = CompleteGraph[5, VertexLabels -> "Name",  PlotRange -> {{-5, 5}, {-5, 5}}]; 
f@g

Mathematica graphics

$\endgroup$
4
  • $\begingroup$ +1 This works well on V9... but for me under V10.0.1 on Win7 64 bit it issues many error messages, apparently due to this bug. $\endgroup$
    – WReach
    Commented Nov 24, 2014 at 3:57
  • $\begingroup$ @WReach Sorry, V9 here. I simplified the code. Is there a way to fix this for V10? Please edit the answer if you feel like that. $\endgroup$ Commented Nov 24, 2014 at 4:05
  • $\begingroup$ V10.0.2? :( I'm afraid I am not aware of a reliable fix. See the chat for another variation of your answer (but it is also flakey on V10). $\endgroup$
    – WReach
    Commented Nov 24, 2014 at 4:10
  • 1
    $\begingroup$ Try this on V10: f[g1_Graph] := DynamicModule[{pts = PropertyValue[{g1, #}, VertexCoordinates] & /@ VertexList@g1}, {Dynamic@Column@pts, LocatorPane[Dynamic[pts], Dynamic@Graph[g1, VertexCoordinates -> pts]]}]; $\endgroup$
    – Gerli
    Commented Mar 13, 2015 at 12:12
2
$\begingroup$

You can use a list of points instead of individual controls and Manipulate will construct a LocatorPane to handle them.

Not fussing much with the rest of the code, except to eliminate the global variables gr5 and coords:

Manipulate[
 With[{gr5 = 
    Graph[el0, EdgeStyle -> LightGray, VertexCoordinates -> v, 
     VertexLabels -> "Name", ImageSize -> 300, 
     PlotRange -> RegionBounds[Point[vc0]]]},
  With[{coords = 
     Grid[Partition[
       Flatten[Riffle[Table["v" <> ToString[i], {i, 10}], 
         VertexCoordinates /. 
          AbsoluteOptions[gr5, VertexCoordinates]]], 3], 
      Alignment -> Left]},
   Row[{gr5, coords}]
   ]],
 {{v, vc0}, Locator}]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.