def _read(self, file_name: str) -> Union["SceneNode", List["SceneNode"]]: """Reads a file using Trimesh. :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. """ # CURA-6739 # GLTF files are essentially JSON files. If you directly give a file name to trimesh.load(), it will # try to figure out the format, but for GLTF, it loads it as a binary file with flags "rb", and the json.load() # doesn't like it. For some reason, this seems to happen with 3.5.7, but not 3.7.1. Below is a workaround to # pass a file object that has been opened with "r" instead "rb" to load a GLTF file. if file_name.lower().endswith(".gltf"): mesh_or_scene = trimesh.load(open(file_name, "r", encoding="utf-8"), file_type="gltf") else: mesh_or_scene = trimesh.load(file_name) meshes = [] # type: List[Union[trimesh.Trimesh, trimesh.Scene, Any]] 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 = [] # type: List[SceneNode] 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_name) 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
def _createEraserMesh(self, parent: CuraSceneNode, position: Vector): node = CuraSceneNode() node.setName("Eraser") node.setSelectable(True) mesh = MeshBuilder() mesh.addCube(10,10,10) node.setMeshData(mesh.build()) active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() definition = stack.getSettingDefinition("anti_overhang_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) op = AddSceneNodeOperation(node, parent) op.push() node.setPosition(position, CuraSceneNode.TransformSpace.World) Application.getInstance().getController().getScene().sceneChanged.emit(node)
def _addShape(self, mesh_data: MeshData) -> None: application = CuraApplication.getInstance() global_stack = application.getGlobalContainerStack() if not global_stack: return node = CuraSceneNode() node.setMeshData(mesh_data) node.setSelectable(True) node.setName("SimpleShape" + str(id(mesh_data))) scene = self._controller.getScene() op = AddSceneNodeOperation(node, scene.getRoot()) op.push() default_extruder_position = application.getMachineManager( ).defaultExtruderPosition default_extruder_id = global_stack.extruders[ default_extruder_position].getId() node.callDecoration("setActiveExtruder", default_extruder_id) active_build_plate = application.getMultiBuildPlateModel( ).activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) application.getController().getScene().sceneChanged.emit(node)
def _replaceSceneNode(self, existing_node, trimeshes): name = existing_node.getName() file_name = existing_node.getMeshData().getFileName() transformation = existing_node.getWorldTransformation() parent = existing_node.getParent() extruder_id = existing_node.callDecoration("getActiveExtruder") build_plate = existing_node.callDecoration("getBuildPlateNumber") selected = Selection.isSelected(existing_node) op = GroupedOperation() op.addOperation(RemoveSceneNodeOperation(existing_node)) for i, tri_node in enumerate(trimeshes): mesh_data = self._toMeshData(tri_node) new_node = CuraSceneNode() new_node.setSelectable(True) new_node.setMeshData(mesh_data) new_node.setName(name if i == 0 else "%s %d" % (name, i)) new_node.callDecoration("setActiveExtruder", extruder_id) new_node.addDecorator(BuildPlateDecorator(build_plate)) new_node.addDecorator(SliceableObjectDecorator()) op.addOperation(AddSceneNodeOperation(new_node, parent)) op.addOperation( SetTransformMatrixOperation(new_node, transformation)) if selected: Selection.add(new_node) op.push()
def _createEraserMesh(self, parent: CuraSceneNode, position: Vector): node = CuraSceneNode() node.setName("Eraser") node.setSelectable(True) node.setCalculateBoundingBox(True) mesh = self._createCube(10) node.setMeshData(mesh.build()) node.calculateBoundingBoxMesh() active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() definition = stack.getSettingDefinition("anti_overhang_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) op = GroupedOperation() # First add node to the scene at the correct position/scale, before parenting, so the eraser mesh does not get scaled with the parent op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot())) op.addOperation(SetParentOperation(node, parent)) op.push() node.setPosition(position, CuraSceneNode.TransformSpace.World) CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def _read(self, file_name): try: self.defs = {} self.shapes = [] tree = ET.parse(file_name) xml_root = tree.getroot() if xml_root.tag != "X3D": return None scale = 1000 # Default X3D unit it one meter, while Cura's is one millimeters if xml_root[0].tag == "head": for head_node in xml_root[0]: if head_node.tag == "unit" and head_node.attrib.get( "category") == "length": scale *= float(head_node.attrib["conversionFactor"]) break xml_scene = xml_root[1] else: xml_scene = xml_root[0] if xml_scene.tag != "Scene": return None self.transform = Matrix() self.transform.setByScaleFactor(scale) self.index_base = 0 # Traverse the scene tree, populate the shapes list self.processChildNodes(xml_scene) if self.shapes: builder = MeshBuilder() builder.setVertices( numpy.concatenate([shape.verts for shape in self.shapes])) builder.setIndices( numpy.concatenate([shape.faces for shape in self.shapes])) builder.calculateNormals() builder.setFileName(file_name) mesh_data = builder.build() # Manually try and get the extents of the mesh_data. This should prevent nasty NaN issues from # leaving the reader. mesh_data.getExtents() node = SceneNode() node.setMeshData(mesh_data) node.setSelectable(True) node.setName(file_name) else: return None except Exception: Logger.logException("e", "Exception in X3D reader") return None return node
def _replaceSceneNode(self, existing_node, trimeshes) -> None: name = existing_node.getName() file_name = existing_node.getMeshData().getFileName() transformation = existing_node.getWorldTransformation() parent = existing_node.getParent() extruder_id = existing_node.callDecoration("getActiveExtruder") build_plate = existing_node.callDecoration("getBuildPlateNumber") selected = Selection.isSelected(existing_node) children = existing_node.getChildren() new_nodes = [] op = GroupedOperation() op.addOperation(RemoveSceneNodeOperation(existing_node)) for i, tri_node in enumerate(trimeshes): mesh_data = self._toMeshData(tri_node, file_name) new_node = CuraSceneNode() new_node.setSelectable(True) new_node.setMeshData(mesh_data) new_node.setName(name if i==0 else "%s %d" % (name, i)) new_node.callDecoration("setActiveExtruder", extruder_id) new_node.addDecorator(BuildPlateDecorator(build_plate)) new_node.addDecorator(SliceableObjectDecorator()) op.addOperation(AddSceneNodeOperation(new_node, parent)) op.addOperation(SetTransformMatrixOperation(new_node, transformation)) new_nodes.append(new_node) if selected: Selection.add(new_node) for child in children: mesh_data = child.getMeshData() if not mesh_data: continue child_bounding_box = mesh_data.getTransformed(child.getWorldTransformation()).getExtents() if not child_bounding_box: continue new_parent = None for potential_parent in new_nodes: parent_mesh_data = potential_parent.getMeshData() if not parent_mesh_data: continue parent_bounding_box = parent_mesh_data.getTransformed(potential_parent.getWorldTransformation()).getExtents() if not parent_bounding_box: continue intersection = child_bounding_box.intersectsBox(parent_bounding_box) if intersection != AxisAlignedBox.IntersectionResult.NoIntersection: new_parent = potential_parent break if not new_parent: new_parent = new_nodes[0] op.addOperation(SetParentOperationSimplified(child, new_parent)) op.push()
def _read(self, file_name): try: self.defs = {} self.shapes = [] tree = ET.parse(file_name) xml_root = tree.getroot() if xml_root.tag != "X3D": return None scale = 1000 # Default X3D unit it one meter, while Cura's is one millimeters if xml_root[0].tag == "head": for head_node in xml_root[0]: if head_node.tag == "unit" and head_node.attrib.get("category") == "length": scale *= float(head_node.attrib["conversionFactor"]) break xml_scene = xml_root[1] else: xml_scene = xml_root[0] if xml_scene.tag != "Scene": return None self.transform = Matrix() self.transform.setByScaleFactor(scale) self.index_base = 0 # Traverse the scene tree, populate the shapes list self.processChildNodes(xml_scene) if self.shapes: builder = MeshBuilder() builder.setVertices(numpy.concatenate([shape.verts for shape in self.shapes])) builder.setIndices(numpy.concatenate([shape.faces for shape in self.shapes])) builder.calculateNormals() builder.setFileName(file_name) mesh_data = builder.build() # Manually try and get the extents of the mesh_data. This should prevent nasty NaN issues from # leaving the reader. mesh_data.getExtents() node = SceneNode() node.setMeshData(mesh_data) node.setSelectable(True) node.setName(file_name) else: return None except Exception: Logger.logException("e", "Exception in X3D reader") return None return node
def _read(self, file_name: str) -> Union["SceneNode", List["SceneNode"]]: mesh_or_scene = trimesh.load(file_name) meshes = [] # type: List[Union[trimesh.Trimesh, trimesh.Scene, Any]] 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 = [] # type: List[SceneNode] 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
def _createEraserMesh(self): node = CuraSceneNode() node.setName("Eraser") node.setSelectable(True) mesh = MeshBuilder() mesh.addCube(10, 10, 10) node.setMeshData(mesh.build()) # Place the cube in the platform. Do it manually so it works if the "automatic drop models" is OFF move_vector = Vector(0, 5, 0) node.setPosition(move_vector) active_build_plate = Application.getInstance().getMultiBuildPlateModel( ).activeBuildPlate node.addDecorator(SettingOverrideDecorator()) node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration( "getStack" ) #Don't try to get the active extruder since it may be None anyway. if not stack: node.addDecorator(SettingOverrideDecorator()) stack = node.callDecoration("getStack") settings = stack.getTop() if not (settings.getInstance("anti_overhang_mesh") and settings.getProperty("anti_overhang_mesh", "value")): definition = stack.getSettingDefinition("anti_overhang_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState( ) # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) scene = self._controller.getScene() op = AddSceneNodeOperation(node, scene.getRoot()) op.push() Application.getInstance().getController().getScene().sceneChanged.emit( node)
def _addShape(self, name, mesh_data: MeshData, ext_pos=0) -> None: application = CuraApplication.getInstance() global_stack = application.getGlobalContainerStack() if not global_stack: return node = CuraSceneNode() node.setMeshData(mesh_data) node.setSelectable(True) if len(name) == 0: node.setName("TestPart" + str(id(mesh_data))) else: node.setName(str(name)) scene = self._controller.getScene() op = AddSceneNodeOperation(node, scene.getRoot()) op.push() extruder_nr = len(global_stack.extruders) # Logger.log("d", "extruder_nr= %d", extruder_nr) # default_extruder_position : <class 'str'> if ext_pos > 0 and ext_pos <= extruder_nr: default_extruder_position = str(ext_pos - 1) else: default_extruder_position = application.getMachineManager( ).defaultExtruderPosition # Logger.log("d", "default_extruder_position= %s", type(default_extruder_position)) # default_extruder_id = global_stack.extruders[default_extruder_position].getId() default_extruder_id = global_stack.extruders[ default_extruder_position].getId() # Logger.log("d", "default_extruder_id= %s", default_extruder_id) node.callDecoration("setActiveExtruder", default_extruder_id) active_build_plate = application.getMultiBuildPlateModel( ).activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) application.getController().getScene().sceneChanged.emit(node)
def _createSupportMesh(self, parent: CuraSceneNode, position: Vector): node = CuraSceneNode() node.setSelectable(True) if self._SupportType == 'cylinder': height = position.y node.setName("CustomSupportCylinder") mesh = self._createCylinder(self._SupportSize,22.5,height) node_position = Vector(position.x,position.y,position.z) else: node.setName("CustomSupportCube") height = position.y-self._SupportSize/2+self._SupportSize*0.1 mesh = self._createCube(self._SupportSize,height) node_position = Vector(position.x,position.y-self._SupportSize/2+self._SupportSize*0.1,position.z) node.setMeshData(mesh.build()) active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() for key in ["support_mesh", "support_mesh_drop_down"]: definition = stack.getSettingDefinition(key) new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) op = GroupedOperation() # First add node to the scene at the correct position/scale, before parenting, so the support mesh does not get scaled with the parent op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot())) op.addOperation(SetParentOperation(node, parent)) op.push() node.setPosition(node_position, CuraSceneNode.TransformSpace.World) CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def _createEraserMesh(self, parent: CuraSceneNode, position: Vector): node = CuraSceneNode() node.setName("Eraser") node.setSelectable(True) mesh = MeshBuilder() mesh.addCube(10, 10, 10) node.setMeshData(mesh.build()) node.setPosition(position) active_build_plate = Application.getInstance().getMultiBuildPlateModel( ).activeBuildPlate node.addDecorator(SettingOverrideDecorator()) node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration( "getStack") # created by SettingOverrideDecorator settings = stack.getTop() definition = stack.getSettingDefinition("anti_overhang_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState( ) # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) root = self._controller.getScene().getRoot() op = GroupedOperation() # First add the node to the scene, so it gets the expected transform op.addOperation(AddSceneNodeOperation(node, root)) op.addOperation(SetParentOperation(node, parent)) op.push() Application.getInstance().getController().getScene().sceneChanged.emit( node)
def _createSupportMesh(self, parent: CuraSceneNode, position: Vector): node = CuraSceneNode() node.setName("RoundTab") node.setSelectable(True) # long=Support Height _long=position.y # get layer_height_0 used to define pastille height _id_ex=0 # This function can be triggered in the middle of a machine change, so do not proceed if the machine change # has not done yet. global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() #extruder = global_container_stack.extruderList[int(_id_ex)] extruder_stack = CuraApplication.getInstance().getExtruderManager().getActiveExtruderStacks()[0] _layer_h_i = extruder_stack.getProperty("layer_height_0", "value") _layer_height = extruder_stack.getProperty("layer_height", "value") _line_w = extruder_stack.getProperty("line_width", "value") # Logger.log('d', 'layer_height_0 : ' + str(_layer_h_i)) _layer_h = (_layer_h_i * 1.2) + (_layer_height * (self._Nb_Layer -1) ) _line_w = _line_w * 1.2 if self._AsCapsule: # Capsule creation Diameter , Increment angle 4°, length, layer_height_0*1.2 , line_width mesh = self._createCapsule(self._UseSize,4,_long,_layer_h,_line_w) else: # Cylinder creation Diameter , Increment angle 4°, length, layer_height_0*1.2 mesh = self._createPastille(self._UseSize,4,_long,_layer_h) node.setMeshData(mesh.build()) active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() # support_mesh type definition = stack.getSettingDefinition("support_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) definition = stack.getSettingDefinition("support_mesh_drop_down") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", False) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) if self._AsCapsule: s_p = global_container_stack.getProperty("support_type", "value") if s_p == 'buildplate' : Message(text = "Info modification current profile support_type parameter\nNew value : everywhere", title = catalog.i18nc("@info:title", "Warning ! Tab Anti Warping")).show() Logger.log('d', 'support_type different : ' + str(s_p)) # Define support_type=everywhere global_container_stack.setProperty("support_type", "value", 'everywhere') # Define support_xy_distance definition = stack.getSettingDefinition("support_xy_distance") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", self._UseOffset) # new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) # Fix some settings in Cura to get a better result id_ex=0 global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() extruder_stack = CuraApplication.getInstance().getExtruderManager().getActiveExtruderStacks()[0] #extruder = global_container_stack.extruderList[int(id_ex)] # hop to fix it in a futur release # https://github.com/Ultimaker/Cura/issues/9882 # if self.Major < 5 or ( self.Major == 5 and self.Minor < 1 ) : _xy_distance = extruder_stack.getProperty("support_xy_distance", "value") if self._UseOffset != _xy_distance : _msg = "New value : %8.3f" % (self._UseOffset) Message(text = "Info modification current profile support_xy_distance parameter\nNew value : %8.3f" % (self._UseOffset), title = catalog.i18nc("@info:title", "Warning ! Tab Anti Warping")).show() Logger.log('d', 'support_xy_distance different : ' + str(_xy_distance)) # Define support_xy_distance extruder_stack.setProperty("support_xy_distance", "value", self._UseOffset) if self._Nb_Layer >1 : s_p = int(extruder_stack.getProperty("support_infill_rate", "value")) Logger.log('d', 'support_infill_rate actual : ' + str(s_p)) if s_p < 99 : Message(text = "Info modification current profile support_infill_rate parameter\nNew value : 100%", title = catalog.i18nc("@info:title", "Warning ! Tab Anti Warping")).show() Logger.log('d', 'support_infill_rate different : ' + str(s_p)) # Define support_infill_rate=100% extruder_stack.setProperty("support_infill_rate", "value", 100) op = GroupedOperation() # First add node to the scene at the correct position/scale, before parenting, so the support mesh does not get scaled with the parent op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot())) op.addOperation(SetParentOperation(node, parent)) op.push() node.setPosition(position, CuraSceneNode.TransformSpace.World) CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def _read(self, file_name): base_name = os.path.basename(file_name) try: zipped_file = zipfile.ZipFile(file_name) xml_document = zipped_file.read(zipped_file.namelist()[0]) zipped_file.close() except zipfile.BadZipfile: raw_file = open(file_name, "r") xml_document = raw_file.read() raw_file.close() try: amf_document = ET.fromstring(xml_document) except ET.ParseError: Logger.log("e", "Could not parse XML in file %s" % base_name) return None if "unit" in amf_document.attrib: unit = amf_document.attrib["unit"].lower() else: unit = "millimeter" if unit == "millimeter": scale = 1.0 elif unit == "meter": scale = 1000.0 elif unit == "inch": scale = 25.4 elif unit == "feet": scale = 304.8 elif unit == "micron": scale = 0.001 else: Logger.log("w", "Unknown unit in amf: %s. Using mm instead." % unit) scale = 1.0 nodes = [] for amf_object in amf_document.iter("object"): for amf_mesh in amf_object.iter("mesh"): amf_mesh_vertices = [] for vertices in amf_mesh.iter("vertices"): for vertex in vertices.iter("vertex"): for coordinates in vertex.iter("coordinates"): v = [0.0, 0.0, 0.0] for t in coordinates: if t.tag == "x": v[0] = float(t.text) * scale elif t.tag == "y": v[2] = -float(t.text) * scale elif t.tag == "z": v[1] = float(t.text) * scale amf_mesh_vertices.append(v) if not amf_mesh_vertices: continue indices = [] for volume in amf_mesh.iter("volume"): for triangle in volume.iter("triangle"): f = [0, 0, 0] for t in triangle: if t.tag == "v1": f[0] = int(t.text) elif t.tag == "v2": f[1] = int(t.text) elif t.tag == "v3": f[2] = int(t.text) indices.append(f) mesh = trimesh.base.Trimesh( vertices=numpy.array(amf_mesh_vertices, dtype=numpy.float32), faces=numpy.array(indices, dtype=numpy.int32)) mesh.merge_vertices() mesh.remove_unreferenced_vertices() mesh.fix_normals() mesh_data = self._toMeshData(mesh, file_name) new_node = CuraSceneNode() new_node.setSelectable(True) new_node.setMeshData(mesh_data) new_node.setName(base_name if len(nodes) == 0 else "%s %d" % (base_name, len(nodes))) new_node.addDecorator( BuildPlateDecorator(CuraApplication.getInstance( ).getMultiBuildPlateModel().activeBuildPlate)) new_node.addDecorator(SliceableObjectDecorator()) nodes.append(new_node) if not nodes: Logger.log("e", "No meshes in file %s" % base_name) return None if len(nodes) == 1: return nodes[0] # Add all scenenodes to a group so they stay together group_node = CuraSceneNode() group_node.addDecorator(GroupDecorator()) group_node.addDecorator(ConvexHullDecorator()) group_node.addDecorator( BuildPlateDecorator(CuraApplication.getInstance(). getMultiBuildPlateModel().activeBuildPlate)) for node in nodes: node.setParent(group_node) return group_node
def _convertSavitarNodeToUMNode(self, savitar_node): self._object_count += 1 node_name = "Object %s" % self._object_count active_build_plate = Application.getInstance().getMultiBuildPlateModel( ).activeBuildPlate um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) um_node.setName(node_name) transformation = self._createMatrixFromTransformationString( savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring( savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: global_container_stack = Application.getInstance( ).getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack( 0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition_id = getMachineDefinitionIDForQualitySearch( global_container_stack.definition) um_node.callDecoration("getStack").getTop().setDefinition( definition_id) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def updateSceneFromOptimizationResult( self, analysis: pywim.smartslice.result.Analysis): type_map = { 'int': int, 'float': float, 'str': str, 'enum': str, 'bool': bool } our_only_node = getPrintableNodes()[0] active_extruder = getNodeActiveExtruder(our_only_node) # TODO - Move this into a common class or function to apply an am.Config to GlobalStack/ExtruderStack if analysis.print_config.infill: infill_density = analysis.print_config.infill.density infill_pattern = analysis.print_config.infill.pattern if infill_pattern is None or infill_pattern == pywim.am.InfillType.unknown: infill_pattern = pywim.am.InfillType.grid infill_pattern_name = SmartSliceJobHandler.INFILL_SMARTSLICE_CURA[ infill_pattern] extruder_dict = { "wall_line_count": analysis.print_config.walls, "top_layers": analysis.print_config.top_layers, "bottom_layers": analysis.print_config.bottom_layers, "infill_sparse_density": analysis.print_config.infill.density, "infill_pattern": infill_pattern_name } Logger.log("d", "Optimized extruder settings: {}".format(extruder_dict)) for key, value in extruder_dict.items(): if value is not None: property_type = type_map.get( active_extruder.getProperty(key, "type")) if property_type: active_extruder.setProperty(key, "value", property_type(value), set_from_cache=True) active_extruder.setProperty(key, "state", InstanceState.User, set_from_cache=True) Application.getInstance().getMachineManager( ).forceUpdateAllSettings() self.optimizationResultAppliedToScene.emit() # Remove any modifier meshes which are present from a previous result mod_meshes = getModifierMeshes() if len(mod_meshes) > 0: for node in mod_meshes: node.addDecorator(SmartSliceRemovedDecorator()) our_only_node.removeChild(node) Application.getInstance().getController().getScene( ).sceneChanged.emit(node) # Add in the new modifier meshes for modifier_mesh in analysis.modifier_meshes: # Building the scene node modifier_mesh_node = CuraSceneNode() modifier_mesh_node.setName("SmartSliceMeshModifier") modifier_mesh_node.setSelectable(True) modifier_mesh_node.setCalculateBoundingBox(True) # Use the data from the SmartSlice engine to translate / rotate / scale the mod mesh modifier_mesh_node.setTransformation( Matrix(modifier_mesh.transform)) # Building the mesh # # Preparing the data from pywim for MeshBuilder modifier_mesh_vertices = [[v.x, v.y, v.z] for v in modifier_mesh.vertices] modifier_mesh_indices = [[triangle.v1, triangle.v2, triangle.v3] for triangle in modifier_mesh.triangles] # Doing the actual build modifier_mesh_data = MeshBuilder() modifier_mesh_data.setVertices( numpy.asarray(modifier_mesh_vertices, dtype=numpy.float32)) modifier_mesh_data.setIndices( numpy.asarray(modifier_mesh_indices, dtype=numpy.int32)) modifier_mesh_data.calculateNormals() modifier_mesh_node.setMeshData(modifier_mesh_data.build()) modifier_mesh_node.calculateBoundingBoxMesh() active_build_plate = Application.getInstance( ).getMultiBuildPlateModel().activeBuildPlate modifier_mesh_node.addDecorator( BuildPlateDecorator(active_build_plate)) modifier_mesh_node.addDecorator(SliceableObjectDecorator()) modifier_mesh_node.addDecorator(SmartSliceAddedDecorator()) bottom = modifier_mesh_node.getBoundingBox().bottom z_offset_decorator = ZOffsetDecorator() z_offset_decorator.setZOffset(bottom) modifier_mesh_node.addDecorator(z_offset_decorator) stack = modifier_mesh_node.callDecoration("getStack") settings = stack.getTop() modifier_mesh_node_infill_pattern = SmartSliceJobHandler.INFILL_SMARTSLICE_CURA[ modifier_mesh.print_config.infill.pattern] definition_dict = { "infill_mesh": True, "infill_pattern": modifier_mesh_node_infill_pattern, "infill_sparse_density": modifier_mesh.print_config.infill.density, "wall_line_count": modifier_mesh.print_config.walls, "top_layers": modifier_mesh.print_config.top_layers, "bottom_layers": modifier_mesh.print_config.bottom_layers, } Logger.log( "d", "Optimized modifier mesh settings: {}".format(definition_dict)) for key, value in definition_dict.items(): if value is not None: definition = stack.getSettingDefinition(key) property_type = type_map.get(stack.getProperty( key, "type")) if property_type: new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", property_type(value)) new_instance.resetState( ) # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) our_only_node.addChild(modifier_mesh_node) # emit changes and connect error tracker Application.getInstance().getController().getScene( ).sceneChanged.emit(modifier_mesh_node)
def _convertSavitarNodeToUMNode( self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]: node_name = savitar_node.getName() node_id = savitar_node.getId() if node_name == "": if file_name != "": node_name = os.path.basename(file_name) else: node_name = "Object {}".format(node_id) active_build_plate = CuraApplication.getInstance( ).getMultiBuildPlateModel().activeBuildPlate um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) um_node.setName(node_name) um_node.setId(node_id) transformation = self._createMatrixFromTransformationString( savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring( savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) if file_name: # The filename is used to give the user the option to reload the file if it is changed on disk # It is only set for the root node of the 3mf file mesh_builder.setFileName(file_name) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: global_container_stack = CuraApplication.getInstance( ).getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack( 0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition_id = ContainerTree.getInstance().machines[ global_container_stack.definition.getId( )].quality_definition um_node.callDecoration("getStack").getTop().setDefinition( definition_id) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def _createSupportMesh(self, parent: CuraSceneNode, position: Vector , position2: Vector): node = CuraSceneNode() if self._SType == 'cylinder': node.setName("CustomSupportCylinder") elif self._SType == 'tube': node.setName("CustomSupportTube") elif self._SType == 'cube': node.setName("CustomSupportCube") elif self._SType == 'abutment': node.setName("CustomSupportAbutment") elif self._SType == 'freeform': node.setName("CustomSupportFreeForm") else: node.setName("CustomSupportCustom") node.setSelectable(True) # long=Support Height long=position.y if self._SType == 'cylinder': # Cylinder creation Diameter , Increment angle 2°, length mesh = self._createCylinder(self._UseSize,self._MaxSize,2,long,self._UseAngle) elif self._SType == 'tube': # Tube creation Diameter , Diameter Int, Increment angle 2°, length mesh = self._createTube(self._UseSize,self._MaxSize,self._UseISize,2,long,self._UseAngle) elif self._SType == 'cube': # Cube creation Size , length mesh = self._createCube(self._UseSize,self._MaxSize,long,self._UseAngle) elif self._SType == 'freeform': # Cube creation Size , length mesh = MeshBuilder() MName = self._SubType + ".stl" model_definition_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "models", MName) # Logger.log('d', 'Model_definition_path : ' + str(model_definition_path)) load_mesh = trimesh.load(model_definition_path) origin = [0, 0, 0] DirX = [1, 0, 0] DirY = [0, 1, 0] DirZ = [0, 0, 1] load_mesh.apply_transform(trimesh.transformations.scale_matrix(self._UseSize, origin, DirX)) load_mesh.apply_transform(trimesh.transformations.scale_matrix(self._UseSize, origin, DirY)) load_mesh.apply_transform(trimesh.transformations.scale_matrix(long, origin, DirZ)) if self._MirrorSupport == True : load_mesh.apply_transform(trimesh.transformations.rotation_matrix(math.radians(180), [0, 0, 1])) if self._UseYDirection == True : load_mesh.apply_transform(trimesh.transformations.rotation_matrix(math.radians(90), [0, 0, 1])) mesh = self._toMeshData(load_mesh) elif self._SType == 'abutment': # Abutement creation Size , length , top if self._EqualizeHeights == True : Logger.log('d', 'SHeights : ' + str(self._SHeights)) if self._SHeights==0 : self._SHeights=position.y top=self._UseSize+(self._SHeights-position.y) else: top=self._UseSize self._SHeights=0 # Logger.log('d', 'top : ' + str(top)) mesh = self._createAbutment(self._UseSize,self._MaxSize,long,top,self._UseAngle,self._UseYDirection) else: # Custom creation Size , P1 as vector P2 as vector # Get support_interface_height as extra distance extruder_stack = self._application.getExtruderManager().getActiveExtruderStacks()[0] extra_top=extruder_stack.getProperty("support_interface_height", "value") mesh = self._createCustom(self._UseSize,self._MaxSize,position,position2,self._UseAngle,extra_top) # Mesh Freeform are loaded via trimesh doesn't aheve the Build method if self._SType != 'freeform': node.setMeshData(mesh.build()) else: node.setMeshData(mesh) # test for init position node_transform = Matrix() node_transform.setToIdentity() node.setTransformation(node_transform) active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() # Define the new mesh as "support_mesh" or "support_mesh_drop_down" # Must be set for this 2 types # for key in ["support_mesh", "support_mesh_drop_down"]: # Don't fix definition = stack.getSettingDefinition("support_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) definition = stack.getSettingDefinition("support_mesh_drop_down") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", False) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() s_p = global_container_stack.getProperty("support_type", "value") if s_p == 'buildplate' : Message(text = "Info modification support_type new value : everywhere", title = catalog.i18nc("@info:title", "Custom Supports Cylinder")).show() Logger.log('d', 'support_type different : ' + str(s_p)) # Define support_type=everywhere global_container_stack.setProperty("support_type", "value", 'everywhere') op = GroupedOperation() # First add node to the scene at the correct position/scale, before parenting, so the support mesh does not get scaled with the parent op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot())) op.addOperation(SetParentOperation(node, parent)) op.push() node.setPosition(position, CuraSceneNode.TransformSpace.World) CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def _convertSavitarNodeToUMNode( self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]: """Convenience function that converts a SceneNode object (as obtained from libSavitar) to a scene node. :returns: Scene node. """ try: node_name = savitar_node.getName() node_id = savitar_node.getId() except AttributeError: Logger.log( "e", "Outdated version of libSavitar detected! Please update to the newest version!" ) node_name = "" node_id = "" if node_name == "": if file_name != "": node_name = os.path.basename(file_name) else: node_name = "Object {}".format(node_id) active_build_plate = CuraApplication.getInstance( ).getMultiBuildPlateModel().activeBuildPlate um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) try: um_node.addDecorator(ConvexHullDecorator()) except: pass um_node.setName(node_name) um_node.setId(node_id) transformation = self._createMatrixFromTransformationString( savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring( savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) if file_name: # The filename is used to give the user the option to reload the file if it is changed on disk # It is only set for the root node of the 3mf file mesh_builder.setFileName(file_name) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: global_container_stack = CuraApplication.getInstance( ).getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack( 0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition_id = ContainerTree.getInstance().machines[ global_container_stack.definition.getId( )].quality_definition um_node.callDecoration("getStack").getTop().setDefinition( definition_id) setting_container = um_node.callDecoration("getStack").getTop() known_setting_keys = um_node.callDecoration( "getStack").getAllKeys() for key in settings: setting_value = settings[key].value # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue if key in known_setting_keys: setting_container.setProperty(key, "value", setting_value) else: um_node.metadata[key] = settings[key] if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: if len(um_node.getAllChildren()) == 1: # We don't want groups of one, so move the node up one "level" child_node = um_node.getChildren()[0] parent_transformation = um_node.getLocalTransformation() child_transformation = child_node.getLocalTransformation() child_node.setTransformation( parent_transformation.multiply(child_transformation)) um_node = cast(CuraSceneNode, um_node.getChildren()[0]) else: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def _readMeshFinished(self, job): Logger.log("d", "read mesh finisihed!") ### START PATCH: detect belt printer global_container_stack = self._application.getGlobalContainerStack() if not global_container_stack: return is_belt_printer = self._preferences.getValue("BeltPlugin/on_plugin") ### END PATCH nodes = job.getResult() file_name = job.getFileName() file_name_lower = file_name.lower() file_extension = file_name_lower.split(".")[-1] self._application._currently_loading_files.remove(file_name) self._application.fileLoaded.emit(file_name) target_build_plate = self._application.getMultiBuildPlateModel( ).activeBuildPlate root = self._application.getController().getScene().getRoot() fixed_nodes = [] for node_ in DepthFirstIterator(root): if node_.callDecoration("isSliceable") and node_.callDecoration( "getBuildPlateNumber") == target_build_plate: fixed_nodes.append(node_) global_container_stack = self._application.getGlobalContainerStack() machine_width = global_container_stack.getProperty( "machine_width", "value") machine_depth = global_container_stack.getProperty( "machine_depth", "value") arranger = Arrange.create(x=machine_width, y=machine_depth, fixed_nodes=fixed_nodes) min_offset = 8 default_extruder_position = self._application.getMachineManager( ).defaultExtruderPosition default_extruder_id = self._application._global_container_stack.extruders[ default_extruder_position].getId() select_models_on_load = self._application.getPreferences().getValue( "cura/select_models_on_load") for original_node in nodes: # Create a CuraSceneNode just if the original node is not that type if isinstance(original_node, CuraSceneNode): node = original_node else: node = CuraSceneNode() node.setMeshData(original_node.getMeshData()) #Setting meshdata does not apply scaling. if (original_node.getScale() != Vector(1.0, 1.0, 1.0)): node.scale(original_node.getScale()) node.setSelectable(True) node.setName(os.path.basename(file_name)) self._application.getBuildVolume().checkBoundsAndUpdate(node) is_non_sliceable = "." + file_extension in self._application._non_sliceable_extensions if is_non_sliceable: self._application.callLater( lambda: self._application.getController().setActiveView( "SimulationView")) block_slicing_decorator = BlockSlicingDecorator() node.addDecorator(block_slicing_decorator) else: sliceable_decorator = SliceableObjectDecorator() node.addDecorator(sliceable_decorator) scene = self._application.getController().getScene() # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) for child in node.getAllChildren(): if not child.getDecorator(ConvexHullDecorator): child.addDecorator(ConvexHullDecorator()) ### START PATCH: don't do standard arrange on load for belt printers ### but place in a line instead if is_belt_printer: half_node_depth = node.getBoundingBox().depth / 2 build_plate_empty = True leading_edge = self._application.getBuildVolume( ).getBoundingBox().front for existing_node in DepthFirstIterator(root): if (not issubclass(type(existing_node), CuraSceneNode) or (not existing_node.getMeshData() and not existing_node.callDecoration("getLayerData")) or (existing_node.callDecoration("getBuildPlateNumber") != target_build_plate)): continue build_plate_empty = False leading_edge = min(leading_edge, existing_node.getBoundingBox().back) if not build_plate_empty or leading_edge < half_node_depth: node.setPosition( Vector( 0, 0, leading_edge - half_node_depth - self._margin_between_models)) if file_extension != "3mf" and not is_belt_printer: ### END PATCH if node.callDecoration("isSliceable"): # Only check position if it's not already blatantly obvious that it won't fit. if node.getBoundingBox( ) is None or self._application._volume.getBoundingBox( ) is None or node.getBoundingBox( ).width < self._application._volume.getBoundingBox( ).width or node.getBoundingBox( ).depth < self._application._volume.getBoundingBox().depth: # Find node location offset_shape_arr, hull_shape_arr = ShapeArray.fromNode( node, min_offset=min_offset) # If a model is to small then it will not contain any points if offset_shape_arr is None and hull_shape_arr is None: Message(self._application._i18n_catalog.i18nc( "@info:status", "The selected model was too small to load."), title=self._application._i18n_catalog. i18nc("@info:title", "Warning")).show() return # Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step=10) # This node is deep copied from some other node which already has a BuildPlateDecorator, but the deepcopy # of BuildPlateDecorator produces one that's associated with build plate -1. So, here we need to check if # the BuildPlateDecorator exists or not and always set the correct build plate number. build_plate_decorator = node.getDecorator(BuildPlateDecorator) if build_plate_decorator is None: build_plate_decorator = BuildPlateDecorator(target_build_plate) node.addDecorator(build_plate_decorator) build_plate_decorator.setBuildPlateNumber(target_build_plate) op = AddSceneNodeOperation(node, scene.getRoot()) op.push() node.callDecoration("setActiveExtruder", default_extruder_id) scene.sceneChanged.emit(node) if select_models_on_load: Selection.add(node) self._application.fileCompleted.emit(file_name)
def _convertSavitarNodeToUMNode(self, savitar_node): self._object_count += 1 node_name = "Object %s" % self._object_count active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) um_node.setName(node_name) transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring(savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: global_container_stack = Application.getInstance().getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack(0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack.definition) um_node.callDecoration("getStack").getTop().setDefinition(definition_id) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def _read(self, file_name): base_name = os.path.basename(file_name) try: zipped_file = zipfile.ZipFile(file_name) xml_document = zipped_file.read(zipped_file.namelist()[0]) zipped_file.close() except zipfile.BadZipfile: raw_file = open(file_name, "r") xml_document = raw_file.read() raw_file.close() try: amf_document = ET.fromstring(xml_document) except ET.ParseError: Logger.log("e", "Could not parse XML in file %s" % base_name) return None if "unit" in amf_document.attrib: unit = amf_document.attrib["unit"].lower() else: unit = "millimeter" if unit == "millimeter": scale = 1.0 elif unit == "meter": scale = 1000.0 elif unit == "inch": scale = 25.4 elif unit == "feet": scale = 304.8 elif unit == "micron": scale = 0.001 else: Logger.log("w", "Unknown unit in amf: %s. Using mm instead." % unit) scale = 1.0 nodes = [] for amf_object in amf_document.iter("object"): for amf_mesh in amf_object.iter("mesh"): amf_mesh_vertices = [] for vertices in amf_mesh.iter("vertices"): for vertex in vertices.iter("vertex"): for coordinates in vertex.iter("coordinates"): v = [0.0, 0.0, 0.0] for t in coordinates: if t.tag == "x": v[0] = float(t.text) * scale elif t.tag == "y": v[2] = float(t.text) * scale elif t.tag == "z": v[1] = float(t.text) * scale amf_mesh_vertices.append(v) if not amf_mesh_vertices: continue indices = [] for volume in amf_mesh.iter("volume"): for triangle in volume.iter("triangle"): f = [0, 0, 0] for t in triangle: if t.tag == "v1": f[0] = int(t.text) elif t.tag == "v2": f[1] = int(t.text) elif t.tag == "v3": f[2] = int(t.text) indices.append(f) mesh = trimesh.base.Trimesh(vertices=numpy.array(amf_mesh_vertices, dtype=numpy.float32), faces=numpy.array(indices, dtype=numpy.int32)) mesh.merge_vertices() mesh.remove_unreferenced_vertices() mesh.fix_normals() mesh_data = self._toMeshData(mesh) new_node = CuraSceneNode() new_node.setSelectable(True) new_node.setMeshData(mesh_data) new_node.setName(base_name if len(nodes)==0 else "%s %d" % (base_name, len(nodes))) new_node.addDecorator(BuildPlateDecorator(CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate)) new_node.addDecorator(SliceableObjectDecorator()) nodes.append(new_node) if not nodes: Logger.log("e", "No meshes in file %s" % base_name) return None if len(nodes) == 1: return nodes[0] # Add all scenenodes to a group so they stay together group_node = CuraSceneNode() group_node.addDecorator(GroupDecorator()) group_node.addDecorator(ConvexHullDecorator()) group_node.addDecorator(BuildPlateDecorator(CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate)) for node in nodes: node.setParent(group_node) return group_node