Ejemplo n.º 1
0
    def getUsedExtruderStacks(self):
        global_stack = UM.Application.getInstance().getGlobalContainerStack()
        container_registry = UM.Settings.ContainerRegistry.getInstance()

        if global_stack.getProperty("machine_extruder_count",
                                    "value") <= 1:  #For single extrusion.
            return [global_stack]

        used_extruder_stack_ids = set()

        #Get the extruders of all meshes in the scene.
        support_enabled = False
        support_interface_enabled = False
        scene_root = UM.Application.getInstance().getController().getScene(
        ).getRoot()
        meshes = [
            node for node in DepthFirstIterator(scene_root)
            if type(node) is SceneNode and node.isSelectable()
        ]  #Only use the nodes that will be printed.
        for mesh in meshes:
            extruder_stack_id = mesh.callDecoration("getActiveExtruder")
            if not extruder_stack_id:  #No per-object settings for this node.
                extruder_stack_id = self.extruderIds["0"]
            used_extruder_stack_ids.add(extruder_stack_id)

            #Get whether any of them use support.
            per_mesh_stack = mesh.callDecoration("getStack")
            if per_mesh_stack:
                support_enabled |= per_mesh_stack.getProperty(
                    "support_enable", "value")
                support_interface_enabled |= per_mesh_stack.getProperty(
                    "support_interface_enable", "value")
            else:  #Take the setting from the build extruder stack.
                extruder_stack = container_registry.findContainerStacks(
                    id=extruder_stack_id)[0]
                support_enabled |= extruder_stack.getProperty(
                    "support_enable", "value")
                support_interface_enabled |= extruder_stack.getProperty(
                    "support_enable", "value")

        #The support extruders.
        if support_enabled:
            used_extruder_stack_ids.add(self.extruderIds[str(
                global_stack.getProperty("support_infill_extruder_nr",
                                         "value"))])
            used_extruder_stack_ids.add(self.extruderIds[str(
                global_stack.getProperty("support_extruder_nr_layer_0",
                                         "value"))])
            if support_interface_enabled:
                used_extruder_stack_ids.add(self.extruderIds[str(
                    global_stack.getProperty("support_interface_extruder_nr",
                                             "value"))])

        #The platform adhesion extruder. Not used if using none.
        if global_stack.getProperty("adhesion_type", "value") != "none":
            used_extruder_stack_ids.add(self.extruderIds[str(
                global_stack.getProperty("adhesion_extruder_nr", "value"))])
        try:
            return [
                container_registry.findContainerStacks(id=stack_id)[0]
                for stack_id in used_extruder_stack_ids
            ]
        except IndexError:  # One or more of the extruders was not found.
            UM.Logger.log("e",
                          "Unable to find one or more of the extruders in %s",
                          used_extruder_stack_ids)
            return []
Ejemplo n.º 2
0
    def render(self) -> None:
        if not self._shader:
            self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
            if self._shader:
                self._shader.setUniformValue("u_overhangAngle", 1.0)
                self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0])
                self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0])
                self._shader.setUniformValue("u_shininess", 20.0)
                self._shader.setUniformValue("u_faceId", -1)  # Don't render any selected faces in the preview.

        if not self._non_printing_shader:
            if self._non_printing_shader:
                self._non_printing_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
                self._non_printing_shader.setUniformValue("u_diffuseColor", [0.5, 0.5, 0.5, 0.5])
                self._non_printing_shader.setUniformValue("u_opacity", 0.6)

        if not self._support_mesh_shader:
            self._support_mesh_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader"))
            if self._support_mesh_shader:
                self._support_mesh_shader.setUniformValue("u_vertical_stripes", True)
                self._support_mesh_shader.setUniformValue("u_width", 5.0)

        self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)

        # Create batches to be rendered
        batch = RenderBatch(self._shader)
        batch_support_mesh = RenderBatch(self._support_mesh_shader)

        # Fill up the batch with objects that can be sliced.
        for node in DepthFirstIterator(self._scene.getRoot()):
            if hasattr(node, "_outside_buildarea") and not getattr(node, "_outside_buildarea"):
                if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
                    per_mesh_stack = node.callDecoration("getStack")
                    if node.callDecoration("isNonThumbnailVisibleMesh"):
                        # Non printing mesh
                        continue
                    elif per_mesh_stack is not None and per_mesh_stack.getProperty("support_mesh", "value"):
                        # Support mesh
                        uniforms = {}
                        shade_factor = 0.6
                        diffuse_color = cast(CuraSceneNode, node).getDiffuseColor()
                        diffuse_color2 = [
                            diffuse_color[0] * shade_factor,
                            diffuse_color[1] * shade_factor,
                            diffuse_color[2] * shade_factor,
                            1.0]
                        uniforms["diffuse_color"] = prettier_color(diffuse_color)
                        uniforms["diffuse_color_2"] = diffuse_color2
                        batch_support_mesh.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms)
                    else:
                        # Normal scene node
                        uniforms = {}
                        uniforms["diffuse_color"] = prettier_color(cast(CuraSceneNode, node).getDiffuseColor())
                        batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms)

        self.bind()

        if self._camera is None:
            render_camera = Application.getInstance().getController().getScene().getActiveCamera()
        else:
            render_camera = self._camera

        batch.render(render_camera)
        batch_support_mesh.render(render_camera)

        self.release()
Ejemplo n.º 3
0
    def _onWriteStarted(self, output_device):
        try:
            if not Preferences.getInstance().getValue("info/send_slice_info"):
                Logger.log("d", "'info/send_slice_info' is turned off.")
                return  # Do nothing, user does not want to send data

            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()
            print_information = Application.getInstance().getPrintInformation()

            data = dict()  # The data that we're going to submit.
            data["time_stamp"] = time.time()
            data["schema_version"] = 0
            data["cura_version"] = Application.getInstance().getVersion()

            active_mode = Preferences.getInstance().getValue(
                "cura/active_mode")
            if active_mode == 0:
                data["active_mode"] = "recommended"
            else:
                data["active_mode"] = "custom"

            definition_changes = global_container_stack.definitionChanges
            machine_settings_changed_by_user = False
            if definition_changes.getId() != "empty":
                # Now a definition_changes container will always be created for a stack,
                # so we also need to check if there is any instance in the definition_changes container
                if definition_changes.getAllKeys():
                    machine_settings_changed_by_user = True

            data[
                "machine_settings_changed_by_user"] = machine_settings_changed_by_user
            data["language"] = Preferences.getInstance().getValue(
                "general/language")
            data["os"] = {
                "type": platform.system(),
                "version": platform.version()
            }

            data["active_machine"] = {
                "definition_id":
                global_container_stack.definition.getId(),
                "manufacturer":
                global_container_stack.definition.getMetaData().get(
                    "manufacturer", "")
            }

            # add extruder specific data to slice info
            data["extruders"] = []
            extruders = list(ExtruderManager.getInstance().getMachineExtruders(
                global_container_stack.getId()))
            extruders = sorted(
                extruders,
                key=lambda extruder: extruder.getMetaDataEntry("position"))

            for extruder in extruders:
                extruder_dict = dict()
                extruder_dict["active"] = ExtruderManager.getInstance(
                ).getActiveExtruderStack() == extruder
                extruder_dict["material"] = {
                    "GUID": extruder.material.getMetaData().get("GUID", ""),
                    "type":
                    extruder.material.getMetaData().get("material", ""),
                    "brand": extruder.material.getMetaData().get("brand", "")
                }
                extruder_position = int(
                    extruder.getMetaDataEntry("position", "0"))
                if extruder_position in print_information.materialLengths:
                    extruder_dict[
                        "material_used"] = print_information.materialLengths[
                            extruder_position]
                extruder_dict["variant"] = extruder.variant.getName()
                extruder_dict["nozzle_size"] = extruder.getProperty(
                    "machine_nozzle_size", "value")

                extruder_settings = dict()
                extruder_settings["wall_line_count"] = extruder.getProperty(
                    "wall_line_count", "value")
                extruder_settings["retraction_enable"] = extruder.getProperty(
                    "retraction_enable", "value")
                extruder_settings[
                    "infill_sparse_density"] = extruder.getProperty(
                        "infill_sparse_density", "value")
                extruder_settings["infill_pattern"] = extruder.getProperty(
                    "infill_pattern", "value")
                extruder_settings[
                    "gradual_infill_steps"] = extruder.getProperty(
                        "gradual_infill_steps", "value")
                extruder_settings[
                    "default_material_print_temperature"] = extruder.getProperty(
                        "default_material_print_temperature", "value")
                extruder_settings[
                    "material_print_temperature"] = extruder.getProperty(
                        "material_print_temperature", "value")
                extruder_dict["extruder_settings"] = extruder_settings
                data["extruders"].append(extruder_dict)

            data[
                "quality_profile"] = global_container_stack.quality.getMetaData(
                ).get("quality_type")

            data["models"] = []
            # Listing all files placed on the build plate
            for node in DepthFirstIterator(CuraApplication.getInstance(
            ).getController().getScene().getRoot()):
                if node.callDecoration("isSliceable"):
                    model = dict()
                    model["hash"] = node.getMeshData().getHash()
                    bounding_box = node.getBoundingBox()
                    model["bounding_box"] = {
                        "minimum": {
                            "x": bounding_box.minimum.x,
                            "y": bounding_box.minimum.y,
                            "z": bounding_box.minimum.z
                        },
                        "maximum": {
                            "x": bounding_box.maximum.x,
                            "y": bounding_box.maximum.y,
                            "z": bounding_box.maximum.z
                        }
                    }
                    model["transformation"] = {
                        "data":
                        str(node.getWorldTransformation().getData()).replace(
                            "\n", "")
                    }
                    extruder_position = node.callDecoration(
                        "getActiveExtruderPosition")
                    model[
                        "extruder"] = 0 if extruder_position is None else int(
                            extruder_position)

                    model_settings = dict()
                    model_stack = node.callDecoration("getStack")
                    if model_stack:
                        model_settings[
                            "support_enabled"] = model_stack.getProperty(
                                "support_enable", "value")
                        model_settings["support_extruder_nr"] = int(
                            model_stack.getProperty("support_extruder_nr",
                                                    "value"))

                        # Mesh modifiers;
                        model_settings[
                            "infill_mesh"] = model_stack.getProperty(
                                "infill_mesh", "value")
                        model_settings[
                            "cutting_mesh"] = model_stack.getProperty(
                                "cutting_mesh", "value")
                        model_settings[
                            "support_mesh"] = model_stack.getProperty(
                                "support_mesh", "value")
                        model_settings[
                            "anti_overhang_mesh"] = model_stack.getProperty(
                                "anti_overhang_mesh", "value")

                        model_settings[
                            "wall_line_count"] = model_stack.getProperty(
                                "wall_line_count", "value")
                        model_settings[
                            "retraction_enable"] = model_stack.getProperty(
                                "retraction_enable", "value")

                        # Infill settings
                        model_settings[
                            "infill_sparse_density"] = model_stack.getProperty(
                                "infill_sparse_density", "value")
                        model_settings[
                            "infill_pattern"] = model_stack.getProperty(
                                "infill_pattern", "value")
                        model_settings[
                            "gradual_infill_steps"] = model_stack.getProperty(
                                "gradual_infill_steps", "value")

                    model["model_settings"] = model_settings

                    data["models"].append(model)

            print_times = print_information._print_time_message_values
            data["print_times"] = {
                "travel":
                int(print_times["travel"].getDisplayString(
                    DurationFormat.Format.Seconds)),
                "support":
                int(print_times["support"].getDisplayString(
                    DurationFormat.Format.Seconds)),
                "infill":
                int(print_times["infill"].getDisplayString(
                    DurationFormat.Format.Seconds)),
                "total":
                int(
                    print_information.currentPrintTime.getDisplayString(
                        DurationFormat.Format.Seconds))
            }

            print_settings = dict()
            print_settings[
                "layer_height"] = global_container_stack.getProperty(
                    "layer_height", "value")

            # Support settings
            print_settings[
                "support_enabled"] = global_container_stack.getProperty(
                    "support_enable", "value")
            print_settings["support_extruder_nr"] = int(
                global_container_stack.getProperty("support_extruder_nr",
                                                   "value"))

            # Platform adhesion settings
            print_settings[
                "adhesion_type"] = global_container_stack.getProperty(
                    "adhesion_type", "value")

            # Shell settings
            print_settings[
                "wall_line_count"] = global_container_stack.getProperty(
                    "wall_line_count", "value")
            print_settings[
                "retraction_enable"] = global_container_stack.getProperty(
                    "retraction_enable", "value")

            # Prime tower settings
            print_settings[
                "prime_tower_enable"] = global_container_stack.getProperty(
                    "prime_tower_enable", "value")

            # Infill settings
            print_settings[
                "infill_sparse_density"] = global_container_stack.getProperty(
                    "infill_sparse_density", "value")
            print_settings[
                "infill_pattern"] = global_container_stack.getProperty(
                    "infill_pattern", "value")
            print_settings[
                "gradual_infill_steps"] = global_container_stack.getProperty(
                    "gradual_infill_steps", "value")

            print_settings[
                "print_sequence"] = global_container_stack.getProperty(
                    "print_sequence", "value")

            data["print_settings"] = print_settings

            # Send the name of the output device type that is used.
            data["output_to"] = type(output_device).__name__

            # Convert data to bytes
            binary_data = json.dumps(data).encode("utf-8")

            # Sending slice info non-blocking
            reportJob = SliceInfoJob(self.info_url, binary_data)
            reportJob.start()
        except Exception:
            # We really can't afford to have a mistake here, as this would break the sending of g-code to a device
            # (Either saving or directly to a printer). The functionality of the slice data is not *that* important.
            Logger.logException(
                "e", "Exception raised while sending slice info."
            )  # But we should be notified about these problems of course.
Ejemplo n.º 4
0
    def run(self):
        if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
            self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
            self._progress.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)

        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is SceneNode and node.getMeshData():
                if node.callDecoration("getLayerData"):
                    self._scene.getRoot().removeChild(node)
            Job.yieldThread()
            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return

        settings = Application.getInstance().getMachineManager().getWorkingProfile()

        mesh = MeshData()
        layer_data = LayerData.LayerData()
        layer_count = len(self._layers)

        # Find the minimum layer number
        # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
        # instead simply offset all other layers so the lowest layer is always 0.
        min_layer_number = 0
        for layer in self._layers:
            if(layer.id < min_layer_number):
                min_layer_number = layer.id

        current_layer = 0

        for layer in self._layers:
            abs_layer_number = layer.id + abs(min_layer_number)

            layer_data.addLayer(abs_layer_number)
            layer_data.setLayerHeight(abs_layer_number, layer.height)
            layer_data.setLayerThickness(abs_layer_number, layer.thickness)

            for p in range(layer.repeatedMessageCount("polygons")):
                polygon = layer.getRepeatedMessage("polygons", p)

                points = numpy.fromstring(polygon.points, dtype="i8")  # Convert bytearray to numpy array
                points = points.reshape((-1,2))  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                new_points[:,0] = points[:,0]
                new_points[:,1] = layer.height
                new_points[:,2] = -points[:,1]

                new_points /= 1000

                layer_data.addPolygon(abs_layer_number, polygon.type, new_points, polygon.line_width)
                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 100
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress:
                    self._progress.hide()
                return
            if self._progress:
                self._progress.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data
        layer_data.build()

        if self._abort_requested:
            if self._progress:
                self._progress.hide()
            return

        # Add LayerDataDecorator to scene node to indicate that the node has layer data
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_data)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        new_node.setParent(self._scene.getRoot())  # Note: After this we can no longer abort!

        if not settings.getSettingValue("machine_center_is_zero"):
            new_node.setPosition(Vector(-settings.getSettingValue("machine_width") / 2, 0.0, settings.getSettingValue("machine_depth") / 2))

        if self._progress:
            self._progress.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress:
            self._progress.hide()

        # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
        self._layers = None
Ejemplo n.º 5
0
    def setMachineExtruderCount(self, extruder_count):
        machine_manager = Application.getInstance().getMachineManager()
        extruder_manager = ExtruderManager.getInstance()

        definition_changes_container = self._global_container_stack.findContainer({"type": "definition_changes"})
        if not self._global_container_stack or not definition_changes_container:
            return

        previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
        if extruder_count == previous_extruder_count:
            return

        extruder_material_id = None
        extruder_variant_id = None
        if extruder_count == 1:
            # Get the material and variant of the first extruder before setting the number extruders to 1
            if machine_manager.hasMaterials:
                extruder_material_id = machine_manager.allActiveMaterialIds[extruder_manager.extruderIds["0"]]
            if machine_manager.hasVariants:
                extruder_variant_id = machine_manager.activeVariantIds[0]

            # Copy any settable_per_extruder setting value from the extruders to the global stack
            extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
            extruder_stacks.reverse() # make sure the first extruder is done last, so its settings override any higher extruder settings

            global_user_container = self._global_container_stack.getTop()
            for extruder_stack in extruder_stacks:
                extruder_index = extruder_stack.getMetaDataEntry("position")
                extruder_user_container = extruder_stack.getTop()
                for setting_instance in extruder_user_container.findInstances():
                    setting_key = setting_instance.definition.key
                    settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        limit_to_extruder = self._global_container_stack.getProperty(setting_key, "limit_to_extruder")

                        if limit_to_extruder == "-1" or limit_to_extruder == extruder_index:
                            global_user_container.setProperty(setting_key, "value", extruder_user_container.getProperty(setting_key, "value"))
                            extruder_user_container.removeInstance(setting_key)

        # Check to see if any features are set to print with an extruder that will no longer exist
        for setting_key in ["adhesion_extruder_nr", "support_extruder_nr", "support_extruder_nr_layer_0", "support_infill_extruder_nr", "support_interface_extruder_nr"]:
            if int(self._global_container_stack.getProperty(setting_key, "value")) > extruder_count - 1:
                Logger.log("i", "Lowering %s setting to match number of extruders", setting_key)
                self._global_container_stack.getTop().setProperty(setting_key, "value", extruder_count - 1)

        # Check to see if any objects are set to print with an extruder that will no longer exist
        root_node = Application.getInstance().getController().getScene().getRoot()
        for node in DepthFirstIterator(root_node):
            if node.getMeshData():
                extruder_nr = node.callDecoration("getActiveExtruderPosition")

                if extruder_nr is not None and int(extruder_nr) > extruder_count - 1:
                    node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId())

        definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
        self.forceUpdate()

        if extruder_count > 1:
            # Multiextrusion

            # Make sure one of the extruder stacks is active
            if extruder_manager.activeExtruderIndex == -1:
                extruder_manager.setActiveExtruderIndex(0)

            # Move settable_per_extruder values out of the global container
            if previous_extruder_count == 1:
                extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
                global_user_container = self._global_container_stack.getTop()

                for setting_instance in global_user_container.findInstances():
                    setting_key = setting_instance.definition.key
                    settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder"))
                        extruder_stack = extruder_stacks[max(0, limit_to_extruder)]
                        extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
                        global_user_container.removeInstance(setting_key)
        else:
            # Single extrusion

            # Make sure the machine stack is active
            if extruder_manager.activeExtruderIndex > -1:
                extruder_manager.setActiveExtruderIndex(-1)

            # Restore material and variant on global stack
            # MachineManager._onGlobalContainerChanged removes the global material and variant of multiextruder machines
            if extruder_material_id or extruder_variant_id:
                # Prevent the DiscardOrKeepProfileChangesDialog from popping up (twice) if there are user changes
                # The dialog is not relevant here, since we're restoring the previous situation as good as possible
                preferences = Preferences.getInstance()
                choice_on_profile_override = preferences.getValue("cura/choice_on_profile_override")
                preferences.setValue("cura/choice_on_profile_override", "always_keep")

                if extruder_material_id:
                    machine_manager.setActiveMaterial(extruder_material_id)
                if extruder_variant_id:
                    machine_manager.setActiveVariant(extruder_variant_id)

                preferences.setValue("cura/choice_on_profile_override", choice_on_profile_override)
Ejemplo n.º 6
0
    def calculateMaxLayers(self) -> None:
        scene = self.getController().getScene()

        self._old_max_layers = self._max_layers
        ## Recalculate num max layers
        new_max_layers = -1
        for node in DepthFirstIterator(scene.getRoot()):  # type: ignore
            layer_data = node.callDecoration("getLayerData")
            if not layer_data:
                continue

            self.setActivity(True)
            min_layer_number = sys.maxsize
            max_layer_number = -sys.maxsize
            for layer_id in layer_data.getLayers():

                # If a layer doesn't contain any polygons, skip it (for infill meshes taller than print objects
                if len(layer_data.getLayer(layer_id).polygons) < 1:
                    continue

                # Store the max and min feedrates and thicknesses for display purposes
                for p in layer_data.getLayer(layer_id).polygons:
                    self._max_feedrate = max(float(p.lineFeedrates.max()),
                                             self._max_feedrate)
                    self._min_feedrate = min(float(p.lineFeedrates.min()),
                                             self._min_feedrate)
                    self._max_thickness = max(float(p.lineThicknesses.max()),
                                              self._max_thickness)
                    try:
                        self._min_thickness = min(
                            float(p.lineThicknesses[numpy.nonzero(
                                p.lineThicknesses)].min()),
                            self._min_thickness)
                    except ValueError:
                        # Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding
                        # the zero) can't be calculated
                        Logger.log(
                            "i",
                            "Min thickness can't be calculated because all the values are zero"
                        )
                if max_layer_number < layer_id:
                    max_layer_number = layer_id
                if min_layer_number > layer_id:
                    min_layer_number = layer_id
            layer_count = max_layer_number - min_layer_number

            if new_max_layers < layer_count:
                new_max_layers = layer_count

        if new_max_layers >= 0 and new_max_layers != self._old_max_layers:
            self._max_layers = new_max_layers

            # The qt slider has a bit of weird behavior that if the maxvalue needs to be changed first
            # if it's the largest value. If we don't do this, we can have a slider block outside of the
            # slider.
            if new_max_layers > self._current_layer_num:
                self.maxLayersChanged.emit()
                self.setLayer(int(self._max_layers))
            else:
                self.setLayer(int(self._max_layers))
                self.maxLayersChanged.emit()
        self._startUpdateTopLayers()
Ejemplo n.º 7
0
    def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
        if self._error_message:
            self._error_message.hide()

        # Note that cancelled slice jobs can still call this method.
        if self._start_slice_job is job:
            self._start_slice_job = None

        if job.isCancelled() or job.getError() or job.getResult(
        ) == StartJobResult.Error:
            self.backendStateChange.emit(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.MaterialIncompatible:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice with the current material as it is incompatible with the selected machine or configuration."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)
            return

        if job.getResult() == StartJobResult.SettingError:
            if self._application.platformActivity:
                if not self._global_container_stack:
                    Logger.log(
                        "w",
                        "Global container stack not assigned to CuraEngineBackend!"
                    )
                    return
                extruders = list(
                    ExtruderManager.getInstance().getMachineExtruders(
                        self._global_container_stack.getId()))
                error_keys = []  #type: List[str]
                for extruder in extruders:
                    error_keys.extend(extruder.getErrorKeys())
                if not extruders:
                    error_keys = self._global_container_stack.getErrorKeys()
                error_labels = set()
                for key in error_keys:
                    for stack in [
                            self._global_container_stack
                    ] + extruders:  #Search all container stacks for the definition of this setting. Some are only in an extruder stack.
                        definitions = cast(
                            DefinitionContainerInterface,
                            stack.getBottom()).findDefinitions(key=key)
                        if definitions:
                            break  #Found it! No need to continue search.
                    else:  #No stack has a definition for this setting.
                        Logger.log(
                            "w",
                            "When checking settings for errors, unable to find definition for key: {key}"
                            .format(key=key))
                        continue
                    error_labels.add(definitions[0].label)

                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice with the current settings. The following settings have errors: {0}"
                ).format(", ".join(error_labels)),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)
            return

        elif job.getResult() == StartJobResult.ObjectSettingError:
            errors = {}
            for node in DepthFirstIterator(
                    self._application.getController().getScene().getRoot()
            ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                stack = node.callDecoration("getStack")
                if not stack:
                    continue
                for key in stack.getErrorKeys():
                    if not self._global_container_stack:
                        Logger.log(
                            "e",
                            "CuraEngineBackend does not have global_container_stack assigned."
                        )
                        continue
                    definition = cast(DefinitionContainerInterface,
                                      self._global_container_stack.getBottom()
                                      ).findDefinitions(key=key)
                    if not definition:
                        Logger.log(
                            "e",
                            "When checking settings for errors, unable to find definition for key {key} in per-object stack."
                            .format(key=key))
                        continue
                    errors[key] = definition[0].label
            self._error_message = Message(catalog.i18nc(
                "@info:status",
                "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}"
            ).format(error_labels=", ".join(errors.values())),
                                          title=catalog.i18nc(
                                              "@info:title",
                                              "Unable to slice"))
            self._error_message.show()
            self.backendStateChange.emit(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.BuildPlateError:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Unable to slice because the prime tower or prime position(s) are invalid."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)

        if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder:
            self._error_message = Message(catalog.i18nc(
                "@info:status",
                "Unable to slice because there are objects associated with disabled Extruder %s."
                % job.getMessage()),
                                          title=catalog.i18nc(
                                              "@info:title",
                                              "Unable to slice"))
            self._error_message.show()
            self.backendStateChange.emit(BackendState.Error)
            self.backendError.emit(job)
            return

        if job.getResult() == StartJobResult.NothingToSlice:
            if self._application.platformActivity:
                self._error_message = Message(catalog.i18nc(
                    "@info:status",
                    "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."
                ),
                                              title=catalog.i18nc(
                                                  "@info:title",
                                                  "Unable to slice"))
                self._error_message.show()
                self.backendStateChange.emit(BackendState.Error)
                self.backendError.emit(job)
            else:
                self.backendStateChange.emit(BackendState.NotStarted)
            self._invokeSlice()
            return

        # Preparation completed, send it to the backend.
        self._socket.sendMessage(job.getSliceMessage())

        # Notify the user that it's now up to the backend to do it's job
        self.backendStateChange.emit(BackendState.Processing)

        if self._slice_start_time:
            Logger.log("d", "Sending slice message took %s seconds",
                       time() - self._slice_start_time)
Ejemplo n.º 8
0
 def _clearLayerData(self, build_plate_numbers=set()):
     for node in DepthFirstIterator(self._scene.getRoot()):
         if node.callDecoration("getLayerData"):
             if not build_plate_numbers or node.callDecoration(
                     "getBuildPlateNumber") in build_plate_numbers:
                 node.getParent().removeChild(node)
Ejemplo n.º 9
0
 def clearFaces(self):
     for bc_node in DepthFirstIterator(self):
         if isinstance(bc_node, HighlightFace):
             self.removeFace(bc_node)
Ejemplo n.º 10
0
    def _update(self, *args) -> None:
        nodes = []
        filter_current_build_plate = Application.getInstance().getPreferences(
        ).getValue("view/filter_current_build_plate")
        active_build_plate_number = self._build_plate_number
        group_nr = 1
        name_count_dict = defaultdict(int)  # type: Dict[str, int]

        for node in DepthFirstIterator(Application.getInstance().getController(
        ).getScene().getRoot()):  # type: ignore
            if not isinstance(node, SceneNode):
                continue
            if (not node.getMeshData()
                    and not node.callDecoration("getLayerData")
                ) and not node.callDecoration("isGroup"):
                continue

            parent = node.getParent()
            if parent and parent.callDecoration("isGroup"):
                continue  # Grouped nodes don't need resetting as their parent (the group) is resetted)
            if not node.callDecoration(
                    "isSliceable") and not node.callDecoration("isGroup"):
                continue
            node_build_plate_number = node.callDecoration(
                "getBuildPlateNumber")
            if filter_current_build_plate and node_build_plate_number != active_build_plate_number:
                continue

            if not node.callDecoration("isGroup"):
                name = node.getName()

            else:
                name = catalog.i18nc(
                    "@label",
                    "Group #{group_nr}").format(group_nr=str(group_nr))
                group_nr += 1

            if hasattr(node, "isOutsideBuildArea"):
                is_outside_build_area = node.isOutsideBuildArea(
                )  # type: ignore
            else:
                is_outside_build_area = False

            #check if we already have an instance of the object based on name
            name_count_dict[name] += 1
            name_count = name_count_dict[name]

            if name_count > 1:
                name = "{0}({1})".format(name, name_count - 1)
                node.setName(name)

            nodes.append({
                "name": name,
                "isSelected": Selection.isSelected(node),
                "isOutsideBuildArea": is_outside_build_area,
                "buildPlateNumber": node_build_plate_number,
                "node": node
            })

        nodes = sorted(nodes, key=lambda n: n["name"])
        self.setItems(nodes)

        self.itemsChanged.emit()
Ejemplo n.º 11
0
    def render(self):
        if not self._layer_shader:
            if self._compatibility_mode:
                shader_filename = "layers.shader"
                shadow_shader_filename = "layers_shadow.shader"
            else:
                shader_filename = "layers3d.shader"
                shadow_shader_filename = "layers3d_shadow.shader"
            self._layer_shader = OpenGL.getInstance().createShaderProgram(
                os.path.join(
                    PluginRegistry.getInstance().getPluginPath(
                        "SimulationView"), shader_filename))
            self._layer_shadow_shader = OpenGL.getInstance(
            ).createShaderProgram(
                os.path.join(
                    PluginRegistry.getInstance().getPluginPath(
                        "SimulationView"), shadow_shader_filename))
            self._current_shader = self._layer_shader
        # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
        self._layer_shader.setUniformValue(
            "u_active_extruder",
            float(max(0, self._extruder_manager.activeExtruderIndex)))
        if self._layer_view:
            self._layer_shader.setUniformValue(
                "u_max_feedrate", self._layer_view.getMaxFeedrate())
            self._layer_shader.setUniformValue(
                "u_min_feedrate", self._layer_view.getMinFeedrate())
            self._layer_shader.setUniformValue(
                "u_max_thickness", self._layer_view.getMaxThickness())
            self._layer_shader.setUniformValue(
                "u_min_thickness", self._layer_view.getMinThickness())
            self._layer_shader.setUniformValue(
                "u_layer_view_type", self._layer_view.getSimulationViewType())
            self._layer_shader.setUniformValue(
                "u_extruder_opacity", self._layer_view.getExtruderOpacities())
            self._layer_shader.setUniformValue(
                "u_show_travel_moves", self._layer_view.getShowTravelMoves())
            self._layer_shader.setUniformValue(
                "u_show_helpers", self._layer_view.getShowHelpers())
            self._layer_shader.setUniformValue("u_show_skin",
                                               self._layer_view.getShowSkin())
            self._layer_shader.setUniformValue(
                "u_show_infill", self._layer_view.getShowInfill())
        else:
            #defaults
            self._layer_shader.setUniformValue("u_max_feedrate", 1)
            self._layer_shader.setUniformValue("u_min_feedrate", 0)
            self._layer_shader.setUniformValue("u_max_thickness", 1)
            self._layer_shader.setUniformValue("u_min_thickness", 0)
            self._layer_shader.setUniformValue("u_layer_view_type", 1)
            self._layer_shader.setUniformValue(
                "u_extruder_opacity",
                [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]])
            self._layer_shader.setUniformValue("u_show_travel_moves", 0)
            self._layer_shader.setUniformValue("u_show_helpers", 1)
            self._layer_shader.setUniformValue("u_show_skin", 1)
            self._layer_shader.setUniformValue("u_show_infill", 1)

        if not self._tool_handle_shader:
            self._tool_handle_shader = OpenGL.getInstance(
            ).createShaderProgram(
                Resources.getPath(Resources.Shaders, "toolhandle.shader"))

        if not self._nozzle_shader:
            self._nozzle_shader = OpenGL.getInstance().createShaderProgram(
                Resources.getPath(Resources.Shaders, "color.shader"))
            self._nozzle_shader.setUniformValue(
                "u_color",
                Color(*Application.getInstance().getTheme().getColor(
                    "layerview_nozzle").getRgb()))

        self.bind()

        tool_handle_batch = RenderBatch(self._tool_handle_shader,
                                        type=RenderBatch.RenderType.Overlay,
                                        backface_cull=True)
        head_position = None  # Indicates the current position of the print head
        nozzle_node = None

        for node in DepthFirstIterator(self._scene.getRoot()):

            if isinstance(node, ToolHandle):
                tool_handle_batch.addItem(node.getWorldTransformation(),
                                          mesh=node.getSolidMesh())

            elif isinstance(node, NozzleNode):
                nozzle_node = node
                nozzle_node.setVisible(False)

            elif isinstance(node, SceneNode) and (node.getMeshData(
            ) or node.callDecoration("isBlockSlicing")) and node.isVisible():
                layer_data = node.callDecoration("getLayerData")
                if not layer_data:
                    continue

                # Render all layers below a certain number as line mesh instead of vertices.
                if self._layer_view._current_layer_num > -1 and (
                    (not self._layer_view._only_show_top_layers) or
                    (not self._layer_view.getCompatibilityMode())):
                    start = 0
                    end = 0
                    element_counts = layer_data.getElementCounts()
                    for layer in sorted(element_counts.keys()):
                        # In the current layer, we show just the indicated paths
                        if layer == self._layer_view._current_layer_num:
                            # We look for the position of the head, searching the point of the current path
                            index = self._layer_view._current_path_num
                            offset = 0
                            for polygon in layer_data.getLayer(layer).polygons:
                                # The size indicates all values in the two-dimension array, and the second dimension is
                                # always size 3 because we have 3D points.
                                if index >= polygon.data.size // 3 - offset:
                                    index -= polygon.data.size // 3 - offset
                                    offset = 1  # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
                                    continue
                                # The head position is calculated and translated
                                head_position = Vector(
                                    polygon.data[index + offset][0],
                                    polygon.data[index + offset][1],
                                    polygon.data[index + offset]
                                    [2]) + node.getWorldPosition()
                                break
                            break
                        if self._layer_view._minimum_layer_num > layer:
                            start += element_counts[layer]
                        end += element_counts[layer]

                    # Calculate the range of paths in the last layer
                    current_layer_start = end
                    current_layer_end = end + self._layer_view._current_path_num * 2  # Because each point is used twice

                    # This uses glDrawRangeElements internally to only draw a certain range of lines.
                    # All the layers but the current selected layer are rendered first
                    if self._old_current_path != self._layer_view._current_path_num:
                        self._current_shader = self._layer_shadow_shader
                        self._switching_layers = False
                    if not self._layer_view.isSimulationRunning(
                    ) and self._old_current_layer != self._layer_view._current_layer_num:
                        self._current_shader = self._layer_shader
                        self._switching_layers = True

                    layers_batch = RenderBatch(
                        self._current_shader,
                        type=RenderBatch.RenderType.Solid,
                        mode=RenderBatch.RenderMode.Lines,
                        range=(start, end),
                        backface_cull=True)
                    layers_batch.addItem(node.getWorldTransformation(),
                                         layer_data)
                    layers_batch.render(self._scene.getActiveCamera())

                    # Current selected layer is rendered
                    current_layer_batch = RenderBatch(
                        self._layer_shader,
                        type=RenderBatch.RenderType.Solid,
                        mode=RenderBatch.RenderMode.Lines,
                        range=(current_layer_start, current_layer_end))
                    current_layer_batch.addItem(node.getWorldTransformation(),
                                                layer_data)
                    current_layer_batch.render(self._scene.getActiveCamera())

                    self._old_current_layer = self._layer_view._current_layer_num
                    self._old_current_path = self._layer_view._current_path_num

                # Create a new batch that is not range-limited
                batch = RenderBatch(self._layer_shader,
                                    type=RenderBatch.RenderType.Solid)

                if self._layer_view.getCurrentLayerMesh():
                    batch.addItem(node.getWorldTransformation(),
                                  self._layer_view.getCurrentLayerMesh())

                if self._layer_view.getCurrentLayerJumps():
                    batch.addItem(node.getWorldTransformation(),
                                  self._layer_view.getCurrentLayerJumps())

                if len(batch.items) > 0:
                    batch.render(self._scene.getActiveCamera())

        # The nozzle is drawn when once we know the correct position of the head,
        # but the user is not using the layer slider, and the compatibility mode is not enabled
        if not self._switching_layers and not self._compatibility_mode and self._layer_view.getActivity(
        ) and nozzle_node is not None:
            if head_position is not None:
                nozzle_node.setVisible(True)
                nozzle_node.setPosition(head_position)
                nozzle_batch = RenderBatch(
                    self._nozzle_shader,
                    type=RenderBatch.RenderType.Transparent)
                nozzle_batch.addItem(nozzle_node.getWorldTransformation(),
                                     mesh=nozzle_node.getMeshData())
                nozzle_batch.render(self._scene.getActiveCamera())

        # Render toolhandles on top of the layerview
        if len(tool_handle_batch.items) > 0:
            tool_handle_batch.render(self._scene.getActiveCamera())

        self.release()
Ejemplo n.º 12
0
 def sliceableNodes(self):
     # Add all sliceable scene nodes to check
     scene = Application.getInstance().getController().getScene()
     for node in DepthFirstIterator(scene.getRoot()):
         if node.callDecoration("isSliceable"):
             yield node
Ejemplo n.º 13
0
    def calculateColorSchemeLimits(self) -> None:
        """
        Calculates the limits of the colour schemes, depending on the layer view data that is visible to the user.
        """
        # Before we start, save the old values so that we can tell if any of the spectrums need to change.
        old_min_feedrate = self._min_feedrate
        old_max_feedrate = self._max_feedrate
        old_min_linewidth = self._min_line_width
        old_max_linewidth = self._max_line_width
        old_min_thickness = self._min_thickness
        old_max_thickness = self._max_thickness

        self._min_feedrate = sys.float_info.max
        self._max_feedrate = sys.float_info.min
        self._min_line_width = sys.float_info.max
        self._max_line_width = sys.float_info.min
        self._min_thickness = sys.float_info.max
        self._max_thickness = sys.float_info.min

        # The colour scheme is only influenced by the visible lines, so filter the lines by if they should be visible.
        visible_line_types = []
        if self.getShowSkin():  # Actually "shell".
            visible_line_types.append(LayerPolygon.SkinType)
            visible_line_types.append(LayerPolygon.Inset0Type)
            visible_line_types.append(LayerPolygon.InsetXType)
        if self.getShowInfill():
            visible_line_types.append(LayerPolygon.InfillType)
        if self.getShowHelpers():
            visible_line_types.append(LayerPolygon.PrimeTowerType)
            visible_line_types.append(LayerPolygon.SkirtType)
            visible_line_types.append(LayerPolygon.SupportType)
            visible_line_types.append(LayerPolygon.SupportInfillType)
            visible_line_types.append(LayerPolygon.SupportInterfaceType)
        if self.getShowTravelMoves():
            visible_line_types.append(LayerPolygon.MoveCombingType)
            visible_line_types.append(LayerPolygon.MoveRetractionType)

        for node in DepthFirstIterator(self.getController().getScene().getRoot()):
            layer_data = node.callDecoration("getLayerData")
            if not layer_data:
                continue

            for layer_index in layer_data.getLayers():
                for polyline in layer_data.getLayer(layer_index).polygons:
                    is_visible = numpy.isin(polyline.types, visible_line_types)
                    visible_indices = numpy.where(is_visible)[0]
                    if visible_indices.size == 0:  # No items to take maximum or minimum of.
                        continue
                    visible_feedrates = numpy.take(polyline.lineFeedrates, visible_indices)
                    visible_linewidths = numpy.take(polyline.lineWidths, visible_indices)
                    visible_thicknesses = numpy.take(polyline.lineThicknesses, visible_indices)
                    self._max_feedrate = max(float(visible_feedrates.max()), self._max_feedrate)
                    self._min_feedrate = min(float(visible_feedrates.min()), self._min_feedrate)
                    self._max_line_width = max(float(visible_linewidths.max()), self._max_line_width)
                    self._min_line_width = min(float(visible_linewidths.min()), self._min_line_width)
                    self._max_thickness = max(float(visible_thicknesses.max()), self._max_thickness)
                    try:
                        self._min_thickness = min(float(visible_thicknesses[numpy.nonzero(visible_thicknesses)].min()), self._min_thickness)
                    except ValueError:
                        # Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding the zero) can't be calculated.
                        Logger.log("i", "Min thickness can't be calculated because all the values are zero")

        if old_min_feedrate != self._min_feedrate or old_max_feedrate != self._max_feedrate \
                or old_min_linewidth != self._min_line_width or old_max_linewidth != self._max_line_width \
                or old_min_thickness != self._min_thickness or old_max_thickness != self._max_thickness:
            self.colorSchemeLimitsChanged.emit()
Ejemplo n.º 14
0
    def run(self):
        stack = Application.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(False)
            return

        with self._scene.getSceneLock():
            # Remove old layer data.
            for node in DepthFirstIterator(self._scene.getRoot()):
                if node.callDecoration("getLayerData"):
                    node.getParent().removeChild(node)
                    break

            # Get the objects in their groups to print.
            object_groups = []
            if stack.getProperty("print_sequence", "value") == "one_at_a_time":
                for node in OneAtATimeIterator(self._scene.getRoot()):
                    temp_list = []

                    # Node can't be printed, so don't bother sending it.
                    if getattr(node, "_outside_buildarea", False):
                        continue

                    children = node.getAllChildren()
                    children.append(node)
                    for child_node in children:
                        if type(child_node
                                ) is SceneNode and child_node.getMeshData(
                                ) and child_node.getMeshData().getVertices(
                                ) is not None:
                            temp_list.append(child_node)

                    if temp_list:
                        object_groups.append(temp_list)
                    Job.yieldThread()
                if len(object_groups) == 0:
                    Logger.log(
                        "w",
                        "No objects suitable for one at a time found, or no correct order found"
                    )
            else:
                temp_list = []
                for node in DepthFirstIterator(self._scene.getRoot()):
                    if type(node) is SceneNode and node.getMeshData(
                    ) and node.getMeshData().getVertices() is not None:
                        if not getattr(node, "_outside_buildarea", False):
                            temp_list.append(node)
                    Job.yieldThread()

                if temp_list:
                    object_groups.append(temp_list)

            if not object_groups:
                return

            self._buildGlobalSettingsMessage(stack)

            for group in object_groups:
                group_message = self._slice_message.addRepeatedMessage(
                    "object_lists")
                if group[0].getParent().callDecoration("isGroup"):
                    self._handlePerObjectSettings(group[0].getParent(),
                                                  group_message)
                for object in group:
                    mesh_data = object.getMeshData().getTransformed(
                        object.getWorldTransformation())

                    obj = group_message.addRepeatedMessage("objects")
                    obj.id = id(object)
                    verts = numpy.array(mesh_data.getVertices())

                    # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                    verts[:, [1, 2]] = verts[:, [2, 1]]
                    verts[:, 1] *= -1

                    obj.vertices = verts

                    self._handlePerObjectSettings(object, obj)

                    Job.yieldThread()

        self.setResult(True)
Ejemplo n.º 15
0
    def run(self):
        stack = Application.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(StartJobResult.Error)
            return

        # Don't slice if there is a setting with an error value.
        if not Application.getInstance().getMachineManager().isActiveStackValid:
            self.setResult(StartJobResult.SettingError)
            return

        if Application.getInstance().getBuildVolume().hasErrors():
            self.setResult(StartJobResult.SettingError)
            return

        for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
            material = extruder_stack.findContainer({"type": "material"})
            if material:
                if material.getMetaDataEntry("compatible") == False:
                    self.setResult(StartJobResult.MaterialIncompatible)
                    return

        # Don't slice if there is a per object setting with an error value.
        for node in DepthFirstIterator(self._scene.getRoot()):
            if type(node) is not SceneNode or not node.isSelectable():
                continue

            if self._checkStackForErrors(node.callDecoration("getStack")):
                self.setResult(StartJobResult.SettingError)
                return

        with self._scene.getSceneLock():
            # Remove old layer data.
            for node in DepthFirstIterator(self._scene.getRoot()):
                if node.callDecoration("getLayerData"):
                    node.getParent().removeChild(node)
                    break

            # Get the objects in their groups to print.
            object_groups = []
            if stack.getProperty("print_sequence", "value") == "one_at_a_time":
                for node in OneAtATimeIterator(self._scene.getRoot()):
                    temp_list = []

                    # Node can't be printed, so don't bother sending it.
                    if getattr(node, "_outside_buildarea", False):
                        continue

                    children = node.getAllChildren()
                    children.append(node)
                    for child_node in children:
                        if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
                            temp_list.append(child_node)

                    if temp_list:
                        object_groups.append(temp_list)
                    Job.yieldThread()
                if len(object_groups) == 0:
                    Logger.log("w", "No objects suitable for one at a time found, or no correct order found")
            else:
                temp_list = []
                for node in DepthFirstIterator(self._scene.getRoot()):
                    if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
                        if not getattr(node, "_outside_buildarea", False):
                            temp_list.append(node)
                    Job.yieldThread()

                if temp_list:
                    object_groups.append(temp_list)

            # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
            # able to find a possible sequence or because there are no objects on the build plate (or they are outside
            # the build volume)
            if not object_groups:
                self.setResult(StartJobResult.NothingToSlice)
                return

            self._buildGlobalSettingsMessage(stack)
            self._buildGlobalInheritsStackMessage(stack)

            for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
                self._buildExtruderMessage(extruder_stack)

            for group in object_groups:
                group_message = self._slice_message.addRepeatedMessage("object_lists")
                if group[0].getParent().callDecoration("isGroup"):
                    self._handlePerObjectSettings(group[0].getParent(), group_message)
                for object in group:
                    mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())

                    obj = group_message.addRepeatedMessage("objects")
                    obj.id = id(object)
                    verts = numpy.array(mesh_data.getVertices())

                    # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                    verts[:, [1, 2]] = verts[:, [2, 1]]
                    verts[:, 1] *= -1

                    obj.vertices = verts

                    self._handlePerObjectSettings(object, obj)

                    Job.yieldThread()

        self.setResult(StartJobResult.Finished)
Ejemplo n.º 16
0
    def run(self):
        start_time = time()
        if Application.getInstance().getController().getActiveView(
        ).getPluginId() == "LayerView":
            self._progress_message.show()
            Job.yieldThread()
            if self._abort_requested:
                if self._progress_message:
                    self._progress_message.hide()
                return

        Application.getInstance().getController().activeViewChanged.connect(
            self._onActiveViewChanged)

        new_node = SceneNode()

        ## Remove old layer data (if any)
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("getLayerData"):
                node.getParent().removeChild(node)
                break
            if self._abort_requested:
                if self._progress_message:
                    self._progress_message.hide()
                return

        # Force garbage collection.
        # For some reason, Python has a tendency to keep the layer data
        # in memory longer than needed. Forcing the GC to run here makes
        # sure any old layer data is really cleaned up before adding new.
        gc.collect()

        mesh = MeshData()
        layer_data = LayerDataBuilder.LayerDataBuilder()
        layer_count = len(self._layers)

        # Find the minimum layer number
        # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we
        # instead simply offset all other layers so the lowest layer is always 0.
        min_layer_number = 0
        for layer in self._layers:
            if layer.id < min_layer_number:
                min_layer_number = layer.id

        current_layer = 0

        for layer in self._layers:
            abs_layer_number = layer.id + abs(min_layer_number)

            layer_data.addLayer(abs_layer_number)
            this_layer = layer_data.getLayer(abs_layer_number)
            layer_data.setLayerHeight(abs_layer_number, layer.height)

            for p in range(layer.repeatedMessageCount("path_segment")):
                polygon = layer.getRepeatedMessage("path_segment", p)

                extruder = polygon.extruder

                line_types = numpy.fromstring(
                    polygon.line_type,
                    dtype="u1")  # Convert bytearray to numpy array
                line_types = line_types.reshape((-1, 1))

                points = numpy.fromstring(
                    polygon.points,
                    dtype="f4")  # Convert bytearray to numpy array
                if polygon.point_type == 0:  # Point2D
                    points = points.reshape(
                        (-1, 2)
                    )  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
                else:  # Point3D
                    points = points.reshape((-1, 3))

                line_widths = numpy.fromstring(
                    polygon.line_width,
                    dtype="f4")  # Convert bytearray to numpy array
                line_widths = line_widths.reshape(
                    (-1, 1)
                )  # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.

                # In the future, line_thicknesses should be given by CuraEngine as well.
                # Currently the infill layer thickness also translates to line width
                line_thicknesses = numpy.zeros(line_widths.shape, dtype="f4")
                line_thicknesses[:] = layer.thickness / 1000  # from micrometer to millimeter

                # Create a new 3D-array, copy the 2D points over and insert the right height.
                # This uses manual array creation + copy rather than numpy.insert since this is
                # faster.
                new_points = numpy.empty((len(points), 3), numpy.float32)
                if polygon.point_type == 0:  # Point2D
                    new_points[:, 0] = points[:, 0]
                    new_points[:,
                               1] = layer.height / 1000  # layer height value is in backend representation
                    new_points[:, 2] = -points[:, 1]
                else:  # Point3D
                    new_points[:, 0] = points[:, 0]
                    new_points[:, 1] = points[:, 2]
                    new_points[:, 2] = -points[:, 1]

                this_poly = LayerPolygon.LayerPolygon(extruder, line_types,
                                                      new_points, line_widths,
                                                      line_thicknesses)
                this_poly.buildCache()

                this_layer.polygons.append(this_poly)

                Job.yieldThread()
            Job.yieldThread()
            current_layer += 1
            progress = (current_layer / layer_count) * 99
            # TODO: Rebuild the layer data mesh once the layer has been processed.
            # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.

            if self._abort_requested:
                if self._progress_message:
                    self._progress_message.hide()
                return
            if self._progress_message:
                self._progress_message.setProgress(progress)

        # We are done processing all the layers we got from the engine, now create a mesh out of the data

        # Find out colors per extruder
        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        manager = ExtruderManager.getInstance()
        extruders = list(
            manager.getMachineExtruders(global_container_stack.getId()))
        if extruders:
            material_color_map = numpy.zeros((len(extruders), 4),
                                             dtype=numpy.float32)
            for extruder in extruders:
                position = int(
                    extruder.getMetaDataEntry("position",
                                              default="0"))  # Get the position
                try:
                    default_color = ExtrudersModel.defaultColors[position]
                except IndexError:
                    default_color = "#e0e000"
                color_code = extruder.material.getMetaDataEntry(
                    "color_code", default=default_color)
                color = colorCodeToRGBA(color_code)
                material_color_map[position, :] = color
        else:
            # Single extruder via global stack.
            material_color_map = numpy.zeros((1, 4), dtype=numpy.float32)
            color_code = global_container_stack.material.getMetaDataEntry(
                "color_code", default="#e0e000")
            color = colorCodeToRGBA(color_code)
            material_color_map[0, :] = color

        # We have to scale the colors for compatibility mode
        if OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance(
        ).getValue("view/force_layer_view_compatibility_mode")):
            line_type_brightness = 0.5  # for compatibility mode
        else:
            line_type_brightness = 1.0
        layer_mesh = layer_data.build(material_color_map, line_type_brightness)

        if self._abort_requested:
            if self._progress_message:
                self._progress_message.hide()
            return

        # Add LayerDataDecorator to scene node to indicate that the node has layer data
        decorator = LayerDataDecorator.LayerDataDecorator()
        decorator.setLayerData(layer_mesh)
        new_node.addDecorator(decorator)

        new_node.setMeshData(mesh)
        # Set build volume as parent, the build volume can move as a result of raft settings.
        # It makes sense to set the build volume as parent: the print is actually printed on it.
        new_node_parent = Application.getInstance().getBuildVolume()
        new_node.setParent(
            new_node_parent)  # Note: After this we can no longer abort!

        settings = Application.getInstance().getGlobalContainerStack()
        if not settings.getProperty("machine_center_is_zero", "value"):
            new_node.setPosition(
                Vector(-settings.getProperty("machine_width", "value") / 2,
                       0.0,
                       settings.getProperty("machine_depth", "value") / 2))

        if self._progress_message:
            self._progress_message.setProgress(100)

        view = Application.getInstance().getController().getActiveView()
        if view.getPluginId() == "LayerView":
            view.resetLayerData()

        if self._progress_message:
            self._progress_message.hide()

        # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed.
        self._layers = None

        Logger.log("d", "Processing layers took %s seconds",
                   time() - start_time)
Ejemplo n.º 17
0
    def beginRendering(self):
        scene = self.getController().getScene()
        renderer = self.getRenderer()

        self._checkSetup()

        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if global_container_stack:
            if Application.getInstance().getPreferences().getValue(
                    "view/show_overhang"):
                # Make sure the overhang angle is valid before passing it to the shader
                if self._support_angle is not None and self._support_angle >= 0 and self._support_angle <= 90:
                    self._enabled_shader.setUniformValue(
                        "u_overhangAngle",
                        math.cos(math.radians(90 - self._support_angle)))
                else:
                    self._enabled_shader.setUniformValue(
                        "u_overhangAngle", math.cos(math.radians(0))
                    )  #Overhang angle of 0 causes no area at all to be marked as overhang.
            else:
                self._enabled_shader.setUniformValue("u_overhangAngle",
                                                     math.cos(math.radians(0)))

        for node in DepthFirstIterator(scene.getRoot()):
            if not node.render(renderer):
                if node.getMeshData() and node.isVisible(
                ) and not node.callDecoration("getLayerData"):
                    uniforms = {}
                    shade_factor = 1.0

                    per_mesh_stack = node.callDecoration("getStack")

                    extruder_index = node.callDecoration(
                        "getActiveExtruderPosition")
                    if extruder_index is None:
                        extruder_index = "0"
                    extruder_index = int(extruder_index)

                    # Use the support extruder instead of the active extruder if this is a support_mesh
                    if per_mesh_stack:
                        if per_mesh_stack.getProperty("support_mesh", "value"):
                            extruder_index = int(
                                global_container_stack.
                                getExtruderPositionValueWithDefault(
                                    "support_extruder_nr"))

                    try:
                        material_color = self._extruders_model.getItem(
                            extruder_index)["color"]
                    except KeyError:
                        material_color = self._extruders_model.defaultColors[0]

                    if extruder_index != ExtruderManager.getInstance(
                    ).activeExtruderIndex:
                        # Shade objects that are printed with the non-active extruder 25% darker
                        shade_factor = 0.6

                    try:
                        # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
                        # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
                        uniforms["diffuse_color"] = [
                            shade_factor * int(material_color[1:3], 16) / 255,
                            shade_factor * int(material_color[3:5], 16) / 255,
                            shade_factor * int(material_color[5:7], 16) / 255,
                            1.0
                        ]

                        # Color the currently selected face-id. (Disable for now.)
                        #face = Selection.getHoverFace()
                        uniforms[
                            "hover_face"] = -1  #if not face or node != face[0] else face[1]
                    except ValueError:
                        pass

                    if node.callDecoration("isNonPrintingMesh"):
                        if per_mesh_stack and (per_mesh_stack.getProperty(
                                "infill_mesh", "value")
                                               or per_mesh_stack.getProperty(
                                                   "cutting_mesh", "value")):
                            renderer.queueNode(
                                node,
                                shader=self._non_printing_shader,
                                uniforms=uniforms,
                                transparent=True)
                        else:
                            renderer.queueNode(
                                node,
                                shader=self._non_printing_shader,
                                transparent=True)
                    elif getattr(node, "_outside_buildarea", False):
                        renderer.queueNode(node, shader=self._disabled_shader)
                    elif per_mesh_stack and per_mesh_stack.getProperty(
                            "support_mesh", "value"):
                        # Render support meshes with a vertical stripe that is darker
                        shade_factor = 0.6
                        uniforms["diffuse_color_2"] = [
                            uniforms["diffuse_color"][0] * shade_factor,
                            uniforms["diffuse_color"][1] * shade_factor,
                            uniforms["diffuse_color"][2] * shade_factor, 1.0
                        ]
                        renderer.queueNode(node,
                                           shader=self._support_mesh_shader,
                                           uniforms=uniforms)
                    else:
                        renderer.queueNode(node,
                                           shader=self._enabled_shader,
                                           uniforms=uniforms)
                if node.callDecoration("isGroup") and Selection.isSelected(
                        node):
                    renderer.queueNode(scene.getRoot(),
                                       mesh=node.getBoundingBoxMesh(),
                                       mode=RenderBatch.RenderMode.LineLoop)
Ejemplo n.º 18
0
    def getUsedExtruderStacks(self) -> List["ContainerStack"]:
        global_stack = Application.getInstance().getGlobalContainerStack()
        container_registry = ContainerRegistry.getInstance()

        used_extruder_stack_ids = set()

        # Get the extruders of all meshes in the scene
        support_enabled = False
        support_bottom_enabled = False
        support_roof_enabled = False

        scene_root = Application.getInstance().getController().getScene(
        ).getRoot()

        # If no extruders are registered in the extruder manager yet, return an empty array
        if len(self.extruderIds) == 0:
            return []

        # Get the extruders of all printable meshes in the scene
        meshes = [
            node for node in DepthFirstIterator(scene_root)
            if isinstance(node, SceneNode) and node.isSelectable()
        ]
        for mesh in meshes:
            extruder_stack_id = mesh.callDecoration("getActiveExtruder")
            if not extruder_stack_id:
                # No per-object settings for this node
                extruder_stack_id = self.extruderIds["0"]
            used_extruder_stack_ids.add(extruder_stack_id)

            # Get whether any of them use support.
            stack_to_use = mesh.callDecoration(
                "getStack")  # if there is a per-mesh stack, we use it
            if not stack_to_use:
                # if there is no per-mesh stack, we use the build extruder for this mesh
                stack_to_use = container_registry.findContainerStacks(
                    id=extruder_stack_id)[0]

            support_enabled |= stack_to_use.getProperty(
                "support_enable", "value")
            support_bottom_enabled |= stack_to_use.getProperty(
                "support_bottom_enable", "value")
            support_roof_enabled |= stack_to_use.getProperty(
                "support_roof_enable", "value")

            # Check limit to extruders
            limit_to_extruder_feature_list = [
                "wall_0_extruder_nr",
                "wall_x_extruder_nr",
                "roofing_extruder_nr",
                "top_bottom_extruder_nr",
                "infill_extruder_nr",
            ]
            for extruder_nr_feature_name in limit_to_extruder_feature_list:
                extruder_nr = int(
                    global_stack.getProperty(extruder_nr_feature_name,
                                             "value"))
                if extruder_nr == -1:
                    continue
                used_extruder_stack_ids.add(self.extruderIds[str(extruder_nr)])

        # Check support extruders
        if support_enabled:
            used_extruder_stack_ids.add(self.extruderIds[str(
                global_stack.getProperty("support_infill_extruder_nr",
                                         "value"))])
            used_extruder_stack_ids.add(self.extruderIds[str(
                global_stack.getProperty("support_extruder_nr_layer_0",
                                         "value"))])
            if support_bottom_enabled:
                used_extruder_stack_ids.add(self.extruderIds[str(
                    global_stack.getProperty("support_bottom_extruder_nr",
                                             "value"))])
            if support_roof_enabled:
                used_extruder_stack_ids.add(self.extruderIds[str(
                    global_stack.getProperty("support_roof_extruder_nr",
                                             "value"))])

        # The platform adhesion extruder. Not used if using none.
        if global_stack.getProperty("adhesion_type", "value") != "none":
            used_extruder_stack_ids.add(self.extruderIds[str(
                global_stack.getProperty("adhesion_extruder_nr", "value"))])

        try:
            return [
                container_registry.findContainerStacks(id=stack_id)[0]
                for stack_id in used_extruder_stack_ids
            ]
        except IndexError:  # One or more of the extruders was not found.
            Logger.log("e",
                       "Unable to find one or more of the extruders in %s",
                       used_extruder_stack_ids)
            return []
Ejemplo n.º 19
0
 def _clearLayerData(self):
     for node in DepthFirstIterator(self._scene.getRoot()):
         if node.callDecoration("getLayerData"):
             node.getParent().removeChild(node)
             break
Ejemplo n.º 20
0
    def run(self):
        if self._build_plate_number is None:
            self.setResult(StartJobResult.Error)
            return

        stack = Application.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(StartJobResult.Error)
            return

        # Don't slice if there is a setting with an error value.
        if Application.getInstance().getMachineManager().stacksHaveErrors:
            self.setResult(StartJobResult.SettingError)
            return

        if Application.getInstance().getBuildVolume().hasErrors():
            self.setResult(StartJobResult.BuildPlateError)
            return

        # Don't slice if the buildplate or the nozzle type is incompatible with the materials
        if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \
                not Application.getInstance().getMachineManager().variantBuildplateUsable:
            self.setResult(StartJobResult.MaterialIncompatible)
            return

        for position, extruder_stack in stack.extruders.items():
            material = extruder_stack.findContainer({"type": "material"})
            if not extruder_stack.isEnabled:
                continue
            if material:
                if material.getMetaDataEntry("compatible") == False:
                    self.setResult(StartJobResult.MaterialIncompatible)
                    return

        # Don't slice if there is a per object setting with an error value.
        for node in DepthFirstIterator(self._scene.getRoot()):
            if not isinstance(node, CuraSceneNode) or not node.isSelectable():
                continue

            if self._checkStackForErrors(node.callDecoration("getStack")):
                self.setResult(StartJobResult.ObjectSettingError)
                return

        with self._scene.getSceneLock():
            # Remove old layer data.
            for node in DepthFirstIterator(self._scene.getRoot()):
                if node.callDecoration("getLayerData") and node.callDecoration(
                        "getBuildPlateNumber") == self._build_plate_number:
                    node.getParent().removeChild(node)
                    break

            # Get the objects in their groups to print.
            object_groups = []
            if stack.getProperty("print_sequence", "value") == "one_at_a_time":
                for node in OneAtATimeIterator(self._scene.getRoot()):
                    temp_list = []

                    # Node can't be printed, so don't bother sending it.
                    if getattr(node, "_outside_buildarea", False):
                        continue

                    # Filter on current build plate
                    build_plate_number = node.callDecoration(
                        "getBuildPlateNumber")
                    if build_plate_number is not None and build_plate_number != self._build_plate_number:
                        continue

                    children = node.getAllChildren()
                    children.append(node)
                    for child_node in children:
                        if child_node.getMeshData() and child_node.getMeshData(
                        ).getVertices() is not None:
                            temp_list.append(child_node)

                    if temp_list:
                        object_groups.append(temp_list)
                    Job.yieldThread()
                if len(object_groups) == 0:
                    Logger.log(
                        "w",
                        "No objects suitable for one at a time found, or no correct order found"
                    )
            else:
                temp_list = []
                has_printing_mesh = False
                for node in DepthFirstIterator(self._scene.getRoot()):
                    if node.callDecoration("isSliceable") and node.getMeshData(
                    ) and node.getMeshData().getVertices() is not None:
                        per_object_stack = node.callDecoration("getStack")
                        is_non_printing_mesh = False
                        if per_object_stack:
                            is_non_printing_mesh = any(
                                per_object_stack.getProperty(key, "value")
                                for key in NON_PRINTING_MESH_SETTINGS)

                        # Find a reason not to add the node
                        if node.callDecoration("getBuildPlateNumber"
                                               ) != self._build_plate_number:
                            continue
                        if getattr(node, "_outside_buildarea",
                                   False) and not is_non_printing_mesh:
                            continue

                        temp_list.append(node)
                        if not is_non_printing_mesh:
                            has_printing_mesh = True

                    Job.yieldThread()

                #If the list doesn't have any model with suitable settings then clean the list
                # otherwise CuraEngine will crash
                if not has_printing_mesh:
                    temp_list.clear()

                if temp_list:
                    object_groups.append(temp_list)

            extruders_enabled = {
                position: stack.isEnabled
                for position, stack in Application.getInstance().
                getGlobalContainerStack().extruders.items()
            }
            filtered_object_groups = []
            has_model_with_disabled_extruders = False
            associated_disabled_extruders = set()
            for group in object_groups:
                stack = Application.getInstance().getGlobalContainerStack()
                skip_group = False
                for node in group:
                    extruder_position = node.callDecoration(
                        "getActiveExtruderPosition")
                    if not extruders_enabled[extruder_position]:
                        skip_group = True
                        has_model_with_disabled_extruders = True
                        associated_disabled_extruders.add(extruder_position)
                if not skip_group:
                    filtered_object_groups.append(group)

            if has_model_with_disabled_extruders:
                self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
                associated_disabled_extruders = [
                    str(c) for c in sorted(
                        [int(p) + 1 for p in associated_disabled_extruders])
                ]
                self.setMessage(", ".join(associated_disabled_extruders))
                return

            # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
            # able to find a possible sequence or because there are no objects on the build plate (or they are outside
            # the build volume)
            if not filtered_object_groups:
                self.setResult(StartJobResult.NothingToSlice)
                return

            self._buildGlobalSettingsMessage(stack)
            self._buildGlobalInheritsStackMessage(stack)

            # Build messages for extruder stacks
            for extruder_stack in ExtruderManager.getInstance(
            ).getMachineExtruders(stack.getId()):
                self._buildExtruderMessage(extruder_stack)

            for group in filtered_object_groups:
                group_message = self._slice_message.addRepeatedMessage(
                    "object_lists")
                if group[0].getParent() is not None and group[0].getParent(
                ).callDecoration("isGroup"):
                    self._handlePerObjectSettings(group[0].getParent(),
                                                  group_message)
                for object in group:
                    mesh_data = object.getMeshData()
                    rot_scale = object.getWorldTransformation().getTransposed(
                    ).getData()[0:3, 0:3]
                    translate = object.getWorldTransformation().getData()[:3,
                                                                          3]

                    # This effectively performs a limited form of MeshData.getTransformed that ignores normals.
                    verts = mesh_data.getVertices()
                    verts = verts.dot(rot_scale)
                    verts += translate

                    # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                    verts[:, [1, 2]] = verts[:, [2, 1]]
                    verts[:, 1] *= -1

                    obj = group_message.addRepeatedMessage("objects")
                    obj.id = id(object)

                    indices = mesh_data.getIndices()
                    if indices is not None:
                        flat_verts = numpy.take(verts,
                                                indices.flatten(),
                                                axis=0)
                    else:
                        flat_verts = numpy.array(verts)

                    obj.vertices = flat_verts

                    self._handlePerObjectSettings(object, obj)

                    Job.yieldThread()

        self.setResult(StartJobResult.Finished)
Ejemplo n.º 21
0
    def run(self) -> None:
        """Runs the job that initiates the slicing."""

        if self._build_plate_number is None:
            self.setResult(StartJobResult.Error)
            return

        stack = CuraApplication.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(StartJobResult.Error)
            return

        # Don't slice if there is a setting with an error value.
        if CuraApplication.getInstance().getMachineManager().stacksHaveErrors:
            self.setResult(StartJobResult.SettingError)
            return

        if CuraApplication.getInstance().getBuildVolume().hasErrors():
            self.setResult(StartJobResult.BuildPlateError)
            return

        # Wait for error checker to be done.
        while CuraApplication.getInstance().getMachineErrorChecker(
        ).needToWaitForResult:
            time.sleep(0.1)

        if CuraApplication.getInstance().getMachineErrorChecker().hasError:
            self.setResult(StartJobResult.SettingError)
            return

        # Don't slice if the buildplate or the nozzle type is incompatible with the materials
        if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \
                not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable:
            self.setResult(StartJobResult.MaterialIncompatible)
            return

        for extruder_stack in stack.extruderList:
            material = extruder_stack.findContainer({"type": "material"})
            if not extruder_stack.isEnabled:
                continue
            if material:
                if material.getMetaDataEntry("compatible") == False:
                    self.setResult(StartJobResult.MaterialIncompatible)
                    return

        # Don't slice if there is a per object setting with an error value.
        for node in DepthFirstIterator(self._scene.getRoot()):
            if not isinstance(node, CuraSceneNode) or not node.isSelectable():
                continue

            if self._checkStackForErrors(node.callDecoration("getStack")):
                self.setResult(StartJobResult.ObjectSettingError)
                return

        # Remove old layer data.
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("getLayerData") and node.callDecoration(
                    "getBuildPlateNumber") == self._build_plate_number:
                # Singe we walk through all nodes in the scene, they always have a parent.
                cast(SceneNode, node.getParent()).removeChild(node)
                break

        # Get the objects in their groups to print.
        object_groups = []
        if stack.getProperty("print_sequence", "value") == "one_at_a_time":
            for node in OneAtATimeIterator(self._scene.getRoot()):
                temp_list = []

                # Node can't be printed, so don't bother sending it.
                if getattr(node, "_outside_buildarea", False):
                    continue

                # Filter on current build plate
                build_plate_number = node.callDecoration("getBuildPlateNumber")
                if build_plate_number is not None and build_plate_number != self._build_plate_number:
                    continue

                children = node.getAllChildren()
                children.append(node)
                for child_node in children:
                    mesh_data = child_node.getMeshData()
                    if mesh_data and mesh_data.getVertices() is not None:
                        temp_list.append(child_node)

                if temp_list:
                    object_groups.append(temp_list)
                Job.yieldThread()
            if len(object_groups) == 0:
                Logger.log(
                    "w",
                    "No objects suitable for one at a time found, or no correct order found"
                )
        else:
            temp_list = []
            has_printing_mesh = False
            for node in DepthFirstIterator(self._scene.getRoot()):
                mesh_data = node.getMeshData()
                if node.callDecoration(
                        "isSliceable") and mesh_data and mesh_data.getVertices(
                        ) is not None:
                    is_non_printing_mesh = bool(
                        node.callDecoration("isNonPrintingMesh"))

                    # Find a reason not to add the node
                    if node.callDecoration(
                            "getBuildPlateNumber") != self._build_plate_number:
                        continue
                    if getattr(node, "_outside_buildarea",
                               False) and not is_non_printing_mesh:
                        continue

                    temp_list.append(node)
                    if not is_non_printing_mesh:
                        has_printing_mesh = True

                Job.yieldThread()

            # If the list doesn't have any model with suitable settings then clean the list
            # otherwise CuraEngine will crash
            if not has_printing_mesh:
                temp_list.clear()

            if temp_list:
                object_groups.append(temp_list)

        global_stack = CuraApplication.getInstance().getGlobalContainerStack()
        if not global_stack:
            return
        extruders_enabled = [
            stack.isEnabled for stack in global_stack.extruderList
        ]
        filtered_object_groups = []
        has_model_with_disabled_extruders = False
        associated_disabled_extruders = set()
        for group in object_groups:
            stack = global_stack
            skip_group = False
            for node in group:
                # Only check if the printing extruder is enabled for printing meshes
                is_non_printing_mesh = node.callDecoration(
                    "evaluateIsNonPrintingMesh")
                extruder_position = int(
                    node.callDecoration("getActiveExtruderPosition"))
                if not is_non_printing_mesh and not extruders_enabled[
                        extruder_position]:
                    skip_group = True
                    has_model_with_disabled_extruders = True
                    associated_disabled_extruders.add(extruder_position)
            if not skip_group:
                filtered_object_groups.append(group)

        if has_model_with_disabled_extruders:
            self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
            associated_disabled_extruders = {
                p + 1
                for p in associated_disabled_extruders
            }
            self.setMessage(", ".join(
                map(str, sorted(associated_disabled_extruders))))
            return

        # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
        # able to find a possible sequence or because there are no objects on the build plate (or they are outside
        # the build volume)
        if not filtered_object_groups:
            self.setResult(StartJobResult.NothingToSlice)
            return

        self._buildGlobalSettingsMessage(stack)
        self._buildGlobalInheritsStackMessage(stack)

        # Build messages for extruder stacks
        for extruder_stack in global_stack.extruderList:
            self._buildExtruderMessage(extruder_stack)

        for group in filtered_object_groups:
            group_message = self._slice_message.addRepeatedMessage(
                "object_lists")
            parent = group[0].getParent()
            if parent is not None and parent.callDecoration("isGroup"):
                self._handlePerObjectSettings(cast(CuraSceneNode, parent),
                                              group_message)

            for object in group:
                mesh_data = object.getMeshData()
                if mesh_data is None:
                    continue
                rot_scale = object.getWorldTransformation().getTransposed(
                ).getData()[0:3, 0:3]
                translate = object.getWorldTransformation().getData()[:3, 3]
                if global_stack.getProperty("adhesion_type",
                                            "value") == "raft":
                    # Add at least support interface thickness so that
                    # the part does not fuse into the raft
                    # bounding_box_bottom is just distance from bottom of object to bed
                    # we need it to be at least support_roof_height to fit in one layer of
                    # support interface
                    bounding_box_bottom = object.getBoundingBox().bottom
                    support_roof_height = global_stack.getProperty(
                        "support_roof_height", "value")
                    if bounding_box_bottom < support_roof_height:
                        difference_needed = support_roof_height - bounding_box_bottom
                        translate[1] += difference_needed

                # This effectively performs a limited form of MeshData.getTransformed that ignores normals.
                verts = mesh_data.getVertices()
                verts = verts.dot(rot_scale)
                verts += translate

                # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                verts[:, [1, 2]] = verts[:, [2, 1]]
                verts[:, 1] *= -1

                obj = group_message.addRepeatedMessage("objects")
                obj.id = id(object)
                obj.name = object.getName()
                indices = mesh_data.getIndices()
                if indices is not None:
                    flat_verts = numpy.take(verts, indices.flatten(), axis=0)
                else:
                    flat_verts = numpy.array(verts)

                obj.vertices = flat_verts

                self._handlePerObjectSettings(cast(CuraSceneNode, object), obj)

                Job.yieldThread()

        self.setResult(StartJobResult.Finished)
Ejemplo n.º 22
0
    def setMachineExtruderCount(self, extruder_count):
        extruder_manager = Application.getInstance().getExtruderManager()

        definition_changes_container = self._global_container_stack.definitionChanges
        if not self._global_container_stack or definition_changes_container == self._empty_container:
            return

        previous_extruder_count = self._global_container_stack.getProperty(
            "machine_extruder_count", "value")
        if extruder_count == previous_extruder_count:
            return

        # reset all extruder number settings whose value is no longer valid
        for setting_instance in self._global_container_stack.userChanges.findInstances(
        ):
            setting_key = setting_instance.definition.key
            if not self._global_container_stack.getProperty(
                    setting_key, "type") in ("extruder", "optional_extruder"):
                continue

            old_value = int(
                self._global_container_stack.userChanges.getProperty(
                    setting_key, "value"))
            if old_value >= extruder_count:
                self._global_container_stack.userChanges.removeInstance(
                    setting_key)
                Logger.log(
                    "d",
                    "Reset [%s] because its old value [%s] is no longer valid ",
                    setting_key, old_value)

        # Check to see if any objects are set to print with an extruder that will no longer exist
        root_node = Application.getInstance().getController().getScene(
        ).getRoot()
        for node in DepthFirstIterator(root_node):
            if node.getMeshData():
                extruder_nr = node.callDecoration("getActiveExtruderPosition")

                if extruder_nr is not None and int(
                        extruder_nr) > extruder_count - 1:
                    node.callDecoration(
                        "setActiveExtruder",
                        extruder_manager.getExtruderStack(extruder_count -
                                                          1).getId())

        definition_changes_container.setProperty("machine_extruder_count",
                                                 "value", extruder_count)

        # Make sure one of the extruder stacks is active
        extruder_manager.setActiveExtruderIndex(0)

        # Move settable_per_extruder values out of the global container
        # After CURA-4482 this should not be the case anymore, but we still want to support older project files.
        global_user_container = self._global_container_stack.getTop()

        if previous_extruder_count == 1:
            extruder_stacks = ExtruderManager.getInstance(
            ).getActiveExtruderStacks()
            global_user_container = self._global_container_stack.getTop()

        for setting_instance in global_user_container.findInstances():
            setting_key = setting_instance.definition.key
            settable_per_extruder = self._global_container_stack.getProperty(
                setting_key, "settable_per_extruder")

            if settable_per_extruder:
                limit_to_extruder = int(
                    self._global_container_stack.getProperty(
                        setting_key, "limit_to_extruder"))
                extruder_stack = extruder_stacks[max(0, limit_to_extruder)]
                extruder_stack.getTop().setProperty(
                    setting_key, "value",
                    global_user_container.getProperty(setting_key, "value"))
                global_user_container.removeInstance(setting_key)

        self.forceUpdate()
Ejemplo n.º 23
0
    def run(self):
        self._scene.acquireLock()

        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.callDecoration("getLayerData"):
                node.getParent().removeChild(node)
                break

        object_groups = []
        if self._profile.getSettingValue("print_sequence") == "one_at_a_time":
            for node in OneAtATimeIterator(self._scene.getRoot()):
                temp_list = []

                if getattr(node, "_outside_buildarea", False):
                    continue

                children = node.getAllChildren()
                children.append(node)
                for child_node in children:
                    if type(child_node) is SceneNode and child_node.getMeshData(
                    ) and child_node.getMeshData().getVertices() is not None:
                        temp_list.append(child_node)

                if temp_list:
                    object_groups.append(temp_list)
                Job.yieldThread()
            if len(object_groups) == 0:
                Logger.log(
                    "w",
                    "No objects suitable for one at a time found, or no correct order found"
                )
        else:
            temp_list = []
            for node in DepthFirstIterator(self._scene.getRoot()):
                if type(node) is SceneNode and node.getMeshData(
                ) and node.getMeshData().getVertices() is not None:
                    if not getattr(node, "_outside_buildarea", False):
                        temp_list.append(node)
                Job.yieldThread()

            if temp_list:
                object_groups.append(temp_list)

        self._scene.releaseLock()

        if not object_groups:
            return

        self._sendSettings(self._profile)

        slice_message = self._socket.createMessage("cura.proto.Slice")

        for group in object_groups:
            group_message = slice_message.addRepeatedMessage("object_lists")
            if group[0].getParent().callDecoration("isGroup"):
                self._handlePerObjectSettings(group[0].getParent(),
                                              group_message)
            for current_object in group:
                mesh_data = current_object.getMeshData().getTransformed(
                    current_object.getWorldTransformation())

                obj = group_message.addRepeatedMessage("objects")
                obj.id = id(current_object)

                verts = numpy.array(mesh_data.getVertices())
                verts[:, [1, 2]] = verts[:, [2, 1]]
                verts[:, 1] *= -1

                obj.vertices = verts

                self._handlePerObjectSettings(current_object, obj)

                Job.yieldThread()

        Logger.log("d", "Sending data to engine for slicing.")
        self._socket.sendMessage(slice_message)
        Logger.log("d", "Sending data to engine is completed")
        self.setResult(True)
Ejemplo n.º 24
0
    def beginRendering(self):
        scene = self.getController().getScene()
        renderer = self.getRenderer()

        if not self._enabled_shader:
            self._enabled_shader = OpenGL.getInstance().createShaderProgram(
                Resources.getPath(Resources.Shaders, "overhang.shader"))

        if not self._disabled_shader:
            self._disabled_shader = OpenGL.getInstance().createShaderProgram(
                Resources.getPath(Resources.Shaders, "striped.shader"))
            self._disabled_shader.setUniformValue("u_diffuseColor1",
                                                  [0.48, 0.48, 0.48, 1.0])
            self._disabled_shader.setUniformValue("u_diffuseColor2",
                                                  [0.68, 0.68, 0.68, 1.0])
            self._disabled_shader.setUniformValue("u_width", 50.0)

        multi_extrusion = False

        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if global_container_stack:
            if Preferences.getInstance().getValue("view/show_overhang"):
                angle = global_container_stack.getProperty(
                    "support_angle", "value")
                # Make sure the overhang angle is valid before passing it to the shader
                # Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None)
                if angle is not None and global_container_stack.getProperty(
                        "support_angle",
                        "validationState") in [None, ValidatorState.Valid]:
                    self._enabled_shader.setUniformValue(
                        "u_overhangAngle", math.cos(math.radians(90 - angle)))
                else:
                    self._enabled_shader.setUniformValue(
                        "u_overhangAngle", math.cos(math.radians(0))
                    )  #Overhang angle of 0 causes no area at all to be marked as overhang.
            else:
                self._enabled_shader.setUniformValue("u_overhangAngle",
                                                     math.cos(math.radians(0)))

            multi_extrusion = global_container_stack.getProperty(
                "machine_extruder_count", "value") > 1

        for node in DepthFirstIterator(scene.getRoot()):
            if not node.render(renderer):
                if node.getMeshData() and node.isVisible():

                    uniforms = {}
                    if not multi_extrusion:
                        if global_container_stack:
                            material = global_container_stack.findContainer(
                                {"type": "material"})
                            material_color = material.getMetaDataEntry(
                                "color_code",
                                default=self._extruders_model.defaultColors[0]
                            ) if material else self._extruders_model.defaultColors[
                                0]
                        else:
                            material_color = self._extruders_model.defaultColors[
                                0]
                    else:
                        # Get color to render this mesh in from ExtrudersModel
                        extruder_index = 0
                        extruder_id = node.callDecoration("getActiveExtruder")
                        if extruder_id:
                            extruder_index = max(
                                0,
                                self._extruders_model.find("id", extruder_id))

                        material_color = self._extruders_model.getItem(
                            extruder_index)["color"]
                    try:
                        # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
                        # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
                        uniforms["diffuse_color"] = [
                            int(material_color[1:3], 16) / 255,
                            int(material_color[3:5], 16) / 255,
                            int(material_color[5:7], 16) / 255, 1.0
                        ]
                    except ValueError:
                        pass

                    if hasattr(node, "_outside_buildarea"):
                        if node._outside_buildarea:
                            renderer.queueNode(node,
                                               shader=self._disabled_shader)
                        else:
                            renderer.queueNode(node,
                                               shader=self._enabled_shader,
                                               uniforms=uniforms)
                    else:
                        renderer.queueNode(node,
                                           material=self._enabled_shader,
                                           uniforms=uniforms)
                if node.callDecoration("isGroup") and Selection.isSelected(
                        node):
                    renderer.queueNode(scene.getRoot(),
                                       mesh=node.getBoundingBoxMesh(),
                                       mode=Renderer.RenderLines)
Ejemplo n.º 25
0
    def getUsedExtruderStacks(self) -> List["ExtruderStack"]:
        global_stack = self._application.getGlobalContainerStack()
        container_registry = ContainerRegistry.getInstance()

        used_extruder_stack_ids = set()

        # Get the extruders of all meshes in the scene
        support_enabled = False
        support_bottom_enabled = False
        support_roof_enabled = False

        scene_root = self._application.getController().getScene().getRoot()

        # If no extruders are registered in the extruder manager yet, return an empty array
        if len(self.extruderIds) == 0:
            return []

        # Get the extruders of all printable meshes in the scene
        meshes = [
            node for node in DepthFirstIterator(scene_root)
            if isinstance(node, SceneNode) and node.isSelectable()
        ]  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.

        # Exclude anti-overhang meshes
        mesh_list = []
        for mesh in meshes:
            stack = mesh.callDecoration("getStack")
            if stack is not None and (
                    stack.getProperty("anti_overhang_mesh", "value")
                    or stack.getProperty("support_mesh", "value")):
                continue
            mesh_list.append(mesh)

        for mesh in mesh_list:
            extruder_stack_id = mesh.callDecoration("getActiveExtruder")
            if not extruder_stack_id:
                # No per-object settings for this node
                extruder_stack_id = self.extruderIds["0"]
            used_extruder_stack_ids.add(extruder_stack_id)

            # Get whether any of them use support.
            stack_to_use = mesh.callDecoration(
                "getStack")  # if there is a per-mesh stack, we use it
            if not stack_to_use:
                # if there is no per-mesh stack, we use the build extruder for this mesh
                stack_to_use = container_registry.findContainerStacks(
                    id=extruder_stack_id)[0]

            support_enabled |= stack_to_use.getProperty(
                "support_enable", "value")
            support_bottom_enabled |= stack_to_use.getProperty(
                "support_bottom_enable", "value")
            support_roof_enabled |= stack_to_use.getProperty(
                "support_roof_enable", "value")

            # Check limit to extruders
            limit_to_extruder_feature_list = [
                "wall_0_extruder_nr",
                "wall_x_extruder_nr",
                "roofing_extruder_nr",
                "top_bottom_extruder_nr",
                "infill_extruder_nr",
            ]
            for extruder_nr_feature_name in limit_to_extruder_feature_list:
                extruder_nr = int(
                    global_stack.getProperty(extruder_nr_feature_name,
                                             "value"))
                if extruder_nr == -1:
                    continue
                if str(extruder_nr) not in self.extruderIds:
                    extruder_nr = int(self._application.getMachineManager().
                                      defaultExtruderPosition)
                used_extruder_stack_ids.add(self.extruderIds[str(extruder_nr)])

        # Check support extruders
        if support_enabled:
            used_extruder_stack_ids.add(
                self.extruderIds[self.extruderValueWithDefault(
                    str(
                        global_stack.getProperty("support_infill_extruder_nr",
                                                 "value")))])
            used_extruder_stack_ids.add(
                self.extruderIds[self.extruderValueWithDefault(
                    str(
                        global_stack.getProperty("support_extruder_nr_layer_0",
                                                 "value")))])
            if support_bottom_enabled:
                used_extruder_stack_ids.add(
                    self.extruderIds[self.extruderValueWithDefault(
                        str(
                            global_stack.getProperty(
                                "support_bottom_extruder_nr", "value")))])
            if support_roof_enabled:
                used_extruder_stack_ids.add(
                    self.extruderIds[self.extruderValueWithDefault(
                        str(
                            global_stack.getProperty(
                                "support_roof_extruder_nr", "value")))])

        # The platform adhesion extruder. Not used if using none.
        if global_stack.getProperty("adhesion_type", "value") != "none" or (
                global_stack.getProperty("prime_tower_brim_enable", "value")
                and
                global_stack.getProperty("adhesion_type", "value") != 'raft'):
            extruder_str_nr = str(
                global_stack.getProperty("adhesion_extruder_nr", "value"))
            if extruder_str_nr == "-1":
                extruder_str_nr = self._application.getMachineManager(
                ).defaultExtruderPosition
            if extruder_str_nr in self.extruderIds:
                used_extruder_stack_ids.add(self.extruderIds[extruder_str_nr])

        try:
            return [
                container_registry.findContainerStacks(id=stack_id)[0]
                for stack_id in used_extruder_stack_ids
            ]
        except IndexError:  # One or more of the extruders was not found.
            Logger.log("e",
                       "Unable to find one or more of the extruders in %s",
                       used_extruder_stack_ids)
            return []
Ejemplo n.º 26
0
    def _onWriteStarted(self, output_device):
        try:
            if not Preferences.getInstance().getValue("info/send_slice_info"):
                Logger.log("d", "'info/send_slice_info' is turned off.")
                return  # Do nothing, user does not want to send data

            # Listing all files placed on the buildplate
            modelhashes = []
            for node in DepthFirstIterator(CuraApplication.getInstance(
            ).getController().getScene().getRoot()):
                if node.callDecoration("isSliceable"):
                    modelhashes.append(node.getMeshData().getHash())

            # Creating md5sums and formatting them as discussed on JIRA
            modelhash_formatted = ",".join(modelhashes)

            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()

            # Get total material used (in mm^3)
            print_information = Application.getInstance().getPrintInformation()
            material_radius = 0.5 * global_container_stack.getProperty(
                "material_diameter", "value")

            # Send material per extruder
            material_used = [
                str(math.pi * material_radius * material_radius *
                    material_length)
                for material_length in print_information.materialLengths
            ]
            material_used = ",".join(material_used)

            containers = {"": global_container_stack.serialize()}
            for container in global_container_stack.getContainers():
                container_id = container.getId()
                try:
                    container_serialized = container.serialize()
                except NotImplementedError:
                    Logger.log("w", "Container %s could not be serialized!",
                               container_id)
                    continue
                if container_serialized:
                    containers[container_id] = container_serialized
                else:
                    Logger.log("i", "No data found in %s to be serialized!",
                               container_id)

            # Bundle the collected data
            submitted_data = {
                "processor":
                platform.processor(),
                "machine":
                platform.machine(),
                "platform":
                platform.platform(),
                "settings":
                json.dumps(
                    containers
                ),  # bundle of containers with their serialized contents
                "version":
                Application.getInstance().getVersion(),
                "modelhash":
                modelhash_formatted,
                "printtime":
                print_information.currentPrintTime.getDisplayString(
                    DurationFormat.Format.ISO8601),
                "filament":
                material_used,
                "language":
                Preferences.getInstance().getValue("general/language"),
            }

            # Convert data to bytes
            submitted_data = urllib.parse.urlencode(submitted_data)
            binary_data = submitted_data.encode("utf-8")

            # Sending slice info non-blocking
            reportJob = SliceInfoJob(self.info_url, binary_data)
            reportJob.start()
        except Exception as e:
            # We really can't afford to have a mistake here, as this would break the sending of g-code to a device
            # (Either saving or directly to a printer). The functionality of the slice data is not *that* important.
            Logger.log(
                "e",
                "Exception raised while sending slice info: %s" % (repr(e))
            )  # But we should be notified about these problems of course.
Ejemplo n.º 27
0
    def _onWriteStarted(self, output_device):
        try:
            if not Preferences.getInstance().getValue("info/send_slice_info"):
                Logger.log("d", "'info/send_slice_info' is turned off.")
                return  # Do nothing, user does not want to send data

            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()

            # Get total material used (in mm^3)
            print_information = Application.getInstance().getPrintInformation()
            material_radius = 0.5 * global_container_stack.getProperty(
                "material_diameter", "value")

            # TODO: Send material per extruder instead of mashing it on a pile
            material_used = math.pi * material_radius * material_radius * sum(
                print_information.materialLengths
            )  #Volume of all materials used

            # Get model information (bounding boxes, hashes and transformation matrix)
            models_info = []
            for node in DepthFirstIterator(Application.getInstance(
            ).getController().getScene().getRoot()):
                if type(node) is SceneNode and node.getMeshData(
                ) and node.getMeshData().getVertices() is not None:
                    if not getattr(node, "_outside_buildarea", False):
                        model_info = {}
                        model_info["hash"] = node.getMeshData().getHash()
                        model_info["bounding_box"] = {}
                        model_info["bounding_box"]["minimum"] = {}
                        model_info["bounding_box"]["minimum"][
                            "x"] = node.getBoundingBox().minimum.x
                        model_info["bounding_box"]["minimum"][
                            "y"] = node.getBoundingBox().minimum.y
                        model_info["bounding_box"]["minimum"][
                            "z"] = node.getBoundingBox().minimum.z

                        model_info["bounding_box"]["maximum"] = {}
                        model_info["bounding_box"]["maximum"][
                            "x"] = node.getBoundingBox().maximum.x
                        model_info["bounding_box"]["maximum"][
                            "y"] = node.getBoundingBox().maximum.y
                        model_info["bounding_box"]["maximum"][
                            "z"] = node.getBoundingBox().maximum.z
                        model_info["transformation"] = str(
                            node.getWorldTransformation().getData())

                        models_info.append(model_info)

            # Bundle the collected data
            submitted_data = {
                "processor":
                platform.processor(),
                "machine":
                platform.machine(),
                "platform":
                platform.platform(),
                "settings":
                global_container_stack.serialize(
                ),  # global_container with references on used containers
                "version":
                Application.getInstance().getVersion(),
                "modelhash":
                "None",
                "printtime":
                print_information.currentPrintTime.getDisplayString(
                    DurationFormat.Format.ISO8601),
                "filament":
                material_used,
                "language":
                Preferences.getInstance().getValue("general/language"),
            }
            for container in global_container_stack.getContainers():
                container_id = container.getId()
                try:
                    container_serialized = container.serialize()
                except NotImplementedError:
                    Logger.log("w", "Container %s could not be serialized!",
                               container_id)
                    continue

                if container_serialized:
                    submitted_data["settings_%s" % (
                        container_id
                    )] = container_serialized  # This can be anything, eg. INI, JSON, etc.
                else:
                    Logger.log("i", "No data found in %s to be serialized!",
                               container_id)

            # Convert data to bytes
            submitted_data = urllib.parse.urlencode(submitted_data)
            binary_data = submitted_data.encode("utf-8")

            # Sending slice info non-blocking
            reportJob = SliceInfoJob(self.info_url, binary_data)
            reportJob.start()
        except Exception as e:
            # We really can't afford to have a mistake here, as this would break the sending of g-code to a device
            # (Either saving or directly to a printer). The functionality of the slice data is not *that* important.
            Logger.log(
                "e",
                "Exception raised while sending slice info: %s" % (repr(e))
            )  # But we should be notified about these problems of course.
Ejemplo n.º 28
0
    def _onWriteStarted(self, output_device):
        if not Preferences.getInstance().getValue("info/send_slice_info"):
            return  # Do nothing, user does not want to send data
        settings = Application.getInstance().getMachineManager(
        ).getActiveProfile()

        # Load all machine definitions and put them in machine_settings dict
        #setting_file_name = Application.getInstance().getActiveMachineInstance().getMachineSettings()._json_file
        machine_settings = {}
        #with open(setting_file_name, "rt", -1, "utf-8") as f:
        #    data = json.load(f, object_pairs_hook = collections.OrderedDict)
        #machine_settings[os.path.basename(setting_file_name)] = copy.deepcopy(data)
        active_machine_definition = Application.getInstance(
        ).getMachineManager().getActiveMachineInstance().getMachineDefinition(
        )
        data = active_machine_definition._json_data
        # Loop through inherited json files
        setting_file_name = active_machine_definition._path
        while True:
            if "inherits" in data:
                inherited_setting_file_name = os.path.dirname(
                    setting_file_name) + "/" + data["inherits"]
                with open(inherited_setting_file_name, "rt", -1, "utf-8") as f:
                    data = json.load(f,
                                     object_pairs_hook=collections.OrderedDict)
                machine_settings[os.path.basename(
                    inherited_setting_file_name)] = copy.deepcopy(data)
            else:
                break

        profile_values = settings.getChangedSettings()

        # Get total material used (in mm^3)
        print_information = Application.getInstance().getPrintInformation()
        material_radius = 0.5 * settings.getSettingValue("material_diameter")
        material_used = math.pi * material_radius * material_radius * print_information.materialAmount  #Volume of material used

        # Get model information (bounding boxes, hashes and transformation matrix)
        models_info = []
        for node in DepthFirstIterator(Application.getInstance().getController(
        ).getScene().getRoot()):
            if type(node) is SceneNode and node.getMeshData(
            ) and node.getMeshData().getVertices() is not None:
                if not getattr(node, "_outside_buildarea", False):
                    model_info = {}
                    model_info["hash"] = node.getMeshData().getHash()
                    model_info["bounding_box"] = {}
                    model_info["bounding_box"]["minimum"] = {}
                    model_info["bounding_box"]["minimum"][
                        "x"] = node.getBoundingBox().minimum.x
                    model_info["bounding_box"]["minimum"][
                        "y"] = node.getBoundingBox().minimum.y
                    model_info["bounding_box"]["minimum"][
                        "z"] = node.getBoundingBox().minimum.z

                    model_info["bounding_box"]["maximum"] = {}
                    model_info["bounding_box"]["maximum"][
                        "x"] = node.getBoundingBox().maximum.x
                    model_info["bounding_box"]["maximum"][
                        "y"] = node.getBoundingBox().maximum.y
                    model_info["bounding_box"]["maximum"][
                        "z"] = node.getBoundingBox().maximum.z
                    model_info["transformation"] = str(
                        node.getWorldTransformation().getData())

                    models_info.append(model_info)

        # Bundle the collected data
        submitted_data = {
            "processor": platform.processor(),
            "machine": platform.machine(),
            "platform": platform.platform(),
            "machine_settings": json.dumps(machine_settings),
            "version": Application.getInstance().getVersion(),
            "modelhash": "None",
            "printtime": str(print_information.currentPrintTime),
            "filament": material_used,
            "language": Preferences.getInstance().getValue("general/language"),
            "materials_profiles ": {}
        }

        # Convert data to bytes
        submitted_data = urllib.parse.urlencode(submitted_data)
        binary_data = submitted_data.encode("utf-8")

        # Submit data
        try:
            f = urllib.request.urlopen(
                "https://stats.youmagine.com/curastats/slice",
                data=binary_data,
                timeout=1)
        except Exception as e:
            print("Exception occured", e)

        f.close()
Ejemplo n.º 29
0
    def snapshot(width = 300, height = 300):
        scene = Application.getInstance().getController().getScene()
        active_camera = scene.getActiveCamera()
        render_width, render_height = active_camera.getWindowSize()
        render_width = int(render_width)
        render_height = int(render_height)
        preview_pass = PreviewPass(render_width, render_height)

        root = scene.getRoot()
        camera = Camera("snapshot", root)

        # determine zoom and look at
        bbox = None
        for node in DepthFirstIterator(root):
            if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible() and not node.callDecoration("isNonThumbnailVisibleMesh"):
                if bbox is None:
                    bbox = node.getBoundingBox()
                else:
                    bbox = bbox + node.getBoundingBox()

        # If there is no bounding box, it means that there is no model in the buildplate
        if bbox is None:
            return None

        look_at = bbox.center
        # guessed size so the objects are hopefully big
        size = max(bbox.width, bbox.height, bbox.depth * 0.5)

        # Looking from this direction (x, y, z) in OGL coordinates
        looking_from_offset = Vector(-1, 1, 2)
        if size > 0:
            # determine the watch distance depending on the size
            looking_from_offset = looking_from_offset * size * 1.3
        camera.setPosition(look_at + looking_from_offset)
        camera.lookAt(look_at)

        satisfied = False
        size = None
        fovy = 30

        while not satisfied:
            if size is not None:
                satisfied = True  # always be satisfied after second try
            projection_matrix = Matrix()
            # Somehow the aspect ratio is also influenced in reverse by the screen width/height
            # So you have to set it to render_width/render_height to get 1
            projection_matrix.setPerspective(fovy, render_width / render_height, 1, 500)
            camera.setProjectionMatrix(projection_matrix)
            preview_pass.setCamera(camera)
            preview_pass.render()
            pixel_output = preview_pass.getOutput()

            min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output)

            size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height)
            if size > 0.5 or satisfied:
                satisfied = True
            else:
                # make it big and allow for some empty space around
                fovy *= 0.5  # strangely enough this messes up the aspect ratio: fovy *= size * 1.1

        # make it a square
        if max_x - min_x >= max_y - min_y:
            # make y bigger
            min_y, max_y = int((max_y + min_y) / 2 - (max_x - min_x) / 2), int((max_y + min_y) / 2 + (max_x - min_x) / 2)
        else:
            # make x bigger
            min_x, max_x = int((max_x + min_x) / 2 - (max_y - min_y) / 2), int((max_x + min_x) / 2 + (max_y - min_y) / 2)
        cropped_image = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y)

        # Scale it to the correct size
        scaled_image = cropped_image.scaled(
            width, height,
            aspectRatioMode = QtCore.Qt.IgnoreAspectRatio,
            transformMode = QtCore.Qt.SmoothTransformation)

        return scaled_image
Ejemplo n.º 30
0
    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)