Robust polygon normal calculation - algorithm

is there a good robust algorithm to calculate normal vector of a convex polygon (in 3D, of course)? For triangles, it is easy: one takes two of the triangle's edges and calculates the cross product:
vec3 u = point[0] - point[1], v = point[0] - point[2];
vec3 n = normalize(cross(u, v));
But this approach does not really extend to polygons very well. Some edges of the polygon can be nearly or "exactly" collinear (this will happen often in meshes where T-Junction removal took place), therefore it is necessary to choose a pair of edges, giving a "strong" normal (both edges are "long enough" and they hold "almost perpendicular" angle).
This approach will still not work for all polygons, though. Imagine a polygon in the shape of a disc. If it is very finely subdivided, all the edges will be very short and all of the consecutive edges will be almost collinear, regardless of the radius of the disc. At the same time, the normal is very well defined.
One solution could be to find the largest inscribed triangle and to calculate the normal of that. However, finding it will have complexity of O(n^2), which seems prohibitive.
A better solution could be to use SVD or Eigenvalue decomposition to calculate the normal, given all the polygon points, not just three or four.
Is there a standard algorithm for this? Anyone has a good way of doing it?

If you factorize the formula for a triangle, you'll get the following:
n ~ (p1 - p0) x (p2 - p0)
= p0 x p1 + p1 x p2 + p2 x p0
You can generalize this formula for arbitrary polygons:
n ~ p0 x p1 + p1 x p2 + ... + pn x p0
So sum the cross product of consecutive edges. This is a robust algorithm and works for non-planar polygons.
If you can be sure that the polygon is planar, I would do the following (to save computation time):
Repeat k times
Pick 3 random polygon vertices
Calculate the normal of the according triangle
Choose the longest normal as the polygon's normal.
You might discard any normal that has a length <= epsilon.

start from an arbitrary vertex(lets call it vertex A), and move to the next vertex in the list(call it vertex B). calculate the perpendicular vector(call it vector P) to the AB vector. Then continue iterating in the vertex list to find the vertex that is perpendicularly the most distant from vector AB. So at each iteration take the dot product of the current element(take vertex B as the origin) with the vector P and take the one that has the greatest result in magnitude(take absolute value) and call it C. calculate the cross product of A B C vectors.
if the poly is convex you can stop iterating untill the perpendicular distances starts to
get smaller in magnitude.
I came up with this idea, i do not know how efficient this method would be since I do not know any other algorithm to compare with.

You can calculate the covariance matrix for all the points of the polygon (which will be a 3x3 matrix for 3D space). The normal to the polygon will be the Eigen vector corresponding to the smallest Eigen value.

Related

Fast algorithm to calculate 3D ray / triangle edge intersections for a large number of triangles

I have a triangle ABC in 3D space and a ray in plane of the triangle given by starting point E (always inside or on the triangle's edge) and direction vector d.
The vertices A, B and C as well as E and d are given in 3D coordinates {x,y,z}.
I am looking for an algorithm to calculate the intersection of the ray with the triangle's edge P.
I can do 3 ray / line segment intersection tests for the 3 edges of the triangle but since I have to do this for a large number of triangles I am looking for a more efficient and faster algorithm.
What is the best way to do this?
Could Barycentric Coordinates help?
Could Barycentric Coordinates help?
It may. It depends a bit on how highly optimized your non-barycentric code is, but I'd say that using barycentric coordinates it's at least easier to write code which is both maintainable and performant.
As far as I can tell, your whole setup is essentially 2d, with both the point E and the direction d contained within the plane spanned by A,B,C. So you have
E = aE*A + bE*B + cE*C with aE+bE+cE=1
d = ad*A + bd*B + cd*C with ad+bd+cd=0
Now you have two subproblems:
How to obtain the barycentric coordinates efficiently
How to find the point of intersection
Let's start with the latter. You simply add to E as many multiples of d until the c coordinate becomes zero.
P = E - (cE/cd)*d
Depending on your setup, you might also be fine using homogeneous coordinates, in which case you could write this as P = cd*E - cE*d.
How do you turn x,y,z coordinates of d and E into barycentric a,b,c? Well, that's just a system of linear equations. You could use the inverse of the matrix formed by the vectors A,B,C. Again, if you are dealing with homogeneous coordinates, you can use the adjunct instead of the inverse.
Here is the homogeneous approach spelled out:
aE = (By*Cz-Bz*Cy)*Ex + (Bz*Cx-Bx*Cz)*Ey + (Bx*Cy-By*Cx)*Ez
bE = (Cy*Az-Cz*Ay)*Ex + (Cz*Ax-Cx*Az)*Ey + (Cx*Ay-Cy*Ax)*Ez
cE = (Ay*Bz-Az*By)*Ex + (Az*Bx-Ax*Bz)*Ey + (Ax*By-Ay*Bx)*Ez
ad = (By*Cz-Bz*Cy)*dx + (Bz*Cx-Bx*Cz)*dy + (Bx*Cy-By*Cx)*dz
bd = (Cy*Az-Cz*Ay)*dx + (Cz*Ax-Cx*Az)*dy + (Cx*Ay-Cy*Ax)*dz
cd = (Ay*Bz-Az*By)*dx + (Az*Bx-Ax*Bz)*dy + (Ax*By-Ay*Bx)*dz
aP = cd*aE - cE*ad
bP = cd*bE - cE*bd
Px = aP/(aP+bP)*Ax + bP/(aP+bP)*Bx
Py = aP/(aP+bP)*Ay + bP/(aP+bP)*By
Pz = aP/(aP+bP)*Az + bP/(aP+bP)*Bz
The first three lines convert E to barycentric coordinates, the next three lines d. Then we compute the barycentric coordinates of P, and turn these back into Cartesian coordinates. In that step we also dehomogenize them, i.e. divide by the sum of the barycentric coordinates.
On the whole, there is a considerable amount of code here. You can reduce the number of operations by moving common expressions to dedicated variables, particularly if your compiler doesn't do that for you. One benefit is that you won't need any divisions except for the final dehomogenization, i.e. the division by (a+b). If you compute 1/(a+b) once, you can make do with but a single division, which is good for performance.
The real benefit of the above code however is likely that you can do many nice things with the barycentric coordinates that you couldn't easily do with other coordinate systems. For example, if you want to check whether the ray hits the line AB on the line segment or somewhere outside the triangle, just check whether aP*bP > 0.
Time complexity is O(1)
It could potentially help.
In case we are talking about 3D and all your points lie on one plane. In cartesian coordinates you need to solve three linear systems to find intersection with three lines, and then determinate whether intersections lies on edge or not.
Or you need to find barycentric coordinates of two points on line. Point A(a1,a2,a3) = E and B(b1,b2,b3) = E+d. Equation of line is
Now you still need to solve three linear systems (where one of the mu = 0 and sum of the others = 1). But it will be 2x2 systems that easer to solve. And then check signes of found roots to detrerminate whether found point lies on edge or not.
Temporarily ignore the axis for which d has the smallest component, and treat the resulting 2D problem, just as we see on your illustration.
You can precompute the equation of the line of support of the ray, in the form ax + by + c = 0 (assuming z was ignored). Plug the coordinates of the three vertices in this expression, and the signs will tell you on what side of the line the vertices are.
The two intersections with the line occur on the edges having different signs at the endpoints.
Then, to determine which is the right edge, the sign of the angle between the ray and the third egde will do (if I am right);
When you know which edge, you can compute the parameter of the parametric equation of the side, which you can deduce from the previously computed signs. For instance, along the edge AB, t = Sb / (Sb - Sa). Also compute 1 - t;
Finally, use the t value to interpolate between the endpoints, this time in 3D.
The computational cost is
evalutation of the three signs Sa, Sb, Sc, taking 6 + and 6 x;
selection of the two edge candidates, taking two or three comparisons;
selection of the single candidate, taking a 2D cross product, 3 + and 2 x and a comparison;
computation of the interpolation parameter, 2 +, 1 /;
final 3D interpolation, 3 +, 6 x.
Will be hard to do shorter.
See the paper
Fast, minimum storage ray-triangle intersection,
by Tomas Möller and Ben Trombone,
in
Journal of Graphics Tools, 2(1):21–28, 1997.
See also this page.
The authors say:
One advantage of this method is that the plane equation need not be computed on the fly nor be stored, which can amount to significant memory savings for triangle meshes. As we found our method to be comparable in speed to previous methods, we believe it is the fastest ray-triangle intersection routine for triangles that do not have precomputed plane equations.

Finding weight value of isosceles right angled triangles

I want to find out the weight value of total number of isosceles right angled triangles inside a n*m rectangular grid. The weight value is the value which is total of adding each point value in the rectangular grid. Let me explain through an example
Here is the rectangular grid with n=1 and m=2. I want to find out the weight of each isosceles right angled triagle present in this grid. Here are the possible right angle isosceles triangles which can be formed from this grid
So I want to find out the weight value of each of these triangles like triangle A has 4, B has 6.
I tried finding total number of right angled triangles using C Program to detect right angled triangles but it is difficult to find each triangle's weight if I would only know how many triangles there are. My approach for this problem was picking every point and find the triangle associated with it and the corresponding weight value. But it takes 4 times the time complexity the number of points in the grid (4 times 2*3 in this case). I want to find an efficient formula so that I can perform this operation for large n and m as well. Any help would be appreciated.
Per the discussion in the comments, you're looking to enumerate all of the possible triangles and discover the sum of all of the points on the edges.
You can enumerate the triangles as follows. Given a point p = (p1, p2) and another point q = (q1, q2) there is exactly one right angled isosceles starting at p, going to q and turning right. The third vertex will be at r = (q1 + q2 - p2, q2 - q1 + p1). If you loop over all pairs of vertices, this will find every possible triangle exactly once.
Next we need the weight of each line segment. Given a line segment from p to q, first find the GCD of (q1 - p1, q2 - p2). (Special case. The GCD of any integer and 0 is 1.) Then divide both coefficients by that GCD to get the smallest vector along that line going from point to point. Let's call that smallest vector v. Now you can add up the weights for p, p+v, p+2v, ... then stop at q. (Note, each line interval should include one point and not the other.)
And there you go. The final algorithm should be O(n^2 m^2 log(n+m)). Which can't be improved much given that the number of right-angled isosceles triangles is O(n^2 m^2). If needed you could improve the log factor by making the weight of (starting point, unit vector, n) recursive then memoizing it. However that requires a O(n^2 m^2) data structure and locality problems addressing it could easily exceed the theoretical performance gain.
OK, improvement! Instead of iterating over pairs of points, iterate over starting vectors v = (v1, v2) with (v1, v2) relatively prime (check with the Euclidean algorithm, then over starting points p = (p1, p2), then over multiples i of the starting vector. The triangles that you are considering will be (p1, p2), (p1 + n*v1, p2 + n*v2), (p1 + n*v1 + n*v2, p2 - n*v1 + n*v2). And NOW for each starting vector, each value of p2 - p1, and each of the 3 directions you could be going, you can calculate the sum of all of the weights you could have from infinity to each point on that line. (A O(nm) data structure.) With that data structure the two inner loops can execute in time O(1) per triangle.
This gives you a O(n^2 m^2) algorithm to find the total weight of all O(n^2 * m^2) right-angled isosceles triangles. Which is as good as you could theoretically do. (And the auxiliary data structures required are O(nm).)

Testing tetrahedron-triangle intersection

I want to determine whether a given triangle intersects a tetrahedron. I do not want to compute the solution polygon(if it exists) itself. Is there any library/package/published practical algorithm which can be used to solve this problem directly(unlike my attempt below)?
I think as a last resort I will have to use standard polygon-polygon intersection algorithm implementations to solve this problem indirectly.
My attempt on this problem:
I thought of breaking it into the problem of polygon-polygon intersection. So for each triangular face(say T1) of the tetrahedron and the given triangle T2, I thought of doing the following:
Compute intersection(a line) between planes corresponding to each triangle, say L1.
For each triangle:
For each edge of the triangle say L2, compute point of intersection P between L1 and L2.
Test(maybe using parametric form) of L2, if the point lies on the edge L2.
If for both triangles T1 and T2, there exists at least one edge on which the intersection point P lies, then it implies that the triangles(and hence the given tetrahedron and the triangle T2) do intersect.
Create an orthonormal frame based on the triangle (origin at some vertex, axis X using the direction of some side, axis Y and Z using the direction of another side and Gram-Schmidt formulas).
Transform the coordinates of the 3 + 4 vertices to the new frame.
If all Z of the 4 tetrahedron vertices are of the same sign, there is no intersection. Otherwise, find the 3 or 4 intersection point of the edges into XY, by linear interpolation on Z.
Now you need to check for intersections between a triangle and a triangle or (convex) quadrilateral, in 2D. You can solve that by means of a standard clipping algorithm, made simple by convexity of the polygons.
As an easy optimization, note that it is useless to compute the Z of the triangle vertices (=0), and the XY of the tetrahedron vertices before you know that there is a possible intersection.
You can also speedup the polygon intersection process by first using a bounding box test.
I just found a function in CGAL library CGAL::do_intersect(Type1< Kernel > obj1,Type2< Kernel > obj2 ) for computing intersection between two geometric objects. It permits Type1 and Type2 to be Triangle_3<Kernel> and Tetrahedron_3<Kernel> respectively.

Find Voronoi tessellation with area constraints

Let's say we want to Voronoi-partition a rectangular surface with N points.
The Voronoi tessellation results in N regions corresponding to the N points.
For each region, we calculate its area and divide it by the total area of the whole surface - call these numbers a1, ..., aN. Their sum equals unity.
Suppose now we have a preset list of N numbers, b1, ..., bN, their sum equaling unity.
How can one find a choice (any) of the coordinates of the N points for Voronoi partitioning, such that a1==b1, a2==b2, ..., aN==bN?
Edit:
After a bit of thinking about this, maybe Voronoi partitioning isn't the best solution, the whole point being to come up with a random irregular division of the surface, such that the N regions have appropriate sizes. Voronoi seemed to me like the logical choice, but I may be mistaken.
I'd go for some genetic algorithm.
Here is the basic process:
1) Create 100 sets of random points that belong in your rectangle.
2) For each set, compute the voronoï diagram and the areas
3) For each set, evaluate how well it compares with your preset weights (call it its score)
4) Sort sets of points by score
5) Dump the 50 worst sets
6) Create 50 new sets out of the 50 remaining sets by mixins points and adding some random ones.
7) Jump to step 2 until you meet a condition (score above a threshold, number of occurrence, time spent, etc...)
You will end up (hopefully) with a "somewhat appropriate" result.
If what you are looking for does not necessarily have to be a Voronoi tesselation, and could be a Power diagram, there is a nice algorithm described in the following article:
F. Aurenhammer, F. Hoffmann, and B. Aronov, "Minkowski-type theorems and least-squares clustering," Algorithmica, 20:61-76 (1998).
Their version of the problem is as follows: given N points (p_i) in a polygon P, and a set of non-negative real numbers (a_i) summing to the area of P, find weights (w_i), such that the area of the intersection of the Power cell Pow_w(p_i) with P is exactly a_i. In Section 5 of the paper, they prove that this problem can be written as a convex optimization problem. To implement this approach, you need:
software to compute Power diagrams efficiently, such as CGAL and
software for convex optimization. I found that using quasi-Newton solvers such as L-BFGS gives very good result in practice.
I have some code on my webpage that does exactly this, under the name "quadratic optimal transport". However this code is not very clean nor very well-documented, so it might be as fast to implement your own version of the algorithm. You can also look at my SGP2011 paper on this topic, which is available on the same page, for a short description of the implementation of Aurenhammer, Hoffman and Aronov's algorithm.
Assume coordinates where the rectangle is axis-aligned with left edge at x = 0 and right edge at x = 1 and horizontal bisector at y = 0. Let B(0) = 0 and B(i) = b1 + ... + bi. Put points at ((B(i-1) + B(i))/2, 0). That isn't right. We the x coordinates to be xi such that bi = (x(i+1) - x(i-1)) / 2, replacing x(0) by 0 and x(n+1) by 1. This is tridiagonal and should have an easy solution, but perhaps you don't want such a boring Voronoi diagram though; it will be a bunch of vertical divisions.
For a more random-looking diagram, maybe something physics inspired: drop points randomly, compute the Voronoi diagram, compute the area of each cell, make overweight cells attractive to the points of their neighbors and underweight cells repulsive and compute a small delta for each point, repeat until equilibrium is reached.
The voronoi tesselation can be compute when you compute the minimum spanning tree and remove the longest edges. Each center of the subtree of the mst is then a point of the voronoi diagram. Thus the voronoi diagram is a subset of the minimum spanning tree.

Largest circle inside a non-convex polygon

How can I find the largest circle that can fit inside a concave polygon?
A brute force algorithm is OK as long as it can handle polygons with ~50 vertices in real-time.
The key to solving this problem is first making an observation: the center of the largest circle that will fit inside an arbitrary polygon is the point that is:
Inside the polygon; and
Furthest from any point on the edges of the polygon.
Why? Because every point on the edge of a circle is equidistant from that center. By definition, the largest circle will have the largest radius and will touch the polygon on at least two points so if you find the point inside furthest from the polygon you've found the center of the circle.
This problem appears in geography and is solved iteratively to any arbitrary precision. Its called the Poles of Inaccessibility problem. See Poles of Inaccessibility: A Calculation Algorithm for the Remotest Places on Earth.
The basic algorithm works like this:
Define R as a rectilinear region from (xmin,ymin) to (xmax,ymax);
Divide R into an arbitrary number of points. The paper uses 21 as a heuristic (meaning divide the height and width by 20);
Clip any points that are outside the polygon;
For the remainder find the point that is furthest from any point on the edge;
From that point define a new R with smaller intervals and bounds and repeat from step 2 to get to any arbitrary precision answer. The paper reduces R by a factor of the square root of 2.
One note, How to test if a point is inside the polygon or not: The simplest solution to this part of the problem is to cast a ray to the right of the point. If it crosses an odd number of edges, it's within the polygon. If it's an even number, it's outside.
Also, as far as testing the distance to any edge there are two cases you need to consider:
The point is perpendicular to a point on that edge (within the bounds of the two vertices); or
It isn't.
(2) is easy. The distance to the edge is the minimum of the distances to the two vertices. For (1), the closest point on that edge will be the point that intersects the edge at a 90 degree angle starting from the point you're testing. See Distance of a Point to a Ray or Segment.
In case anyone is looking for a practical implementation, I designed a faster algorithm that solves this problem for a given precision and made it a JavaScript library. It's similar to the iterative grid algorithm described by #cletus, but it's guaranteed to obtain global optimum, and is also 20-40 times faster in practice.
Check it out: https://github.com/mapbox/polylabel
An O(n log(n)) algorithm:
Construct the Voronoi Diagram of the edges in P. This can be done with, for example, Fortunes algorithm.
For Voronoi nodes (points equidistant to three or more edges) inside P;
Find the node with the maximum distance to edges in P. This node is the centre of the maximum inscribed circle.
Summary: In theory, this can be done in O(n) time. In practice you can do it in O(n log n) time.
Generalized Voronoi diagrams.
If you consider the vertices and edges of the polygon as a set of sites and tessellate the interior into the "nearest neighbor cells" then you get the so-called (generalized) Voronoi diagram. The Voronoi diagram consists of nodes and edges connecting them. The clearance of a node is the distance to its defining polygon faces.
(Here the polygon even has holes; the principle still works.)
The key observation now is that the center of the maximum inscribed circle touches three faces (vertices or edges) of the polygon, and no other face can be closer. So the center has to lie on a Voronoi node, i.e, the node with the largest clearance.
In the example above the node that marks the center of the maximum inscribed circle touches two edges and a vertex of the polygon.
The medial axis, by the way, is the Voronoi diagram with those Voronoi edges removed that emanate from reflex vertices. Hence, the center of the maximum inscribed circle also lies on the medial axis.
Source: A blog article of mine that deals with generalizations of maximum inscribed circles at some point. There you can find more on Voronoi diagrams and their relation to maximum inscribed circles.
Algorithms & implementations.
You could actually compute the Voronoi diagram. A worst-case O(n log n) algorithm for points and segments is given by Fortune, A sweepline algorithm for Voronoi diagrams, SoCG'86. Held published the software package Vroni with an expected O(n log n) time complexity, which actually computes the maximum inscribed circle, too. And there seems to be an implementation in boost, too.
For simple polygons (i.e., without holes) a time-optimal algorithm that runs in O(n) time is due to Chin et al., Finding the Medial Axis of a Simple Polygon in Linear Time, 1999.
Brute force.
However, as you stated that you are fine with a brute-force algorithm: What about simply trying out all triplets of sites (vertices and edges). For each triplet you find candidate Voronoi nodes, i.e., equidistant loci to the three sites and check whether any other site would intersect the candidate maximum inscribed circle. If there is an intersection you dismiss the candidate. Take the greatest you can find over all triplets.
See chapter 3 in my Master thesis about more details on computing equidistant loci for three sites.
I implemented a piece of python code based on cv2 to get the maximum/largest inscribed circle inside mask/polygon/contours. It supports non-convex/hollow shape.
import cv2
import numpy as np
def get_test_mask():
# Create an image
r = 100
mask = np.zeros((4 * r, 4 * r), dtype=np.uint8)
# Create a sequence of points to make a contour
vert = [None] * 6
vert[0] = (3 * r // 2, int(1.34 * r))
vert[1] = (1 * r, 2 * r)
vert[2] = (3 * r // 2, int(2.866 * r))
vert[3] = (5 * r // 2, int(2.866 * r))
vert[4] = (3 * r, 2 * r)
vert[5] = (5 * r // 2, int(1.34 * r))
# Draw it in mask
for i in range(6):
cv2.line(mask, vert[i], vert[(i + 1) % 6], (255), 63)
return mask
mask = get_test_mask()
"""
Get the maximum/largest inscribed circle inside mask/polygon/contours.
Support non-convex/hollow shape
"""
dist_map = cv2.distanceTransform(mask, cv2.DIST_L2, cv2.DIST_MASK_PRECISE)
_, radius, _, center = cv2.minMaxLoc(dist_map)
result = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
cv2.circle(result, tuple(center), int(radius), (0, 0, 255), 2, cv2.LINE_8, 0)
# minEnclosingCircle directly by cv2
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
center2, radius2 = cv2.minEnclosingCircle(np.concatenate(contours, 0))
cv2.circle(result, (int(center2[0]), int(center2[1])), int(radius2), (0, 255, 0,), 2)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
Red circle is max inscribed circle
Source: https://gist.github.com/DIYer22/f82dc329b27c2766b21bec4a563703cc
I used Straight Skeletons to place an image inside a polygon with three steps:
Find the straight skeleton using the Straight Skeleton algorithm (pic 1)
Base on the straight skeleton, find the largest circle (pic 2)
Draw the image inside that circle (pic 3)
Try it at: https://smartdiagram.com/simple-infographics-3d-charts-2/
An O(n log X) algorithm, where X depends on the precision you want.
Binary search for largest radius R for a circle:
At each iteration, for a given radius r, push each edge E, "inward" by R, to get E'. For each edge E', define half-plane H as the set of all points "inside" the the polygon (using E' as the boundary). Now, compute the intersection of all these half-planes E', which could be done in O(n) time. If the intersection is non-empty, then if you draw a circle with radius r using any point in the intersection as the center, it will be inside the given polygon.

Resources