I have cobbled together a solution that does exactly what I need, but I don't fully understand how it works:

pts = {{2, 0}, {0, 2}};

LocatorPane[Dynamic[pts, (pts = #; pts = Function[{pnt}, If[Norm[pnt, 2] < 3, pnt,
    3*Normalize[pnt, (Norm[#, 2] &)]]] /@pts) &], Graphics[{LightGray, Disk[{0, 0}, 3]}, ImageSize -> 200]]

My goal is to have two locators constrained to a circle with a radius of 3.

I have a vague idea of the replacement operator & and the Map command /@, but there is too much going on at once. A step-by-step explanation is much appreciated.

What I do understand is the part inside the If clause: if the locator being dragged is still inside the disk those coordinates are returned, but if its coordinates have reached the edge of the disk it will stay there, restricting movement.


1 Answer 1


Dynamic can have an expression followed by a single function evaluated during the interactive editing or a list of functions to be evaluated at various times (including before, during, and after interaction). What you have is essentially Dynamic[expression, (function part 1; function part 2)&]. The expression is the thing that needs to be updated—a list of points in this case.

Since you have the single function version, the function is evaluated essentially continuously as you move the locators around—any time the position of a locator changes, it is updated. In this case, the function (which is a compound expression connected by a semicolon) has two parts, even though you might be tempted to think of the function as the thing labeled Function.

First, pts is updated to be the current value by pts = #. The & after the closing parenthesis essentially feeds the value to the # (also called a Slot). The value that the & receives automatically from the system is a list of the current positions of the locators. This part is necessary since you have a second part where things might actually change, but if you didn't then Dynamic[pts] and Dynamic[pts, (pts = #)&] would be identical.

In the second part we have pts = Function[{pnt}, If[Norm[pnt, 2] < 3, pnt, 3*Normalize[pnt, (Norm[#, 2] &)]]] /@ pts. At the outermost level, we have Function that is being mapped (/@) onto the list pts. This means it takes each element from pts one at a time, applies the Function to each of them, and returns the list. In this case, pts is a list of two points. It feeds each point into the Function where it is temporarily renamed pnt for easy reference throughout the rest of the function. This pnt is then tested (first part of the If) to determine whether the 2-norm of pnt is less than 3. If that is true, the pnt is returned as-is (second part of the If) and nothing is done to it. If it is not true (3rd part of the If), it is outside the disk and pnt is modified to be the closest point which is within the disk before being returned. Finally, pts is updated with this newest list and Mathematica can then update the graphic.


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.