Related
Imagine you have a 3D space between 0 and d meters in the x,y and z directions (it's a cube of length d). now let's assume I want to break this up into a 3D array of smaller cubes. We want n cubes in each direction. This means each cube has a length, width and height of d/n (you can assume that d/n divides perfectly).
This can be held in the following array:
Cube* cubes[n][n][n];
Now let us assume we can have a sphere of radius r and position p, where p can be anywhere inside the 3d space, and r has no constraints. What is the most efficient way of finding and returning all the cubes that intersect with this sphere?
Currently, I am looping through all the cubes in the array and using the Pythagorean theorem to check the distance between each cube and p, which I wrote the pseudo code for below.
Cube* getIntersectingCubes(r,p) {
Cube* cubes[n][n][n];
Cube* result[];
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
for(int k=0;k<n;k++) {
if(distanceBetween(cubes[i][j][k],p) < r) {
result.add(cubes[i][j][k]);
}
}
}
}
return result;
}
However, there is definitely a better way to do this. How could I return the same output but check as few cubes as possible?
If you are still having trouble working out a coordinate system and your algorithm, start with a diagram:
Essentially what you have is a 3D grid of cubes and your points p can be chosen any way you like so long as they describe a cube completely. What you want to determine is for which cubes does the radius r intersect any part of the cube. You can handle that with three vectors. You simply treat the cube origin as a vector. You know the lengths of the sides of each cube (d / n) you can use to form a second vector (call it the extent of p or p_extent) which you can add to the origin vector to determine the farthest point in the cube. So p_extent would equal d/ni + d/nj + d/nk.
You can create a struct to help in handling the coordinates as a vector, e.g.:
typedef struct {
double x, y, z;
} vect_pos;
With your two vectors, you can simply add them together (e.g. p + p_extent) to form a vector describing the farthest point in the cube from the origin. (you want the direction of the vector inside your cube to have the same sign direction as your origin vector (so you can simply multiply it with a unit vector in that direction). For example is your point is -3i -1j and 2k, you can multiple by the p_extent vector by -1i -1j 1k. (picture the cube origin vector in each of the 8-quadrants and the values that x, y, z would have in each.
To add two vector_pos together, you can simply use vector addition, e.g.
/* vector addition */
vect_pos vect_add (const vect_pos *a, const vect_pos *b)
{
vect_pos c = { .x = 0. };
c.x = a->x + b->x;
c.y = a->y + b->y;
c.z = a->z + b->z;
return c;
}
The last wrinkle that choosing the coordinate at a corner of the cube will require you solve is that the origin vector will not always point to the corner closest to the origin (picture in the diagram above where p would point if you rotated the origin counter-clockwise by 90 deg about the axis. (your alternative is to choose the center of the cube as its origin, but then you have twice the number of calculations).
You can handle the closest corner issue by a simple check and shifting the corner for the origin by a cube side length for purposes of the calculation of whether r is between the closest and farthest corners (the cube is still the same cube) With your cube origin and the farthest corner described, you can simply use the vector-distance formula to calculate the distance each is from the origin and if r is between the two, the sphere intersects that cube.
To choose the closest point to the origin before calculating the distances you can use:
/* point in cube closest to origin */
vect_pos closest_pt (const vect_pos *p, const int len)
{
vect_pos c = { p->x >= 0 ? p->x : p->x + len,
p->y >= 0 ? p->y : p->y + len,
p->z >= 0 ? p->z : p->z + len };
return c;
}
(choosing your cube at origin 0, 0, 0 with positive sides allows you to simply add a cube side length in the case that coordinate is negative to choose the closest point on the cube as the origin)
and for the distance calculations, a simple square-root of the sum of the squares:
/* vector distance */
double vect_dist (const vect_pos *p)
{
return sqrt (p->x * p->x + p->y * p->y + p->z * p->z);
}
To check if r falls between the closest and farthest distance within each cube you can put it altogether with a short function that handles choosing the extent vector in the same direction as the origin, adds the vectors and then compares the distances of each to r, e.g.
/* does r intersect cube at postition p */
int intersect (vect_pos *p, const double r, const int len)
{
vect_pos c = closest_pt (p, len), /* closest pt to origin */
p_extent = { .x = c.x >= 0 ? len : -len, /* length, unit vector */
.y = c.y >= 0 ? len : -len, /* pointing outward */
.z = c.z >= 0 ? len : -len },
p_farthest = vect_add (&c, &p_extent); /* farthest point in cube */
double nearest = vect_dist (&c), /* distance to nearest */
farthest = vect_dist (&p_farthest); /* distance to farthest */
return nearest <= r && r <= farthest; /* return radius in between */
}
That's basically it. You can add a short function to output the points to as a convenience, e.g.
void prn_pos (const vect_pos *p)
{
printf ("(% d, % d, % d)\n",
(int)p->x, (int)p->y, (int)p->z);
}
and then add a main() that allows entry of r, d and n and outputs the results:
int main (int argc, char **argv) {
double r = argc > 1 ? strtod (argv[1], NULL) : .9; /* r */
int d = argc > 2 ? strtol (argv[2], NULL, 0) : 1, /* d */
n = argc > 3 ? strtol (argv[3], NULL, 0) : d, /* n */
dn = 0, /* d/n */
nc = 0, /* number of cubes in each direction */
total = 0, /* total cubes in search grid */
intersecting = 0; /* number of cubes that intersect sphere */
if (r < 0 || d < 0) { /* validate length inputs */
fputs ("error: negtive length in input of r or d.\n", stderr);
return 1;
}
if (!n || n < 0) { /* validate n not zero or negative */
fputs ("error: n - divide by zero or negative.\n", stderr);
return 1;
}
if (d < r) { /* ensure d >= r */
int min_d = ceil (r);
n *= min_d / d;
d = min_d;
}
if (d % n) { /* validate d equally divisible by n */
fputs ("error: d not equally divisible by n.\n", stderr);
return 1;
}
dn = d / n; /* cube side length */
nc = ceil (r / dn) + 1; /* limit of cubes needed per-direction */
total = nc * nc * nc * 8; /* total number of cubes in search grid */
printf ("\nOut of %d cubes, the following intersect sphere of radius %.2f\n\n",
total, r);
for (int i = -d; i <= d; i += dn) /* loop -d to d */
for (int j = -d; j <= d; j += dn)
for (int k = -d; k <= d; k += dn) {
vect_pos p = { i, j, k }; /* vector to cube origin */
if (intersect (&p, r, dn)) { /* does it intersect sphere? */
prn_pos (&p); /* output cube origin */
intersecting++; /* increment count */
}
}
printf ("\nintersecting cubes: %d\n", intersecting);
}
Add the headers:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
and then compile linking against the math library -lm and you have a working program to test. If no arguments for r, d or n are provided, the code uses .90, 1, and 1 as defaults. Note the loop limits are at minimum from -(next int above r) to (next int above r) in each dimension or as given by d whichever is greater and not shorter than r.
Example Use/Output
Default case with r = .9 and d = 1 and n = 1:
$ ./bin/cube-sphere-intersect
Out of 64 cubes, the following intersect sphere of radius 0.90
(-1, -1, -1)
(-1, -1, 0)
(-1, 0, -1)
(-1, 0, 0)
( 0, -1, -1)
( 0, -1, 0)
( 0, 0, -1)
( 0, 0, 0)
intersecting cubes: 8
If you draw the cubes out on a grid, those are the 8 cubes surrounding the origin.
Choosing r = 5.5, d = 6 and n = 1 would give the same result other than the sides of the cubes being of different length:
$ ./bin/cube-sphere-intersect 5.5 6 1
Out of 64 cubes, the following intersect sphere of radius 5.50
(-6, -6, -6)
(-6, -6, 0)
(-6, 0, -6)
(-6, 0, 0)
( 0, -6, -6)
( 0, -6, 0)
( 0, 0, -6)
( 0, 0, 0)
intersecting cubes: 8
If you enter an r length of exacly 1, then this is where your choice of whether to consider r equal to the min or max length will come into play. There are arguments for saying "touching" a cube doesn't really mean it intersects as the point will technically lie on a tangent that distance out, but there are equally good arguments that say that intersects (the choice of whether you want to use <= or < in the comparison is left to you)
For example if you enter anything between 1.0 < sqrt(2) (1.414...) you will find 32 cube intersect, e.g.
$ ./bin/cube-sphere-intersect 1.1
Out of 216 cubes, the following intersect sphere of radius 1.10
(-2, -1, -1)
(-2, -1, 0)
(-2, 0, -1)
(-2, 0, 0)
(-1, -2, -1)
(-1, -2, 0)
(-1, -1, -2)
(-1, -1, -1)
(-1, -1, 0)
(-1, -1, 1)
(-1, 0, -2)
(-1, 0, -1)
(-1, 0, 0)
(-1, 0, 1)
(-1, 1, -1)
(-1, 1, 0)
( 0, -2, -1)
( 0, -2, 0)
( 0, -1, -2)
( 0, -1, -1)
( 0, -1, 0)
( 0, -1, 1)
( 0, 0, -2)
( 0, 0, -1)
( 0, 0, 0)
( 0, 0, 1)
( 0, 1, -1)
( 0, 1, 0)
( 1, -1, -1)
( 1, -1, 0)
( 1, 0, -1)
( 1, 0, 0)
intersecting cubes: 32
Then for radius between sqrt(2) <= sqrt(3) you would find 56 cube intersect and so on.
This is just one way to implement it. If you are curious, go rewrite it to use the center of each cube as the origin of the cube. You don't have to use a struct, you can just operate on each x, y, z directly, but the struct helps keep the logic clear. Look things over and let me know if you have further questions.
Divide and conquer:
Assume the entire domain as a single cube:
Figure out if there is an intersection
If so, divide into octants and repeat (1)
Because you only need to search the octants where an intersection is present, you won't have to preallocate a massive array of cells.
The intersection algorithm can be simplified to any cube where at least one of the nodes is less than the radius from p and at least one is greater. It should be possible to write this algorithm in a way that is easy to vectorise.
Note: Probably don't want to store your 3d array on the stack, even if you pick a relatively small number for n, say 50, that's already 125000 doubles, enough to cause a stack overflow on some systems.
I would start with the cube containing point p and go into any one direction while given cube still intersects the sphere. That would give you a distance +-1 to all "edge" cubes.
This provides early termination of the search through cubes you know won't intersect and gives you an approximation of end points in all directions.
Let's say a cube is in the outer shell if it intersects the sphere but isn't fully contained in the sphere. Say the initial point p = (x, y, z). We'll proceed by finding the cubes in the outer shell. Given these, it's easy to find interior cubes.
1. Find the cube containing p in O(1).
2. Find the most distant cube intersecting the sphere that can be reached by decreasing x.
3. Add this to a set: unexplored
4. repeatedly do the following (each iteration is O(1):
a. Pull a cube from unexplored.
b. Add any neighbors that intersect the outer shell and aren't explored to unexplored.
c. Mark the current cube as explored.
How long does this take? It's linear with the surface area of the sphere measured in cubes, so O(4pi(r/d)^2) = O((r/d)^2). I.e. the formula for the surface area of a sphere, but with the radius measured in cubes.
If it suffices to return the outer shell then you're done. If you need to return the contents then this grows to be linear with the output, which is O((r/d)^3)
First of all you need a function that will map any point in 3D into three integers:
(x,y,z) -> (floor(x/dx), floor(y/dy), floor(z/dz))
where, in your case, dx = dy = dz = d/n is the size (along axes x, y, z) of small cubes (which I shall rather call "cells") you divide the whole space into. This function maps any point in 3D into a discrete cell.
Now, you should find the minimum cuboid, which is made of cells, and which contains the sphere centered at P and of radius r. In a general case this is a cuboid, though often it will be just a cube. After all, it kinda circumscribes a sphere. Let your cube be denoted A, and let this cuboid be denoted B. The answer to your problem lies inside both A and B. For this reason find the intersection C of A and B. Focus entirely on C. This trick alone should result in a huge reduction of the problem complexity, with practically zero cost.
Now I assume you have a well written, well tested function distance(cube, point). Remember that the point on the cube that minimize the distance to point may lie on one of cube's corners, edges, sides or its interior volume and so such a function will need several if-else branches.
Check if distance returns a value less or equal than r. If not, the answer is an empty set, you're done.
If the distance is <= r, then you can apply a modified method reported here by M.A., but applied to C. This C is not a cube, so octants are no solution here. However, C is a cuboid, for an intersection of 2 cuboids is a cuboid. Denote its side lengths as Lx, Ly, Lz. Thee numbers are integers, becassue now you measure the lengths in units of dx, dy and dz along axes x, y, z, repectively. Find the largest of them. Suppose, for clarity, that it is Lx. If Lx > 1 you divide C along the x axis into two equal-sized (when Lx is even) or almost equal (Lx is odd) sub-cuboids. You check the distance to each of them. If it is larger than r then you reject this whole sub-cuboid. Otherwise you repeat the division along the largest side of the current cuboid. This is very similar to, for example, implementation of the BVH tree.
Once you reached the "sub-cuboid" of size 1x1x1, you verify if it intersects with the sphere. If yes, add it to the solution set.
* * *
This algorithm is very general because your question contains no details as to where the answer to this problem is to be used. For example, how large can n be? Will the solution be used for fixed radius and varying sphere centers? Knowing such details could help develop a better solution.
For example, the sphere can contain O(N^3) cells, so one might expect that he solution cannot be found in time smaller than O(N^3). But perhaps you do nod a list of the cells? Perhaps a different format would do?
The set of cells forming the solution can be regarded as a union of disjoint columns of cells, oriented along the z axis. With this representation, the solution can be written as a set of pairs (cell, height). Here cell denote the lowest cell in a column, and height - its height. If a solution in this format is OK. the problem can be solved in O(N^2) or at mot O(N^2 log N) time.
I have a couple of points in a 3D space, and I want to write a condition to determine whether two points are adjacent: are they only a single unit apart in the integer lattice?
I have a struct named Point that holds x,y and z coordinates. Then in the main function, I set the values for points a,b,c,d,e and push them into a vector. Then in the for loop I want to check if two points are adjacent or not. Currently I'm just checking if they are on the same axis, but I do not know how to proceed.
struct Point {
int x;
int y;
int z;
};
bool adjacent(Point a, Point b) { ??? }
int main() {
struct Point a = {0, 0, 0};
struct Point b = {0, 0, -1};
struct Point c = {1, 0, -1};
struct Point d = {1, -1, -1};
struct Point e = {2, -1, -1};
assert(adjacent(a, b));
assert(adjacent(b, c));
assert(adjacent(c, d));
assert(adjacent(d, e));
assert(!adjacent(a, c));
}
By adjacent I mean something like in this photo:
Very briefly:
for each pair of points:
if two of the three coordinates are equal AND
the other coordinate differs by 1:
then mark the pair as adjacent.
Iterating through point pairs is simple enough: the first point a walks through indices 0-(n-2); the second point b walks through indices from a's position through the end, n-1.
Checking adjacency is also easy, given integer coordinates.
diff = abs(a.x - b.x) +
abs(a.y - b.y) +
abs(a.z - b.z)
diff = 1 iff the points are adjacent.
I want to fit a plane to a 3D point cloud. I use a RANSAC approach, where I sample several points from the point cloud, calculate the plane, and store the plane with the smallest error. The error is the distance between the points and the plane. I want to do this in C++, using Eigen.
So far, I sample points from the point cloud and center the data. Now, I need to fit the plane to the samples points. I know I need to solve Mx = 0, but how do I do this? So far I have M (my samples), I want to know x (the plane) and this fit needs to be as close to 0 as possible.
I have no idea where to continue from here. All I have are my sampled points and I need more data.
From you question I assume that you are familiar with the Ransac algorithm, so I will spare you of lengthy talks.
In a first step, you sample three random points. You can use the Random class for that but picking them not truly random usually gives better results. To those points, you can simply fit a plane using Hyperplane::Through.
In the second step, you repetitively cross out some points with large Hyperplane::absDistance and perform a least-squares fit on the remaining ones. It may look like this:
Vector3f mu = mean(points);
Matrix3f covar = covariance(points, mu);
Vector3 normal = smallest_eigenvector(covar);
JacobiSVD<Matrix3f> svd(covariance, ComputeFullU);
Vector3f normal = svd.matrixU().col(2);
Hyperplane<float, 3> result(normal, mu);
Unfortunately, the functions mean and covariance are not built-in, but they are rather straightforward to code.
Recall that the equation for a plane passing through origin is Ax + By + Cz = 0, where (x, y, z) can be any point on the plane and (A, B, C) is the normal vector perpendicular to this plane.
The equation for a general plane (that may or may not pass through origin) is Ax + By + Cz + D = 0, where the additional coefficient D represents how far the plane is away from the origin, along the direction of the normal vector of the plane. [Note that in this equation (A, B, C) forms a unit normal vector.]
Now, we can apply a trick here and fit the plane using only provided point coordinates. Divide both sides by D and rearrange this term to the right-hand side. This leads to A/D x + B/D y + C/D z = -1. [Note that in this equation (A/D, B/D, C/D) forms a normal vector with length 1/D.]
We can set up a system of linear equations accordingly, and then solve it by an Eigen solver as follows.
// Example for 5 points
Eigen::Matrix<double, 5, 3> matA; // row: 5 points; column: xyz coordinates
Eigen::Matrix<double, 5, 1> matB = -1 * Eigen::Matrix<double, 5, 1>::Ones();
// Find the plane normal
Eigen::Vector3d normal = matA.colPivHouseholderQr().solve(matB);
// Check if the fitting is healthy
double D = 1 / normal.norm();
normal.normalize(); // normal is a unit vector from now on
bool planeValid = true;
for (int i = 0; i < 5; ++i) { // compare Ax + By + Cz + D with 0.2 (ideally Ax + By + Cz + D = 0)
if ( fabs( normal(0)*matA(i, 0) + normal(1)*matA(i, 1) + normal(2)*matA(i, 2) + D) > 0.2) {
planeValid = false; // 0.2 is an experimental threshold; can be tuned
break;
}
}
This method is equivalent to the typical SVD-based method, but much faster. It is suitable for use when points are known to be roughly in a plane shape. However, the SVD-based method is more numerically stable (when the plane is far far away from origin) and robust to outliers.
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.
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.