4
$\begingroup$

I am struggling with Manipulate dynamic updating. I would like to have an image containing two points which can both be moved in real time to any location on the image EXCEPT if the distance between them becomes smaller than a given value. The difficulty is that I do not want to be able to move them to a position where their distance is smaller than this value and then disable the sliders, like I have done below. I would like the slider to not enable me to go to the forbidden locations in the first place. Please find my code below.

Manipulate[
 pt1 = {x1, y1};
 pt2 = {x2, y2};
 dist = N[EuclideanDistance[pt1, pt2]];
 Graphics[{Point[pt1], Point[pt1]}],
 {{x1, 0}, -5, 5, Enabled -> Dynamic[dist > 2]},
 {{y1, 0}, -5, 5, Enabled -> Dynamic[dist > 2]},
 {{x2, 2}, -5, 5, Enabled -> Dynamic[dist > 2]},
 {{y2, 2}, -5, 5, Enabled -> Dynamic[dist > 2]}]

Your help would be very much appreciated. Thank you in advance. Ella

$\endgroup$
5
$\begingroup$
DynamicModule[
  { pt1, pt2, dist
  , trackingFunction = Function[{val, sym}
    , If[Block[{sym = val}, dist >= 2], sym = val]
    , HoldRest
    ]
  }
, Manipulate[
    Graphics[{Point[Dynamic@pt1], Point[Dynamic@pt2]}
    , PlotRange -> 5, Frame -> True
    ]

  , {{x1, 0}, -5, 5, TrackingFunction :> trackingFunction}
  , {{y1, 0}, -5, 5, TrackingFunction :> trackingFunction}
  , {{x2, 2}, -5, 5, TrackingFunction :> trackingFunction}
  , {{y2, 2}, -5, 5, TrackingFunction :> trackingFunction}

  , Initialization :> (
      pt1 := {x1, y1};  pt2 := {x2, y2}; dist := N[EuclideanDistance[pt1, pt2]];        
    )
  ]
]

Our trackingFunction checks whether dist<0 and proceeds with update or not. Notice Block usage, we want to check without committing.

Let me know if anything is not clear.

Btw, it feels better to use Locators instead of Sliders:

DynamicModule[ { pts = RandomReal[{-5, 5}, {2, 2}] }
, LocatorPane[
    Dynamic[pts, If[EuclideanDistance @@ # > 2, pts = #] &]
  , Graphics[{Point[Dynamic[pts]]}, PlotRange -> 5, Frame -> True]
  ]
]
$\endgroup$
  • $\begingroup$ Super good. Thank you very much. $\endgroup$ – E Crane Oct 23 '18 at 14:43
2
$\begingroup$

This is probably too late, since kuba's answer has already been accepted, but I couldn't get his solution using a locator pane to work, so I'm posting my own more pedestrian version.

constraintF[q1_, q2_, pts_] :=
  Module[{p1, p2},
    {p1, p2} = pts;
    If[Norm[p2 - p1] >= 2,
      pts,
      If[q1 == p1,
        {q1, q1 + 2 Normalize[p2 - q1]},
        {q2, q2 + 2 Normalize[p1 - q2]}]]]

DynamicModule[{p1 = {0, 0}, p2 = {2, 2}, q1, q2, pts},
  LocatorPane[
    Dynamic[pts,
      {({q1, q2} = pts) &, ({p1, p2} = constraintF[q1, q2, #]) &, (pts = #) &}],
    Dynamic @ 
      Graphics[
        {{FaceForm[Transparent], Rectangle[{-5, -5}, {5, 5}]},
         {Red, AbsolutePointSize[10], Point[{p1, p2}]}},
        Frame -> True],
    Appearance -> None],
  Initialization :> (pts = {p1, p2})]

demo

$\endgroup$
  • $\begingroup$ There was a typo so my LocatorPane should work now. Let me know if it still makes troubles for you. $\endgroup$ – Kuba Oct 24 '18 at 6:17
  • $\begingroup$ @Kuba. Your corrected LocatorPane solution works, but on my computer it does not work nearly as smoothly as the code given above. $\endgroup$ – m_goldberg Oct 25 '18 at 22:27

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.