Is there such an algorithm to sort an array of 3D points in clockwise order?
I'm specifically dealing with right triangle in my case so only 3 points. (for build mesh)
Assume you have two edges connecting your three vertices.
E1 = V2 - V1
E2 = V1 - V3
They span a triangle. You can calculate the triangle's normal N like this:
N = cross(E1, E2)
This tells you which direction the triangle is facing. You can calculate whether the triangle is facing towards, or away from, a certain point of view P by projecting N onto the distance of your triangle from P.
D = V1 - P
d = dot(N, D)
If d is positive, the triangle looks away from P, if it is negative, it faces P.
You are now able to judge for every set of (V1,V2,V3) whether they are sorted correctly or not. If not, just swap V2 and V3 and they will be.
There is one pitfall though. If you are trying to build the hull of a closed mesh, the requirement is that all triangles are facing towards the outside. This can not be modelled by trying to make all triangles face a certain point, because that point would have to be different for each triangle. If the mesh is convex through, you can model it by requiring that all triangles face away from a certain point, which lies inside the convex mesh.
The algorithm for sorting is not difficult. The problem is, what plane that those points lie on. And which side it facing
Only a bunch of points cannot clockwise or counter clockwise by itself. You need a plane and a side to reference those point
edit : Actually what I used to said is a bit inaccurate. What you really need is a position and direction to reference, not a plane
Related
I've got a 3d sphere which I've been able to plot a point on using longitude and latitude thanks to some work of another developer I've found online. I think I understand what its doing.
What I need to do now is rotate my planet so the point is always at the top most point (ie the north pole) but I'm not sure how to do this. I'm probably missing some important fundamentals here so I'm hoping the answer can assist in my future learning.
Here's an image showing what I have - The blue line is a line coming from the longitude and latitude I have plotted and I need to rotate the planet so that line is basically pointing directly upwards.
https://ibb.co/2y24FxS
If anyone is able to advise it'd be very much appreciated.
If I'm not mistaken, Unity uses a coordinate system where the y-axis points up.
If the point on your sphere was in the xy-plane, you'd just have to determine the angle between the radius-vector (starts in the center of the sphere, ends on the point in question) and the y-axis, and than rotate by that amount around the z-axis, so that the radius vector becomes vertical. But your point is at an arbitrary location in 3D space - see the image below. So one way to go about it is to first bring the point to the xy-plane, then continue from there.
Calculate the radius vector, which is just r = x-sphereCenter. Make a copy of it, set y to zero, so that you have (x, 0, z) - which is just the projection of the vector r on the horizontal xz-plane - let's call the copy rXZ.
Determine the signed angle between the x-axis and rXZ (use Vector3.SignedAngle(xAxis, rXZ, yAxis), see docs), and create a rotation matrix M1 that rotates the sphere in the opposite direction around the vertical (negate the angle). This should place your point in the xy-plane.
Now determine the angle between r and the y-axis (Vector3.SignedAngle(r, yAxis, zAxis)), and create a new rotation matrix M2 that rotates by that angle around the zAxis. (I think for this second one, the simpler Vector3.Angle will work as well.)
So, what you want now is to combine the two matrices (by multiplying them) into a single transform (I'm assuming this is a transformation in the local coordinate system of the sphere, where (0, 0, 0) is the sphere's center). If I'm not mistaken, Unity uses column-major matrices, so the multiplication order should be M = M2 * M1 (the rightmost matrix is applied first).
Reorient your globe using M as a local transform, and it should bring your point to the top. You can also create M3 = M1.inverse, and then do M = M3 * M2 * M1, to preserve the original angular offset from the xy-plane.
Check for edge cases, such as r already being vertical (pointing straight up, or straight down).
In the toy example I show, one of the surface normals is clearly incorrectly pointing inwards. I can create a new cube with the normals outward facing as expected, but after processing with Catmull Clarke, there is no guarantee that all normals will remain extant facing.
Since I'm using quadrilaterals by necessity, I know I can fix the face by transposing the vertice order i.e. [a b c d] -> [d c b a] and thus fix the normal. But how do I determine that a given face's normal is pointing the wrong direction?
(not enough rep to embed) https://gyazo.com/e20576e700196a43a2378eb055a71b38
You can check for sign of the dot product between the face normal vector and the vector from the cube's centroid to any point on the face.
Let's say the normal vector of the face [a,b,c,d] is n. Next, compute the centroid of the cube by averaging its 8 vertex coordinates, and let's call it p.
Then, calculate dir = dot(n,(a-p)). If dir > 0, the normal n is pointing outwards from the cube. If dir is negative, you have to flip the normal.
This method will work for the faces of any convex polyhedron. If you're dealing with non-convex polyhedra, you will have to use an approach like the one mentioned here.
I have two independent 3D shapes; one is a square and another is a cone.
Let's assume the cone lies inside the square. How can I find out that the surface of the cone touches the square's surface when I move the cone in any direction?
It will be helpful if anyone can suggest an algorithm to check whether the surface touches another shape.
I am working with MATLAB, but the underlying logic will be appreciated in any language will be appriciated.
https://in.mathworks.com/matlabcentral/answers/367565-findout-surface-to-surface-intersection-between-two-3d-shapes
There is a relatively easy solution, thanks to the fact that the truncated cone is a convex shape, and finding its AABB is not so hard.
First rotate space so that the cube become axis aligned (and the cone in arbitrary position). Then to find the AABB of the base, is suffices to get the maxima an minima of the coordinates, using the parametric equation, C + R cos t + R' sin t, where C is the position vector of the center, and R, R' two orthogonal radii. You find the limit angles by canceling the derivative.
After finding the extrema on the three coordinates, the global bounding box is the one that surrounds these six points plus the apex.
By comparing the AABB to the cube, you can tell what distance remains before a collision, in any direction.
I want to check whether a given point lies inside a polygon.
I have looked into the point in polygon method using ray casting, but I'm unsure of how it will work in cases where the given polygon in disjoint. For example, if the polygon has a "hole" right in the center. How do I use this function in such cases?
I've answered a similar question here, even though it wasn't clear if I understood the OP's question too well. Well , here it is again -
Let's take a very general case .
Polygon with a hole and Intersections
The ray casting (point in polygon) algorithm shoots off a ray counting the intersections with the sides of the POLYGON (Odd intersections = inside, Even = outside).
Hence it accurately gives the correct result regardless of whether you start from inside the disjoint trapezoidal hole or the triangular hole (inner edges?) or even if a part of the polygon is completely seperated and/or self intersecting.
However, in what order do you feed the vertices of the polygon such that all the points are evaluated correctly?
Though this is code specific, if you're using an implementation that is counting every intersection with the sides of the polygon then this approach will work -
- Break the master polygon into polygonal components. eg - trapezoidal hole is a polygonal component.
- Start with (0,0) vertex (doesn't matter where (0,0) actually lies wrt your polygon) followed by the first component's vertices, repeating its first vertex after the last vertex.
- Include another (0,0) vertex.
- Include the next component , repeating its first vertex after the last vertex.
- Repeat the above two steps for each component.
- End with a final (0,0) vertex.
Example -
Let's take the picture to the right, the shell with the hole and apply the above method. The vertices of a singular continuous polygon which is mathematically equivalent to the 2 components (polygon+hole) is -
(outside)-------(inside hole)
0,1,2,3,4,5,1,0,1,2,3,4,1,0
To understand how it works, try drawing this mathematically equivalent polygon on a scratch pad.-
1. Mark all the vertices but don't join them yet.
2. Mark the repeated vertices separately also. Do this by marking them close to the original points, but not on them. (at a distance e, where e->0 (tends to/approaches) ) (to help visualize)
3. Now join all the vertices in the right order (as in the example above)
You will notice that this forms a continuous polygon and only becomes disjoint at the e=0 limit.
You can now send this mathematically equivalent polygon to your ray casting function (and maybe even winding number function?) without any issues.
I have a convex polygon in 3D. For simplicity, let it be a square with vertices, (0,0,0),(1,1,0),(1,1,1),(0,0,1).. I need to arrange these vertices in counter clockwise order. I found a solution here. It is suggested to determine the angle at the center of the polygon and sort them. I am not clear how is that going to work. Does anyone have a solution? I need a solution which is robust and even works when the vertices get very close.
A sample MATLAB code would be much appreciated!
This is actually quite a tedious problem so instead of actually doing it I am just going to explain how I would do it. First find the equation of the plane (you only need to use 3 points for this) and then find your rotation matrix. Then find your vectors in your new rotated space. After that is all said and done find which quadrant your point is in and if n > 1 in a particular quadrant then you must find the angle of each point (theta = arctan(y/x)). Then simply sort each quadrant by their angle (arguably you can just do separation by pi instead of quadrants (sort the points into when the y-component (post-rotation) is greater than zero).
Sorry I don't have time to actually test this but give it a go and feel free to post your code and I can help debug it if you like.
Luckily you have a convex polygon, so you can use the angle trick: find a point in the interior (e.g., find the midpoint of two non-adjacent points), and draw vectors to all the vertices. Choose one vector as a base, calculate the angles to the other vectors and order them. You can calculate the angles using the dot product: A · B = A B cos θ = |A||B| cos θ.
Below are the steps I followed.
The 3D planar polygon can be rotated to 2D plane using the known formulas. Use the one under the section Rotation matrix from axis and angle.
Then as indicated by #Glenn, an internal points needs to be calculated to find the angles. I take that internal point as the mean of the vertex locations.
Using the x-axis as the reference axis, the angle, on a 0 to 2pi scale, for each vertex can be calculated using atan2 function as explained here.
The non-negative angle measured counterclockwise from vector a to vector b, in the range [0,2pi], if a = [x1,y1] and b = [x2,y2], is given by:
angle = mod(atan2(y2-y1,x2-x1),2*pi);
Finally, sort the angles, [~,XI] = sort(angle);.
It's a long time since I used this, so I might be wrong, but I believe the command convhull does what you need - it returns the convex hull of a set of points (which, since you say your points are a convex set, should be the set of points themselves), arranged in counter-clockwise order.
Note that MathWorks recently delivered a new class DelaunayTri which is intended to superseded the functionality of convhull and other older computational geometry stuff. I believe it's more accurate, especially when the points get very close together. However I haven't tried it.
Hope that helps!
So here's another answer if you want to use convhull. Easily project your polygon into an axes plane by setting one coordinate zero. For example, in (0,0,0),(1,1,0),(1,1,1),(0,0,1) set y=0 to get (0,0),(1,0),(1,1),(0,1). Now your problem is 2D.
You might have to do some work to pick the right coordinate if your polygon's plane is orthogonal to some axis, if it is, pick that axis. The criterion is to make sure that your projected points don't end up on a line.