How do I get three non-colinear points on a plane? - C++ - c++

I'm trying to implement at line-plane intersection algorithm. According to Wikipedia I need three non-colinear points on the plane to do that.
I therefore tried implementing this algorithm in C++, however. Something is definitely wrong, cause it makes no sense that I can choose whatever x and y coordinates and they'll fit in the plane. What if the plane is vertical and along the x-axis? No point with y=1 would then be in the plane.
I realize that this problem has been posted a lot on StackOverflow, and I see lots of solutions where the plane is defined by 3 points. But I only have a normal and a position. And I can't test my line-plane intersection algorithm before I sort out my non-colinear point finder.
The problem right now, is that I'm dividing by normal.z, and that obviously won't work when normal.z is 0.
I'm testing with this plane: Plane* p = new Plane(Color(), Vec3d(0.0,0.0,0.0), Vec3d(0.0,1.0,0.0)); // second parameter : position, third parameter : normal
The current code gives this incorrect answer:
{0 , 0 , 0} // alright, this is the original
{12.8377 , 17.2728 , -inf} // obviously this is not a non-colinear point on the given plane
Here's my code:
std::vector<Vec3d>* Plane::getThreeNonColinearPoints() {
std::vector<Vec3d>* v = new std::vector<Vec3d>();
v->push_back(Vec3d(position.x, position.y, position.z)); // original position can serve as one of the three non-colinear points.
srandom(time(NULL));
double rx, ry, rz, start;
rx = Plane::fRand(10.0, 20.0);
ry = Plane::fRand(10.0, 20.0);
// Formula from here: http://en.wikipedia.org/wiki/Plane_(geometry)#Definition_with_a_point_and_a_normal_vector
// nx(x-x0) + ny(y-y0) + nz(z-z0) = 0
// |-----------------| <- this is "start"
//I'll try to insert position as x0,y0,z0 and normal as nx,ny,nz, and solve the equation
start = normal.x * (rx - position.x) + normal.y * (ry - position.y);
// nz(z-z0) = -start
start = -start;
// (z-z0) = start/nz
start /= normal.z; // division by zero
// z = start+z0
start += position.z;
rz = start;
v->push_back(Vec3d(rx, ry, rz));
// TODO one more point
return v;
}
I realize that I might be trying to solve this totally wrong. If so, please link a concrete implementation of this. I'm sure it must exist, when I see so many line-plane intersection implementations.
Thanks in advance.

A plane can be defined with several ways. Typically a point on the plane and a normal vector is used. To get the normal vector from three points (P1, P2, P3 ) take the cross product of the side of the triangle
P1 = {x1, y1, z1};
P2 = {x2, y2, z2};
P3 = {x3, y3, z3};
N = UNIT( CROSS( P2-P1, P3-P1 ) );
Plane P = { P1, N }
The reverse, to go from a point P1 and normal N to three points, you start from any direction G not along the normal N such that DOT(G,N)!=0. The two orthogonal directions along the plane are then
//try G={0,0,1} or {0,1,0} or {1,0,0}
G = {0,0,1};
if( MAG(CROSS(G,N))<TINY ) { G = {0,1,0}; }
if( MAG(CROSS(G,N))<TINY ) { G = {1,0,0}; }
U = UNIT( CROSS(N, G) );
V = CROSS(U,N);
P2 = P1 + U;
P3 = P1 + V;
A line is defined by a point and a direction. Typically two points (Q1, Q2) define the line
Q1 = {x1, y1, z1};
Q2 = {x2, y2, z2};
E = UNIT( Q2-Q1 );
Line L = { Q1, E }
The intersection of the line and plane are defined by the point on the line r=Q1+t*E that intersects the plane such that DOT(r-P1,N)=0. This is solved for the scalar distance t along the line as
t = DOT(P1-Q1,N)/DOT(E,N);
and the location as
r = Q1+(t*E);
NOTE: The DOT() returns the dot-product of two vector, CROSS() the cross-product, and UNIT() the unit vector (with magnitude=1).
DOT(P,Q) = P[0]*Q[0]+P[1]*Q[1]+P[2]*Q[2];
CROSS(P,Q) = { P[1]*Q[2]-P[2]*Q[1], P[2]*Q[0]-P[0]*Q[2], P[0]*Q[1]-P[1]*Q[0] };
UNIT(P) = {P[0]/sqrt(DOT(P,P)), P[1]/sqrt(DOT(P,P)), P[2]/sqrt(DOT(P,P))};
t*P = { t*P[0], t*P[1], t*P[2] };
MAG(P) = sqrt(P[0]*P[0]+P[1]*P[1]+P[2]*P[2]);

One approach you may find easy to implement is to see where the plane intersects the coordinate axes. For the plane given by the equationaX + bY + cZ - d = 0 hold two variables at 0 and solve for the third. So the solutions would be (assuming a, b, c, and d are all non-zero):
(d/a, 0, 0)
(0, d/b, 0)
(0, 0, d/c)
You will need to consider the cases where one or more of the coefficients are 0 so you don't get a degenerate or colinear solutions. As an example if exactly one of the coefficients is 0 (say a=0) you instead use
(1, d/b, 0)
(0, d/b, 0)
(0, 0, d/c)
If exactly two of the coefficients are 0 (say a=0 and b=0) you can use:
(1, 0, d/c)
(0, 1, d/c)
(0, 0, d/c)
If d=0, the plane intersects the three axes at the origin, and so you can use:
(1, 0, -a/c)
(0, -c/b, 1)
(-b/a, 1, 0)
You will need to work out simular cases for d and exactly one other coefficient being 0, as well as d and two others being 0. There should be a total of 16 cases, but there are a few things that come to mind which should make that somewhat more manageable.

Where N=(Nx,Ny,Nz) is the normal, you could project the points N, (Ny,Nz,Nx), (Nz,Nx,Ny) onto the plane: they're guaranteed to be distinct.
Alternatively, if P and Q are on the plane, P+t(Q-P)xN is also on the plane for any t!=0 where x is the cross product.
Alternatively if M!=N is an arbitrary vector, K=MxN and L=KxN are colinear with the plane and any point p on the plane can be written as p=Origin+sK+tL for some s,t.

Related

Circle collision with compound object

I would like to do a collision detection between circle and section of a circular ring. The circle is defined by it's position position and it's radius. The other object is defined by inner and outer radius and then a startPoint and endPoint both [x, y] points.
In the examples below, this is the circle and other is the ring section.
First I just check if it's colliding with the full ring. This works without a problem.
float mag = this.position.Magnitude();
if (mag < other.InnerRadius() - this.radius ||
mag > other.OuterRadius() + this.radius) {
return false;
}
But then I need to check if the circle is inside or outside of the section defined by the two points. Closest I was able to get was to check if it isn't colliding with the start and end vectors, but this returns wrong results when the circle is fully inside the ring section.
auto dot1 = Vector::Dot(position, other.StartPoint());
auto projected1 = dot1 / Vector::Dot(other.StartPoint(), other.StartPoint()) * other.StartPoint();
auto distance1 = Vector::Distance(position, projected1);
auto dot2 = Vector::Dot(position, other.EndPoint());
auto projected2 = dot2 / Vector::Dot(other.EndPoint(), other.EndPoint()) * other.EndPoint();
auto distance2 = Vector::Distance(position, projected2);
return distance1 < radius || distance2 < radius;
What is the easiest way to check if a circle is colliding with a object defined by these two vectors?
Edit: all the point objects I'm using here are my custom Vector class that has implemented all the vector operations.
Edit2: just to clarify, the ring object has it's origin in [0, 0]
Here is a simple algorithm.
First, let's agree on variable names:
Here r1 ≤ r2, -π/2 ≤ a1 ≤ a2 ≤ π/2.
(As I was reminded in comments, you have start and end points rather than angles, but I'm going to use angles as they seem more convenient. You can easily obtain angles from points via atan2(y-ry, x-rx), just make sure that a1 ≤ a2. Or you can rewrite the algorithm to not use angles at all.)
We need to consider 3 different cases. The case depends on where the circle center is located relative to the ring segment:
In the 1st case, as you already figured, collision occurs if length of vector (cx-rx, cy-ry) is greater than r1-rc and less than r2+rc.
In the 2nd case collision occurs if the distane between the circle center and the closest straight edge is less than rc.
In the 3rd case collision occurs if the distance between the circle center and the closest of 4 corners is less than rc.
Here's some pseudocode:
rpos = vec2(rx,ry); // Ring segment center coordinates
cpos = vec2(cx,cy); // Circle coordinates
a = atan2(cy-ry, cx-rx); // Relative angle
r = length(cpos - rpos); // Distance between centers
if (a > a1 && a < a2) // Case 1
{
does_collide = (r+rc > a1 && r-rc < a2);
}
else
{
// Ring segment corners:
p11 = vec2(cos(a1), sin(a1)) * r1;
p12 = vec2(cos(a1), sin(a1)) * r2;
p21 = vec2(cos(a2), sin(a2)) * r1;
p22 = vec2(cos(a2), sin(a2)) * r2;
if (((cpos-p11) · (p12-p11) > 0 && (cpos-p12) · (p11-p12) > 0) ||
((cpos-p21) · (p22-p21) > 0 && (cpos-p22) · (p21-p22) > 0)) // Case 2
{
// Normals of straight edges:
n1 = normalize(vec2(p12.y - p11.y, p11.x - p12.x));
n2 = normalize(vec2(p21.y - p22.y, p22.x - p21.x));
// Distances to edges:
d1 = n1 · (cpos - p11);
d2 = n2 · (cpos - p21);
does_collide = (min(d1, d2) < rc);
}
else // Case 3
{
// Squared distances to corners
c1 = length_sqr(cpos-p11);
c2 = length_sqr(cpos-p12);
c3 = length_sqr(cpos-p21);
c4 = length_sqr(cpos-p22);
does_collide = (sqrt(min(c1, c2, c3, c4)) < rc);
}
}
To compare the small circle to a ray:
First check to see whether the circle encloses the origin; if it does, then it intersects the ray. Otherwise, read on.
Consider the vector v from the origin to the center of the circle. Normalize that, normalize the ray R, and take the cross product Rxv. If it's positive, v is counterclockwise from R, otherwise it's clockwise from R. Either way, take acos to get the angle between them.
If the circle has radius r and its center is a distance d from the origin, then the angular half-width of the circle (as seen from the origin) is asin(r/d). If the angle between R and v is less than that, then the circle intersects the ray.
Assume that you know whether the object extends clockwise or counterclockwise from Start to End. (The numbers won't tell you that, you must know it already or the problem is unsolvable.) In your example, it's clockwise. Now you have to be careful; if the angular length of the arc is <= pi, then you can proceed, otherwise it is easier to determine whether the circle is in the smaller sector outside the sector of the object. But assuming the object spans less that pi, the circle is inside the sector of the object (i.e. between the rays) if and only if it is clockwise from the Start and counterclockwise from the End.

What is wrong with my Z-buffer calculations?

I am implementing a Z-buffer to determine which pixels should be drawn in a simple scene filled with triangles. I have structural representations of a triangle, a vertex, a vector (the mathematical (x, y, z) kind, of course), as well as a function that draws an individual pixel to the screen. Here are the structures I have:
struct vertex{
float x, y, z;
... //other members for lighting, etc. that will be used later and are not relevant here
};
struct myVector{
float x, y, z;
};
struct triangle{
... //other stuff
vertex v[3];
};
Unfortunately, as I scan convert my triangles to the screen, which relies on calculating depths to determine what is visible and gets to be drawn, I am getting incorrect/unrealistic Z values (e.g., the depth at a point in the triangle is out of bounds of the depths of all 3 of its vertices)! I have been looking through my code over and over and cannot figure out whether my math is off or I have a careless mistake somewhere, so I will try to present exactly what I am trying to do in the hopes that someone else can see something that I don't. (And I have looked carefully at making sure that floating point values remain floating point values, that I am passing in arguments correctly, etc., so this is really baffling!)
Overall, my scan conversion algorithm fills pixels across a scan line like this (pseudocode):
for all triangles{
... //Do edge-related sorting stuff, etc...get ready to fill pixels
float zInit; //the very first z-value, with a longer calculation
float zPrev; //the "zk" needed when interpolating "zk+1" across a scan line
for(xPos = currentX at left side edge; xPos != currentX at right side edge; currentX++){
*if this is first pixel acorss scan line, calculate zInit and draw pixel/store z if depth is less
than current zBuffer value at this point. Then set zPrev = zInit.
*otherwise, interpolate zNext using zPrev. Draw pixel/store z if depth < current zBuffer value at
this point. Then set zPrev = zNext.
}
... //other scan conversion stuff...update x values, etc.
}
To get the value of zInit for each scan line, I consider the plane equation Ax + By + Cz + D = 0 and rearrange it to get z = -1*(Ax + By + D)/C, where x and y are plugged in as the current x value across a scan line and the current scan line value itself, respectively.
For subsequent z values across a scan line, I interpolate as zk+1 = zk - A/C, where A and C come from the plane equation.
To get the A, B and C for these z calculations, I need the normal vector of the plane defined by the 3 vertices (the array vertex v[3]) of the current triangle. To get this normal (which I named planeNormal in the code), I defined a cross product function:
myVector cross(float x1, float y1, float z1, float x2, float y2, float z2)
{
float crX = (y1*z2) - (z1*y2);
float crY = (z1*x2) - (x1*z2);
float crZ = (x1*y2) - (y1*x2);
myVector res;
res.x = crX;
res.y = crY;
res.z = crZ;
return res;
}
To get the D value for the plane equation/my z calculations, I use the plane equation A(x-x1) + B(y-y1) + C(z-z1) = 0, where (x1, y1, z1) is just a reference point in the plane. I just chose the triangle vertex v[0] for the reference point and rearranged:
Ax + By + Cz = Ax1 + By1 + Cz1
Thus, D = Ax1 + By1 + Cz1
So, finally, to get the A, B, C, and D for the z calculations, I did this for each triangle, where trianglelist[nt] is the triangle at current index nt in the overall triangle array for the scene:
float pA = planeNormal.x;
float pB = planeNormal.y;
float pC = planeNormal.z;
float pD = (pA*trianglelist[nt].v[0].x)+(pB*trianglelist[nt].v[0].y)+(pC*trianglelist[nt].v[0].z);
From here, within the scan conversion algorithm I described, I calculated the zs:
zInit = -1*((pA*cx)+(pB*scanLine)+(pD))/(pC); //cx is current x value; scanLine is current y value
...
...
float zNext = zPrev - (pA/pC);
Alas, after all that careful work, something is off! In some triangles, the depth values come out realistic (except for the sign). With triangle given by the vertices (200, 10, 75), (75, 200, 75) and (15, 60, 75), all depths come out as -75. The same happened for other triangles with all vertices at the same depth. But with the vertices (390, 300, 105), (170, 360, 80), (190, 240, 25), all of the z values are over 300! The very first one comes out as 310.5, and the rest just get bigger, with a max around 365. This should not happen when the deepest vertex is at z = 105!!! So, after all of the rambling, can anyone see what might have caused this? I wouldn't be surprised if it's a sign-related thing, but where (after all, the absolute values are right in the constant depth cases)?
The correct equations are:
n = cross (v[2] - v[0], v[1] - v[0]);
D = - dot (n, v[0]);
Note the minus sign.
you should have a look at www.scratchapixel.com, particularly this lesson:
http://scratchapixel.com/lessons/3d-advanced-lessons/perspective-and-orthographic-projection-matrix/
It contains a self-contained program that shows you how to project vertices.

How to normalize a mesh into -1 to 1, then revert from normalized mesh to original one?

I have a mesh model in X, Y, Z format. Lets say.
Points *P;
In first step, I want to normalize this mesh into (-1, -1, -1) to (1, 1, 1).
Here normalize means to fit this mesh into a box of (-1, -1, -1) to (1, 1, 1).
then after that I do some processing to normalized mesh, finally i want to revert the dimensions to similar with the original mesh.
step-1:
P = Original Mesh dimensions;
step-2:
nP = Normalize(P); // from (-1, -1, -1) to (1, 1, 1)
step-3:
cnP = do something with (nP), number of vertices has increased or decreased.
step-4:
Original Mesh dimensions = Revert(cnP); // dimension should be same with the original mesh
how can I do that?
I know how easy it can be to get lost in programming and completely miss the simplicity of the underlying math. But trust me, it really is simple.
The most intuitive way to go about your problem is probably this:
determine the minimum and maximum value for all three coordinate axes (i.e., x, y and z). This information is contained by the eight corner vertices of your cube. Save these six values in six variables (e.g., min_x, max_x, etc.).
For all points p = (x,y,z) in the mesh, compute
q = ( 2.0*(x-min_x)/(max_x-min_x) - 1.0
2.0*(y-min_y)/(max_y-min_y) - 1.0
2.0*(z-min_z)/(max_z-min_z) - 1.0 )
now q equals p translated to the interval (-1,-1,-1) -- (+1,+1,+1).
Do whatever you need to do on this intermediate grid.
Convert all coordinates q = (xx, yy, zz) back to the original grid by doing the inverse operation:
p = ( (xx+1.0)*(max_x-min_x)/2.0 + min_x
(yy+1.0)*(max_y-min_y)/2.0 + min_y
(zz+1.0)*(max_z-min_z)/2.0 + min_z )
Clean up any mess you've made and continue with the rest of your program.
This is so easy, it's probably a lot more work to find out which library contains these functions than it is to write them yourself.
It's easy - use shape functions. Here's a 1D example for two points:
-1 <= u <= +1
x(u) = x1*(1-u)/2.0 + x2*(1+u)/2.0
x(-1) = x1
x(+1) = x2
You can transform between coordinate systems using the Jacobean.
Let's see what it looks like in 2D:
-1 <= u <= =1
-1 <= v <= =1
x(u, v) = x1*(1-u)*(1-v)/4.0 + x2*(1+u)*(1-v)/4.0 + x3*(1+u)*(1+v)/4.0 + x4*(1-u)*(1+v)/4.0
y(u, v) = y1*(1-u)*(1-v)/4.0 + y2*(1+u)*(1-v)/4.0 + y3*(1+u)*(1+v)/4.0 + y4*(1-u)*(1+v)/4.0

3D Line Segment and Plane Intersection

I'm trying to implement a line segment and plane intersection test that will return true or false depending on whether or not it intersects the plane. It also will return the contact point on the plane where the line intersects, if the line does not intersect, the function should still return the intersection point had the line segmenent had been a ray. I used the information and code from Christer Ericson's Real-time Collision Detection but I don't think im implementing it correctly.
The plane im using is derived from the normal and vertice of a triangle. Finding the location of intersection on the plane is what i want, regardless of whether or not it is located on the triangle i used to derive the plane.
The parameters of the function are as follows:
contact = the contact point on the plane, this is what i want calculated
ray = B - A, simply the line from A to B
rayOrigin = A, the origin of the line segement
normal = normal of the plane (normal of a triangle)
coord = a point on the plane (vertice of a triangle)
Here's the code im using:
bool linePlaneIntersection(Vector& contact, Vector ray, Vector rayOrigin, Vector normal, Vector coord) {
// calculate plane
float d = Dot(normal, coord);
if (Dot(normal, ray)) {
return false; // avoid divide by zero
}
// Compute the t value for the directed line ray intersecting the plane
float t = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
// scale the ray by t
Vector newRay = ray * t;
// calc contact point
contact = rayOrigin + newRay;
if (t >= 0.0f && t <= 1.0f) {
return true; // line intersects plane
}
return false; // line does not
}
In my tests, it never returns true... any ideas?
I am answering this because it came up first on Google when asked for a c++ example of ray intersection :)
The code always returns false because you enter the if here :
if (Dot(normal, ray)) {
return false; // avoid divide by zero
}
And a dot product is only zero if the vectors are perpendicular, which is the case you want to avoid (no intersection), and non-zero numbers are true in C.
Thus the solution is to negate ( ! ) or do Dot(...) == 0.
In all other cases there will be an intersection.
On to the intersection computation :
All points X of a plane follow the equation
Dot(N, X) = d
Where N is the normal and d can be found by putting a known point of the plane in the equation.
float d = Dot(normal, coord);
Onto the ray, all points s of a line can be expressed as a point p and a vector giving the direction D :
s = p + x*D
So if we search for which x s is in the plane, we have
Dot(N, s) = d
Dot(N, p + x*D) = d
The dot product a.b is transpose(a)*b.Let transpose(N) be Nt.
Nt*(p + x*D) = d
Nt*p + Nt*D*x = d (x scalar)
x = (d - Nt*p) / (Nt*D)
x = (d - Dot(N, p)) / Dot(N, D)
Which gives us :
float x = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
We can now get the intersection point by putting x in the line equation
s = p + x*D
Vector intersection = rayOrigin + x*ray;
The above code updated :
bool linePlaneIntersection(Vector& contact, Vector ray, Vector rayOrigin,
Vector normal, Vector coord) {
// get d value
float d = Dot(normal, coord);
if (Dot(normal, ray) == 0) {
return false; // No intersection, the line is parallel to the plane
}
// Compute the X value for the directed line ray intersecting the plane
float x = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
// output contact point
*contact = rayOrigin + normalize(ray)*x; //Make sure your ray vector is normalized
return true;
}
Aside 1:
What does the d value mean ?
For two vectors a and b a dot product actually returns the length of the orthogonal projection of one vector on the other times this other vector.
But if a is normalized (length = 1), Dot(a, b) is then the length of the projection of b on a. In case of our plane, d gives us the directional distance all points of the plane in the normal direction to the origin (a is the normal). We can then get whether a point is on this plane by comparing the length of the projection on the normal (Dot product).
Aside 2:
How to check if a ray intersects a triangle ? (Used for raytracing)
In order to test if a ray comes into a triangle given by 3 vertices, you first have to do what is showed here, get the intersection with the plane formed by the triangle.
The next step is to look if this point lies in the triangle. This can be achieved using the barycentric coordinates, which express a point in a plane as a combination of three points in it. See Barycentric Coordinates and converting from Cartesian coordinates
I could be wrong about this, but there are a few spots in the code that seem very suspicious. To begin, consider this line:
// calculate plane
float d = Dot(normal, coord);
Here, your value d corresponds to the dot product between the plane normal (a vector) and a point in space (a point on the plane). This seems wrong. In particular, if you have any plane passing through the origin and use the origin as the coordinate point, you will end up computing
d = Dot(normal, (0, 0, 0)) = 0
And immediately returning false. I'm not sure what you intended to do here, but I'm pretty sure that this isn't what you meant.
Another spot in the code that seems suspicious is this line:
// Compute the t value for the directed line ray intersecting the plane
float t = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
Note that you're computing the dot product between the plane's normal vector (a vector) and the ray's origin point (a point in space). This seems weird because it means that depending on where the ray originates in space, the scaling factor you use for the ray changes. I would suggest looking at this code one more time to see if this is really what you meant.
Hope this helps!
This all looks fine to me. I've independently checked the algebra and this looks fine for me.
As an example test case:
A = (0,0,1)
B = (0,0,-1)
coord = (0,0,0)
normal = (0,0,1)
This gives:
d = Dot( (0,0,1), (0,0,0)) = 0
Dot( (0,0,1), (0,0,-2)) = -2 // so trap for the line being in the plane passes.
t = (0 - Dot( (0,0,1), (0,0,1) ) / Dot( (0,0,1), (0,0,-2)) = ( 0 - 1) / -2 = 1/2
contact = (0,0,1) + 1/2 (0,0,-2) = (0,0,0) // as expected.
So given the emendation following #templatetypedef's answer, the only area where I can see a problem is with the implementation of one of the other operations, be it Dot(), or the Vector operators.
This version worked for me in OpenGL C# application.
bool GetLinePlaneIntersection(out vec3 contact, vec3 ray_origin, vec3 ray_end, vec3 normal, vec3 coord)
{
contact = new vec3();
vec3 ray = ray_end - ray_origin;
float d = glm.dot(normal, coord);
if (glm.dot(normal, ray) == 0)
{
return false;
}
float t = (d - glm.dot(normal, ray_origin)) / glm.dot(normal, ray);
contact = ray_origin + ray * t;
return true;
}

Find if point lies within a rectangle

How can a find if a point lies within a 2D rectangle given 4 points?
Transform the point to a coordinate frame aligned with the rectangle, then the problem becomes axis-aligned and trivial.
If the rectangle consists of the following 4 points:
a b
c d
Then get the "x-axis" and "y-axis" of the rectangle as:
x = Normalize(d-c)
y = Normalize(a-c)
Then construct a rotation matrix using x and y as columns:
r = [ x | y ]
If you're using 3-d coordinates, we need a z axis:
z = CrossProduct(x, y)
r = [ x | y | z ]
Your transform matrix from world coordinates to your rectangle's axis-aligned coordinates becomes:
T = [ r^T | -r^T * c ]
[ 0^T | 1 ]
Here we've chosen the lower-left corner c to be the local origin. "r^T" is r transposed. "0^T" is either a 2-d or 3-d row-vector filled with zeros. 1 is just a one. Note that this is just the inverse of the simpler rectangle-to-world transform, which is
T^-1 = [ r | c ]
[ 0^T | 1 ]
We can use T to transform the point to axis-aligned coordinates. Remember to pad p with a trailing 1, since T is a homogeneous matrix.
tp = T * p; // Don't forget to pad p with a trailing 1 before multiplying.
// Checks that p isn't below or to the left of the rectangle.
for ( int d = 0; d < num_dimensions; ++d ) {
if ( tp[d] < 0.0 ) {
return false;
}
}
// Checks that p isn't to the right of the rectangle
double width = Length(d-c);
if ( tp[0] > width ) {
return false;
}
// Checks that p isn't above the rectangle.
double height = Length(a-c);
if ( tp[1] > height ) {
return false;
}
// p must be inside or on the rectangle.
return true
If you're using 3d coordinates, note that the above disregards the local z value of transformed point tp. Even if p is out of the plane of the rectangle, the above behaves as if it's been projected to the rectangle surface. If you want to check for coplanarity, just do the following beforehand:
if ( fabs(tp[2]) > some_small_positive_number ) {
return false; // point is out of the rectangle's plane.
}
I think this might answer your question
full disclosure - I went to Drexel for my grad dregree
To make it OpenGl specific:
I suppose your 2D rectangle is in screen coordinates!
first:
gluProject (bli, bla, blorp, ...);
to get from 3d to screen coordinates.
Then: Noah's suggestion.
Only shoot yourself if your point in question is already 2D ;)
For a non-axis-aligned rectangle, use the same algorithm as for general polygons: the point-in-polygon test:
Imagine a ray pointing rightward from the test point. Test whether each line in the polygon crosses the ray. If an even number of lines crosses the ray, then the point is outside the polygon. If an odd number of lines crosses the ray, then the point is inside the polygon.
In the case of a rectangle, between zero and two lines will cross the ray.
If a line touches the ray but does not cross it, the result is ambiguous. Therefore, in your calculations, imagine that the ray is an infinitely small amount ɛ higher than its y coordinate, so that it is impossible for a line to touch the ray without crossing it.
Given your test point (x,y) and line (x1,y1,x2,y2), testing whether a line crosses the ray is pretty simple. Assume, without loss of generality, that y1 < y2. Then
if y < y2 and y >= y1:
let x0 = x1 + (y-y1)/(y2-y1) * (x2-x1) // crossing point (x0,y)
if x0 > x:
crossing_detected++
http://en.wikipedia.org/wiki/Point_in_polygon
It's easy to test if a point lies in a triangle, so you can split your rectangle in two triangles and test these. See e.g. http://www.blackpawn.com/texts/pointinpoly/default.html
Generic test for point in quadrilateral is sufficient. The quad is defined as an ordered series of points. Handles both clockwise and counter-clockwise winding:
typedef struct {float x; float y} vec2;
bool pointIsInQuad(const vec2 point, const vec2 quad[4])
{
bool sides[4];
for (int i = 0; i < 4; i++) {
sides[i] = ((point.x - quad[i].x)*(quad[(i + 1)%4].y - quad[i].y) - (point.y - quad[i].y)*(quad[(i + 1)%4].x - quad[i].x)) > 0.0f;
}
return ((sides[0] == sides[1]) && (sides[0] == sides[2]) && (sides[0] == sides[3]));
}