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:
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.
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;};