2
$\begingroup$

Old

I would like to make the locator follow a plot, giving the coordinates as it moves. I don't really know where to start with this one, but I would like the dynamic plot to look something like this:

enter image description here

the code for which is as follows:

Plot[{Sqrt[x]}, {x, 0, 10}, PlotRange -> {{0, 12}, {0, 3.5}},
Epilog -> {PointSize[Large], Red, Point[{4, 2}]}]

with the locator showing its coordinates as it is moved along the plot. Obviously, the movement of the locator needs to be constrained to the plot, just as it is constrained to the coordinates of the circle in this Wolfram Documentation example:

enter image description here

by the following code:

Deploy[DynamicModule[{p = {1, 0}}, Graphics[{Red, Disk[], 
Locator[Dynamic[p, (p = Normalize[#]) &]]}, PlotRange -> 1.5]]]

basically, so it behaves in a similar way to Google graphs:

enter image description here

where the coordinates appear to the top right of the plot.

New

@Timothy Wofford The solution you gave works great for functions like Sqrt[x], but runs into difficulties when complex values are encountered. For example:

enter image description here

generated by the code:

f[x_] := ((\[Pi]^x/Zeta[x])^(1/x)/\[Pi]) - 1/Zeta[x]
Manipulate[Plot[f[x], {x, 0, 10}, Epilog -> {PointSize[Large], Red, 
Tooltip[Point[#], #] &@{p[[1]], f[p[[1]]]}}, PlotLabel -> 
{p[[1]], f[p[[1]]]}], {{p, {1, 0}}, Locator, Appearance -> None}, 
AppearanceElements -> None]

shows as a red screen when the locator moves off screen into the imaginary values, generating an error screen as follows:

enter image description here

Is there any way of preventing this from happening? - ie, from stopping the locator moving past {1,0}?

$\endgroup$
2
  • 1
    $\begingroup$ I edited my answer to handle moving out of the domain of the function. p = {0, 0} is an example point for Sqrt (and really any {x,y} would work with x>0 as the y coordinate of the locator is not used). For your new function, p={x,y} where y is any value and x is in the domain of your new function. $\endgroup$ Oct 19 '13 at 19:38
  • $\begingroup$ @ Timothy Wofford, Your fix works well for Sqrt[x], but still gives error as {0,Indeterminate} for above plot. Many thanks for your contributions though - they have been really helpful :-) $\endgroup$
    – martin
    Oct 19 '13 at 22:58
4
$\begingroup$

Using another variable and Clip is easier than dealing with the second argument to Dynamic. Below I clipped the x coordinate of p to the plot domain, which won't let the red dot go beyond the ends of the curve. If you just want to stop it at 1, change the upper limit in Clip to Infinity. The clipped x coordinate is stored in x0 and inserted into the plot.

f[x_] := ((\[Pi]^x/Zeta[x])^(1/x)/\[Pi]) - 1/Zeta[x];
Manipulate[
 With[{x0 = Clip[p[[1]], {1., 10. (* or Infinity *)}]},
  Plot[f[x], {x, 0, 10},
   Epilog -> {PointSize[Large], Red, 
     Tooltip[Point[#], #] &@{x0, f[x0]}}, PlotLabel -> {x0, f[x0]}]],
 {{p, {1, 0}}, Locator, Appearance -> None},
 AppearanceElements -> None]

One can use Dynamic, but you need to start the Manipulate body with your own LocatorPane. Something like

LocatorPane[Dynamic[p, (p = {#, f[#]} &@ Clip[#[[1]], {1., 10.}])&],
   Plot[..]]

Declare the variable p as {{p, {1, 0}}, ControlType -> None}.

$\endgroup$
6
  • $\begingroup$ @ Michael E2, Thank you very much - this is just what I was after. My next little project is to control 2 locators at once - is it appropriate to ask this as a new question, or to update this one? $\endgroup$
    – martin
    Oct 19 '13 at 21:09
  • $\begingroup$ @ Michael E2, Your solution does provide the answer to my problem, but I was unsure that if I ticked it as the answer, whether my update would be looked at. Sorry - fairly new to this forum and am unsure whether I should post a new question, or just update this one. I will definitely be ticking your solution in due course though. $\endgroup$
    – martin
    Oct 19 '13 at 21:30
  • $\begingroup$ Also, is it possible to format the coordinates(font colour, remove the curly brackets, etc.)? $\endgroup$
    – martin
    Oct 19 '13 at 21:31
  • $\begingroup$ @martin I was just about to suggest, that the question of multiple locators probably ought to be a separate question. You might look at this question, too: mathematica.stackexchange.com/questions/22134/… $\endgroup$
    – Michael E2
    Oct 19 '13 at 21:33
  • $\begingroup$ Use Style to control font attributes. I think you have to construct parentheses manually: e.g., PlotLabel -> Style[Row[{"(", x0, ", ", f[x0], ")"}], Blue] $\endgroup$
    – Michael E2
    Oct 19 '13 at 21:43
3
$\begingroup$

The basic functionality is given by this.

f[x_] := Sqrt[x]
Manipulate[
  Plot[f[x], {x, 0, 10}, 
    Epilog -> {PointSize[Large], Red, 
      Tooltip[Point[#], #]&@{p[[1]], f[p[[1]]]}},
    PlotLabel -> {p[[1]], f@p[[1]]}
  ],{{p, {0, 0}}, Locator, Appearance -> None}, 
 AppearanceElements -> None]

The locator is not actually constrained to the curve. Instead the locator has Appearance->None and we add a Point. The x coordinate of the point is given by the locator p[[1]] and the y coordinate is given by f@p[[1]].

I wrapped the built-in Tooltip around the point to display the coordinates. If you hover the mouse over the point, a tooltip will show the coordinates.

I wasn't sure if you wanted the coordinates displayed as in the Google example, so I added a PlotLabel as one way of displaying the coordinates.

For the Sqrt function, we assume x>0, but the user could drag the locator into the x<0 region. I'm not sure how to prevent that, but we can handle it.

Manipulate[
  If[Im@f@p[[1]] != 0, p = {0, 0}];
  Plot[...

Here, I check to see if the function returns a nonzero imaginary part, which used to cause the Plot to return an error. Now, we just move the locator back into the domain of the function.

$\endgroup$
10
  • $\begingroup$ @ Timothy Wofford Great, thanks - perfect! I don't really understand the coding as yet - but I will work through it step by step - again, many thanks :-) $\endgroup$
    – martin
    Oct 19 '13 at 18:58
  • $\begingroup$ @ Timothy Wofford The function I actually want to plot goes into imaginary values - is there any way of stopping the locator moving off screen & showing up as an error? $\endgroup$
    – martin
    Oct 19 '13 at 19:09
  • 2
    $\begingroup$ Another way is If[Im@#[[2]] == 0, Tooltip[Point[#], #], Unevaluated@Sequence[]] & $\endgroup$
    – ybeltukov
    Oct 19 '13 at 19:39
  • $\begingroup$ @ybeltukov, where did you come up with Unevaluated@Sequence[]? Having Sequence without any arguments isn't an example in the reference page. $\endgroup$ Oct 19 '13 at 19:47
  • $\begingroup$ @ybeltukov, do you know of any way of preventing the error screen as illustrated above? $\endgroup$
    – martin
    Oct 19 '13 at 19:52
1
$\begingroup$

Note that a LocatorPane[] is sufficient here. No Manipulate[] is required:

DynamicModule[{loc = {0, 0}, pt, tip, fmt, b = False},
 fmt[val_] := ToString[NumberForm[N[val], {2, 1}]];
 tip[{x_, y_}] := "x=" <> fmt[x] <> " y=" <> fmt[y];
 pt[] := With[{px = Clip[loc[[1]], {0, 12}]}, {px, Sqrt[px]}];
 EventHandler[
  LocatorPane[
   Dynamic[loc],
   Plot[{Sqrt[x]}, {x, 0, 12},
    PlotRange -> {0, 3.5},
    PlotLabel -> Framed@Dynamic[tip[pt[]]],
    Epilog -> {PointSize[Large], Red, Dynamic[Point[pt[]]]}
    ],
   Appearance -> None,
   AutoAction -> Dynamic[b]
   ],
  {"MouseClicked" :> (b = Not[b])}
  ]
 ]

Here's what it looks like:

enter image description here

As in the examples up-thread, we render the position of the point using PlotLabel. You could also use Tooltip[] or StatusArea[] (though those will only render if the mouse is near the locator). You could even use a dedicated graphics primitive for rendering the info in the list that is passed to Epilog.

We use Dynamic[] for both the PlotLabel and Epilog options, so they update properly as the location position changes.

We Clip[] the x-coordinate of the locator position, use that to compute the y-coordinate (from Sqrt[], what is shown in the Plot[]), and use that pair of coordinates for our Point[]. The Clip[] ensures that we can always safely compute the point position, no matter where the locator happens to be. The examples in the documentation use the second parameter of Dynamic[] to perform similar manipulation, but that's not necessary here.

Now you can just drag your mouse horizontally and the point will move accordingly, along the plot line. I find it awkward to have to hold the left mouse button down while doing this (I'm on a oldish laptop), so I used an EventHandler[] to turn AutoAction for the locator on and off by a simple click.

$\endgroup$

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.