99
1010from . import util
1111
12+ from .constants import tol
13+
1214# how many significant figures to use for each field of the identifier
1315id_sigfig = np .array ([5 , # area
1416 10 , # euler number
@@ -26,11 +28,13 @@ def identifier_simple(mesh):
2628
2729 Parameters
2830 ----------
29- mesh: Trimesh object
31+ mesh : Trimesh object
32+ Source geometry
3033
3134 Returns
3235 ----------
33- identifier: (6,) float, identifying values of the mesh
36+ identifier : (6,) float
37+ Identifying values of the mesh
3438 """
3539 # verify the cache once
3640 mesh ._cache .verify ()
@@ -48,9 +52,11 @@ def identifier_simple(mesh):
4852 # note that we're going to try to make all parameters relative
4953 # to area so other values don't get blown up at weird scales
5054 identifier [0 ] = mesh_area
55+
5156 # topological constant and the only thing we can really
5257 # trust in this fallen world
5358 identifier [1 ] = mesh .euler_number
59+
5460 # if we have a watertight mesh include volume and inertia
5561 if mesh .is_volume :
5662 # side length of a cube ratio
@@ -100,16 +106,20 @@ def identifier_simple(mesh):
100106
101107def identifier_hash (identifier , sigfig = None ):
102108 """
103- Hash an identifier array to a specified number of significant figures.
109+ Hash an identifier array to a specified number of
110+ significant figures.
104111
105112 Parameters
106113 ----------
107- identifier: (n,) float
108- sigfig: (n,) int
114+ identifier : (n,) float
115+ Vector of properties
116+ sigfig : (n,) int
117+ Number of sigfigs per property
109118
110119 Returns
111120 ----------
112- md5: str, MD5 hash of identifier
121+ md5 : str
122+ MD5 hash of identifier
113123 """
114124 if sigfig is None :
115125 sigfig = id_sigfig
@@ -122,3 +132,52 @@ def identifier_hash(identifier, sigfig=None):
122132 hashable = (as_int * (10 ** multiplier )).astype (np .int64 )
123133 md5 = util .md5_object (hashable )
124134 return md5
135+
136+
137+ def face_ordering (mesh ):
138+ """
139+ Triangles with three different length sides are
140+ ordered in two ways:
141+ [small edge, medium edge, large edge] (SML)
142+ [small edge, large edge, medium edge] (SLM)
143+
144+ This function returns [-1, 0, 1], depending on whether
145+ the triangle is SML or SLM, and 0 if M == L.
146+
147+ The reason this is useful as it as a rare property that is
148+ invarient to translation and rotation but changes when a
149+ mesh is reflected or inverted. It is NOT invarient to
150+ different tesselations of the same surface.
151+
152+ Parameters
153+ -------------
154+ mesh : trimesh.Trimesh
155+ Source geometry to calculate ordering on
156+
157+ Returns
158+ --------------
159+ order : (len(mesh.faces), ) int
160+ Is each face SML (-1), SLM (+1), or M==L (0)
161+ """
162+
163+ # the length of each edge in faces
164+ norms = mesh .edges_unique_length [
165+ mesh .edges_unique_inverse ].reshape ((- 1 , 3 ))
166+
167+ # the per- row index of the shortest edge
168+ small = norms .argmin (axis = 1 )
169+
170+ # the ordered index for the medium and large edge norm
171+ # aranged to reference flattened norms for indexing
172+ MLidx = np .column_stack ((small + 1 , small + 2 )) % 3
173+ MLidx += (np .arange (len (small )) * 3 ).reshape ((- 1 , 1 ))
174+
175+ # subtract the two largest edge lengths from each other
176+ diff = np .subtract (* norms .reshape (- 1 )[MLidx .T ])
177+
178+ # mark by sign but keep zero values zero
179+ order = np .zeros (len (norms ), dtype = np .int64 )
180+ order [diff < tol .merge ] = - 1
181+ order [diff > tol .merge ] = 1
182+
183+ return order
0 commit comments