Finding points on a rectangle at a given angle Finding points on a rectangle at a given angle python python

Finding points on a rectangle at a given angle


Let's call a and b your rectangle sides, and (x0,y0) the coordinates of your rectangle center.

You have four regions to consider:

alt text

    Region    from               to                 Where    ====================================================================       1      -arctan(b/a)       +arctan(b/a)       Right green triangle       2      +arctan(b/a)        π-arctan(b/a)     Upper yellow triangle       3       π-arctan(b/a)      π+arctan(b/a)     Left green triangle       4       π+arctan(b/a)     -arctan(b/a)       Lower yellow triangle

With a little of trigonometry-fu, we can get the coordinates for your desired intersection in each region.

alt text

So Z0 is the expression for the intersection point for regions 1 and 3
And Z1 is the expression for the intersection point for regions 2 and 4

The desired lines pass from (X0,Y0) to Z0 or Z1 depending the region. So remembering that Tan(φ)=Sin(φ)/Cos(φ)

    Lines in regions      Start                   End    ======================================================================       1 and 3           (X0,Y0)      (X0 + a/2 , (a/2 * Tan(φ))+ Y0       2 and 4           (X0,Y0)      (X0 + b/(2* Tan(φ)) , b/2 + Y0)

Just be aware of the signs of Tan(φ) in each quadrant, and that the angle is always measured from THE POSITIVE x axis ANTICLOCKWISE.

HTH!


Ok, whew!, I finally got this one.

NOTE: I based this off of belisarius's awesome answer. If you like this, please like his, too. All I did was turn what he said into code.

Here's what it looks like in Objective-C. It should be simple enough to convert to whatever your favorite language is.

+ (CGPoint) edgeOfView: (UIView*) view atAngle: (float) theta{    // Move theta to range -M_PI .. M_PI    const double twoPI = M_PI * 2.;    while (theta < -M_PI)    {        theta += twoPI;    }    while (theta > M_PI)    {        theta -= twoPI;    }    // find edge ofview    // Ref: http://stackoverflow.com/questions/4061576/finding-points-on-a-rectangle-at-a-given-angle    float aa = view.bounds.size.width;                                          // "a" in the diagram    float bb = view.bounds.size.height;                                         // "b"    // Find our region (diagram)    float rectAtan = atan2f(bb, aa);    float tanTheta = tan(theta);    int region;    if ((theta > -rectAtan)    &&  (theta <= rectAtan) )    {        region = 1;    }    else if ((theta >  rectAtan)    &&       (theta <= (M_PI - rectAtan)) )    {        region = 2;    }    else if ((theta >   (M_PI - rectAtan))    ||       (theta <= -(M_PI - rectAtan)) )    {        region = 3;    }    else    {        region = 4;    }    CGPoint edgePoint = view.center;    float xFactor = 1;    float yFactor = 1;    switch (region)    {        case 1: yFactor = -1;       break;        case 2: yFactor = -1;       break;        case 3: xFactor = -1;       break;        case 4: xFactor = -1;       break;    }    if ((region == 1)    ||  (region == 3) )    {        edgePoint.x += xFactor * (aa / 2.);                                     // "Z0"        edgePoint.y += yFactor * (aa / 2.) * tanTheta;    }    else                                                                        // region 2 or 4    {        edgePoint.x += xFactor * (bb / (2. * tanTheta));                        // "Z1"        edgePoint.y += yFactor * (bb /  2.);    }    return edgePoint;}

In addition, here's a little test-view I created to verify that it works. Create this view and put it somewhere, it will make another little view scoot around the edge.

@interface DebugEdgeView(){    int degrees;    UIView *dotView;    NSTimer *timer;}@end@implementation DebugEdgeView- (void) dealloc{    [timer invalidate];}- (id) initWithFrame: (CGRect) frame{    self = [super initWithFrame: frame];    if (self)    {        self.backgroundColor = [[UIColor magentaColor] colorWithAlphaComponent: 0.25];        degrees = 0;        self.clipsToBounds = NO;        // create subview dot        CGRect dotRect = CGRectMake(frame.size.width / 2., frame.size.height / 2., 20, 20);        dotView = [[DotView alloc] initWithFrame: dotRect];        dotView.backgroundColor = [UIColor magentaColor];        [self addSubview: dotView];        // move it around our edges        timer = [NSTimer scheduledTimerWithTimeInterval: (5. / 360.)                                                 target: self                                               selector: @selector(timerFired:)                                               userInfo: nil                                                repeats: YES];    }    return self;}- (void) timerFired: (NSTimer*) timer{    float radians = ++degrees * M_PI / 180.;    if (degrees > 360)    {        degrees -= 360;    }    dispatch_async(dispatch_get_main_queue(), ^{        CGPoint edgePoint = [MFUtils edgeOfView: self atAngle: radians];        edgePoint.x += (self.bounds.size.width  / 2.) - self.center.x;        edgePoint.y += (self.bounds.size.height / 2.) - self.center.y;        dotView.center = edgePoint;    });}@end


Javascript version:

function edgeOfView(rect, deg) {  var twoPI = Math.PI*2;  var theta = deg * Math.PI / 180;    while (theta < -Math.PI) {    theta += twoPI;  }    while (theta > Math.PI) {    theta -= twoPI;  }    var rectAtan = Math.atan2(rect.height, rect.width);  var tanTheta = Math.tan(theta);  var region;    if ((theta > -rectAtan) && (theta <= rectAtan)) {      region = 1;  } else if ((theta > rectAtan) && (theta <= (Math.PI - rectAtan))) {      region = 2;  } else if ((theta > (Math.PI - rectAtan)) || (theta <= -(Math.PI - rectAtan))) {      region = 3;  } else {      region = 4;  }    var edgePoint = {x: rect.width/2, y: rect.height/2};  var xFactor = 1;  var yFactor = 1;    switch (region) {    case 1: yFactor = -1; break;    case 2: yFactor = -1; break;    case 3: xFactor = -1; break;    case 4: xFactor = -1; break;  }    if ((region === 1) || (region === 3)) {    edgePoint.x += xFactor * (rect.width / 2.);                                     // "Z0"    edgePoint.y += yFactor * (rect.width / 2.) * tanTheta;  } else {    edgePoint.x += xFactor * (rect.height / (2. * tanTheta));                        // "Z1"    edgePoint.y += yFactor * (rect.height /  2.);  }    return edgePoint;};