2
$\begingroup$

I write this code

region = ParametricRegion[{Sin[u], 
   Sin[2*u]}, {{u, 0, 2*Pi}}]; DynamicModule[{p = {0, 0}}, 
 Show[ParametricPlot[{Sin[u], Sin[2*u]}, {u, 0, 2*Pi}, 
   PlotLabel -> Dynamic[p]], 
  Graphics[Locator[
    Dynamic[p, 
     If[RegionDistance[region, #1] < 0.2, 
       p = RegionNearest[region, #1]] &]]]]]

enter image description here

But the efficiency is very poor.

$\endgroup$
  • $\begingroup$ Could you not use TrackingFunction to achieve better performance? $\endgroup$ – e.doroskevic Jan 19 '16 at 9:25
4
$\begingroup$

With RegionNearest

RegionNearest is a function that basically processes the region and returns a function of type RegionNearestFunction which is optimized for finding the nearest point in the region quickly. You should use the returned function to find the closest point in the region. As it is now you are recomputing this function again and again, which creates a lot of overhead. I might also suggest LocatorPane which has a background argument. This might allow for some further optimizations in which Mathematica does not have to redraw the background as much as it would have with the naive approach, although I don't know if this is the case. It also makes the syntax a bit simpler.

region = ParametricRegion[{Sin[u], Sin[2*u]}, {{u, 0, 2*Pi}}];
background = ParametricPlot[{Sin[u], Sin[2*u]}, {u, 0, 2*Pi}];
nf = RegionNearest[region];

DynamicModule[{pt = {0, 0}},
 LocatorPane[Dynamic[pt, (pt = nf[#]) &], background]
 ]

You can make background a function of pt if you wish to update the plot label. Another option would be to use Column[{Dynamic[pt], LocatorPane[...]}].

With Nearest

If you need something that is very, very fast and which you have more control over, you can try Nearest instead.

region = Table[{Sin[u], Sin[2 u]}, {u, 0, 2 Pi, 0.01}];
background = ParametricPlot[{Sin[u], Sin[2 u]}, {u, 0, 2 Pi}];
nf = Nearest[region];

DynamicModule[{pt = {0, 0}},
 LocatorPane[Dynamic[pt, (pt = First@nf[#]) &], background]
 ]

The upside and the downside is that you have to decide how to sample the curve yourself.

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ That's great, much much faster. Like you say, you can make background a function, or just use Show[background , PlotLabel -> Dynamic[pt]] in place of background. $\endgroup$ – Jason B. Jan 19 '16 at 11:36
2
$\begingroup$

This seems to be a bit faster. First off, there's no reason to redraw the ParametricPlot every time you change p. That sped it up a bit. Then I figured, why make the If statement? Let's just always set p equal to the nearest region point. Getting rid of the If statement sped it up some more.

region = ParametricRegion[{Sin[u], Sin[2*u]}, {{u, 0, 2*Pi}}];
plot = ParametricPlot[{Sin[u], Sin[2*u]}, {u, 0, 
   2*Pi}]; 
DynamicModule[{p = {0, 0}},
 Show[plot,
  Graphics[
   Locator[
    Dynamic[
     p,
     (p = RegionNearest[region, #1]; &)
     ]
    ]
   ]
  , PlotLabel -> Dynamic[p]]]
| improve this answer | |
$\endgroup$
  • $\begingroup$ It's seem to be slightly slow too. $\endgroup$ – yode Jan 19 '16 at 14:11
  • $\begingroup$ It's faster than your original version, but there's no question that Pickett's solution is the best. $\endgroup$ – Jason B. Jan 19 '16 at 14:13

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.