Repository: mikedh/trimesh
Pull request #1884 (2023-03-30 16:40:13)

Release: PLY + Widget Fix


Patch:
diff --git a/examples/widget.py b/examples/widget.py
index 99889db5..2e4ddd8b 100644
--- a/examples/widget.py
+++ b/examples/widget.py
@@ -1,5 +1,5 @@
 """
-Glooey widget example. Only runs with python>=3.6 (because of Glooey).
+Glooey widget example. Only runs with python>=3.6 and pyglet<=1.5.27 (because of Glooey).
 """
 
 import io
diff --git a/pyproject.toml b/pyproject.toml
index f63b293a..d9914659 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,8 @@ select = ["E", "F", # the default rules
           #"I001",   # isort
           #"D"]      # pydocstyle
 ignore = ["B905", # `zip()` without an explicit `strict=`
-          "B904"] # `raise ... from err` seems a bit silly
+          "B904", # `raise ... from err` seems a bit silly
+	  "B018"] # useless expression ideally ignore only on `tests`
 line-length = 90
 
 [tool.ruff.pydocstyle]
diff --git a/trimesh/exchange/gltf.py b/trimesh/exchange/gltf.py
index 4dc36531..5e8d5ad4 100644
--- a/trimesh/exchange/gltf.py
+++ b/trimesh/exchange/gltf.py
@@ -887,13 +887,21 @@ def _append_mesh(mesh,
         # prefixed with an underscore
         if not key.startswith("_"):
             key = "_" + key
+
+        # GLTF has no floating point type larger than 32 bits so clip
+        # any float64 or larger to float32
+        if attrib.dtype.kind == 'f' and attrib.dtype.itemsize > 4:
+            data = attrib.astype(np.float32)
+        else:
+            data = attrib
+
         # store custom vertex attributes
         current["primitives"][0][
             "attributes"][key] = _data_append(
                 acc=tree['accessors'],
                 buff=buffer_items,
-                blob=_build_accessor(attrib),
-                data=attrib)
+                blob=_build_accessor(data),
+                data=data)
 
     tree["meshes"].append(current)
 
@@ -959,7 +967,7 @@ def _build_accessor(array):
         data_type = "MAT%d" % shape[2]
 
     # get the array data type as a str stripping off endian
-    lookup = array.dtype.str[-2:]
+    lookup = array.dtype.str.lstrip('<>')
 
     if lookup == 'u4':
         # spec: UNSIGNED_INT is only allowed when the accessor
diff --git a/trimesh/exchange/ply.py b/trimesh/exchange/ply.py
index 8b327acd..b1e36660 100644
--- a/trimesh/exchange/ply.py
+++ b/trimesh/exchange/ply.py
@@ -263,6 +263,10 @@ def export_ply(mesh,
     # if we want to include mesh attributes in the export
     if include_attributes:
         if hasattr(mesh, 'vertex_attributes'):
+            # make sure to export texture coordinates as well
+            if hasattr(mesh, "visual") and hasattr(mesh.visual, "uv"):
+                mesh.vertex_attributes["s"] = mesh.visual.uv[:, 0]
+                mesh.vertex_attributes["t"] = mesh.visual.uv[:, 1]
             _assert_attributes_valid(mesh.vertex_attributes)
         if hasattr(mesh, 'face_attributes'):
             _assert_attributes_valid(mesh.face_attributes)
diff --git a/trimesh/version.py b/trimesh/version.py
index 01bc27e7..88a361e4 100644
--- a/trimesh/version.py
+++ b/trimesh/version.py
@@ -1,4 +1,4 @@
-__version__ = '3.21.2'
+__version__ = '3.21.3'
 
 if __name__ == '__main__':
     # print version if run directly i.e. in a CI script
diff --git a/trimesh/viewer/widget.py b/trimesh/viewer/widget.py
index 0efc184f..e37d9cd1 100644
--- a/trimesh/viewer/widget.py
+++ b/trimesh/viewer/widget.py
@@ -176,7 +176,7 @@ class SceneWidget(glooey.Widget):
                 size=self.scene.camera.resolution,
                 scale=self.scene.scale,
                 target=self.scene.centroid)}
-        self.scene.camera_transform[...] = self.view['ball'].pose
+        self.scene.camera_transform = self.view['ball'].pose
 
     def do_claim(self):
         return 0, 0


Test patch:
diff --git a/tests/test_ply.py b/tests/test_ply.py
index ce926089..1f36505c 100644
--- a/tests/test_ply.py
+++ b/tests/test_ply.py
@@ -200,6 +200,23 @@ class PlyTest(g.unittest.TestCase):
             assert hasattr(m, 'visual') and hasattr(m.visual, 'uv')
             assert m.visual.uv.shape[0] == m.vertices.shape[0]
 
+    def test_uv_export(self):
+        m = g.get_mesh("fuze.ply")
+        assert hasattr(m, 'visual') and hasattr(m.visual, 'uv')
+        assert m.visual.uv.shape[0] == m.vertices.shape[0]
+
+        # create empty file to export to
+
+        with g.TemporaryDirectory() as D:
+            name = g.os.path.join(D, 'file.ply')
+
+            # export should contain the uv data
+            m.export(name)
+            m2 = g.trimesh.load(name)
+
+        assert hasattr(m2, 'visual') and hasattr(m2.visual, 'uv')
+        assert g.np.allclose(m.visual.uv, m2.visual.uv)
+
     def test_fix_texture(self):
         # test loading of face indices when uv-coordinates are also contained
         m1 = g.get_mesh('plane.ply')