Circle/Arc Constrain

Date:    Mon, 21 Oct 1996 15:38:45 -0700
From:    Eric Woods 
Subject: Re: Circular Slider Code...

Steven Shane wrote about circular slider code...
> The other day Robert sent out a neat variation on a slider handler.
> but lets get funky and see if we can make the smile on a happy face
> into a slider path.
Well, it sounded fun, so I spent a little time on it (too much time!), and learned some things along the way. My code to make a curved slider path follows.

The main thing I learned was that the way to construct something like this is to begin by constraining a sprite path along something simple (like a circle) and then further constrain it along something more complex (like an arc, which represents the smile on a happy face).

Here's some code you can use to constrain a sprite to a circle or an arc. Please note that when you define the limiting angles you are measuring clockwise from the x-axis (horizontal), not counter-clockwise like you typically do for standard graphing. (The reason for this, of course, is that a screen location is defined with down be positive, rather than negative.)


on mousedown
  set constraintCenter = point(100,100)
  set constraintRadius = 25.0

  --* FOR CONSTRAINING TO AN ARC, ADD THESE ANGLES
  set constraintAngleMin = 20 * pi()/180
  set constraintAngleMax = 160 * pi()/180

  repeat while the mouseDown
    set mouseX = the mouseH - the locH of constraintCenter
    set mouseY = the mouseV - the locV of constraintCenter

    --* IF YOU JUST WANT TO CONSTRAIN TO A CIRCLE, USE THIS...
    set mouseRadius = sqrt((mouseX * mouseX) + (mouseY * mouseY))
    if mouseRadius = 0 then next repeat
    set reductionRatio = constraintRadius/mouseRadius
    set constrainedX = integer(mouseX * reductionRatio)
    set constrainedY = integer(mouseY * reductionRatio)

    --* IF YOU WANT TO CONSTRAIN TO AN ARC, ADD THIS...
    if mouseX = 0 then next repeat
    set mouseAngle = atan(float(mouseY)/float(mouseX))   
    if mouseX * mouseY > 0 then
      if mouseX > 0 then set quadrant = 1
      else set quadrant = 3
    else
      if mouseY > 0 then set quadrant = 2
      else set quadrant = 4
    end if

    case quadrant of
      1: set mouseAngle = mouseAngle
      2: set mouseAngle = pi() + mouseAngle
      3: set mouseAngle = pi() + mouseAngle
      4: set mouseAngle = 2*pi() + mouseAngle
    end case

    if (mouseAngle > constraintAngleMax) OR 
       (mouseAngle << constraintAngleMin) then next repeat

    set the loc of sprite the clickOn = 
       point(constrainedX, constrainedY) + constraintCenter
    updatestage
  end repeat
end
I separated the if-then clauses from the case clauses just for clarity (to show that the if-then statements are in there to determine what quadrant the arctangent function is describing). They could easily be compressed into just the if-then statements.

Anyone want to come up with a more efficient method of doing this? Or to continue in the spirit of the first post, anyone want to try to add to this function the ability to constrain something to a path of arcs, instead of just one (for instance, an "S" or string made up of semi-circle arcs)?


Date:    Thu, 24 Oct 1996 13:10:58 -0700
From:    Eric Woods 
Subject: Re: Circular Slider Code...

At 5:16 PM -0700 10/23/96, John Dowdell wrote:
>Richard Schedler writes on Oct 22, "Or even a wavy line? Sorry maths wasn't my
>strong point either. Anyone got any ideas?" Gary Martin later adds, "Can an
>object be constrained to a line on an angle?"
Well, with some help from Darrel Plant and his bezier curve demo (see http://www.moshplant.com/direct-or/), I put several solutions to this constraint idea into one movie. The first two solutions I've already posted the code for (they were handlers that constrained a sprite to a circle or an arc and constrained a sprite to an angled line). The third solution is the one that could be a universal solution that John's code didn't handle -- constraint curves that have crossover points, such a bezier curve which loops back on itself. You can view the solution at http://home.earthlink.net/~lizneric/constraints/constraint.html. (Note: you can grab a copy of the movie, which isn't protected, by locating it in your browser's cache directory, openning it with Director and then saving it under a different name).

The key to the bezier curve solution (and thanks to Darrel for the insight on this) is to measure the distance between the current slider position (call it 't') and the mouse position, and compare this with the distance between t+1 and t-1 positions and the mouse -- then move the slider to the position which is closest to the mouse. The primary code for this function is below, and it probably makes more sense than my explanation. The bezier curve solution can map a slider graphic to any *single* bezier curve. But it should be easy put several beziers together simply by offsetting the variable which defines the location on the curve. In other words, let t range from 0 to the number of curves and let t1, t2, t3, etc. be defined as t1 = t, t2=t-1, t3=t-2, etc. Then when t<1, you map to curve 1, and when 1


on mousedown
  --* these globals are used to define the bezier curve
  --* the curve gets defined in another handler
  global x1,y1,ax,ay,bx,by,cx,cy,t

  set fx = []
  set fy = []
  set dx = []
  set dy = []

  set x0 = 0
  set y0 = 0

  set numPoints = 50
  if t < 0 then set t = 0

  repeat while the stillDown
    set prevt = t

    repeat with i = 1 to 3
      set t = prevt + (i - 2)

      --* put in equation here to determine X and Y for t-1, t, and t+1
      set t1 = float (t) / numPoints
      set t2 = float (t1 * t1)
      set t3 = float (t2 * t1)
      set constraintX = ax * t3 + bx * t2 + cx * t1 + x1
      set constraintY = ay * t3 + by * t2 + cy * t1 + y1

      setAt(fx,i,constraintX)
      setAt(fy,i,constraintY)
    end repeat

    --* this calculates the closest point for t-1, t, and t+1
    set x0 = the mouseH
    set y0 = the mouseV
    set dx = fx - x0
    set dy = fy - y0
    set r2 = dx * dx + dy * dy
    set r2min = min(r2)
    set i = getPos(r2, r2min)
    set t = prevt + (i - 2)
    if t > numPoints then set t = numPoints
    else if (t < 0) then set t = 0

    --* put in equation here, using the new t to determine X and Y
    set t1 = float (t) / numPoints
    set t2 = float (t1 * t1)
    set t3 = float (t2 * t1)
    set constrainedX = integer (ax * t3 + bx * t2 + cx * t1 + x1)
    set constrainedY = integer (ay * t3 + by * t2 + cy * t1 + y1)

    --* this relocates the control sprite
    set the locH of sprite 12 = constrainedX
    set the locV of sprite 12 = constrainedY
    updateStage
  end repeat
end