8
$\begingroup$

I would like to be able to move any curve (for example f(x)=x, or g(x)=x^2) by mouse dragging the particular curve.

I would like to keep the functionality I have for panning across the screen in the code below. This uses Manipulate, (provided by @Karsten 7) but other solutions using Dynamic can be found here

Manipulate[

 Plot[{x, x^2}, {x, -3, 3}, Axes -> True, AxesOrigin -> {0, 0}, 
  PlotRange -> pr, GridLines -> Range @@@ Round@pr, 
  GridLinesStyle -> LightGray],


 {{p, {0, 0}}, Locator, 
  TrackingFunction -> {p = 
       MousePosition[{"Graphics", Graphics}, {0, 0}]; &, 
    If[MousePosition["GraphicsScaled"] \[Element] Rectangle[], 
       pr += p - MousePosition[{"Graphics", Graphics}, {0, 0}]]; &, 
    None}, Appearance -> None},
 {{pr, {{-5, 5}, {-5, 5}}},
  None}]

I'm guessing I would like to put a locator on each curve and then move the curve as long as I am close enough to the locator. What do you think?

Below is a video of what I want to emulate from desmos.com.

enter image description here

$\endgroup$
  • 1
    $\begingroup$ The current mechanism works because wherever you click in the plane the closest locator is always the one that controls the plot range. If you introduce more locators into the plane this will not be true any longer. Thus you need two different strategies: one for the curves (this could be achieved with locators as you suggest), and one for the panning (this could be m_goldberg's solution, for example.) You will probably need to use global variables to indicate when locators are activated, so that you can inactivate the panning mechanism. $\endgroup$ – C. E. Dec 30 '15 at 3:42
  • 1
    $\begingroup$ Do you want to have a dragging point for the curve, as shown in your video, or should clicking anywhere on the curve make it draggable? $\endgroup$ – Karsten 7. Dec 30 '15 at 10:38
  • 1
    $\begingroup$ I would rather have a point. But not important right now. The nice thing about having a point on the curve is that it opens up more opportunities for functionality like rotating the curve about that point for choosing a second point and moving it to alter the slope of a line. I want it to have the intuitive usability as demos.com but with some more advanced functionality using Mathematica. $\endgroup$ – Michael McCain Dec 30 '15 at 12:50
  • 1
    $\begingroup$ I have some code you might be interested in. It's not a proper answer to this question because it doesn't allow for dragging of curves. But what it does do is to allow arbitrary panning, by interactively changing the variable range and PlotRange. So, unlike the other solutions here, you won't hit a wall if you pan too far. It also, somewhat amusingly, supports "throwing". I don't have it anywhere at the moment, but if there's interest, I could maybe throw it up on a Github gist or something. $\endgroup$ – John Fultz Dec 31 '15 at 19:56
  • 1
    $\begingroup$ @JohnFultz Yes I am interested!. I would be interested in how you implement throwing. Thank you! $\endgroup$ – Michael McCain Jan 1 '16 at 0:04
9
$\begingroup$

This isn't a very elegant implementation, but since nobody else has answered so far, I've decided to post it. It is an extension of what I posted in this previous question.

f1[x_] := x^2
f2[x_] := x + 1

With[{span = 10., δ = .1, f1DragPt = {0., f1[0.]}, f2DragPt = {0., f2[0.]}},
  Framed @
    DynamicModule[{mXY, action, xmin, xmax, ymin, ymax, p1, p2},
      {xmin, xmax} = {ymin, ymax} = span {-1., 1.}/2.;
      p1 = p2 = {0., 0.};
    EventHandler[
      Dynamic @
        Plot[
          {f1[t - p1[[1]]] + p1[[2]], f2[t - p2[[1]]] + p2[[2]]}, 
          {t, xmin, xmax}, 
          AspectRatio -> Automatic, 
          PlotRange -> {{xmin, xmax}, {ymin, ymax}},
          GridLines ->
            {Floor /@ Range[xmin, xmax], Floor /@ Range[ymin, ymax]}],
      {"MouseDown" :>
        (mXY = MousePosition["Graphics"];
         Which[
           Abs[f1[mXY[[1]] - p1[[1]]] + p1[[2]] - mXY[[2]]] < δ, action = "moveF1",
           Abs[f2[mXY[[1]] - p2[[1]]] + p2[[2]] - mXY[[2]]] < δ, action = "moveF2",
           True, action = "pan"])},
      {"MouseDragged" :>
        Module[{xy, dx, dy},
          xy = MousePosition["Graphics"];
          Switch[action,
            "pan", 
              {dx, dy} = xy - mXY; xmin -= dx; xmax -= dx; ymin -= dy; ymax -= dy,
            "moveF1", p1 = xy - f1DragPt,
            "moveF2", p2 = xy - f2DragPt]]}]]]

Initial plot

initial

Plot after some panning and curve dragging.

plot

To select a curve for moving, click anywhere along the curve. However, dragging will always be with the mouse cursor at the designated drag point. Having to designate drag points for each curve is one of the inelegant features of this solution.

Update

Here is a version that doesn't require designating drag points in advance. Any point of a curve can serve as a drag point.

With[{span = 10., δ = .1},
  Framed @
    DynamicModule[
        {mXY, action, f1DragPt, f2DragPt, xmin, xmax, ymin, ymax, p1, p2},
      {xmin, xmax} = {ymin, ymax} = span {-1., 1.}/2.;
      p1 = p2 = {0., 0.};
      EventHandler[
        Dynamic @
          Plot[
            {f1[t - p1[[1]]] + p1[[2]], f2[t - p2[[1]]] + p2[[2]]}, 
            {t, xmin, xmax}, 
            AspectRatio -> Automatic, 
            PlotRange -> {{xmin, xmax}, {ymin, ymax}},
            GridLines ->
              {Floor /@ Range[xmin, xmax], Floor /@ Range[ymin, ymax]}],
        {"MouseDown" :>
          (mXY = MousePosition["Graphics"];
           Which[
             Abs[f1[mXY[[1]] - p1[[1]]] + p1[[2]] - mXY[[2]]] < δ, 
               f1DragPt = mXY - p1; action = "moveF1",
             Abs[f2[mXY[[1]] - p2[[1]]] + p2[[2]] - mXY[[2]]] < δ,
               f2DragPt = mXY - p2; action = "moveF2",
             True, action = "pan"])},
        {"MouseDragged" :>
          Module[{xy, dx, dy},
            xy = MousePosition["Graphics"];
            Switch[action,
              "pan", 
                 {dx, dy} = xy - mXY; xmin -= dx; xmax -= dx; ymin -= dy; ymax -= dy,
              "moveF1", p1 = xy - f1DragPt,
              "moveF2", p2 = xy - f2DragPt]]}]]]
$\endgroup$
  • $\begingroup$ Great solution. I especially like the "delta" for recognizing closeness to the curves. I have been working on a solution using a locator for the curve and event handler for panning as suggested. : ) I'll try and get mine running and post here, although I like your solution better. $\endgroup$ – Michael McCain Dec 30 '15 at 12:46
1
$\begingroup$

Here's a solution using Manipulate.

xmin = -5;
ymin = -5;
xmax = 5;
ymax = 5;
Manipulate[EventHandler[Dynamic@Show[
    Plot[{t - qt[[1]] + qt[[2]], (t - pt[[1]])^2 + pt[[2]]}, {t, xmin,
       xmax}],
    PlotRange -> {{xmin, xmax}, {ymin, ymax}},
    Axes -> True,
    GridLines -> {Floor /@ Range[xmin, xmax], 
      Floor /@ Range[ymin, ymax]},
    AspectRatio -> Automatic,
    Ticks -> False],
  If[Norm[MousePosition["Graphics"] - pt] > .3 && 
    Norm[MousePosition["Graphics"] - qt] > .3,
   {
    {"MouseDown" :> (mouseXY = MousePosition["Graphics"])},
    {"MouseDragged" :> Module[{dx, dy},
       {dx, dy} = (MousePosition["Graphics"] - mouseXY);
       xmin -= dx;
       xmax -= dx;
       ymin -= dy;
       ymax -= dy]}}]],
 {{pt, {-1, 1}}, Locator}, {{qt, {3, 3}}, Locator}]

enter image description here

If anyone sees any bad programming practice in my code or how it can be made better, let me know. I would appreciate it. Thank you!

$\endgroup$
  • $\begingroup$ Click close to the locators to move the curves. Click further away to pan. $\endgroup$ – Michael McCain Dec 31 '15 at 10:33
  • $\begingroup$ +1, but conceptual it's almost like replacing Framed in the answer by m_goldberg with Manipulate. Maybe it's time for you to advance to using DynamicModule instead of Manipulate. Or do you have any specific reason for using Manipulate? A straight implementation of this application in a pure Manipulate way might not even be possible though. $\endgroup$ – Karsten 7. Dec 31 '15 at 14:48
  • $\begingroup$ You're right. I started off working on dynamicmodule for a few hours and couldn't get it to work so tried to fix it using manipulate but turns out I didn't change much. Will work on leaning dynamicmodule. Thanks! $\endgroup$ – Michael McCain Dec 31 '15 at 17:50

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.