views:

64

answers:

3

I need to limit an angle so it fits into a segment. I have drawn and links to a diagram below to better describe what I am after.

I am trying to calculate this for a computer program, where I have an angle (slope), and a point (the mouse pointer). The distance does not matter to me, just the angles. If the point is within b1 (green area) then that's fine. But if the point is within b2 or b3 (red or orange) areas, then the angle should snap back to the limit of the green area (along the line s).

Diagram of my problem

The main problem I am having in figuring this out, is snapping the angle to the correct side e.g. If the point is in the red area, then the angle should snap to the angle s on the red side and vice versa.

I am also having trouble because s could be any angle, so I am being tripped up because I can't just do something like this:

if a(radians) is greater than s(radians) then set a to the value of s

or I will get errors when the angle goes between zero and 2Pi.

So How would I work this out? Do I have to rotate everything back to a zero point or something then put it back when I have made my calculations?

Thanks for reading my questing.

A: 

Are you intending on tweening back to the line s ? If so, you can use greensock's TweenLite library and use the rotation plugin ShortRotationPlugin. Then all you need to do is determine the deltas between your position and the line s and use the smallest delta.

sberry2A
A: 

I'd have to make some assumptions since you don't specify how you define slope. Either way you should be able to get a theta from the equation.

Here is an example that does what you require. It was a fun challenge to get it to work and it should get you started. Note: one thing that helped me was always keeping angles between -pi and pi. I also use vector projection to get the line that is drawn to look like it correctly lies on the slope.

package
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

public class CircleSnap extends Sprite
{
    private static const DEG_TO_RAD:Number = (Math.PI / 180);
    private static const RAD_TO_DEG:Number = (180 / Math.PI);

    private static const centerPoint:Point = new Point(200, 200);
    private static const RADIUS:int = 100;

    private var slope:Number;

    private var circle:Sprite;
    private var line:Sprite;

    public function CircleSnap()
    {
        addEventListener(Event.ADDED_TO_STAGE, addedToStage);
    }

    private function addedToStage(event:Event):void
    {
        // choose a random slope (between -Math.PI to Math.PI)
        slope = (Math.random()*Math.PI*2) - Math.PI;

        // draw the circle
        circle = makeColoredCircle();
        addChild(circle);

        circle.x = centerPoint.x;
        circle.y = centerPoint.y;
        circle.rotation = slope * RAD_TO_DEG;

        line = new Sprite();
        addChild(line);

        stage.addEventListener(MouseEvent.MOUSE_MOVE, drawLine);
    }

    private function drawLine(event:MouseEvent):void
    {
        line.graphics.clear();

        // calculate the angle of the line
        var lineAngle:Number = Math.atan2(
            stage.mouseY - centerPoint.y,
            stage.mouseX - centerPoint.x);

        var angleDiff:Number = (lineAngle - slope);
        if(Math.abs(angleDiff) > Math.PI)
        {
            // wrap the angle between -pi and pi
            var angleDir:int = angleDiff > 0 ? -1 : 1;
            angleDiff = (Math.PI*2 - Math.abs(angleDiff)) * angleDir;
        }

        // assume we just draw to the mouse position
        var destX:Number = stage.mouseX;
        var destY:Number = stage.mouseY;
        // if we are in the top area of the circle
        if(angleDiff < 0)
        {
            // calculate the length
            var xDiff:Number = stage.mouseX - centerPoint.x;
            var yDiff:Number = stage.mouseY - centerPoint.y;
            // we use Math.cos here to project the new line onto the slope
            var len:Number = Math.cos(angleDiff) * Math.sqrt(xDiff*xDiff+yDiff*yDiff);
            destX = Math.cos(slope) * len + centerPoint.x;
            destY = Math.sin(slope) * len + centerPoint.y;
        }

        // draw the line
        line.graphics.lineStyle(3, 0x00FFFF);
        line.graphics.moveTo(centerPoint.x, centerPoint.y);
        line.graphics.lineTo(destX, destY);
    }

    private function makeColoredCircle():Sprite
    {
        var circle:Sprite = new Sprite();
        var bottomHalf:Sprite = new Sprite();
        circle.addChild(bottomHalf);
        bottomHalf.graphics.beginFill(0xFF0000);
        halfCircle(bottomHalf.graphics, 0, 0, RADIUS);
        var topLeftQuarter:Sprite = new Sprite();
        circle.addChild(topLeftQuarter);
        topLeftQuarter.graphics.beginFill(0x00FF00);
        quarterCircle(topLeftQuarter.graphics, 0, 0, RADIUS);
        topLeftQuarter.rotation = 180
        var topRightQuarter:Sprite = new Sprite();
        circle.addChild(topRightQuarter);
        topRightQuarter.graphics.beginFill(0x0000FF);
        quarterCircle(topRightQuarter.graphics, 0, 0, RADIUS);
        topRightQuarter.rotation = -90;
        return circle;
    }

    // found this here: http://actionsnippet.com/?p=1515
    private function halfCircle(g:Graphics, x:Number,y:Number,r:Number):void
    {
        var c1:Number=r * (Math.SQRT2 - 1);
        var c2:Number=r * Math.SQRT2 / 2;
        g.moveTo(x+r,y);
        g.curveTo(x+r,y+c1,x+c2,y+c2);
        g.curveTo(x+c1,y+r,x,y+r);
        g.curveTo(x-c1,y+r,x-c2,y+c2);
        g.curveTo(x-r,y+c1,x-r,y);
    }

    // modified from halfCircle found here: http://actionsnippet.com/?p=1515
    private function quarterCircle(g:Graphics, x:Number,y:Number,r:Number):void
    {
        var c1:Number=r * (Math.SQRT2 - 1);
        var c2:Number=r * Math.SQRT2 / 2;
        g.moveTo(x+r,y);
        g.curveTo(x+r,y+c1,x+c2,y+c2);
        g.curveTo(x+c1,y+r,x,y+r);
        g.lineTo(x, y);
    }
}

}
James Fassett
+1  A: 

First code in an ATAN2() function, to calculate the absolute angle for the point relative to horizontal plane. Then subtract the angle of the slope. if the result is <0 then snap to 0 and if the result is >180 snap to 180. The add the angle of the slope back to get your final angle

psi = (angle of slope)
a = atan2(x,y)
th = a-psi
if( th<0 ) { th=0; }
if( th>pi ) { th=pi; }
a = th+psi

try it.

jalexiou