# How do I constrain movement for lists of Locators using 2nd argument of Dynamic?

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


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.

-
@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. –  fairflow Apr 15 at 13:55
@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. –  fairflow Apr 15 at 14:01
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. –  m_goldberg Apr 15 at 14:23
To clarify the point of my previous comment, the reported behavior is not a property peculiar to locators, but applies to any dynamic graphic. –  m_goldberg Apr 15 at 14:27
Is this better @Kuba? I'm making a separate question about the cacheing and possibly another about why my approach failed. –  fairflow Apr 15 at 17:37

## 1 Answer

This is how I'd do that:

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

, None]]

, Initialization :> (
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}]) &]
]]] &

)]


-
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). –  fairflow Apr 15 at 17:16
@fairflow I'm glad it does :) good luck with your project :) –  Kuba Apr 15 at 18:17
Cheers: no other solutions yet & yours is perfect for me so accepted. –  fairflow Apr 15 at 18:20