Efficient line segment-triangle intersection - algorithm

I have a collection of triangles that compose an arbitrary geometry (read from an OFF file). Each triangle is defined by three vertexes, which are 3D points. I have an observation point, and I want to remove from the object all those vertexes that are not visible, meaning that the segment line that joins the observation point and the vertex does not intersect with any triangle.
It is important to note that the number of vertexes and triangles in a single object is in the other of 10^4. A simple approach will be to follow [1] accepted answer, which uses the signed volumes. I have implemented it, but it includes two nested for loops: for each vertex, I need to check if the line to the observation point intersects with ANY of the triangles. Of course, I break the look when it intersects with one, but it still leads to 192M calls to the signed volume function.
Some efficient algorithms [2] assume an infinite line, not a line segment, which leads to the deletion of the vertex even if the intersection is with a further away triangle.
Any ideas on how to solve this efficiently?
[1] Intersection between line and triangle in 3D
[2] Möller and Trumbore, « Fast, Minimum Storage Ray-Triangle Intersection », Journal of Graphics Tools, vol. 2,‎ 1997, p. 21–28

Your problem statement is literally a ray-tracing problem: find if a bunch of points are occluded by the geometry or not. Therefore you can apply any ray-tracing tricks to make it fast; i.e. build some spacial index over your model and trace the rays using that spacial index. Some of the most common spacial indexes are BVH, BSP trees, or kd-trees.
If an approximate solution is good-enough, you can rasterize the geometry from the vantage point, and then test all your vertices against the generated Z-buffer.
Notice that either of these methods will filter the vertices (which is what you asked for). However, some triangles may be visible even if all their vertices are occluded. Those triangles will be removed too, even though they are visible. The rasterization method is easy to modify to filter triangles instead of vertices.

There are two question in one:
efficiency
segment-triangle intersection instead of ray-triangle intersection
efficiency
You can use a Bounding Volume Hierarchy (BVH)
The idea is to construct a hierarchy of boxes. Each box contains either other boxes or a bunch of triangles. The boxes are organized as a tree, and the root of the tree corresponds to a box that contains everything.
The function that computes an intersection works like that:
Compute the intersection between the ray and the box. If there is an intersection, open the box and recursively compute the intersections between the ray and what's in the box.
In more formal terms:
Intersect_Ray_Node(Ray, node)
if(Intersect_Ray_Box(Ray, node.box)) {
if(node.is_leaf) {
for each triangle t in node
Intersect_Ray_Triangle(Ray, t)
} else {
for each child in children(node)
Intersect_Ray_Node(Ray,child)
}
}
Now, the "boxes" can be different things. The simplest thing to use is an AABB (Axis Aligned Bounding Box), that is, simply the minimum and maximum values of the coordinates of what's in the box.
There are several ways of implementing the tree. The most efficient implementations use no data structure and simply reorder the facets in the mesh, in such a way that facets that are in the same node have contiguous indices. Then you just need a couple of additional arrays, one that stores for each node the bounds of the boxes, and another one that indicate for each node how many triangles there are in the left child (then the number of triangles in the right child is implicit).
An implementation is available here, in my geogram library. Constructing the AABB is just a matter
of sorting the triangles geometrically here and constructing the bouding box recursively.
I made also a ShaderToy implementation here (and a big comment at the end of BufferA contains the C++ source of my "MeshCompiler" that creates the AABB).
Note that if you want to be even more compact in memory, Pierre Terdiman proposed the Zero Byte AABB-tree (see here and here). The idea is to encode everything in the order of the triangles and the order of the vertices, based on the interesting remark that the bounds of the boxes are always coordinates of existing points in the mesh.
line segment versus ray
If what you want to intersect is a segment (with two vertices) instead of a ray (line that starts from a point and that extends towards infinity in a direction), you can do that with a simple modification of my answer here. Replace the last line
of the intersect_triangle() function with:
return (det >= 1e-6 && t >= 0.0 && t <= 1.0 && u >= 0.0 && v >= 0.0 && (u+v) <= 1.0);
(test that t is between 0 and 1, which means that the point is inside the segment)

Related

Algorithm to quickly check amount of intersection in a list of shapes

So I got a list of shapes and already have an algorithm to check if two shapes intersect. Now I'm trying to figure out an efficient algorithm to go through a list and count the number of intersections.
What I have now is simply two for loops that compare the first element of the list to everything that comes after it, then do the same for the second element and etc. That's O(n^2) isn't it? I'm not quite sure how I would go about making it more efficient as I don't think you can sort a list of shapes or use hashtables if that does anything here.
Split your 2D space into a "nice power of 2" number of tiles. For example, you might have 8 rows and 8 columns to get 64 tiles.
For each polygon, add a tileFlags field. This will be 1 bit per tile, which indicates if the polygon does/doesn't overlap with the tile (and is the reason why you want a "nice power of 2" number of tiles).
To determine a polygon's tileFlags field; find the vertex/vertices with the lowest y-coord (and if there's 2 or more find the left-most and right-most vertex). From the starting vertex/vertices; trace the polygon's left and right edges from one vertex to the next, as you set flags corresponding to "tiles between left and right edge" each time you reach a new left/right vertex and each time you cross a "tile top/bottom". Note that this is a minor adaption of the algorithm you'd use for software rendering ("rasterization" for drawing polygons); just with "pixel" replaced with "tile" and "color" replaced with "1-bit flag"; and you should be able to find a better description of it (maybe even with example code). It is somewhat error prone (e.g. you have to take some care when vertices are perfectly on a tile's boundary); and becomes much harder and more expensive if the polygon can be concave (better to disallow concave polygons or maybe detect them and temporarily split them into convex polygons).
Once that's done, you can do if( (polygon1->tileFlags & polygon2->tileFlags) != 0) { /* Polygons might intersect */ } else { /* Polygons can't intersect */ } as a preliminary intersection test to avoid a much more expensive intersection test.

Intersection test with kd-tree

My current understanding of a kd-tree is; that on every node we split our points into two equally big groups for one axis.
We iterate through the individual axis until the tree is saturated.
This kind of data-structure is, of course, is interesting for raytracing applications because we don't have to search through every triangle face to test our intersection with the ray, we simply know where it would be likely that a triangle would intersect with our ray.
I have some question on how this is done.
How do we handle weird triangles where we cannot make easy splits(triangles intersecting other triangles or triangles which span the entire?
Do we even split on the triangles or do we split on the vertices?
How exactly do we test for an intersection of a ray that we send out from the camera ?
I see a couple of methods. First, we could build bounding boxes from our scene and the splitting planes and test for intersection with those boxes or we could test for intersection with the splitting planes and see where the intersection is relative to the camera
The short answer is: This all depends on your application. There are several variations of kd-trees.
How do we handle weird triangles where we cannot make easy splits?
I believe you are referring to the choice of the splitting plane for a given set of triangles. This is a pretty hard optimization problem, which is usually solved with a heuristic. E.g., you could sort the centroids of triangles along one axis and choose the median as the splitting plane. Nothing is stopping you from implementing some more intelligent criterion.
If you find that your splitting plane passes through a primitive, you have two options. Either split the primitive or add it to both subtrees. What you should do depends on your application.
Do we even split on the triangles or do we split on the vertices?
That depends on the primitives you want to add to your tree. If you want to use the tree for raycasting, then it makes sense to have the triangles in the tree. kd-trees are a very general concept that works with any kind of primitives, though. E.g., they are also widely used for point clouds.
How exactly do we test for an intersection of a ray that we send out from the camera?
You do this by traversing the tree. So, you start at the root node and check if the ray intersects with the associated bounding box (which is the entire space). Then you check which of the two subtrees are first intersected by the ray and continue to this one. And then you repeat: You test for intersection with the node's AABB (which you build incrementally from the splitting planes). If the ray does not intersect the AABB, then immediately return back to the parent node. If it does, continue to the first child. When you come back from the first child, go to the second child (unless you already found an intersection).
For some more details, I would advice to take a look at application-specific instances of kd-trees.

Efficient checking of whether a point is inside a large number of triangles in 2D

I have implemented an algorithm to check whether a point is inside a triangle in 2D by calling the 1st algorithm in this question: how to determine if a point is inside a 2d triangle.
This algorithm works, but I think if I preprocess the triangle and use a smart data structure, I can make this efficient. I'm using about 10 million triangles. I think one way of making this fast is to compute bounding rectangles for the triangles and do a point in rectangle check, but I feel even this case can be made faster by making use of some data structure to only check for rectangles that are closeby.
Does such a data structure and algorithm exist?
The classical approach from computational geometry is to triangulate and then do point location, but it's a lot easier to just use a k-d tree, putting triangles that intersect the partitioning line of an interior tree node in that node so that they can be checked on the way down to the leaves.
Constructing the k-d tree is a recursive process. Given a list of triangles and a coordinate to focus on (x or y, alternating with each layer), find a separating line that is vaguely in the middle, by sampling randomly, by taking a median, or by some combination or sampling and taking a median. Gather the triangles whose points are all strictly less than the separation coordinate and use them to construct the left subtree. Gather the triangles whose points are all strictly greater than the separation coordinate and use them to construct the right subtree. Store the remaining triangles (the ones that intersect the coordinate line) in the root.
To query, test whether the point belongs to any of the triangle in the root. Then, if the point is less than the separation coordinate, check the left subtree. If the point is greater, check the right.

How to find if a 3D object fits in another 3D object (the container)?

Given two 3d objects, how can I find if one fits inside the second (and find the location of the object in the container).
The object should be translated and rotated to fit the container - but not modified otherwise.
Additional complications:
The same situation - but look for the best fit solution, even if it's not a proper match (minimize the volume of the object that doesn't fit in the container)
Support for elastic objects - find the best fit while minimizing the "distortion" in the objects
This is a pretty general question - and I don't expect a complete solution.
Any pointers to relevant papers \ articles \ libraries \ tools would be useful
Here is one perhaps less than ideal method.
You could try fixing the position (in 3D space) of 1 shape. Placing the other shape on top of that shape. Then create links that connect one point in shape to a point in the other shape. Then simulate what happens when the links are pulled equally tight. Causing the point that isn't fixed to rotate and translate until it's stable.
If the fit is loose enough, you could use only 3 links (the bare minimum number of links for 3D) and try every possible combination. However, for tighter fit fits, you'll need more links, perhaps enough to place them on every point of the shape with the least number of points. Which means you'll some method to determine how to place the links, which is not trivial.
This seems like quite hard problem. Probable approach is to have some heuristic to suggest transformation and than check is it good one. If transformation moves object only slightly out of interior (e.g. on one part) than make slightly adjust to transformation and test it. If object is 'lot' out (e.g. on same/all axis on both sides) than make new heuristic guess.
Just an general idea for a heuristic. Make a rasterisation of an objects with same pixel size. It can be octree of an object volume. Make connectivity graph between pixels. Check subgraph isomorphism between graphs. If there is a subgraph than that position is for a testing.
This approach also supports 90deg rotation(s).
Some tests can be done even on graphs. If all volume neighbours of a subgraph are in larger graph, than object is in.
In general this is 'refined' boundary box approach.
Another solution is to project equal number of points on both objects and do a least squares best fit on the point sets. The point sets probably will not be ordered the same so iterating between the least squares best fit and a reordering of points so that the points on both objects are close to same order. The equation development for this is a lot of algebra but not conceptually complicated.
Consider one polygon(triangle) in the target object. For this polygon, find the equivalent polygon in the other geometry (source), ie. the length of the sides, angle between the edges, area should all be the same. If there's just one match, find the rigid transform matrix, that alters the vertices that way : X' = M*X. Since X' AND X are known for all the points on the matched polygons, this should be doable with linear algebra.
If you want a one-one mapping between the vertices of the polygon, traverse the edges of the polygons in the same order, and make a lookup table that maps each vertex one one poly to a vertex in another. If you have a half edge data structure of your 3d object that'll simplify this process a great deal.
If you find more than one matching polygon, traverse the source polygon from both the points, and keep matching their neighbouring polygons with the target polygons. Continue until one of them breaks, after which you can do the same steps as the one-match version.
There're more serious solutions that're listed here, but I think the method above will work as well.
What a juicy problem !. As is typical in computational geometry this problem
can be very complicated with a mismatched geometric abstraction. With all kinds of if-else cases etc.
But pick the right abstraction and the solution becomes trivial with few sub-cases.
Compute the Distance Transform of your shapes and Voilà! Your solution is trivial.
Allow me to elaborate.
The distance map of a shape on a grid (pixels) encodes the distance of the closest point on the
shape's border to that pixel. It can be computed in both directions outwards or inwards into the shape.
In this problem, the outward distance map suffices.
Step 1: Compute the distance map of both shapes D_S1, D_S2
Step 2: Subtract the distance maps. Diff = D_S1-D_S2
Step 3: if Diff has only positive values. Then your shapes can be contained in each other(+ve => S1 bigger than S2 -ve => S2 bigger than S1)
If the Diff has both positive and negative values, the shapes intersect.
There you have it. Enjoy !

Locating Bounding 2D Entities

Given a point and a set of arbitrary 2D entities (circles, polygons, lines, polylines, arcs, etc.), does anyone know of existing strategies to:
Determine if the point is enclosed (bounded) by any combination of entities? I know that it is easy enough to do an 'inside' test on the closed shapes, but this won't always give me what I want - particularly with nested or intersecting shapes.
Find the smallest (closest?) set of lines / entities that form a closed polygon around my point? (think of a flood-fill, but without relying on colour)
I've addressed this problem in a commercial product in the past. You've asked about analytic curves, but I'll address it more generally for curves that are at least twice differentiable. Handle polygons as a set of separate line segments. There is no need to segment the curves, but if you want to you can and adapt the algorithm slightly.
Also, you might want to see my paper Matrix-Based Ellipse Geometry in Graphics Gems V to find the intersections between your ellipses.
Basic idea:
Consider a ray from your test point in the +x direction.
Now consider an ant walking along your ray from the test point.
When the ant hits the first intersection with one of the curves, it makes the sharpest left it can, and leaves an arrow at that intersection indicating the direction it's chosen. (If there is no intersection, then obviously the point isn't bounded.)
If it comes to the end of a curve, it doubles back on itself.
If there are multiple curves intersecting at that point, it chooses the curve that is most to the left.
If one or more of the curves is in fact tangent to the ray at the intersection, higher derivatives can be used decide which curve and direction to choose. (This ant knows calculus.)
Now as the ant strolls along the curves, it always makes the biggest left turn it can as above. If there is tangency between curves at the intersection, use higher derivatives to decide the one that is "most to the left". (Details are left to the ant).
In its travels, the ant may come to the starting intersection with the ray multiple times. But as soon as it finds itself proceeding in the direction of the arrow (the one it left in step 3), it's travels are done and it has traversed a "contour". The problem is reduced to deciding if the point is in that contour.
A "contour" is a topological entity. It's closed ring of "segments" connected at "vertices".
A "segment" is a piece of a curve used by the contour in a particular direction.
A "vertex" is a connection between segments. A vertex is associated with a (x, y) position on the plane, but there may be multiple vertices at the same position, one for each pair of segments in the contour that meet at that point. There is a vertex for each curve endpoint (a spur vertex), or curve-curve intersection encountered by the ant.
A contour (in this context) is not a geometric entity! Don't think of it as a simple closed path on the plane. The ant might go along a segment, get to the end, and go back the way it came--this is called a "spur" and includes two contour segments, one for either direction. Or it might go along one direction of a curve segment, wander around a bit along other curves, and return along the other direction of the segment.
So even if your set of curves has only one line in it from A to B (I'm assuming you don't have infinite lines) and your ray hits it at P, you still have the contour V0(P)-V1(A)-V2(P)-V3(B)-V0 with 4 segments V0-V1, V1-V2, V2-V3, V3-V0. Note that V0 and V2 are distinct vertices, both positioned at P.
Now to test if your point is in the contour.
Find the intersections of your ray (any ray originating at your test point will do) with the contour. We only really want the parity (even or odd) of the number of intersections with the contour. If the parity is odd, the point is bounded by the curves, if it's even it's not.
Because doubly traversed segments contribute nothing to the parity, we can ignore them. This is because there are always an even number of intersections on doubly traversed segments, since they're in the contour twice.
Examples:
Consider this curve set. I use lines so I don't work too hard:
Case 1 - The point is not bounded. The contour's use of the curve segments is indicated by the dotted arrows. The number of ray-contour intersection parity is even.
Case 2 - The point is bounded. The ray-contour intersection parity is odd.
Here's what can go wrong:
You can't find a contour for various numerical reasons. For example, you might miss intersections, e.g. two curves are almost tangent at a curve. You might see it as a single intersection, but when you do the ray intersection parity test you see a single crossing so that the parity flips when it shouldn't.
You might not be able to compute enough derivatives to make the correct turn decisions. In the case of analytic geometry this should never be the case.
Your ray hits a vertex (connections between segments) of your contour. (Note that there can be multiple vertices at a single (x, y) point. Each of these has to be handled separately.)
In this case, you have to decide if the incoming and outgoing segments of the vertex are on the same side of the ray at the vertex. If they're on the same side, the parity is not affected. Otherwise the parity flips. If one of the curves is tangent to the ray at the vertex, you may have to use higher derivatives to decide this.
A line segment is collinear with your test ray. This is actually a special case of 2, but easy to handle: Ignore it.
There are lots of details, but you should be able to handle them. Be sure to use spatial trees to avoid computing unnecessary intersections.
The answer to your second question comes from removing from the contour any doubly traversed segments. This may yield multiple sub-contours. One of them will contain your point.

Resources