4
$\begingroup$

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?

$\endgroup$

1 Answer 1

3
$\begingroup$

First, it seems that using the second argument of Dynamic inside a LocatorPane doesn't recognize when locators are removed. That's unexpected, but not un-fixable since your Graphics is dynamic as well and und can make the assignment there.

If you want a bi-directional behaviour, you need both transformations i.e. from and to your transformed space. Furthermore, you need both ways of information flow: When loc is changed then pts needs to be changed and vice versa. Let us simplify this and use two global variables pts1 and pts2, and the two transformation functions f and finv.

The core of the code is similar to what I had posted in my original answer. I thought you can work from there by yourself, but maybe it wasn't so obvious after all. Sorry for this. Here is a running example:

f[pt_] := RotationMatrix[Pi/4].pt;
finv[pt_] := RotationMatrix[-Pi/4].pt;

With[{lines = Table[{i, j}, {j, -1, 1, .25}, {i, -1, 1, .25}]},
 ClearAll[locator];
 SetAttributes[locator, {HoldAll}];
 locator[pts1_, pts2_, f_] :=
  LocatorPane[Dynamic[pts1],
   Dynamic[
    pts2 = f /@ pts1;
    Graphics[{Line[lines], Line[Transpose[lines]]},
     PlotRange -> {{-1.1, 1.1}, {-1.1, 1.1}},
     ImageSize -> 200
     ]
    ],
   LocatorAutoCreate -> All
   ]
 ]

The crucial part is that you need the symbols for your lists and not the values. You achieved this by using Dynamic[pts] which is perfectly fine and is used very often especially in GUI programming. I'm going to set an attribute to locator to provide the same behaviour.

Now, create the two dual locator panes:

pts1 = {};
pts2 = {};
Row[{locator[pts1, pts2, f], locator[pts2, pts1, finv]}]

enter image description here

$\endgroup$
4
  • $\begingroup$ Thanks, but this has the same problem. Manipulations in the flipper1 Dynamic are mirrored in the second LocatorPane, but manipulations of the locators in the second LocatorPane do not effect the locators in flipper1. (Would have been kicking myself if it were this simple, though) $\endgroup$
    – Daniel W
    Commented Aug 25, 2017 at 11:11
  • $\begingroup$ @DanielW Sorry. Please see my rewritten answer. $\endgroup$
    – halirutan
    Commented Aug 25, 2017 at 12:24
  • $\begingroup$ Again, thank you. I studied your solution and realized it works because the second LocatorPane is displayed and executing the inverse function. But, if you never display the second LocatorPane, the inverse function is never executed. This gave me the inspiration for the DynamicWrapper solution I will post in my question. $\endgroup$
    – Daniel W
    Commented Aug 25, 2017 at 15:59
  • $\begingroup$ @DanielW When I answer a question, it's not unusual that I might have misunderstood the key-point of the question. You are really welcome to join the Mathematica Chat every once in a while. Sometimes it's better when we can discuss things so that I get a better picture beyond the simplified example. $\endgroup$
    – halirutan
    Commented Aug 26, 2017 at 16:52

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.