From 7f2aa95f6732fe32b91597394675ec6cc0197725 Mon Sep 17 00:00:00 2001 From: Michael Dawson-Haggerty Date: Fri, 19 Jul 2024 12:38:28 -0400 Subject: [PATCH] add an early exit for polygon creation --- trimesh/base.py | 2 +- trimesh/convex.py | 2 +- trimesh/path/exchange/misc.py | 1 + trimesh/path/polygons.py | 17 ++++++++++++++--- trimesh/triangles.py | 27 ++++++++++++++++----------- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/trimesh/base.py b/trimesh/base.py index aafec90e6..082007317 100644 --- a/trimesh/base.py +++ b/trimesh/base.py @@ -2667,7 +2667,7 @@ def area_faces(self) -> NDArray[float64]: area_faces : (n, ) float Area of each face """ - return triangles.area(crosses=self.triangles_cross, sum=False) + return triangles.area(crosses=self.triangles_cross) @caching.cache_decorator def mass_properties(self) -> MassProperties: diff --git a/trimesh/convex.py b/trimesh/convex.py index 396589e73..3e507b4cb 100644 --- a/trimesh/convex.py +++ b/trimesh/convex.py @@ -91,7 +91,7 @@ def convex_hull(obj, qhull_options="QbB Pp Qt", repair=True): crosses = crosses[valid] # each triangle area and mean center - triangles_area = triangles.area(crosses=crosses, sum=False) + triangles_area = triangles.area(crosses=crosses) triangles_center = vertices[faces].mean(axis=1) # since the convex hull is (hopefully) convex, the vector from diff --git a/trimesh/path/exchange/misc.py b/trimesh/path/exchange/misc.py index d035244d4..de76fdcd0 100644 --- a/trimesh/path/exchange/misc.py +++ b/trimesh/path/exchange/misc.py @@ -111,6 +111,7 @@ def polygon_to_path(polygon): "entities": entities, "vertices": np.vstack(vertices) if len(vertices) > 0 else vertices, } + return kwargs diff --git a/trimesh/path/polygons.py b/trimesh/path/polygons.py index dda5af7a6..c2d8edf42 100644 --- a/trimesh/path/polygons.py +++ b/trimesh/path/polygons.py @@ -50,6 +50,14 @@ def enclosure_tree(polygons): contained by another polygon """ + # nodes are indexes in polygons + contains = nx.DiGraph() + + if len(polygons) == 1: + # add an early exit for only a single polygon + contains.add_node(0) + return np.array([0], dtype=np.int64), contains + # get the bounds for every valid polygon bounds = { k: v @@ -59,8 +67,6 @@ def enclosure_tree(polygons): if len(v) == 4 } - # nodes are indexes in polygons - contains = nx.DiGraph() # make sure we don't have orphaned polygon contains.add_nodes_from(bounds.keys()) @@ -551,13 +557,18 @@ def paths_to_polygons(paths, scale=None): # non-zero area continue try: - polygons[i] = repair_invalid(Polygon(path), scale) + polygon = Polygon(path) + if polygon.is_valid: + polygons[i] = polygon + else: + polygons[i] = repair_invalid(polygon, scale) except ValueError: # raised if a polygon is unrecoverable continue except BaseException: log.error("unrecoverable polygon", exc_info=True) polygons = np.array(polygons) + return polygons diff --git a/trimesh/triangles.py b/trimesh/triangles.py index 7af0513b6..b6efa3285 100644 --- a/trimesh/triangles.py +++ b/trimesh/triangles.py @@ -16,7 +16,7 @@ from .util import diagonal_dot, unitize -def cross(triangles): +def cross(triangles: NDArray) -> NDArray: """ Returns the cross product of two edges from input triangles @@ -30,13 +30,19 @@ def cross(triangles): crosses : (n, 3) float Cross product of two edge vectors """ - vectors = np.diff(triangles, axis=1) - crosses = np.cross(vectors[:, 0], vectors[:, 1]) + vectors = triangles[:, 1:, :] - triangles[:, :2, :] + if triangles.shape[2] == 3: + return np.cross(vectors[:, 0], vectors[:, 1]) + elif triangles.shape[2] == 2: + a = vectors[:, 0] + b = vectors[:, 1] + # numpy 2.0 deprecated 2D cross productes + return a[:, 0] * b[:, 1] - a[:, 1] * b[:, 0] - return crosses + raise ValueError(triangles.shape) -def area(triangles=None, crosses=None, sum=False): +def area(triangles=None, crosses=None): """ Calculates the sum area of input triangles @@ -55,11 +61,8 @@ def area(triangles=None, crosses=None, sum=False): Individual or summed area depending on `sum` argument """ if crosses is None: - crosses = cross(triangles) - areas = np.sqrt((crosses**2).sum(axis=1)) / 2.0 - if sum: - return areas.sum() - return areas + crosses = cross(np.asanyarray(triangles, dtype=np.float64)) + return np.sqrt((crosses**2).sum(axis=1)) / 2.0 def normals(triangles=None, crosses=None): @@ -80,6 +83,8 @@ def normals(triangles=None, crosses=None): valid : (n,) bool Was the face nonzero area or not """ + if triangles is not None and triangles.shape[-1] == 2: + return np.tile([0.0, 0.0, 1.0], (triangles.shape[0], 1)) if crosses is None: crosses = cross(triangles) # unitize the cross product vectors @@ -435,7 +440,7 @@ def extents(triangles, areas=None): raise ValueError("Triangles must be (n, 3, 3)!") if areas is None: - areas = area(triangles=triangles, sum=False) + areas = area(triangles=triangles) # the edge vectors which define the triangle a = triangles[:, 1] - triangles[:, 0]