10
$\begingroup$

This is a cross-post of http://community.wolfram.com/groups/-/m/t/1186782

I am working on something where I need to know which locator out of many is currently being changed.

Here is some working code that uses the second argument to Dynamic to identify which locator is active for a limited set of Locators. I'd like to extend this to many Locators. In this code, the Locators are hard-coded. I'd like to extend this programmatically so I can slot in the Locators and the Manipulator controls. I've tried various combinations of Hold* without really knowing what I am doing.

Does anyone know how I can achieve my goal of finding which is the active locator?

See some additional comments below the working code about an attempt to do this with LocatorPane.

DynamicModule[
 {active = 0, p},
 Manipulate[
  Column[
   {active,
     Deploy@Graphics[
      {
       {
        Locator@
         Dynamic[p[1, 1, 
           1], {((p[1, 1, 1] = {-1, Last[#]}; 
              active = {1, 1, 1}) &), ((active = 0) &)}],
        Locator@
         Dynamic[p[1, 1, 
           2], {((p[1, 1, 2] = #; 
              active = {1, 1, 2}) &), ((active = 0) &)}],
        Locator@
         Dynamic[p[1, 1, 
           3], {((p[1, 1, 3] = {1, Last[#]}; 
              active = {1, 1, 3}) &), (active = 0) &}]
        },
       Line[{p[1, 1, 1], p[1, 1, 2], p[1, 1, 3]}] 
       },
      PlotRange -> 2
      ]
    }
   ],
  {{p[1, 1, 1], {-1, -1}}, ControlType -> None},
  {{p[1, 1, 2], {0, 0}}, ControlType -> None},
  {{p[1, 1, 3], {1, 1}}, ControlType -> None}
  ]
 ]

Trying to do the same thing with a LocatorPane and Nearest. It is not clear to me that the 2 argument form of Dynamic is working here:

DynamicModule[{active = False, pt1 = {1, 1}/2, pt2 = {-1, 1}/2, 
  pt3 = {1, -1}/2},
 Row[
  {active,
   LocatorPane[
    {
     Dynamic[
      pt1, {((active = Nearest[pt1, #, 1][[1]]); 
         pt1 = {0, 1} #) &, ((active = 0) &)}],
     Dynamic[
      pt2, {((active = Nearest[pt2, #, 1][[1]]); 
         pt2 = #) &, ((active = 0) &)}]
     },
    Graphics[{Yellow, Disk[{0, 0}, 2]},
     PlotRange -> 2
     ]
    ]
   }
  ]
 ]
$\endgroup$
  • 1
    $\begingroup$ I'd suggest using an Association back-end to your Locator GUI if you need to do big stuff with it. That's what I did for this answer. Then you can use some merger of user6014 and Mathe172's ideas. $\endgroup$ – b3m2a1 Sep 18 '17 at 21:21
  • $\begingroup$ Thanks. Yes, this is nice. Yours and @Mathe172's method are a nice set of bookends for this problem. I just noticed, b3m2a1's comment of using Associations which make sense as well. Many thanks all, I consider this one solved. $\endgroup$ – Craig Carter Sep 18 '17 at 22:30
  • $\begingroup$ This issue is addressed in my answer here: mathematica.stackexchange.com/questions/22134/… (almost the same as Mathe172's) $\endgroup$ – Michael E2 Sep 19 '17 at 0:26
  • $\begingroup$ It seems as though someone from Wolfram has posted a better solution than the ones below in the Wolfram Community. Neat! $\endgroup$ – user6014 Sep 19 '17 at 14:37
6
$\begingroup$

You may use MousePosition set to the "Graphics" of the LocatorPane with Nearest.

pts = {{1, 1}/2, {-1, 1}/2, {1, -1}/2};
selectedLocator = None;
LocatorPane[
 Dynamic[pts,
  {
   (selectedLocator = 
      FirstPosition[pts, First@Nearest[pts, MousePosition[{"Graphics", LocatorPane}]]]) &,
   Automatic,
   None
   }],
 Graphics[{Yellow, Disk[{0, 0}, 2]}, PlotRange -> 2],
 LocatorAutoCreate -> True
 ]

Mathematica graphics

Dynamic[selectedLocator]

When the LocatorPane beings to update locator the MousePosition in the pane's "Graphics" coordinates will be used to calculate the index of the nearest locator in pts and store this position in selectedLocator. This calculation only occurs at the start of updating a locator which reduces the load on the system.

With LocatorAutoCreate set to True additional locators can be added by holding Alt and clicking on the LocatorPane.

selectedLocator can be set to None in the third item of the second parameter of Dynamic if needed; (selectedLocator = None)&.

Hope this helps.

$\endgroup$
  • $\begingroup$ Great. Nice to see several alternatives. Thanks. $\endgroup$ – Craig Carter Sep 19 '17 at 0:37
  • 1
    $\begingroup$ @CraigCarter Note the this solution works without the limitations listed in Mathe172 update. $\endgroup$ – Edmund Sep 19 '17 at 0:41
  • $\begingroup$ And thank you @Edmund for the nice explanatory text. $\endgroup$ – Craig Carter Sep 19 '17 at 0:46
6
$\begingroup$

Does this work for you?

DynamicModule[
 {pts = Array[pt, 5], active},
 Column@{
   Dynamic@active,
   Dynamic@pts,
   LocatorPane[
    Dynamic[
     pts,
     {
      (active = First@FirstPosition[pts - #, _?(# != {0, 0} &)]; pts = #)&,
      (active = None) &
     }
    ],
    Graphics[{Yellow, Disk[{0, 0}, 2]}, PlotRange -> 2]
    ]
   }
 ]

This simply finds the currently updating point by checking which value has changed.

Update

Seeing @user6014's solution, I thought I'd point out some issues I see with both apporaches:

  • My approach fails if you don't actually change the position of the locator (you can do this by clicking once to get the locator directly under your cursor and then clicking again without moving the mouse)
  • @user6014's approach fails if you manage to put two locators on top of each other (or at least it might not find the correct locator)
$\endgroup$
  • $\begingroup$ Thanks. Yes, this is nice. Yours and @Mathe172's method are a nice set of bookends for this problem. I just noticed, b3m2a1's comment of using Associations which make sense as well. Many thanks all, I consider this one solved $\endgroup$ – Craig Carter Sep 18 '17 at 22:32
  • $\begingroup$ You should change your active setting call to be in the first function of the 3 function version Dynamic and clear active in the final function. It'll be cleaner that way. $\endgroup$ – b3m2a1 Sep 18 '17 at 22:33
  • $\begingroup$ Good point, updated. (Although I had have to use the two function version, otherwise you'll run into the problem I mentioned at the bottom of the answer) $\endgroup$ – Lukas Lang Sep 18 '17 at 22:39
5
$\begingroup$

Does this work for you?

DynamicModule[{pts = {{-1, 1}/2, {1, 1}/2}, a = False, 
  pointNames = Table["Point " <> ToString@i, {i, 10}]},
 EventHandler[
  {LocatorPane[Dynamic[pts], Framed@Graphics[{}], 
    LocatorAutoCreate -> True], Dynamic[a]}
  ,
  {"MouseUp" :> {a = "Null"}, 
   "MouseDragged" :> {a = 
      pointNames[[Position[pts, 
          Nearest[pts, MousePosition["Graphics"]][[1]]][[1, 1]]]]}}
  , PassEventsDown -> True]
 ]

This simply finds the point closest to your current mouse position when pressed down.

Use Alt+Click to create new locators.

$\endgroup$
  • $\begingroup$ Yes. This works like a charm. Both yours and that of @Mathe172 can be used for my purposes. I like this approach with the EventHandler. Many thanks. Many thanks, I consider this one solved. $\endgroup$ – Craig Carter Sep 18 '17 at 22:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.