I have a terrain, which is represented by elements and coordinates ( they are just a list of mesh), and I have a plane.
How to find the intersection between the plane and the terrain? Should I just break the plane up into a list of lines, and then use ray casting technique or other technique to compute the list of intersection, and join them up together? Or is there other ways of doing?
This question is a special case of my question here, in which I actually discretized the plane by breaking it into a lot of lines, and find the intersection points in order to get the lines where the plane intersect with the terrain, but I really wonder is there a better method.
define the plane using its implicit equation. Find a, b, c, and d so that ax + by + cz + d = 0 for all (x, y, z) on the plane
Go through every square that defines a segment of your terrain. Interpret the square as two separate triangles
For each vertex v1 = (x1, y1, z1), v2 = (x2, y2, z2), v3 = (x3, y3, z3) of a triangle, determine which side of the plane it lies on:
l1 = ax1 + by1 + c*z1 + d
l2 = ax2 + by2 + c*z2 + d
l3 = ax3 + by3 + c*z3 + d
Now there are two options.
either l1, l2, l3 are all > 0 or l1, l2, l3 are all < 0 - in that case the plane does not intersect the triangle and you have nothing to do.
or two of the l values are positive/negative and the third one is the opposite, e.g. l1>0, l2>0 and l3<0 - then the triangle edges v1-v3 and v2-v3 are being intersected
edge-plane intersection is easy to calculate. Assuming the v1-v2 edge is intersected:
xi = (x1*l1 - x2*l2)/(l1 - l2)
yi = (y1*l1 - y2*l2)/(l1 - l2)
zi = (z1*l1 - z2*l2)/(l1 - l2)
This way you will obtain a list of edges that correspond to intersection of the plane with your terrain. It is a slow method, but easy to explain... if you wanted to speed it up, you would need to use octrees or similar data structure to limit the amount of point/plane tests you need to do.
Related
I found out an equation of a plane,from three vertices.
Now,if I have a bounding box(i.e. a large cube),How can I determine the grid positions(small cubes),where the plane cuts the large cube.
I am currently following this approach:
For each small cube center, say(Xp, Yp, Zp), calculate perpendicular distance to the plane i.e., (aXp + bYp + c*Zp + d)/ (SquareRoot Of (a^2 + b^2 + c^2)). This should be less than or equal to (length of smallCube * SquareRoot(3))/2.
If this criteria,gets satisfied,then I assume my plane to cut the large cube at this small cube position.
a,b,c,d are coefficients of the plane,of the form ax+by+cz+d = 0.
I would be really glad,if someone can let me know,if I am doing something wrong (or) also,any other simple approach.
Seems you want to get a list of small cubes (grid voxels) intersected by given plane.
The simplest approach:
Find intersection of the plane with any cube edge. For example, intersection with vertical edge of AAB (X0,Z0 are constant) might be calculated by solving this equation for unknown Y:
aX0 + bY + c*Z0 + d = 0
and checking that Y is in cube range. Get small cube coordinates (0, ky=Floor(Y/VoxelSize), 0) and then check neighbor voxels in order (account for plane coefficients to check only real candidates).
candidates:
0,ky,0
1,ky,0
0,ky-1,0
0,ky+1,0
0,ky,1
There are more advanced methods to generate voxel sequence for ray case (both 2d and 3d) like Amanatides/Woo algorithm. Perhaps something similar exists also for plane voxelization
Here is AABB-plane intersection test code from this page (contains some explanations)
// Test if AABB b intersects plane p
int TestAABBPlane(AABB b, Plane p) {
// Convert AABB to center-extents representation
Point c = (b.max + b.min) * 0.5f; // Compute AABB center
Point e = b.max - c; // Compute positive extents
// Compute the projection interval radius of b onto L(t) = b.c + t * p.n
float r = e[0]*Abs(p.n[0]) + e[1]*Abs(p.n[1]) + e[2]*Abs(p.n[2]);
// Compute distance of box center from plane
float s = Dot(p.n, c) - p.d;
// Intersection occurs when distance s falls within [-r,+r] interval
return Abs(s) <= r;
}
Note that e and r remain the same for all cubes, so calculate them once and use later.
I do not know how to approach this problem as I am using API that has Plane data structure that is basically origin point, x y and z vectors that define a plane.
If I have two planes, how can I find bisector plane?
Is there a mathematical description for such plane.
Geometrically I would approach this problem by calculating intersection line between planes and then no idea how to define a point for direction that plane.
Any help would be much appreciated.
Before I tried something like this, and this get what I want, but I am wondering if there is a solution without doing intersections:
public static Plane BisectorPlane(Plane a, Plane b)
{
Rhino.Geometry.Intersect.Intersection.PlanePlane(a, b, out Line lnA);
a.Translate(a.ZAxis);
b.Translate(b.ZAxis);
Rhino.Geometry.Intersect.Intersection.PlanePlane(a, b, out Line lnB);
return new Plane( lnA.From,lnA.To,lnB.PointAt(0.5));
}
I am wondering if it is possible to solve this is not geometrically (calculating intersections) but mathematically.
You already have got line of intersection. Now choose any point P at this line to make base point.
Get direction vector for this line (dL)
Get sum of unit normals for given planes S. This is vector lying in bisector plane and perpendicular to intersection line
S = (a.normal + b.normal)
Now calculate vector product of dL and S to get normal
BisectorN = dL x S
And normalize it (make unit length dividing by its length) if needed
bN = BisectorN.Normalize
Base point P and normal bN define bisector plane.
I tried your approach and it gives bisector plane. But the problem is that planes shifts as it is constructed from origin and normal, not from origin and two x and y axis:
Point3d origin = lnA.PointAt(0.5);
Vector3d S = a.Normal + b.Normal;
Vector3d dL = Vector3d.Subtract((Vector3d)lnA.From, (Vector3d)lnA.To);
Vector3d BisectorN = Vector3d.CrossProduct(dL,S);
BisectorN.Unitize();
return new Plane(origin, BisectorN);
I have a 2D map which wraps at the edges. So if you move off the right edge you will reappear at the left side of the map. Likewise with the three other edges.
This is inheritable a problem for the KDTree which I use to find elements in range of points. Normally you would check whether the hyper sphere collides with the hyper plane to see if you should continue searching the other side of the tree, but this check does not work with wrapping edges.
Is there any way to modify the KD Tree to work with donut 2D spaces?
The data structure doesn't have to change, but the search procedure does. Represent each point by coordinates (x, y) in [0, w) * [0, h), where w is the width of the map, h is the height, and * denotes a Cartesian product. Store these points in a normal KD tree.
The fundamental primitive for searching a KD tree is, given a point (x, y) and a rectangle [a, b] * [c, d], determine the distance (squared) from the point to the rectangle. Normally this is g(x, a, b)2 + g(y, c, d)2, where
g(z, e, f) = e - z if z < e
0 if e <= z <= f
z - f if f < z
is the one-dimensional distance of z to [e, f]. In a toroidal space, we modify g slightly to account for wraparound.
g(z, e, f, v) = min(e - z, (z + v) - f) if z < e
0 if e < z < f
min(z - f, (e + v) - z) if f < z.
and the distance squared is g(x, a, b, w)2 + g(y, c, d, h)2. I expect that the running times will be comparable for this variant. (I'd redo the recurrences, but the worst-case for regular KD trees is much worse than practice most of the time - O(n1/2) for identifying the nearest neighbor in 2D among n points.)
Jitamaro suggested but didn't explain a method based on a "2x size" quadtree. It's a reasonable suggestion, except the quadtree uses four times as many nodes rather than two, as I'll explain below before tentatively suggesting an alternative method.
Suppose each (x,y) coordinate has -.5 < x <= .5 and -.5 < y <= .5 and whenever j, k are integers, point (x+j,y+k) is identical with point (x,y). Let quadtree T cover points with coordinates in the range -1 < x,y <= 1. Each time you add an item at (x,y) to T, where -.5 < x,y <= .5, let x' = {x-1 if x>0 else x+1}, and y' = {y-1 if y>0 else y+1}. Also add the item at (x,y'), (x',y'), and (x',y). [When you delete points later, again calculate (x', y') et al and delete them too.] It's easy to see that nearest-point lookups will work properly, so long as any lookup coordinate outside interval (-.5,.5] is adjusted properly.
If the four-fold number of nodes is a deal-breaker, note that if coordinates as described above are used in subtrees above say level k=3, and relative coordinates are stored below level k, it should be possible to maintain single copies of subtrees below level k. That is, each subtree at level k would have four parents. (I haven't thought about this enough to know if this totally works; will edit answer if I find it doesn't.)
A quadtree is a KD-tree with 4 leafs. A quadtree doesn't help to wrap because its data structure is a wrap itself. You just need to use a quadtree 2x size of your structure.
Preferably without using any kind of loop, as this'll be used in a game.
I wish to intersect a line with a rectangle, of arbitrary size.
But I also wish for the intersection point[s] to be returned.
It's possible, I've done a little googling, but still have not worked it out.
The line is defined using (x1,y1,x2,y2).
The rectangle has these two points too.
I would recommend simply doing a line-segment-line-segment intersection check on each line segment (edge) that makes up the rectangle. Here is a line segment intersection detection algorithm I wrote ages ago, dredged up from one of my old XNA projects:
// a1 is line1 start, a2 is line1 end, b1 is line2 start, b2 is line2 end
static bool Intersects(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, out Vector2 intersection)
{
intersection = Vector2.Zero;
Vector2 b = a2 - a1;
Vector2 d = b2 - b1;
float bDotDPerp = b.X * d.Y - b.Y * d.X;
// if b dot d == 0, it means the lines are parallel so have infinite intersection points
if (bDotDPerp == 0)
return false;
Vector2 c = b1 - a1;
float t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
if (t < 0 || t > 1)
return false;
float u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
if (u < 0 || u > 1)
return false;
intersection = a1 + t * b;
return true;
}
I will leave inputting each edge into the above method and collecting the results as an exercise to the reader :)
Edit 1 year later now I've gone to university and done a Graphics course:
Take a look at the Cohen–Sutherland algorithm to do this efficiently when you have a large set of lines where most do not intersect the rectangle. It uses a 9 segment grid and you place each endpoint of the line in a region of said grid:
Using this we can tell if there will not be any line intersections:
For example here CD will not intersect the rectangle (shown in red in the first image) as both C and D are in the top row and neither will AB. For the ones where the line may intersect the the rectangle we have to try the line-line intersections.
They way the sections are numbered/labelled allows us to simply do x AND y != 0 (where x and y are the labels of the sections for each of the line's endpoints) to determine if there will not be an intersection.
Using this method means we have to many, many fewer line-line intersections which speeds up the whole thing massively.
Finding the point of intersection for two 2D line segments is easy; the formula is straight forward. But finding the point of intersection for two 3D line segments is not, I afraid.
What is the algorithm, in C# preferably that finds the point of intersection of two 3D line segments?
I found a C++ implementation here. But I don't trust the solution because it makes preference of a certain plane (look at the way perp is implemented under the implementation section, it assumes a preference for z plane. Any generic algorithm must not assume any plane orientation or preference).
Is there a better solution?
Most 3D lines do not intersect. A reliable method is to find the shortest line between two 3D lines. If the shortest line has a length of zero (or distance less than whatever tolerance you specify) then you know that the two original lines intersect.
A method for finding the shortest line between two 3D lines, written by Paul Bourke is summarized / paraphrased as follows:
In what follows a line will be defined by two points lying on it, a
point on line "a" defined by points P1 and P2 has an equation
Pa = P1 + mua (P2 - P1)
similarly a point on a second line "b" defined by points P4 and P4
will be written as
Pb = P3 + mub (P4 - P3)
The values of mua and mub range from negative to positive infinity.
The line segments between P1 P2 and P3 P4 have their corresponding mu
between 0 and 1.
There are two approaches to finding the shortest line segment between
lines "a" and "b".
Approach one:
The first is to write down the length of the line
segment joining the two lines and then find the minimum. That is,
minimise the following
|| Pb - Pa ||^2
Substituting the equations of the lines gives
|| P1 - P3 + mua (P2 - P1) - mub (P4 - P3) ||^2
The above can then be expanded out in the (x,y,z) components.
There are conditions to be met at the minimum, the derivative with
respect to mua and mub must be zero. ...the above function only has
one minima and no other minima or maxima. These two equations can then
be solved for mua and mub, the actual intersection points found by
substituting the values of mu into the original equations of the line.
Approach two:
An alternative approach but one that gives the exact same equations is
to realise that the shortest line segment between the two lines will
be perpendicular to the two lines. This allows us to write two
equations for the dot product as
(Pa - Pb) dot (P2 - P1) = 0
(Pa - Pb) dot (P4 - P3) = 0
Expanding these given the equation of the lines
( P1 - P3 + mua (P2 - P1) - mub (P4 - P3) ) dot (P2 - P1) = 0
( P1 - P3 + mua (P2 - P1) - mub (P4 - P3) ) dot (P4 - P3) = 0
Expanding these in terms of the coordinates (x,y,z) ...
the result is as follows
d1321 + mua d2121 - mub d4321 = 0
d1343 + mua d4321 - mub d4343 = 0
where
dmnop = (xm - xn)(xo - xp) + (ym - yn)(yo - yp) + (zm - zn)(zo - zp)
Note that dmnop = dopmn
Finally, solving for mua gives
mua = ( d1343 d4321 - d1321 d4343 ) / ( d2121 d4343 - d4321 d4321 )
and back-substituting gives mub
mub = ( d1343 + mua d4321 ) / d4343
This method was found on Paul Bourke's website which is an excellent geometry resource. The site has been reorganized, so scroll down to find the topic.
// This code in C++ works for me in 2d and 3d
// assume Coord has members x(), y() and z() and supports arithmetic operations
// that is Coord u + Coord v = u.x() + v.x(), u.y() + v.y(), u.z() + v.z()
inline Point
dot(const Coord& u, const Coord& v)
{
return u.x() * v.x() + u.y() * v.y() + u.z() * v.z();
}
inline Point
norm2( const Coord& v )
{
return v.x() * v.x() + v.y() * v.y() + v.z() * v.z();
}
inline Point
norm( const Coord& v )
{
return sqrt(norm2(v));
}
inline
Coord
cross( const Coord& b, const Coord& c) // cross product
{
return Coord(b.y() * c.z() - c.y() * b.z(), b.z() * c.x() - c.z() * b.x(), b.x() * c.y() - c.x() * b.y());
}
bool
intersection(const Line& a, const Line& b, Coord& ip)
// http://mathworld.wolfram.com/Line-LineIntersection.html
// in 3d; will also work in 2d if z components are 0
{
Coord da = a.second - a.first;
Coord db = b.second - b.first;
Coord dc = b.first - a.first;
if (dot(dc, cross(da,db)) != 0.0) // lines are not coplanar
return false;
Point s = dot(cross(dc,db),cross(da,db)) / norm2(cross(da,db));
if (s >= 0.0 && s <= 1.0)
{
ip = a.first + da * Coord(s,s,s);
return true;
}
return false;
}
I tried #Bill answer and it actually does not work every time, which I can explain. Based on the link in his code.Let's have for example these two line segments AB and CD.
A=(2,1,5), B=(1,2,5) and C=(2,1,3) and D=(2,1,2)
when you try to get the intersection it might tell you It's the point A (incorrect) or there is no intersection (correct). Depending on the order you put those segments in.
x = A+(B-A)s
x = C+(D-C)t
Bill solved for s but never solved t. And since you want that intersection point to be on both line segments both s and t have to be from interval <0,1>. What actually happens in my example is that only s if from that interval and t is -2. A lies on line defined by C and D, but not on line segment CD.
var s = Vector3.Dot(Vector3.Cross(dc, db), Vector3.Cross(da, db)) / Norm2(Vector3.Cross(da, db));
var t = Vector3.Dot(Vector3.Cross(dc, da), Vector3.Cross(da, db)) / Norm2(Vector3.Cross(da, db));
where da is B-A, db is D-C and dc is C-A, I just preserved names provided by Bill.
Then as I said you have to check if both s and t are from <0,1> and you can calculate the result. Based on formula above.
if ((s >= 0 && s <= 1) && (k >= 0 && k <= 1))
{
Vector3 res = new Vector3(this.A.x + da.x * s, this.A.y + da.y * s, this.A.z + da.z * s);
}
Also another problem with Bills answer is when two lines are collinear and there is more than one intersection point. There would be division by zero. You want to avoid that.
The original source you mention is only for the 2d case. The implementation for perp is fine. The use of x and y are just variables not an indication of preference for a specific plane.
The same site has this for the 3d case:
"http://geomalgorithms.com/a07-_distance.html"
Looks like Eberly authored a response:
"https://www.geometrictools.com/Documentation/DistanceLine3Line3.pdf"
Putting this stuff here because google points to geomalgorithms and to this post.
I found a solution: it's here.
The idea is to make use of vector algebra, to use the dot and cross to simply the question until this stage:
a (V1 X V2) = (P2 - P1) X V2
and calculate the a.
Note that this implementation doesn't need to have any planes or axis as reference.
But finding the point of intersection for two 3D line segment is not, I afraid.
I think it is. You can find the point of intersection in exactly the same way as in 2d (or any other dimension). The only difference is, that the resulting system of linear equations is more likely to have no solution (meaning the lines do not intersect).
You can solve the general equations by hand and just use your solution, or solve it programmatically, using e.g. Gaussian elemination.
I found an answer!
in an answer from above, I found these equations:
Eq#1: var s = Vector3.Dot(Vector3.Cross(dc, db), Vector3.Cross(da, db)) / Norm2(Vector3.Cross(da, db));
Eq#2: var t = Vector3.Dot(Vector3.Cross(dc, da), Vector3.Cross(da, db)) / Norm2(Vector3.Cross(da, db));
Then I modified #3rd Equation:
Eq#3:
if ((s >= 0 && s <= 1) && (k >= 0 && k <= 1))
{
Vector3 res = new Vector3(this.A.x + da.x * s, this.A.y + da.y * s, this.A.z + da.z * s);
}
And while keeping Eq#1 and Eq#2 just the same, I created this equations:
MyEq#1: Vector3f p0 = da.mul(s).add(A<vector>);
MyEq#2: Vector3f p1 = db.mul(t).add(C<vector>);
then I took a wild guess at creating these three more equations:
MyEq#3: Vector3f p0z = projUV(da, p0).add(A<vector>);
MyEq#4: Vector3f p1z = projUV(db, p1).add(C<vector>);
and finally to get the subtraction of the two magnitudes of the projUV(1, 2) gives you the margin of the error between 0 and 0.001f to find whether the two lines intersect.
MyEq#5: var m = p0z.magnitude() - p1z.magnitude();
Now I mind you, this was done in Java. This explanation is not java convention ready. Just put it to work from the above equations. (Tip: Don't transform to World Space yet so that both projection of UV equations fall exactly where you want them).
And these equations are visually correct in my program.
In addition to Bobs answer:
I find on testing that the intersection() function as written solves half the original problem, which was an algorithm to find the point of intersection of two 3D line segments.
Assuming the lines are coplanar, there are 5 possible outcomes to this question:
The line segments are parallel, so they don't intersect, or,
The line segments aren't parallel, and the infinite length lines they lie upon do intersect, but the intersection point is not within the bounds of either line segment, or,
The lines intersect and the intersection point is within the bounds of line a but not line b, or,
The lines intersect and the intersection point is within the bounds of line b but not line a, or,
The lines intersect and the intersection point is within the bounds of both line segments.
Bob's intersection() function returns true when the lines intersect and the point of intersection is within the bounds of line a, but returns false if the lines intersect and the point of intersection is within the bounds of only line b.
But if you call intersect() twice, first with lines a then b and then a second time with lines b and a (first and second params swapped), then if both calls return true, then the intersect is contained within both line segments (case 5). If both calls return false, then neither line segment contains the intersect (case 2). If only one of the calls returns true, then the segment passed as the first parameter on that call contains the point of intersection (cases 3 or 4).
Also, if the return from the call to norm2(cross(da,db)) equals 0.0, then the line segments are parallel (case 1).
The other thing I noted in testing is that with fixed precision floating point numbers of the kind code is often implemented with, it can be quite unusual for dot(dc, cross(da,db)) to return 0.0, so returning false when its not the case might not be what you want. You might want to introduce a threshold below which the code continues to execute rather than return false. This indicates the line segments are skew in 3D, but depending on your application you might want to tolerate a small amount of skew.
The final thing I noticed was this statement in Bill's code:
ip = a.first + da * Coord(s,s,s);
That da * Coord(s,s,s) looks to be a vector times vector multiply. When I replaced it with a scalar multiple of da * s, I found it worked fine.
But in any case, many thanks to Bob. He figured out the hard part.
https://bloodstrawberry.tistory.com/1037
This blog was implemented by Unity c#.
Vector3 getContactPoint(Vector3 normal, Vector3 planeDot, Vector3 A, Vector3 B)
{
Vector3 nAB = (B - A).normalized;
return A + nAB * Vector3.Dot(normal, planeDot - A) / Vector3.Dot(normal, nAB);
}
(Vector3 point1, Vector3 point2) getShortestPath(Vector3 A, Vector3 B, Vector3 C, Vector3 D)
{
Vector3 AB = A - B;
Vector3 CD = C - D;
Vector3 line = Vector3.Cross(AB, CD);
Vector3 crossLineAB = Vector3.Cross(line, AB);
Vector3 crossLineCD = Vector3.Cross(line, CD);
return (getContactPoint(crossLineAB, A, C, D), getContactPoint(crossLineCD, C, A, B));
}