2
$\begingroup$

I need to display edge labels of a graph in a way that allows the edge labels to be moved. Locator seems the simplest and most obvious function to use. My application requires an interface that generates new graphs with different numbers of vertices and edges, so I can't treat the locators individually.

With[
 {coords = {{1.08, 0.94}, {1.08, 0.036}, {0., 0.97}, {0., 0.}, {1.94, 0.49}},
  edges = {{1, 2}, {1, 3}, {2, 5}, {3, 4}, {4, 2}, {5, 1}}},
 DynamicModule[
  {edgePosns = Table[0.5, {Length@edges}]},
  (betweenPnt[a_, b_, l_] := (1 - l) a + l b;
   DynamicModule[
    {edgeCentres =
      MapThread[
       With[{av = coords[[#1[[1]]]], bv = coords[[#1[[2]]]]},
         Dynamic[betweenPnt[av, bv, #2]]] &,
       {edges, edgePosns}]},
      Graphics[
       GraphicsComplex[
        coords,
        {{Line[edges]}, {Darker@Red, PointSize[0.02], 
          Map[Point, Range[5]]},
         Map[Locator, edgeCentres]}], ImageSize -> 400]])]]

picture of output of above

The locators can be moved, as expected. What I did not expect was that, (even) if the output is deleted and the cell is re-evaluated, the locators retain their new positions. However, I've now learned that this is standard behaviour (see m_goldberg's comment below), which can be fixed by Initialization (see Kuba's solution).

Also, I would like to constrain the movement of locators to lie on the edges, for which I hope to use the 2nd argument of Dynamic. Can I do it with this (admittedly flawed) design? My attempts so far have resulted in unresponsive locators. I think I need to update edgeCentres using the callback of the 2nd argument, but whether it is because it is a list, or for some other reason, this is ineffective. I do not know how (or if) I can implement this constraint by adding a second argument to Dynamic in the code above.

In fact, I prefer to update edgePosns, which is list of the proportions of respective edges that the locators mark, but I need to be able to walk first.

Related question now split from original question following Kuba's suggestion.

$\endgroup$
7
  • $\begingroup$ @Kuba, I don't need to move the edges, I need to move the edge labels (currently shown as default Locator symbols). I prefer to keep the labels on their own edges; the current code allows them to be moved anywhere at all. $\endgroup$
    – fairflow
    Commented Apr 15, 2014 at 13:55
  • $\begingroup$ @Kuba, yes, thanks, good example: but in fact I had no problem constraining a single locator to its edge, it is when I have a list of locators (on a set of edges of variable size) that I run into trouble. I'll edit my question to make that point clearer. $\endgroup$
    – fairflow
    Commented Apr 15, 2014 at 14:01
  • $\begingroup$ The behavior you describe is general to expressions displayed dynamically. For example, if you create a 3D graphics object, evaluate it, rotate the object in the output image with the mouse, and delete the output, when you re-evaluate the 3D graphic, the new output image appears in the rotated position of the deleted image. $\endgroup$
    – m_goldberg
    Commented Apr 15, 2014 at 14:23
  • $\begingroup$ To clarify the point of my previous comment, the reported behavior is not a property peculiar to locators, but applies to any dynamic graphic. $\endgroup$
    – m_goldberg
    Commented Apr 15, 2014 at 14:27
  • $\begingroup$ Is this better @Kuba? I'm making a separate question about the cacheing and possibly another about why my approach failed. $\endgroup$
    – fairflow
    Commented Apr 15, 2014 at 17:37

1 Answer 1

3
$\begingroup$

This is how I'd do that:

DynamicModule[{coords, edges, lines, centers, locators},

  coords = {{1.08, 0.94}, {1.08, 0.036}, {0., 0.97}, {0., 0.}, {1.94, 0.49}};
  edges = {{1, 2}, {1, 3}, {2, 5}, {3, 4}, {4, 2}, {5, 1}};
  lines = (coords[[#]] & /@ edges);
  centers = .5 (# + #2) & @@@ lines;
  locators =   With[{i = #2[[1]], p1 = #[[1]], p2 = #[[2]]}
    , 
    With[{norm = Norm@N@(p2 - p1)}
      , 
      Locator[Dynamic[ centers[[i]], 
        (centers[[i]] = p1 + Normalize[(p2 - p1)] Clip[(p2 - p1).(# - p1), {0, 
          norm}]) &]]]] &;

 Graphics[{GraphicsComplex[
    coords, {{Line[edges]}, {Darker@Red, PointSize[0.02], 
    Map[Point, Range[5]]}}], MapIndexed[locators, lines]}, 
  ImageSize -> 400, Frame -> True, PlotRange -> 2]
]

enter image description here

$\endgroup$
4
  • $\begingroup$ That solves 2 of my problems at one go: the Initialization part ensures the state is suitably refreshed and the locators code does what I wanted (with the added bonus of Clip which is also what I needed, as you thought). $\endgroup$
    – fairflow
    Commented Apr 15, 2014 at 17:16
  • $\begingroup$ @fairflow I'm glad it does :) good luck with your project :) $\endgroup$
    – Kuba
    Commented Apr 15, 2014 at 18:17
  • $\begingroup$ Cheers: no other solutions yet & yours is perfect for me so accepted. $\endgroup$
    – fairflow
    Commented Apr 15, 2014 at 18:20
  • $\begingroup$ The latest edit clarifies the code @Kuba but it does mean my response no longer makes any sense as the Initialization part has gone now. $\endgroup$
    – fairflow
    Commented Jan 19, 2016 at 22:16

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.