Repository: mikedh/trimesh
Pull request #2416 (2025-06-10 23:55:19)

Updated integral_mean_curvature function
The integral_mean_curvature (IMC) function did not agree with the analytical value for non-convex objects such as a torus and an annulus. The angle in the function needs to have an appropriate sign to detect concave angles. This code corrects that error and also adds a test function for IMC to test for non-convex objects. 




Patch:
diff --git a/README.md b/README.md
index 2fd131fd..0ef50014 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 [![Github Actions](https://github.com/mikedh/trimesh/workflows/Release%20Trimesh/badge.svg)](https://github.com/mikedh/trimesh/actions) [![codecov](https://codecov.io/gh/mikedh/trimesh/branch/main/graph/badge.svg?token=4PVRQXyl2h)](https://codecov.io/gh/mikedh/trimesh)  [![Docker Image Version (latest by date)](https://img.shields.io/docker/v/trimesh/trimesh?label=docker&sort=semver)](https://hub.docker.com/r/trimesh/trimesh/tags) [![PyPI version](https://badge.fury.io/py/trimesh.svg)](https://badge.fury.io/py/trimesh)
 
 
-Trimesh is a pure Python 3.8+ library for loading and using [triangular meshes](https://en.wikipedia.org/wiki/Triangle_mesh) with an emphasis on watertight surfaces. The goal of the library is to provide a full featured and well tested Trimesh object which allows for easy manipulation and analysis, in the style of the Polygon object in the [Shapely library](https://github.com/Toblerity/Shapely).
+Trimesh is a pure Python 3.7+ library for loading and using [triangular meshes](https://en.wikipedia.org/wiki/Triangle_mesh) with an emphasis on watertight surfaces. The goal of the library is to provide a full featured and well tested Trimesh object which allows for easy manipulation and analysis, in the style of the Polygon object in the [Shapely library](https://github.com/Toblerity/Shapely).
 
 The API is mostly stable, but this should not be relied on and is not guaranteed: install a specific version if you plan on deploying something using trimesh.
 
diff --git a/trimesh/base.py b/trimesh/base.py
index b1be6f0d..0beb4ada 100644
--- a/trimesh/base.py
+++ b/trimesh/base.py
@@ -1523,7 +1523,11 @@ class Trimesh(Geometry3D):
         edges_length = np.linalg.norm(
             np.subtract(*self.vertices[self.face_adjacency_edges.T]), axis=1
         )
-        return (self.face_adjacency_angles * edges_length).sum() * 0.5
+        # assign signs based on convex adjacency of face pairs
+        signs = np.array([-1.0, 1.0])[self.face_adjacency_convex.astype(np.int64)]
+        # adjust face adjacency angles with signs to reflect orientation
+        angles = self.face_adjacency_angles * signs
+        return (angles * edges_length).sum() * 0.5
 
     @cache_decorator
     def vertex_adjacency_graph(self) -> Graph:
diff --git a/trimesh/creation.py b/trimesh/creation.py
index 49dd8d4a..9f6a61ec 100644
--- a/trimesh/creation.py
+++ b/trimesh/creation.py
@@ -1021,9 +1021,9 @@ def cone(
     Parameters
     ----------
     radius : float
-      The radius of the cone at the widest part.
+      The radius of the cylinder
     height : float
-      The height of the cone.
+      The height of the cylinder
     sections : int or None
       How many pie wedges per revolution
     transform : (4, 4) float or None
diff --git a/trimesh/path/exchange/misc.py b/trimesh/path/exchange/misc.py
index 7da49adf..d4bb3dd0 100644
--- a/trimesh/path/exchange/misc.py
+++ b/trimesh/path/exchange/misc.py
@@ -29,13 +29,8 @@ def dict_to_path(as_dict):
     entities = [None] * len(as_dict["entities"])
     # run constructor for dict kwargs
     for entity_index, entity in enumerate(as_dict["entities"]):
-      if entity["type"] == 'Line':
         entities[entity_index] = loaders[entity["type"]](
-          points=entity["points"]
-        )
-      else:
-        entities[entity_index] = loaders[entity["type"]](
-          points=entity["points"], closed=entity["closed"]
+            points=entity["points"], closed=entity["closed"]
         )
     result["entities"] = entities
 


Test patch:
diff --git a/tests/test_integralmeancurvature.py b/tests/test_integralmeancurvature.py
index 4375cde5..83733d84 100644
--- a/tests/test_integralmeancurvature.py
+++ b/tests/test_integralmeancurvature.py
@@ -38,6 +38,19 @@ class IntegralMeanCurvatureTest(g.unittest.TestCase):
             ref = 4 * extent.sum() * g.np.pi / 2 * 0.5
             assert g.np.isclose(IMC, ref, rtol=tol)
 
+    def test_IMCtorus(self):
+        # how close do we need to be - relative tolerance
+        tol = 1e-2
+        r = 1.1
+        for aspect_ratio in [1.1, 2.1, 4.0, 10.0]:
+            R = r * aspect_ratio
+            m = g.trimesh.creation.torus(
+                major_radius=R, minor_radius=r, major_sections=100, minor_sections=100
+            )
+            IMC = m.integral_mean_curvature
+            ref = 2 * (g.np.pi**2) * R
+            assert g.np.isclose(IMC, ref, rtol=tol)
+
 
 if __name__ == "__main__":
     g.trimesh.util.attach_to_log()