Browse Source

Handle Trimesh returning scene of objects

These objects are added as a group node, if they are plural.

Contributes to issue CURA-6739.
Ghostkeeper 5 years ago
parent
commit
1f534db508
1 changed files with 43 additions and 15 deletions
  1. 43 15
      plugins/TrimeshReader/TrimeshReader.py

+ 43 - 15
plugins/TrimeshReader/TrimeshReader.py

@@ -3,6 +3,7 @@
 
 # The _toMeshData function is taken from the AMFReader class which was built by fieldOfView.
 
+from typing import List, Union, TYPE_CHECKING
 import numpy  # To create the mesh data.
 import os.path  # To create the mesh name for the resulting mesh.
 import trimesh  # To load the files into a Trimesh.
@@ -10,12 +11,17 @@ import trimesh  # To load the files into a Trimesh.
 from UM.Mesh.MeshData import MeshData, calculateNormalsFromIndexedVertices  # To construct meshes from the Trimesh data.
 from UM.Mesh.MeshReader import MeshReader  # The plug-in type we're extending.
 from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType  # To add file types that we can open.
+from UM.Scene.GroupDecorator import GroupDecorator  # Added to the parent node if we load multiple nodes at once.
 
 from cura.CuraApplication import CuraApplication
 from cura.Scene.BuildPlateDecorator import BuildPlateDecorator  # Added to the resulting scene node.
+from cura.Scene.ConvexHullDecorator import ConvexHullDecorator  # Added to group nodes if we load multiple nodes at once.
 from cura.Scene.CuraSceneNode import CuraSceneNode  # To create a node in the scene after reading the file.
 from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator  # Added to the resulting scene node.
 
+if TYPE_CHECKING:
+    from UM.Scene.SceneNode import SceneNode
+
 ##  Class that leverages Trimesh to import files.
 class TrimeshReader(MeshReader):
     def __init__(self) -> None:
@@ -34,21 +40,43 @@ class TrimeshReader(MeshReader):
     #   \param file_name The file path. This is assumed to be one of the file
     #   types that Trimesh can read. It will not be checked again.
     #   \return A scene node that contains the file's contents.
-    def _read(self, file_name: str) -> CuraSceneNode:
-        mesh = trimesh.load(file_name)
-        mesh.merge_vertices()
-        mesh.remove_unreferenced_vertices()
-        mesh.fix_normals()
-        mesh_data = self._toMeshData(mesh)
-
-        file_base_name = os.path.basename(file_name)
-        new_node = CuraSceneNode()
-        new_node.setMeshData(mesh_data)
-        new_node.setSelectable(True)
-        new_node.setName(file_base_name)
-        new_node.addDecorator(BuildPlateDecorator(CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate))
-        new_node.addDecorator(SliceableObjectDecorator())
-        return new_node
+    def _read(self, file_name: str) -> Union["SceneNode", List["SceneNode"]]:
+        mesh_or_scene = trimesh.load(file_name)
+        meshes = []
+        if isinstance(mesh_or_scene, trimesh.Trimesh):
+            meshes = [mesh_or_scene]
+        elif isinstance(mesh_or_scene, trimesh.Scene):
+            meshes = [mesh for mesh in mesh_or_scene.geometry.values()]
+
+        active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
+        nodes = []
+        for mesh in meshes:
+            if not isinstance(mesh, trimesh.Trimesh):  # Trimesh can also receive point clouds, 2D paths, 3D paths or metadata. Skip those.
+                continue
+            mesh.merge_vertices()
+            mesh.remove_unreferenced_vertices()
+            mesh.fix_normals()
+            mesh_data = self._toMeshData(mesh)
+
+            file_base_name = os.path.basename(file_name)
+            new_node = CuraSceneNode()
+            new_node.setMeshData(mesh_data)
+            new_node.setSelectable(True)
+            new_node.setName(file_base_name if len(meshes) == 1 else "{file_base_name} {counter}".format(file_base_name = file_base_name, counter = str(len(nodes) + 1)))
+            new_node.addDecorator(BuildPlateDecorator(active_build_plate))
+            new_node.addDecorator(SliceableObjectDecorator())
+            nodes.append(new_node)
+
+        if len(nodes) == 1:
+            return nodes[0]
+        # Add all nodes to a group so they stay together.
+        group_node = CuraSceneNode()
+        group_node.addDecorator(GroupDecorator())
+        group_node.addDecorator(ConvexHullDecorator())
+        group_node.addDecorator(BuildPlateDecorator(active_build_plate))
+        for node in nodes:
+            node.setParent(group_node)
+        return group_node
 
     ##  Converts a Trimesh to Uranium's MeshData.
     #   \param tri_node A Trimesh containing the contents of a file that was