In "Introduction to Dynamic", an example is given of a transformed slider, which goes to the left as the value increases. I am trying to do something similar with LocatorPane
, but am having trouble extending methods of the example.
I am trying to create a modified LocatorPane
control where I can move the locators in a transformed space. For the simplified case I show here I move locators about in the first quadrant, but I want to transform them dynamically to the third quadrant. This behavior should be bi-directional, so if I change my third quadrant dynamic values, I want the control display to update; this is where I have the trouble.
(I am aware that this particular example could be handled easily with the ScalingFunctions
option in ListPlot
, I can't use that approach in the more complex problem I am actually trying to solve.)
First, using the second argument to Dynamic
draw[locs_] := Graphics[{FontSize -> 18, FontWeight -> Bold,
(Text["*", #1] & ) /@ locs}];
flipper1[Dynamic[pts_]] := DynamicModule[{locs},
LocatorPane[Dynamic[locs, (pts = -#1; locs = #1) & ],
Dynamic[Show[draw[locs], PlotRange -> {{1, 0}, {0, 1}}, Frame -> True]],
Appearance -> None, LocatorAutoCreate -> All], Initialization :> {locs = -pts}];
pts = {};
flipper1[Dynamic[pts]]
This works fine while adding and moving locators, but when deleting locators the pts list is not updated, and when changing the pts
list the LocatorPane
does not update. For example, drop some locators and then evaluate pts={}
; the locators do not go away.
DynamicWrapper
solves the first problem:
flipper2[Dynamic[pts_]] := DynamicModule[{locs},
DynamicWrapper[LocatorPane[Dynamic[locs],
Dynamic[Show[draw[locs], PlotRange -> {{1, 0}, {0, 1}}, Frame -> True]],
Appearance -> None, LocatorAutoCreate -> All], pts = -locs],
Initialization :> {locs = -pts}];
flipper2[Dynamic[pts]]
but I still can't get the bi-directional behavior I need. How can I can manipulate locators in a transformed space -and- change the values in the pts list and have the locators adjust accordingly?
Update
@halirutan 's solution inspired me, so he gets full credit for this. His solution works because he displays a second LocatorPane
which is constantly executing the inverse transform. I wasn't intending to display a second pane, but we have DynamicWrapper
for that:
flipper3[Dynamic[pts_]] :=
DynamicModule[{locs},
DynamicWrapper[
DynamicWrapper[
LocatorPane[
Dynamic[locs],
Dynamic[
Show[draw[locs], PlotRange -> {{1, 0}, {0, 1}}, Frame -> True]
], Appearance -> None, LocatorAutoCreate -> All
], pts = -locs
], locs = -pts
], Initialization :> {locs = -pts}
];
So, the trick seems to be: wrap the LocatorPane
in 2 layers of DynamicWrapper
, one executing the transform and the other the inverse transform.
So, now bonus question: This seems awkward. This seems like just the type of situation the second argument to Dynamic
was designed for. Is there a more elegant solution?