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$
6
  • 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, 2015 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$
    – Karsten7
    Dec 30, 2015 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$
    – B flat
    Dec 30, 2015 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, 2015 at 19:56
  • 1
    $\begingroup$ @JohnFultz Yes I am interested!. I would be interested in how you implement throwing. Thank you! $\endgroup$
    – B flat
    Jan 1, 2016 at 0:04

2 Answers 2

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$
1
  • $\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$
    – B flat
    Dec 30, 2015 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$
3
  • $\begingroup$ Click close to the locators to move the curves. Click further away to pan. $\endgroup$
    – B flat
    Dec 31, 2015 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$
    – Karsten7
    Dec 31, 2015 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$
    – B flat
    Dec 31, 2015 at 17:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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