Skip to content

Commit ec7d233

Browse files
committed
add face ordering for comparison
1 parent e64fc9d commit ec7d233

3 files changed

Lines changed: 84 additions & 8 deletions

File tree

trimesh/base.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ def edges_unique(self):
809809
# edges_unique will be added automatically by the decorator
810810
# additional terms generated need to be added to the cache manually
811811
self._cache['edges_unique_idx'] = unique
812-
self._cache['edges_unique_inv'] = inverse
812+
self._cache['edges_unique_inverse'] = inverse
813813
return edges_unique
814814

815815
@caching.cache_decorator
@@ -826,6 +826,23 @@ def edges_unique_length(self):
826826
length = np.linalg.norm(vector, axis=1)
827827
return length
828828

829+
@caching.cache_decorator
830+
def edges_unique_inverse(self):
831+
"""
832+
Return the inverse required to reproduce
833+
self.edges_sorted from self.edges_unique.
834+
835+
Useful for referencing edge properties:
836+
mesh.edges_unique[mesh.edges_unique_inverse] == m.edges_sorted
837+
838+
Returns
839+
----------
840+
inverse : (len(self.edges),) int
841+
Indexes of self.edges_unique
842+
"""
843+
populate = self.edges_unique
844+
return self._cache['edges_unique_inverse']
845+
829846
@caching.cache_decorator
830847
def edges_sorted(self):
831848
"""

trimesh/comparison.py

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
from . import util
1111

12+
from .constants import tol
13+
1214
# how many significant figures to use for each field of the identifier
1315
id_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

101107
def 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

trimesh/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '2.35.15'
1+
__version__ = '2.35.16'

0 commit comments

Comments
 (0)