# [ACCEPTED]-Finding points on a rectangle at a given angle-geometry

Score: 42

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

You 12 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 11 of trigonometry-fu, we can get the coordinates 10 for your desired intersection in each region. So 9 Z0 is the expression for the intersection 8 point for regions 1 and 3
And Z1 is the expression 7 for the intersection point for regions 2 6 and 4

The desired lines pass from (X0,Y0) to 5 Z0 or Z1 depending the region. So remembering 4 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 3 be aware of the signs of Tan(φ) in each 2 quadrant, and that the angle is always measured 1 from THE POSITIVE x axis ANTICLOCKWISE.

HTH!

Score: 14

Ok, whew!, I finally got this one.

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

Here's 7 what it looks like in Objective-C. It should 6 be simple enough to convert to whatever 5 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 4 a little test-view I created to verify that 3 it works. Create this view and put it somewhere, it 2 will make another little view scoot around 1 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];

// 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
``````
Score: 12

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;
};``````

0

Score: 4

Following your picture, I'm going to assume 13 that the rectangle is centered at (0,0), and 12 that the upper right corner is (w,h). Then 11 the line connecting (0,0) to (w,h) forms 10 an angle φ with the X axis, where tan(φ) = h/w.

Assuming 9 that θ > φ, we are looking 8 for the point (x,y) where the line that 7 you have drawn intersects the top edge of 6 the rectangle. Then y/x = tan(θ). We 5 know that y=h so, solving for x, we get 4 x = h/tan(θ).

If θ < φ, the 3 line intersects with the right edge of the 2 rectangle at (x,y). This time, we know 1 that x=w, so y = tan(θ)*w.

Score: 1

There's a good (more programmatic iOS / Objective-C) answer 2 to this question at Find the CGPoint on a UIView rectangle intersected by a straight line at a given angle from the center point involving the following 1 steps:

1. Assume that the angle is greater than or equal to 0 and less than 2*π, going counterclockwise from 0 (East).
2. Get the y coordinate of the intersection with the right edge of the rectangle [tan(angle)*width/2].
3. Check whether this y coordinate is in the rectangle frame (absolute value less than or equal to half the height).
4. If the y intersection is in the rectangle, then if the angle is less than π/2 or greater than 3π/2 choose the right edge (width/2, -y coord). Otherwise choose the left edge (-width/2, y coord).
5. If the y coordinate of the right edge intersection was out-of-bounds, calculate the x coordinate of the intersection with the bottom edge [half the height/tan(angle)].
6. Next determine whether you want the top edge or the bottom edge. If the angle is less than π, we want the bottom edge (x, -half the height). Otherwise, we want the top edge (-x coord, half the height).
7. Then (if the center of the frame is not 0,0), offset the point by the actual center of the frame.
Score: 0

For Java, LibGDX. I've let the angle be 1 a double to increase precision.

``````public static Vector2 projectToRectEdge(double angle, float width, float height, Vector2 out)
{
}

public static Vector2 projectToRectEdgeRad(double angle, float width, float height, Vector2 out)
{
float theta = negMod((float)angle + MathUtils.PI, MathUtils.PI2) - MathUtils.PI;

float diag = MathUtils.atan2(height, width);
float tangent = (float)Math.tan(angle);

if (theta > -diag && theta <= diag)
{
out.x = width / 2f;
out.y = width / 2f * tangent;
}
else if(theta > diag && theta <= MathUtils.PI - diag)
{
out.x = height / 2f / tangent;
out.y = height / 2f;
}
else if(theta > MathUtils.PI - diag && theta <= MathUtils.PI + diag)
{
out.x = -width / 2f;
out.y = -width / 2f * tangent;
}
else
{
out.x = -height / 2f / tangent;
out.y = -height / 2f;
}

return out;
}
``````
Score: 0

Unreal Engine 4 (UE4) C++ Version.

Note: This 4 is based off of Olie's Code. Based on Belisarius's 3 Answer. Give those guys upvotes if this helps 2 you.

Changes: Uses UE4 syntax and functions, and 1 Angle is negated.

``````UFUNCTION(BlueprintCallable, meta = (DisplayName = "Project To Rectangle Edge (Radians)"), Category = "Math|Geometry")
static void ProjectToRectangleEdgeRadians(FVector2D Extents, float Angle, FVector2D & EdgeLocation);
``````

Code

``````void UFunctionLibrary::ProjectToRectangleEdgeRadians(FVector2D Extents, float Angle, FVector2D & EdgeLocation)
{
// Move theta to range -M_PI .. M_PI. Also negate the angle to work as expected.

// Ref: http://stackoverflow.com/questions/4061576/finding-points-on-a-rectangle-at-a-given-angle
float a = Extents.X; // "a" in the diagram | Width
float b = Extents.Y; // "b"                | Height

// Find our region (diagram)
float rectAtan = FMath::Atan2(b, a);
float tanTheta = FMath::Tan(theta);

int region;
if ((theta > -rectAtan) && (theta <= rectAtan))
{
region = 1;
}
else if ((theta > rectAtan) && (theta <= (PI - rectAtan)))
{
region = 2;
}
else if ((theta > (PI - rectAtan)) || (theta <= -(PI - rectAtan)))
{
region = 3;
}
else
{
region = 4;
}

float xFactor = 1.f;
float yFactor = 1.f;

switch (region)
{
case 1: yFactor = -1; break;
case 2: yFactor = -1; break;
case 3: xFactor = -1; break;
case 4: xFactor = -1; break;
}

EdgeLocation = FVector2D(0.f, 0.f); // This rese is nessesary, UE might re-use otherwise.

if (region == 1 || region == 3)
{
EdgeLocation.X += xFactor * (a / 2.f);              // "Z0"
EdgeLocation.Y += yFactor * (a / 2.f) * tanTheta;
}
else // region 2 or 4
{
EdgeLocation.X += xFactor * (b / (2.f * tanTheta)); // "Z1"
EdgeLocation.Y += yFactor * (b / 2.f);
}
}
``````
Score: 0

PYTHON

``````import math
import matplotlib.pyplot as plt

twoPI = math.pi * 2.0
PI = math.pi

def get_points(width, height, theta):
theta %= twoPI

aa = width
bb = height

rectAtan = math.atan2(bb,aa)
tanTheta = math.tan(theta)

xFactor = 1
yFactor = 1

# determine regions
if theta > twoPI-rectAtan or theta <= rectAtan:
region = 1
elif theta > rectAtan and theta <= PI-rectAtan:
region = 2

elif theta > PI - rectAtan and theta <= PI + rectAtan:
region = 3
xFactor = -1
yFactor = -1
elif theta > PI + rectAtan and theta < twoPI - rectAtan:
region = 4
xFactor = -1
yFactor = -1
else:
print(f"region assign failed : {theta}")
raise

# print(region, xFactor, yFactor)
edgePoint = [0,0]
## calculate points
if (region == 1) or (region == 3):
edgePoint += xFactor * (aa / 2.)
edgePoint += yFactor * (aa / 2.) * tanTheta
else:
edgePoint += xFactor * (bb / (2. * tanTheta))
edgePoint += yFactor * (bb /  2.)

return region, edgePoint

l_x = []
l_y = []
theta = 0
for _ in range(10000):
r, (x, y) = get_points(600,300, theta)
l_x.append(x)
l_y.append(y)
theta += (0.01 / PI)

if _ % 100 == 0:
print(r, x,y)

plt.plot(l_x, l_y)
plt.show()
``````

0

More Related questions