How are triangle normals specified within a mesh

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
mcaroba
Posts: 2
Joined: Sun Dec 04, 2022 12:36 pm
Contact:

How are triangle normals specified within a mesh

Post by mcaroba »

I am using Python scripting to define a mesh from a set of triangles, where the triangles are given by the XYZ coordinates of their 3 vertices. These triangles correspond to the outer contour of a roughly spherical nanoparticle (I work in atomistic modeling). When I pass the list of tirangles to a mesh object, I get some of the triangles are oriented the way I want (the normal pointing outward from the center of the nanoparticle) in gray color, and some show in black, which I understand to mean the surface is pointing inward. Intuitively, I assumed the normal is determined by the order in which the vertices are defined. So I wrote a script that calculates the cross product of (v12, v13) (where 1, 2, 3 are the vertices) and aligns the normal defined in this way with the vector pointing from the center of mass of the nanoparticle to the center of mass of the triangle in question. Indeed, I can get the triangles to be aligned or "antialigned" using this method, i.e., I can make all gray triangles black and viceversa, but I always get a random distribution of gray and black triangles, whereas I would expect all of them to be aligned or antialigned. So clearly I am missing something, but can't find on the online documentation how the order in which the triangle vertices are defined is linked to the definition of their normal.

The code I wrote is something like this:

Code: Select all

# This function changes the order in which the points are defined in the triangle to make sure
# its normal points outside of the nanoparticle. tri = [i, j, k] gives the indices of the vertices in
# a master list, and xyz gives the XYZ coordinates of these 3 vertices. Origin is the center of
# mass of the nanoparticle.
def tri_orient(tri, xyz, origin):
    xyz = np.array(xyz)
    origin = np.array(origin)
    cm = (xyz[0] + xyz[1] + xyz[2])/3.
    dir = cm - origin
    v1 = xyz[1] - xyz[0]
    v2 = xyz[2] - xyz[0]
    if np.dot(np.cross(v1,v2), dir) >= 0:
        return tri
    else:
        return [tri[0], tri[2], tri[1]]
        
# This loop goes over all triangles and orients them with the function above. tris is a list of index
# triplets and all_xyz is a numpy array that contains the XYZ coordinates of all the vertices.
triangles = []
for tri in tris:
    tri = tri_orient(tri, all_xyz[tri], origin)
    triangles.append(all_xyz[tri])

meshObject = Mesh.Mesh(triangles)
I must be on to something, in the sense that the order of definition of the vertices must be linked to the normal somehow (if I change the condition from >= 0 to < 0 then all the triangles change color). So my question is how I can ensure that the normal that FreeCAD sees is the same normal I *think* I'm setting up. Is this only determined by the vertices or is there another bit of missing information I need to provide to Mesh?

I'm using FreeCAD 0.18.4 on Ubuntu 20.04.
heda
Veteran
Posts: 1348
Joined: Sat Dec 12, 2015 5:49 pm

Re: How are triangle normals specified within a mesh

Post by heda »

suppose it is right-hand rule,
if you order the points counterclockwise from your viewpoint, the normal will be pointing towards the viewpoint

anyhow, one does not necessarily have to bother about it too much, hopefully

Code: Select all

mesh.harmonizeNormals( )
will fix it, just one more line in your script...
mcaroba
Posts: 2
Joined: Sun Dec 04, 2022 12:36 pm
Contact:

Re: How are triangle normals specified within a mesh

Post by mcaroba »

~~The cross product code I wrote exploits the right hand rule, but it doesn't do the job. The harmonize normals function doesn't fix this problem either.~~

Edit:

I cleaned up my code and I managed to get this to work, and also made a shape, then a solid out of this mesh. It must have been some issue adapting from the Fem convention (which starts with 1 instead of 0) to regular Python convention, which Mesh uses. So my code above can indeed be used to generate triangles with the right normal. harmonizeNormals did not work in my full problem (it mush have been too complicated for it) but could handle smaller problems with a handful of triangles only.
wmayer
Founder
Posts: 20243
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: How are triangle normals specified within a mesh

Post by wmayer »

So I wrote a script that calculates the cross product of (v12, v13) (where 1, 2, 3 are the vertices) and aligns the normal defined in this way with the vector pointing from the center of mass of the nanoparticle to the center of mass of the triangle in question.
But keep in mind that this method only works for convex objects. As soon as you have a non-convex object the surface normal could point to the center of the particle and then this method will fail. Since you work with roughly spherical objects this should be unproblematic.
harmonizeNormals did not work in my full problem (it mush have been too complicated for it) but could handle smaller problems with a handful of triangles only.
This mainly depends on your mesh. If it's just a collection of individual and unconnected triangles (i.e. a not 2-manifold mesh) then harmonizeNormal() won't help. As long as your mesh is 2-manifold and doesn't have any other defect then the algorithm works very reliably.
Post Reply