Injecting list of controls into manipulate

7

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!

GraphMan

Posted 2014-11-23T23:52:34.497

Reputation: 883

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. – Nasser – 2014-11-24T00:05:49.240

1

The closest you can get is this: How to define part of a Manipulate control variable definition to reduce code duplication

– Nasser – 2014-11-24T00:09:25.790

@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. – Rolf Mertig – 2014-11-24T00:34:49.807

@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. – Nasser – 2014-11-24T00:41:49.593

@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. – Rolf Mertig – 2014-11-24T00:53:27.780

@RolfMertig no problem. what I meant by not possible, is the way the OP was doing it. May be be V11 the Wolfram language will add support for this (i.e being able to make function calls to build the controls themselves) But I am still looking for an easy to use debugger build right into the notebook itself and for line numbers! Maybe will have to wait for V20 for these :) – Nasser – 2014-11-24T01:10:53.563

Answers

6

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

Dr. belisarius

Posted 2014-11-23T23:52:34.497

Reputation: 112 848

+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.

– WReach – 2014-11-24T03:57:39.247

@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. – Dr. belisarius – 2014-11-24T04:05:17.043

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). – WReach – 2014-11-24T04:10:38.053

1Try this on V10: f[g1_Graph] := DynamicModule[{pts = PropertyValue[{g1, #}, VertexCoordinates] & /@ VertexList@g1}, {Dynamic@Column@pts, LocatorPane[Dynamic[pts], Dynamic@Graph[g1, VertexCoordinates -> pts]]}]; – Gerli – 2015-03-13T12:12:17.347

2

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}]

Michael E2

Posted 2014-11-23T23:52:34.497

Reputation: 190 928