Tutorial :Calculate a vector from a point in a rectangle to edge based on angle



Question:

According to Daniel, in his answer, there is no easy way to modify the below function so I bit the bullet and started from scratch. Solution is below (as an answer). Actually, ignore my answer. See Tom Sirgedas' answer it's a lot shorter.


I need to modify the solution found here: Calculate a vector from the center of a square to edge based on radius, that calcs the vector from the center of a rectangle, to work for any point within the rectangle.

Here's the previous solution, from the link:

    double magnitude;      double abs_cos_angle= fabs(cos(angle));      double abs_sin_angle= fabs(sin(angle));      if (width/2*abs_sin_angle <= height/2*abs_cos_angle)      {              magnitude= width/2/abs_cos_angle;      }      else      {              magnitude= height/2/abs_sin_angle;      }        double check_x= x + cos(angle)*magnitude;      double check_y= y + sin(angle)*magnitude;  

check_x and check_y return the point on the edge of the rectangle that a line drawn from the center, at angle, will intersect.

It's a while since I went to school, so I blindly tried replacing width/2 and height/2 with the point I'm interested in. Unfortunately that didn't work.

Any ideas?

ETA:

This blind modification always returns the correct result if the line intersects the rectangle at the Top or Left. Depending on the quadrant the originating point is in, it returns a point too far away or too close when the line intersects the Right or Bottom.


Solution:1

Let's say the rectangle is defined by (x1,y1,x2,y2) and let's say the ray starts at (px,py).

Let vx = cos(angle)

Let vy = sin(angle)

Traveling a distance of t along the ray will bring you to the point (px+tvx, py+tvy).

Traveling along the ray,

  • we hit the left wall when px+t*vx = x1, or t=(x1-px)/vx
  • we hit the right wall when px+t*vx = x2, or t=(x2-px)/vx
  • we hit the top wall when py+t*vy = y1, or t=(y1-py)/vy
  • we hit the bottom wall when py+t*vy = y2, or t=(y2-py)/vy

So, there are four possible solutions for t. The correct value of t (among the four) is the smallest positive one. The actual intersection is at the point (px+tvx, py+tvy). Just be careful not to divide by zero!


Solution:2

The simplest solution is probably to just perform four ray line segment intersection tests. I doubt that a special-purpose solution will be much more efficient or easier to understand and maintain.


Solution:3

Let's clarify one thing right off the bat: it sounds to me like you're worried about vectors in 2D. All your points are in the plane. Is it also true that it's a rectangle you're interested in (four corners, all with 90 degree angles, two pairs of opposite sides have identical lengths)?

The center of your rectangle is given by the average of the four corner points:

alt text http://www.equationsheet.com/latexrender/pictures/ccdca519c24bb6ab2b6164144645572a.gif

alt text http://www.equationsheet.com/latexrender/pictures/84550340ace7dade26dc3294fef8e156.gif

There's the starting point of your vector.

Any vector is defined by two points, so you just need the second point (e.g., the midpoint of an edge) to calculate the vector in 2D space:

alt text http://www.equationsheet.com/latexrender/pictures/7c680a2354f4a2a0d05d6c97ba949bc4.gif

The problem with starting from the center point and an angle is that it gives you an infinite number of vectors, not just one. (There are an infinite number of ending points along the line passing through your center point at a given angle.) You'll have to pluck the precise one that you're interested in from that infinite set. If it happens to be the one that intersects one of your sides at an arbitrary point, you'll have to calculate that first. It's a root finding problem along the vector line at that point. Maybe you can check out secant or other numerical methods to figure out how to do it.


Solution:4

The below solution builds a formula for the line that passes through the point provided and crosses the rectangle border at the specified angle. Depending on the angle, I test to see if it intersects with either of 2 rectangle borders. I always base the check on an angle from 0 - 90 degrees. To account for this, the test in Quadrants Q2 and Q4 use a line that is perpendicular to the line in Q1 and Q4.

When angle = 0, the line points eastwards.
I've subtracted the angle from 360 so that the line rotates clockwise instead of anti-clockwise.

Private Function GetIntersectionPoint(ByVal rectangleSize As SizeF, ByVal p As Point, ByVal degreeAngle As Single) As PointF        Dim w = CInt(rectangleSize.Width)      Dim h = CInt(rectangleSize.Height)      degreeAngle = ((360 - degreeAngle) Mod 360)        If degreeAngle = 0 Then          Return New Point(w, p.Y)      ElseIf degreeAngle = 90 Then          Return New Point(p.X, 0)      ElseIf degreeAngle = 180 Then          Return New Point(0, p.Y)      ElseIf degreeAngle = 270 Then          Return New Point(p.X, h)      End If        Dim x, y As Integer        If (degreeAngle > 0 AndAlso degreeAngle < 90) Then          y = YFromX(degreeAngle, w, p)          If y <= 0 AndAlso y >= -h Then              Return New Point(w, -y)          End If          x = XFromY(degreeAngle, 0, p)          Return New Point(x, 0)      End If        If (degreeAngle > 90 AndAlso degreeAngle < 180) Then          degreeAngle -= 90          y = YFromX_Perpedicular(degreeAngle, 0, p)          If y <= 0 AndAlso y >= -h Then              Return New Point(0, -y)          End If          x = XFromY_Perpendicular(degreeAngle, 0, p)          Return New Point(x, 0)      End If        If (degreeAngle > 180 AndAlso degreeAngle < 270) Then          degreeAngle -= 180          y = YFromX(degreeAngle, 0, p)          If y <= 0 AndAlso y >= -h Then              Return New Point(0, -y)          End If          x = XFromY(degreeAngle, -h, p)          Return New Point(x, h)      End If        If (degreeAngle > 270 AndAlso degreeAngle < 360) Then          degreeAngle -= 270          y = YFromX_Perpedicular(degreeAngle, w, p)          If y <= 0 AndAlso y >= -h Then              Return New Point(w, -y)          End If          x = XFromY_Perpendicular(degreeAngle, -h, p)          Return New Point(x, h)      End If    End Function    Private Function YFromX(ByVal degreeAngle As Single, ByVal x As Integer, ByVal p As Point) As Integer      Dim alpha As Double = degreeAngle * Math.PI / 180      Dim sinAlpha = Sin(alpha)      Dim cosAlpha = Cos(alpha)      Return CInt(sinAlpha / cosAlpha * (x - p.X) - p.Y)  End Function    Private Function XFromY(ByVal degreeAngle As Single, ByVal y As Integer, ByVal p As Point) As Integer      Dim alpha As Double = degreeAngle * Math.PI / 180      Dim sinAlpha = Sin(alpha)      Dim cosAlpha = Cos(alpha)      Return CInt(cosAlpha / sinAlpha * (y + p.Y) + p.X)  End Function    Private Function YFromX_Perpedicular(ByVal degreeAngle As Single, ByVal x As Integer, ByVal p As Point) As Integer      Dim alpha As Double = degreeAngle * Math.PI / 180      Dim sinAlpha = Sin(alpha)      Dim cosAlpha = Cos(alpha)      Return CInt((cosAlpha / sinAlpha) * (p.X - x) - p.Y)  End Function    Private Function XFromY_Perpendicular(ByVal degreeAngle As Single, ByVal y As Integer, ByVal p As Point) As Integer      Dim alpha As Double = degreeAngle * Math.PI / 180      Dim sinAlpha = Sin(alpha)      Dim cosAlpha = Cos(alpha)      Return CInt(p.X - sinAlpha / cosAlpha * (y + p.Y))  End Function  


Solution:5

You are basically asking for the polar equation of a rectangle, and it is

r(t) = min(R, w*abs(sec(t)), h*abs(csc(t))); t = [0, 2pi]  

where w and h are the half-width and half-height, and R is any number greater than or equal to sqrt(w^2+h^2). This assumes the rectangle is at the origin, and if it's not, all you have to do is add the coordinates the center to the result, which you obtain by (rcos(t), rsin(t)). Of course, this approach is nasty since sec and csc have singularities, but you can cap them at max(w,h).


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »