Ejemplo n.º 1
0
    def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]:
        self._empty_project = False
        result = []
        # The base object of 3mf is a zipped archive.
        try:
            archive = zipfile.ZipFile(file_name, "r")
            self._base_name = os.path.basename(file_name)
            parser = Savitar.ThreeMFParser()
            scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read())
            self._unit = scene_3mf.getUnit()

            for key, value in scene_3mf.getMetadata().items():
                CuraApplication.getInstance().getController().getScene(
                ).setMetaDataEntry(key, value)

            for node in scene_3mf.getSceneNodes():
                um_node = self._convertSavitarNodeToUMNode(node, file_name)
                if um_node is None:
                    continue

                # compensate for original center position, if object(s) is/are not around its zero position
                transform_matrix = Matrix()
                mesh_data = um_node.getMeshData()
                if mesh_data is not None:
                    extents = mesh_data.getExtents()
                    if extents is not None:
                        center_vector = Vector(extents.center.x,
                                               extents.center.y,
                                               extents.center.z)
                        transform_matrix.setByTranslation(center_vector)
                transform_matrix.multiply(um_node.getLocalTransformation())
                um_node.setTransformation(transform_matrix)

                global_container_stack = CuraApplication.getInstance(
                ).getGlobalContainerStack()

                # Create a transformation Matrix to convert from 3mf worldspace into ours.
                # First step: flip the y and z axis.
                transformation_matrix = Matrix()
                transformation_matrix._data[1, 1] = 0
                transformation_matrix._data[1, 2] = 1
                transformation_matrix._data[2, 1] = -1
                transformation_matrix._data[2, 2] = 0

                # Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the
                # build volume.
                if global_container_stack:
                    translation_vector = Vector(
                        x=-global_container_stack.getProperty(
                            "machine_width", "value") / 2,
                        y=-global_container_stack.getProperty(
                            "machine_depth", "value") / 2,
                        z=0)
                    translation_matrix = Matrix()
                    translation_matrix.setByTranslation(translation_vector)
                    transformation_matrix.multiply(translation_matrix)

                # Third step: 3MF also defines a unit, whereas Cura always assumes mm.
                scale_matrix = Matrix()
                scale_matrix.setByScaleVector(
                    self._getScaleFromUnit(self._unit))
                transformation_matrix.multiply(scale_matrix)

                # Pre multiply the transformation with the loaded transformation, so the data is handled correctly.
                um_node.setTransformation(
                    um_node.getLocalTransformation().preMultiply(
                        transformation_matrix))

                # Check if the model is positioned below the build plate and honor that when loading project files.
                node_meshdata = um_node.getMeshData()
                if node_meshdata is not None:
                    aabb = node_meshdata.getExtents(
                        um_node.getWorldTransformation())
                    if aabb is not None:
                        minimum_z_value = aabb.minimum.y  # y is z in transformation coordinates
                        if minimum_z_value < 0:
                            um_node.addDecorator(ZOffsetDecorator())
                            um_node.callDecoration("setZOffset",
                                                   minimum_z_value)

                result.append(um_node)

            if len(result) == 0:
                self._empty_project = True

        except Exception:
            Logger.logException("e", "An exception occurred in 3mf reader.")
            return []

        return result
    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)