From ab6effb712522c3e96225663d2b95832e375a58c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 16 Jan 2020 12:58:01 +0100 Subject: [PATCH 1/3] No longer use sceneLock for startSliceJob --- plugins/CuraEngineBackend/StartSliceJob.py | 247 ++++++++++----------- 1 file changed, 123 insertions(+), 124 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index d30a77177f..26b78dbfbd 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -171,146 +171,145 @@ class StartSliceJob(Job): 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: - # Singe we walk through all nodes in the scene, they always have a parent. - cast(SceneNode, node.getParent()).removeChild(node) - break + # 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: + # 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 = [] - 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 + # Node can't be printed, so don't bother sending it. + if getattr(node, "_outside_buildarea", False): + continue - temp_list.append(node) - if not is_non_printing_mesh: - has_printing_mesh = True + # 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 - 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() + 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")) - global_stack = CuraApplication.getInstance().getGlobalContainerStack() - if not global_stack: - return - extruders_enabled = {position: stack.isEnabled for position, stack in global_stack.extruders.items()} - 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 = 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 = {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 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: + # 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 - 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 + temp_list.append(node) + if not is_non_printing_mesh: + has_printing_mesh = True - # Convert from Y up axes to Z up axes. Equals a 90 degree rotation. - verts[:, [1, 2]] = verts[:, [2, 1]] - verts[:, 1] *= -1 + Job.yieldThread() - 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) + # 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() - obj.vertices = flat_verts + if temp_list: + object_groups.append(temp_list) - self._handlePerObjectSettings(cast(CuraSceneNode, object), obj) + global_stack = CuraApplication.getInstance().getGlobalContainerStack() + if not global_stack: + return + extruders_enabled = {position: stack.isEnabled for position, stack in global_stack.extruders.items()} + 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 = 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) - Job.yieldThread() + 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 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] + + # 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) From f5a64704bd040aca0765142568b5b27c4ed43472 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 16 Jan 2020 13:11:22 +0100 Subject: [PATCH 2/3] Fix branch checkout for PRs with GitHub workflow --- .github/workflows/cicd.yml | 1 + docker/build.sh | 46 +++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 40acbc44f3..ff0923f9b6 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -6,6 +6,7 @@ on: - master - 'WIP**' - '4.*' + - 'CURA-*' pull_request: jobs: build: diff --git a/docker/build.sh b/docker/build.sh index 6aa0678ca3..5b035ca08a 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -17,24 +17,40 @@ cd "${PROJECT_DIR}" # Clone Uranium and set PYTHONPATH first # -# Check the branch to use: -# 1. Use the Uranium branch with the branch same if it exists. -# 2. Otherwise, use the default branch name "master" +# Check the branch to use for Uranium. +# It tries the following branch names and uses the first one that's available. +# - GITHUB_HEAD_REF: the branch name of a PR. If it's not a PR, it will be empty. +# - GITHUB_BASE_REF: the branch a PR is based on. If it's not a PR, it will be empty. +# - GITHUB_REF: the branch name if it's a branch on the repository; +# refs/pull/123/merge if it's a pull_request. +# - master: the master branch. It should always exist. + +# For debugging. echo "GITHUB_REF: ${GITHUB_REF}" +echo "GITHUB_HEAD_REF: ${GITHUB_HEAD_REF}" echo "GITHUB_BASE_REF: ${GITHUB_BASE_REF}" -GIT_REF_NAME="${GITHUB_REF}" -if [ -n "${GITHUB_BASE_REF}" ]; then - GIT_REF_NAME="${GITHUB_BASE_REF}" -fi -GIT_REF_NAME="$(basename "${GIT_REF_NAME}")" - -URANIUM_BRANCH="${GIT_REF_NAME:-master}" -output="$(git ls-remote --heads https://github.com/Ultimaker/Uranium.git "${URANIUM_BRANCH}")" -if [ -z "${output}" ]; then - echo "Could not find Uranium banch ${URANIUM_BRANCH}, fallback to use master." - URANIUM_BRANCH="master" -fi +GIT_REF_NAME_LIST=( "${GITHUB_HEAD_REF}" "${GITHUB_BASE_REF}" "${GITHUB_REF}" "master" ) +for git_ref_name in "${GIT_REF_NAME_LIST[@]}" +do + if [ -z "${git_ref_name}" ]; then + continue + fi + git_ref_name="$(basename "${git_ref_name}")" + # Skip refs/pull/1234/merge as pull requests use it as GITHUB_REF + if [[ "${git_ref_name}" == "merge" ]]; then + echo "Skip [${git_ref_name}]" + continue + fi + URANIUM_BRANCH="${git_ref_name}" + output="$(git ls-remote --heads https://github.com/Ultimaker/Uranium.git "${URANIUM_BRANCH}")" + if [ -n "${output}" ]; then + echo "Found Uranium branch [${URANIUM_BRANCH}]." + break + else + echo "Could not find Uranium banch [${URANIUM_BRANCH}], try next." + fi +done echo "Using Uranium branch ${URANIUM_BRANCH} ..." git clone --depth=1 -b "${URANIUM_BRANCH}" https://github.com/Ultimaker/Uranium.git "${PROJECT_DIR}"/Uranium From d18c0703b49aba38da18810c1df226166f61cf63 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 16 Jan 2020 13:59:02 +0100 Subject: [PATCH 3/3] Fix getting correct initial extruder number As found in the discussion in #6847. This was done as a 5 minute fix. --- cura/Settings/ExtruderManager.py | 21 ++++++++++++++++++++- plugins/CuraEngineBackend/StartSliceJob.py | 7 ++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 62bf396878..e0ec6c4d14 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt. @@ -275,6 +275,25 @@ class ExtruderManager(QObject): Logger.log("e", "Unable to find one or more of the extruders in %s", used_extruder_stack_ids) return [] + ## Get the extruder that the print will start with. + # + # This should mirror the implementation in CuraEngine of + # ``FffGcodeWriter::getStartExtruder()``. + def getInitialExtruderNr(self) -> int: + application = cura.CuraApplication.CuraApplication.getInstance() + global_stack = application.getGlobalContainerStack() + + # Starts with the adhesion extruder. + if global_stack.getProperty("adhesion_type", "value") != "none": + return global_stack.getProperty("adhesion_extruder_nr", "value") + + # No adhesion? Well maybe there is still support brim. + if (global_stack.getProperty("support_enable", "value") or global_stack.getProperty("support_tree_enable", "value")) and global_stack.getProperty("support_brim_enable", "value"): + return global_stack.getProperty("support_infill_extruder_nr", "value") + + # REALLY no adhesion? Use the first used extruder. + return self.getUsedExtruderStacks()[0].getProperty("extruder_nr", "value") + ## Removes the container stack and user profile for the extruders for a specific machine. # # \param machine_id The machine to remove the extruders for. diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 26b78dbfbd..c6841c6ea9 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import numpy @@ -343,10 +343,7 @@ class StartSliceJob(Job): result["time"] = time.strftime("%H:%M:%S") #Some extra settings. result["date"] = time.strftime("%d-%m-%Y") result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))] - - initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0] - initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value") - result["initial_extruder_nr"] = initial_extruder_nr + result["initial_extruder_nr"] = CuraApplication.getInstance().getExtruderManager().getInitialExtruderNr() return result