# Preventing LocatorPane Locator from moving when PlotRange changes

I have a LocatorPane within a Manipulate such that the range of the plot behind the Locator can vary. When it does, this naturally causes the Locator to move on-screen to remain at a constant position with respect to the plot data. However, for my purposes, I need to prevent it from doing that. Ideally, it should stay at the same spot on-screen when the plot range is changed.

Here's a minimal example of the situation. Dragging the slider causes the Locator to move on-screen. Calculating the approximate position of the Locator as a fraction of the total plot size is easy, and a Dynamic displaying that is included. As mentioned in the Manipulate code, that value will ideally not change when the plot range is modified.

(* I need "point" (the locator's position) to always remain at the
same position on-screen when end is changed. *)
Manipulate[
Row[{
LocatorPane[
Dynamic[point],
Plot[x, {x, 0, end}, ImageSize -> 400]
],
"This value should change ONLY when the Locator is dragged: ",
Dynamic[point / end]
}],
{end, 1, 2}
]


Notes:

• Using a Locator directly or via a Manipulate control is not allowed -- the locator must come from a LocatorPane. (Reason: I also need to be able to restrict the Locator to a certain rectangular region, and doing that with a raw Locator is possible but relatively slow.)
• Preventing the range of the plot within the LocatorPane from changing is also impossible (although wrapping it in another plot somehow is fine, if that is possible and helps).
• Using intermediate variables is fine: my attempts to solve the problem thus far have mainly involved separate abspt and relpt (absolute/relative point position) variables.

I have a feeling that there's some clever way to accomplish this with Dynamic, but I'm not familiar enough with Dynamic to know how.

Using the same logic as Edmund in his answer.

Manipulate[
Row[{
LocatorPane[
Dynamic[point],
Plot[x, {x, 0, end}, ImageSize -> 400]],
"This value should change ONLY when the Locator is dragged: ",
Dynamic[point/end]}],
{{point, {0.5, 0.5}}, None}, {{scaledPoint, {0.5, 0.5}}, None},
{end, 1, 2,
TrackingFunction :> {(scaledPoint = point/end; &),
(end = #; point = end*scaledPoint; &), None}}]


You may use the function evaluation syntax of Manipulate.

Manipulate[
Row[{LocatorPane[Dynamic[point],
Plot[x, {x, 0, end}, ImageSize -> 400]],
"This value should change ONLY when the Locator is dragged: ",
Dynamic[point/end]}],
Row[{Slider[Dynamic[end, {(ss = point/end;) &, (end = #; point = end ss) &}], {1, 2}]}],
Initialization :> {point = {1, 1};}
]


From the documentation for LocatorPane:

For a Graphics object g, LocatorPane[pt,g] by default takes the range of coordinates for pt to be the range of graphics coordinates corresponding to PlotRange in g.

We can get around this. The Dynamic for end in Slider captures the scaled position of the Locator at the start of moving the slider. Then, as it is moved it updates point to the new scaled position. In the case were the x- and y-axis plot range changes non-linearly and/or at different rates you will need to account for this in your rescaling.

Hope this helps.

• Thank you for your answer! Unfortunately, this causes recursion errors on a fresh kernel session until the locator is first dragged: image 1 , image 2 . Also, the Slider no longer seems to slide smoothly, even with ContinuousAction -> True explicitly set. Can these issues be worked around? (I'm writing a Wolfram Demonstration submission, so I need everything to work as smoothly as possible.) – KevinS May 26 '16 at 3:04
• @KevinS Initialize point to stop recursion error in fresh kernel. See update. – Edmund May 26 '16 at 11:59