Repository: mikedh/trimesh
Pull request #1604 (2022-06-10 19:38:59)

Add transform property to primitives
Addresses issue #1603 


Patch:
diff --git a/trimesh/primitives.py b/trimesh/primitives.py
index 6bf6a58a..36baf465 100644
--- a/trimesh/primitives.py
+++ b/trimesh/primitives.py
@@ -10,6 +10,7 @@ and then use trimesh operations on them at any point.
 import numpy as np
 import pprint
 import copy
+import abc
 
 from . import util
 from . import sample
@@ -87,6 +88,18 @@ class _Primitive(Trimesh):
         if values is not None:
             log.warning('Primitive face normals are immutable! Not setting!')
 
+    @abc.abstractproperty
+    def transform(self):
+        """
+        The transform of the Primitive object.
+
+        Returns
+        -------------
+        transform : (4, 4) float
+          Homogeneous transformation matrix
+        """
+        pass
+
     def copy(self, **kwargs):
         """
         Return a copy of the Primitive object.
@@ -243,6 +256,10 @@ class Cylinder(_Primitive):
                                               defaults,
                                               kwargs)
 
+    @property
+    def transform(self):
+        return self.primitive.transform
+
     @caching.cache_decorator
     def volume(self):
         """
@@ -364,6 +381,10 @@ class Capsule(_Primitive):
                                               defaults,
                                               kwargs)
 
+    @property
+    def transform(self):
+        return self.primitive.transform
+
     @caching.cache_decorator
     def direction(self):
         """
@@ -428,6 +449,20 @@ class Sphere(_Primitive):
                         np.append(self.primitive.center, 1.0))[:3]
         self.primitive.center = center
 
+    @property
+    def transform(self):
+        transform = np.eye(4)
+        transform[:3, 3] = self.primitive.center
+        return transform
+
+    @property
+    def center(self):
+        return self.primitive.center
+
+    @center.setter
+    def center(self, value):
+        self.primitive.center = value
+
     @property
     def bounds(self):
         # no docstring so will inherit Trimesh docstring
@@ -514,6 +549,10 @@ class Box(_Primitive):
         self.primitive = _PrimitiveAttributes(
             self, defaults, kwargs)
 
+    @property
+    def transform(self):
+        return self.primitive.transform
+
     def sample_volume(self, count):
         """
         Return random samples from inside the volume of the box.
@@ -717,6 +756,10 @@ class Extrusion(_Primitive):
         """
         return self.primitive.transform[:3, 3]
 
+    @property
+    def transform(self):
+        return self.primitive.transform
+
     @caching.cache_decorator
     def bounding_box_oriented(self):
         # no docstring for inheritance


Test patch:
diff --git a/tests/test_primitives.py b/tests/test_primitives.py
index 8edc5f4f..d52cc6fd 100644
--- a/tests/test_primitives.py
+++ b/tests/test_primitives.py
@@ -159,6 +159,27 @@ class PrimitiveTest(g.unittest.TestCase):
         # should contain line segment
         assert b.contains(c.segment).all()
 
+    def test_transform_attribute(self):
+        for primitive in self.primitives:
+            assert hasattr(primitive, 'transform')
+
+            assert g.trimesh.util.is_shape(primitive.transform,
+                                           (4, 4))
+
+            if hasattr(primitive.primitive, 'center'):
+                assert g.np.allclose(primitive.primitive.center,
+                                     primitive.transform[:3, 3])
+
+    def test_sphere_center(self):
+        s = g.trimesh.primitives.Sphere(center=[0, 0, 100], radius=10.0, subdivisions=5)
+        assert g.np.allclose(s.center, [0, 0, 100])
+
+        s.center = [1, 1, 1]
+        assert g.np.allclose(s.center, [1, 1, 1])
+
+        s.center[:2] = [0, 3]
+        assert g.np.allclose(s.center, [0, 3, 1])
+
 
 if __name__ == '__main__':
     g.trimesh.util.attach_to_log()