diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index aef051c838..387bf92688 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -1,5 +1,6 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from PyQt5.QtCore import QCoreApplication from UM.Application import Application from UM.Job import Job @@ -94,6 +95,7 @@ class ArrangeObjectsJob(Job): status_message.setProgress((idx + 1) / len(nodes_arr) * 100) Job.yieldThread() + QCoreApplication.processEvents() grouped_operation.push() diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 315eca9fe5..c858897942 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -92,6 +92,8 @@ class BuildVolume(SceneNode): self._adhesion_type = None # type: Any self._platform = Platform(self) + self._edge_disallowed_size = None + self._build_volume_message = Message(catalog.i18nc("@info:status", "The build volume height has been reduced due to the value of the" " \"Print Sequence\" setting to prevent the gantry from colliding" @@ -106,8 +108,6 @@ class BuildVolume(SceneNode): self._application.globalContainerStackChanged.connect(self._onStackChanged) - self._onStackChanged() - self._engine_ready = False self._application.engineCreatedSignal.connect(self._onEngineCreated) @@ -118,7 +118,7 @@ class BuildVolume(SceneNode): self._scene_objects = set() # type: Set[SceneNode] self._scene_change_timer = QTimer() - self._scene_change_timer.setInterval(100) + self._scene_change_timer.setInterval(200) self._scene_change_timer.setSingleShot(True) self._scene_change_timer.timeout.connect(self._onSceneChangeTimerFinished) @@ -747,6 +747,7 @@ class BuildVolume(SceneNode): self._error_areas = [] used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks() + self._edge_disallowed_size = None # Force a recalculation disallowed_border_size = self.getEdgeDisallowedSize() result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) # Normal machine disallowed areas can always be added. @@ -1124,6 +1125,9 @@ class BuildVolume(SceneNode): if not self._global_container_stack or not self._global_container_stack.extruderList: return 0 + if self._edge_disallowed_size is not None: + return self._edge_disallowed_size + container_stack = self._global_container_stack used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks() @@ -1139,8 +1143,8 @@ class BuildVolume(SceneNode): # Now combine our different pieces of data to get the final border size. # Support expansion is added to the bed adhesion, since the bed adhesion goes around support. # Support expansion is added to farthest shield distance, since the shields go around support. - border_size = max(move_from_wall_radius, support_expansion + farthest_shield_distance, support_expansion + bed_adhesion_size) - return border_size + self._edge_disallowed_size = max(move_from_wall_radius, support_expansion + farthest_shield_distance, support_expansion + bed_adhesion_size) + return self._edge_disallowed_size def _clamp(self, value, min_value, max_value): return max(min(value, max_value), min_value) diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py index bc9ef723d4..818d62de7c 100644 --- a/cura/Machines/MachineErrorChecker.py +++ b/cura/Machines/MachineErrorChecker.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import time @@ -14,6 +14,7 @@ from UM.Settings.Validator import ValidatorState import cura.CuraApplication + class MachineErrorChecker(QObject): """This class performs setting error checks for the currently active machine. @@ -50,6 +51,8 @@ class MachineErrorChecker(QObject): self._error_check_timer.setInterval(100) self._error_check_timer.setSingleShot(True) + self._keys_to_check = set() # type: Set[str] + def initialize(self) -> None: self._error_check_timer.timeout.connect(self._rescheduleCheck) @@ -103,6 +106,7 @@ class MachineErrorChecker(QObject): if property_name != "value": return + self._keys_to_check.add(key) self.startErrorCheck() def startErrorCheck(self, *args: Any) -> None: @@ -140,7 +144,10 @@ class MachineErrorChecker(QObject): # Populate the (stack, key) tuples to check self._stacks_and_keys_to_check = deque() for stack in global_stack.extruderList: - for key in stack.getAllKeys(): + if not self._keys_to_check: + self._keys_to_check = stack.getAllKeys() + + for key in self._keys_to_check: self._stacks_and_keys_to_check.append((stack, key)) self._application.callLater(self._checkStack) @@ -181,18 +188,25 @@ class MachineErrorChecker(QObject): validator = validator_type(key) validation_state = validator(stack) if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid): - # Finish - self._setResult(True) + # Since we don't know if any of the settings we didn't check is has an error value, store the list for the + # next check. + keys_to_recheck = {setting_key for stack, setting_key in self._stacks_and_keys_to_check} + keys_to_recheck.add(key) + self._setResult(True, keys_to_recheck = keys_to_recheck) return # Schedule the check for the next key self._application.callLater(self._checkStack) - def _setResult(self, result: bool) -> None: + def _setResult(self, result: bool, keys_to_recheck = None) -> None: if result != self._has_errors: self._has_errors = result self.hasErrorUpdated.emit() self._machine_manager.stacksValidationChanged.emit() + if keys_to_recheck is None: + self._keys_to_check = set() + else: + self._keys_to_check = keys_to_recheck self._need_to_check = False self._check_in_progress = False self.needToWaitForResultChanged.emit() diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 134e579746..7507f2520e 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -4,6 +4,8 @@ import copy from typing import List +from PyQt5.QtCore import QCoreApplication + from UM.Job import Job from UM.Operations.GroupedOperation import GroupedOperation from UM.Message import Message @@ -93,8 +95,9 @@ class MultiplyObjectsJob(Job): nodes.append(new_node) current_progress += 1 status_message.setProgress((current_progress / total_progress) * 100) + QCoreApplication.processEvents() Job.yieldThread() - + QCoreApplication.processEvents() Job.yieldThread() if nodes: diff --git a/cura/PickingPass.py b/cura/PickingPass.py index 13a115f64b..6ffc63cbd4 100644 --- a/cura/PickingPass.py +++ b/cura/PickingPass.py @@ -54,7 +54,7 @@ class PickingPass(RenderPass): # Fill up the batch with objects that can be sliced. ` for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible(): - batch.addItem(node.getWorldTransformation(), node.getMeshData()) + batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData()) self.bind() batch.render(self._scene.getActiveCamera()) diff --git a/cura/PreviewPass.py b/cura/PreviewPass.py index 9cae5ad9bd..ba139bb2b3 100644 --- a/cura/PreviewPass.py +++ b/cura/PreviewPass.py @@ -114,12 +114,12 @@ class PreviewPass(RenderPass): 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) + batch_support_mesh.addItem(node.getWorldTransformation(copy = False), 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) + batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData(), uniforms = uniforms) self.bind() diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 46caa5b9e0..6717a98dcd 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -53,6 +53,8 @@ class ConvexHullDecorator(SceneNodeDecorator): CuraApplication.getInstance().getController().toolOperationStarted.connect(self._onChanged) CuraApplication.getInstance().getController().toolOperationStopped.connect(self._onChanged) + self._root = Application.getInstance().getController().getScene().getRoot() + self._onGlobalStackChanged() def createRecomputeConvexHullTimer(self) -> None: @@ -198,23 +200,16 @@ class ConvexHullDecorator(SceneNodeDecorator): CuraApplication.getInstance().callLater(self.recomputeConvexHullDelayed) def recomputeConvexHull(self) -> None: - controller = Application.getInstance().getController() - root = controller.getScene().getRoot() - if self._node is None or controller.isToolOperationActive() or not self.__isDescendant(root, self._node): - # If the tool operation is still active, we need to compute the convex hull later after the controller is - # no longer active. - if controller.isToolOperationActive(): - self.recomputeConvexHullDelayed() - return - + if self._node is None or not self.__isDescendant(self._root, self._node): if self._convex_hull_node: + # Convex hull node still exists, but the node is removed or no longer in the scene. self._convex_hull_node.setParent(None) self._convex_hull_node = None return if self._convex_hull_node: self._convex_hull_node.setParent(None) - hull_node = ConvexHullNode.ConvexHullNode(self._node, self.getPrintingArea(), self._raft_thickness, root) + hull_node = ConvexHullNode.ConvexHullNode(self._node, self.getPrintingArea(), self._raft_thickness, self._root) self._convex_hull_node = hull_node def _onSettingValueChanged(self, key: str, property_name: str) -> None: @@ -273,7 +268,7 @@ class ConvexHullDecorator(SceneNodeDecorator): if mesh is None: return Polygon([]) # Node has no mesh data, so just return an empty Polygon. - world_transform = self._node.getWorldTransformation() + world_transform = self._node.getWorldTransformation(copy= False) # Check the cache if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform: diff --git a/cura/Scene/ConvexHullNode.py b/cura/Scene/ConvexHullNode.py index 765dae26a2..cb4cffca12 100644 --- a/cura/Scene/ConvexHullNode.py +++ b/cura/Scene/ConvexHullNode.py @@ -57,13 +57,20 @@ class ConvexHullNode(SceneNode): self._hull = hull if self._hull: hull_mesh_builder = MeshBuilder() + if self._thickness == 0: + if hull_mesh_builder.addConvexPolygon( + self._hull.getPoints()[::], # bottom layer is reversed + self._mesh_height, color = self._color): - if hull_mesh_builder.addConvexPolygonExtrusion( - self._hull.getPoints()[::-1], # bottom layer is reversed - self._mesh_height - thickness, self._mesh_height, color = self._color): + hull_mesh = hull_mesh_builder.build() + self.setMeshData(hull_mesh) + else: + if hull_mesh_builder.addConvexPolygonExtrusion( + self._hull.getPoints()[::-1], # bottom layer is reversed + self._mesh_height - thickness, self._mesh_height, color = self._color): - hull_mesh = hull_mesh_builder.build() - self.setMeshData(hull_mesh) + hull_mesh = hull_mesh_builder.build() + self.setMeshData(hull_mesh) def getHull(self): return self._hull @@ -79,15 +86,15 @@ class ConvexHullNode(SceneNode): ConvexHullNode.shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader")) ConvexHullNode.shader.setUniformValue("u_diffuseColor", self._color) ConvexHullNode.shader.setUniformValue("u_opacity", 0.6) - - if self.getParent(): - if self.getMeshData() and isinstance(self._node, SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getMultiBuildPlateModel().activeBuildPlate: - # The object itself (+ adhesion in one-at-a-time mode) - renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8) - if self._convex_hull_head_mesh: - # The full head. Rendered as a hint to the user: If this area overlaps another object A; this object - # cannot be printed after A, because the head would hit A while printing the current object - renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8) + batch = renderer.getNamedBatch("convex_hull_node") + if not batch: + batch = renderer.createRenderBatch(transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8) + renderer.addRenderBatch(batch, name = "convex_hull_node") + batch.addItem(self.getWorldTransformation(copy = False), self.getMeshData()) + if self._convex_hull_head_mesh: + # The full head. Rendered as a hint to the user: If this area overlaps another object A; this object + # cannot be printed after A, because the head would hit A while printing the current object + renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8) return True @@ -97,7 +104,3 @@ class ConvexHullNode(SceneNode): convex_hull_head_builder = MeshBuilder() convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness) self._convex_hull_head_mesh = convex_hull_head_builder.build() - - if not node: - return - diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index c22277a4b0..93a1511681 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -118,7 +118,7 @@ class CuraSceneNode(SceneNode): self._aabb = None if self._mesh_data: - self._aabb = self._mesh_data.getExtents(self.getWorldTransformation()) + self._aabb = self._mesh_data.getExtents(self.getWorldTransformation(copy = False)) else: # If there is no mesh_data, use a bounding box that encompasses the local (0,0,0) position = self.getWorldPosition() self._aabb = AxisAlignedBox(minimum = position, maximum = position) @@ -139,7 +139,7 @@ class CuraSceneNode(SceneNode): """Taken from SceneNode, but replaced SceneNode with CuraSceneNode""" copy = CuraSceneNode(no_setting_override = True) # Setting override will be added later - copy.setTransformation(self.getLocalTransformation()) + copy.setTransformation(self.getLocalTransformation(copy= False)) copy.setMeshData(self._mesh_data) copy.setVisible(cast(bool, deepcopy(self._visible, memo))) copy._selectable = cast(bool, deepcopy(self._selectable, memo)) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 2cc9ec4631..67c582ad5e 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -204,49 +204,50 @@ class ExtruderManager(QObject): # If no extruders are registered in the extruder manager yet, return an empty array if len(self.extruderIds) == 0: return [] + number_active_extruders = len([extruder for extruder in self.getActiveExtruderStacks() if extruder.isEnabled]) # 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. + nodes = [node for node in DepthFirstIterator(scene_root) if node.isSelectable() and not node.callDecoration("isAntiOverhangMesh") and not node.callDecoration("isSupportMesh")] #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") + for node in nodes: + extruder_stack_id = node.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) + if len(used_extruder_stack_ids) == number_active_extruders: + # We're already done. Stop looking. + # Especially with a lot of models on the buildplate, this will speed up things rather dramatically. + break + # Get whether any of them use support. - stack_to_use = mesh.callDecoration("getStack") # if there is a per-mesh stack, we use it + stack_to_use = node.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") + if not support_enabled: + support_enabled |= stack_to_use.getProperty("support_enable", "value") + if not support_bottom_enabled: + support_bottom_enabled |= stack_to_use.getProperty("support_bottom_enable", "value") + if not support_roof_enabled: + 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 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: diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py index 1b5fb84f4a..9d2b248ba7 100644 --- a/cura/Settings/SettingOverrideDecorator.py +++ b/cura/Settings/SettingOverrideDecorator.py @@ -35,7 +35,7 @@ class SettingOverrideDecorator(SceneNodeDecorator): """ _non_thumbnail_visible_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"} - def __init__(self): + def __init__(self, *, force_update = True): super().__init__() self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self))) self._stack.setDirty(False) # This stack does not need to be saved. @@ -46,6 +46,10 @@ class SettingOverrideDecorator(SceneNodeDecorator): self._is_non_printing_mesh = False self._is_non_thumbnail_visible_mesh = False + self._is_support_mesh = False + self._is_cutting_mesh = False + self._is_infill_mesh = False + self._is_anti_overhang_mesh = False self._stack.propertyChanged.connect(self._onSettingChanged) @@ -53,13 +57,14 @@ class SettingOverrideDecorator(SceneNodeDecorator): Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack) self.activeExtruderChanged.connect(self._updateNextStack) - self._updateNextStack() + if force_update: + self._updateNextStack() def _generateUniqueName(self): return "SettingOverrideInstanceContainer-%s" % uuid.uuid1() def __deepcopy__(self, memo): - deep_copy = SettingOverrideDecorator() + deep_copy = SettingOverrideDecorator(force_update = False) """Create a fresh decorator object""" instance_container = copy.deepcopy(self._stack.getContainer(0), memo) @@ -74,11 +79,6 @@ class SettingOverrideDecorator(SceneNodeDecorator): # Properly set the right extruder on the copy deep_copy.setActiveExtruder(self._extruder_stack) - # use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh" - # has not been updated yet. - deep_copy._is_non_printing_mesh = self._evaluateIsNonPrintingMesh() - deep_copy._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh() - return deep_copy def getActiveExtruder(self): @@ -104,7 +104,7 @@ class SettingOverrideDecorator(SceneNodeDecorator): """ # for support_meshes, always use the support_extruder - if self.getStack().getProperty("support_mesh", "value"): + if self._is_support_mesh: global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: return str(global_container_stack.getProperty("support_extruder_nr", "value")) @@ -114,6 +114,30 @@ class SettingOverrideDecorator(SceneNodeDecorator): container_stack = containers[0] return container_stack.getMetaDataEntry("position", default=None) + def isCuttingMesh(self): + return self._is_cutting_mesh + + def isSupportMesh(self): + return self._is_support_mesh + + def isInfillMesh(self): + return self._is_infill_mesh + + def isAntiOverhangMesh(self): + return self._is_anti_overhang_mesh + + def _evaluateAntiOverhangMesh(self): + return bool(self._stack.userChanges.getProperty("anti_overhang_mesh", "value")) + + def _evaluateIsCuttingMesh(self): + return bool(self._stack.userChanges.getProperty("cutting_mesh", "value")) + + def _evaluateIsSupportMesh(self): + return bool(self._stack.userChanges.getProperty("support_mesh", "value")) + + def _evaluateInfillMesh(self): + return bool(self._stack.userChanges.getProperty("infill_mesh", "value")) + def isNonPrintingMesh(self): return self._is_non_printing_mesh @@ -132,6 +156,16 @@ class SettingOverrideDecorator(SceneNodeDecorator): # Trigger slice/need slicing if the value has changed. self._is_non_printing_mesh = self._evaluateIsNonPrintingMesh() self._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh() + + if setting_key == "anti_overhang_mesh": + self._is_anti_overhang_mesh = self._evaluateAntiOverhangMesh() + elif setting_key == "support_mesh": + self._is_support_mesh = self._evaluateIsSupportMesh() + elif setting_key == "cutting_mesh": + self._is_cutting_mesh = self._evaluateIsCuttingMesh() + elif setting_key == "infill_mesh": + self._is_infill_mesh = self._evaluateInfillMesh() + Application.getInstance().getBackend().needsSlicing() Application.getInstance().getBackend().tickle() diff --git a/cura/UI/ObjectsModel.py b/cura/UI/ObjectsModel.py index 1383476665..02d4096278 100644 --- a/cura/UI/ObjectsModel.py +++ b/cura/UI/ObjectsModel.py @@ -98,7 +98,8 @@ class ObjectsModel(ListModel): return True - def _renameNodes(self, node_info_dict: Dict[str, _NodeInfo]) -> List[SceneNode]: + @staticmethod + def _renameNodes(node_info_dict: Dict[str, _NodeInfo]) -> List[SceneNode]: # Go through all names and find out the names for all nodes that need to be renamed. all_nodes = [] # type: List[SceneNode] for name, node_info in node_info_dict.items(): @@ -118,9 +119,7 @@ class ObjectsModel(ListModel): else: new_group_name = "{0}#{1}".format(name, current_index) - old_name = node.getName() node.setName(new_group_name) - Logger.log("d", "Node [%s] renamed to [%s]", old_name, new_group_name) all_nodes.append(node) return all_nodes @@ -186,11 +185,18 @@ class ObjectsModel(ListModel): if per_object_stack: per_object_settings_count = per_object_stack.getTop().getNumInstances() - for mesh_type in ["anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"]: - if per_object_stack.getProperty(mesh_type, "value"): - node_mesh_type = mesh_type - per_object_settings_count -= 1 # do not count this mesh type setting - break + if node.callDecoration("isAntiOverhangMesh"): + node_mesh_type = "anti_overhang_mesh" + per_object_settings_count -= 1 # do not count this mesh type setting + elif node.callDecoration("isSupportMesh"): + node_mesh_type = "support_mesh" + per_object_settings_count -= 1 # do not count this mesh type setting + elif node.callDecoration("isCuttingMesh"): + node_mesh_type = "cutting_mesh" + per_object_settings_count -= 1 # do not count this mesh type setting + elif node.callDecoration("isInfillMesh"): + node_mesh_type = "infill_mesh" + per_object_settings_count -= 1 # do not count this mesh type setting if per_object_settings_count > 0: if node_mesh_type == "support_mesh": diff --git a/cura/XRayPass.py b/cura/XRayPass.py index edc20ce62d..eb5f33cea2 100644 --- a/cura/XRayPass.py +++ b/cura/XRayPass.py @@ -29,7 +29,7 @@ class XRayPass(RenderPass): batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, backface_cull = False, blend_mode = RenderBatch.BlendMode.Additive) for node in DepthFirstIterator(self._scene.getRoot()): if isinstance(node, CuraSceneNode) and node.getMeshData() and node.isVisible(): - batch.addItem(node.getWorldTransformation(), node.getMeshData()) + batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData()) self.bind() diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 693b129f43..bd42d81566 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -8,12 +8,14 @@ import time from typing import Any, cast, Dict, List, Optional, Set import re import Arcus #For typing. +from PyQt5.QtCore import QCoreApplication from UM.Job import Job from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode from UM.Settings.ContainerStack import ContainerStack #For typing. from UM.Settings.InstanceContainer import InstanceContainer +from UM.Settings.Interfaces import ContainerInterface from UM.Settings.SettingDefinition import SettingDefinition from UM.Settings.SettingRelation import SettingRelation #For typing. @@ -352,8 +354,7 @@ class StartSliceJob(Job): result = {} for key in stack.getAllKeys(): - value = stack.getProperty(key, "value") - result[key] = value + result[key] = stack.getProperty(key, "value") Job.yieldThread() result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings. @@ -373,9 +374,11 @@ class StartSliceJob(Job): self._all_extruders_settings = { "-1": self._buildReplacementTokens(global_stack) } + QCoreApplication.processEvents() # Ensure that the GUI does not freeze. for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks(): extruder_nr = extruder_stack.getProperty("extruder_nr", "value") self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack) + QCoreApplication.processEvents() # Ensure that the GUI does not freeze. def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str: """Replace setting tokens in a piece of g-code. @@ -420,10 +423,15 @@ class StartSliceJob(Job): settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], extruder_nr) settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], extruder_nr) + global_definition = cast(ContainerInterface, cast(ContainerStack, stack.getNextStack()).getBottom()) + own_definition = cast(ContainerInterface, stack.getBottom()) + for key, value in settings.items(): # Do not send settings that are not settable_per_extruder. - if not stack.getProperty(key, "settable_per_extruder"): - continue + # Since these can only be set in definition files, we only have to ask there. + if not global_definition.getProperty(key, "settable_per_extruder") and \ + not own_definition.getProperty(key, "settable_per_extruder"): + continue setting = message.getMessage("settings").addRepeatedMessage("settings") setting.name = key setting.value = str(value).encode("utf-8") @@ -454,11 +462,10 @@ class StartSliceJob(Job): print_temperature_settings = ["material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"] pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(print_temperature_settings) # match {setting} as well as {setting, extruder_nr} settings["material_print_temp_prepend"] = re.search(pattern, start_gcode) == None + # Replace the setting tokens in start and end g-code. # Use values from the first used extruder by default so we get the expected temperatures - initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0] - initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value") - + initial_extruder_nr = CuraApplication.getInstance().getExtruderManager().getInitialExtruderNr() settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr) settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], initial_extruder_nr) diff --git a/plugins/PostProcessingPlugin/Script.py b/plugins/PostProcessingPlugin/Script.py index 3228870dca..1cc9b59c9c 100644 --- a/plugins/PostProcessingPlugin/Script.py +++ b/plugins/PostProcessingPlugin/Script.py @@ -141,52 +141,46 @@ class Script: All other keyword parameters are put in the result in g-code's format. For instance, if you put ``G=1`` in the parameters, it will output ``G1``. If you put ``G=1, X=100`` in the parameters, it will output - ``G1 X100``. The parameters G and M will always be put first. The - parameters T and S will be put second (or first if there is no G or M). - The rest of the parameters will be put in arbitrary order. + ``G1 X100``. The parameters will be added in order G M T S F X Y Z E. + Any other parameters will be added in arbitrary order. :param line: The original g-code line that must be modified. If not provided, an entirely new g-code line will be produced. :return: A line of g-code with the desired parameters filled in. """ - - #Strip the comment. - comment = "" + # Strip the comment. if ";" in line: comment = line[line.find(";"):] - line = line[:line.find(";")] #Strip the comment. + line = line[:line.find(";")] + else: + comment = "" - #Parse the original g-code line. + # Parse the original g-code line and add them to kwargs. for part in line.split(" "): if part == "": continue parameter = part[0] + if parameter not in kwargs: + value = part[1:] + kwargs[parameter] = value + + # Start writing the new g-code line. + line_parts = list() + # First add these parameters in order + for parameter in ["G", "M", "T", "S", "F", "X", "Y", "Z", "E"]: if parameter in kwargs: - continue #Skip this one. The user-provided parameter overwrites the one in the line. - value = part[1:] - kwargs[parameter] = value + value = kwargs.pop(parameter) # get the corresponding value and remove the parameter from kwargs + line_parts.append(parameter + str(value)) + # Then add the rest of the parameters + for parameter, value in kwargs.items(): + line_parts.append(parameter + str(value)) - #Write the new g-code line. - result = "" - priority_parameters = ["G", "M", "T", "S", "F", "X", "Y", "Z", "E"] #First some parameters that get priority. In order of priority! - for priority_key in priority_parameters: - if priority_key in kwargs: - if result != "": - result += " " - result += priority_key + str(kwargs[priority_key]) - del kwargs[priority_key] - for key, value in kwargs.items(): - if result != "": - result += " " - result += key + str(value) - - #Put the comment back in. + # If there was a comment, put it at the end. if comment != "": - if result != "": - result += " " - result += ";" + comment + line_parts.append(comment) - return result + # Add spaces and return the new line + return " ".join(line_parts) def execute(self, data: List[str]) -> List[str]: """This is called when the script is executed. diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 20a563c291..284389064c 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -199,7 +199,7 @@ class SliceInfo(QObject, Extension): "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", "")} + model["transformation"] = {"data": str(node.getWorldTransformation(copy = False).getData()).replace("\n", "")} extruder_position = node.callDecoration("getActiveExtruderPosition") model["extruder"] = 0 if extruder_position is None else int(extruder_position) diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index dc88265d68..48bd704ccb 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -64,17 +64,23 @@ class SolidView(View): self._old_layer_bindings = None self._next_xray_checking_time = time.time() - self._xray_checking_update_time = 1.0 # seconds + self._xray_checking_update_time = 30.0 # seconds self._xray_warning_cooldown = 60 * 10 # reshow Model error message every 10 minutes self._xray_warning_message = Message( catalog.i18nc("@info:status", "Your model is not manifold. The highlighted areas indicate either missing or extraneous surfaces."), lifetime = 60 * 5, # leave message for 5 minutes title = catalog.i18nc("@info:title", "Model errors"), + option_text = catalog.i18nc("@info:option_text", "Do not show this message again"), + option_state = False ) + self._xray_warning_message.optionToggled.connect(self._onDontAskMeAgain) application.getPreferences().addPreference(self._show_xray_warning_preference, True) application.engineCreatedSignal.connect(self._onGlobalContainerChanged) + def _onDontAskMeAgain(self, checked: bool) -> None: + Application.getInstance().getPreferences().setValue(self._show_xray_warning_preference, not checked) + def _onGlobalContainerChanged(self) -> None: if self._global_stack: try: @@ -103,7 +109,9 @@ class SolidView(View): except IndexError: pass else: - self._support_angle = support_angle_stack.getProperty("support_angle", "value") + angle = support_angle_stack.getProperty("support_angle", "value") + if angle is not None: + self._support_angle = angle def _checkSetup(self): if not self._extruders_model: @@ -115,6 +123,7 @@ class SolidView(View): if not self._enabled_shader: self._enabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) self._enabled_shader.setUniformValue("u_overhangColor", Color(*self._theme.getColor("model_overhang").getRgb())) + self._enabled_shader.setUniformValue("u_renderError", 0.0) if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) @@ -140,6 +149,7 @@ class SolidView(View): self._composite_pass.setCompositeShader(self._old_composite_shader) self._old_layer_bindings = None self._old_composite_shader = None + self._enabled_shader.setUniformValue("u_renderError", 0.0) # We don't want any error markers!. self._xray_warning_message.hide() else: if not self._xray_shader: @@ -157,7 +167,7 @@ class SolidView(View): # Currently the RenderPass constructor requires a size > 0 # This should be fixed in RenderPass's constructor. self._xray_pass = XRayPass.XRayPass(1, 1) - + self._enabled_shader.setUniformValue("u_renderError", 1.0) # We don't want any error markers!. renderer.addRenderPass(self._xray_pass) if not self._composite_pass: @@ -178,77 +188,77 @@ class SolidView(View): 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: + if 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))) - + disabled_batch = renderer.createRenderBatch(shader = self._disabled_shader) + normal_object_batch = renderer.createRenderBatch(shader = self._enabled_shader) + renderer.addRenderBatch(disabled_batch) + renderer.addRenderBatch(normal_object_batch) 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 + if node.render(renderer): + continue - per_mesh_stack = node.callDecoration("getStack") + if node.getMeshData() and node.isVisible(): + uniforms = {} + shade_factor = 1.0 - extruder_index = node.callDecoration("getActiveExtruderPosition") - if extruder_index is None: - extruder_index = "0" - extruder_index = int(extruder_index) + per_mesh_stack = node.callDecoration("getStack") - # 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")) + extruder_index = node.callDecoration("getActiveExtruderPosition") + if extruder_index is None: + extruder_index = "0" + extruder_index = int(extruder_index) - try: - material_color = self._extruders_model.getItem(extruder_index)["color"] - except KeyError: - material_color = self._extruders_model.defaultColors[0] + 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 + 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 - ] + 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 + # 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) + if node.callDecoration("isNonPrintingMesh"): + if per_mesh_stack and (node.callDecoration("isInfillMesh") or node.callDecoration("isCuttingMesh")): + renderer.queueNode(node, shader = self._non_printing_shader, uniforms = uniforms, transparent = True) 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) + renderer.queueNode(node, shader = self._non_printing_shader, transparent = True) + elif getattr(node, "_outside_buildarea", False): + disabled_batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData()) + elif per_mesh_stack and node.callDecoration("isSupportMesh"): + # 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: + normal_object_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData(), uniforms=uniforms) + if node.callDecoration("isGroup") and Selection.isSelected(node): + renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = RenderBatch.RenderMode.LineLoop) def endRendering(self): # check whether the xray overlay is showing badness diff --git a/resources/definitions/beamup_l.def.json b/resources/definitions/beamup_l.def.json new file mode 100644 index 0000000000..7de0481b7a --- /dev/null +++ b/resources/definitions/beamup_l.def.json @@ -0,0 +1,58 @@ +{ + "version": 2, + "name": "BeamUp L", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "BeamUp", + "manufacturer": "BeamUp", + "file_formats": "text/x-gcode", + "platform": "beamup_l.3mf", + "platform_offset": [0, -2.5, -2.5], + "has_machine_quality": true, + "has_materials": true, + "machine_extruder_trains": + { + "0": "beamup_l_extruder_0" + } + }, + + "overrides": { + "machine_name": { + "default_value": "BeamUp L" + }, + "machine_width": { + "default_value": 320 + }, + "machine_depth": { + "default_value": 320 + }, + "machine_height": { + "default_value": 300 + }, + "machine_heated_bed": { + "default_value": false + }, + "machine_center_is_zero": { + "default_value": false + }, + "machine_nozzle_heat_up_speed": { + "default_value": 2 + }, + "machine_nozzle_cool_down_speed": { + "default_value": 2 + }, + "gantry_height": { + "value": "0" + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": { + "default_value": "G28 ; home\nG29 ; level\nM80 ; led\nG1 Z15.0 F6000\nT0\nG92 E0.0000\nG1 E-1.4500 F1800\nG1 X50 Y0 Z0.300 F6000\nM300 S3000 P300\nG1 E1.0000 F1800\nG92 E0.0000\nG1 X250 Y0 E15 F662" + }, + "machine_end_gcode": { + "default_value": "G28 ; home\nM104 S0 ; turn off\n M140 S0 ; turn off\nM84 ; disable motors\nM107 ; fan off" + } + } +} diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index babd4cca3e..8483b7a9cb 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -6290,7 +6290,7 @@ "slicing_tolerance": { "label": "Slicing Tolerance", - "description": "How to slice layers with diagonal surfaces. The areas of a layer can be generated based on where the middle of the layer intersects the surface (Middle). Alternatively each layer can have the areas which fall inside of the volume throughout the height of the layer (Exclusive) or a layer has the areas which fall inside anywhere within the layer (Inclusive). Exclusive retains the most details, Inclusive makes for the best fit and Middle takes the least time to process.", + "description": "Vertical tolerance in the sliced layers. The contours of a layer are normally generated by taking cross sections through the middle of each layer's thickness (Middle). Alternatively each layer can have the areas which fall inside of the volume throughout the entire thickness of the layer (Exclusive) or a layer has the areas which fall inside anywhere within the layer (Inclusive). Inclusive retains the most details, Exclusive makes for the best fit and Middle stays closest to the original surface.", "type": "enum", "options": { diff --git a/resources/definitions/hms434.def.json b/resources/definitions/hms434.def.json index cd20906ccc..8d70b065c7 100644 --- a/resources/definitions/hms434.def.json +++ b/resources/definitions/hms434.def.json @@ -81,9 +81,6 @@ "layer_height": {"maximum_value": "(0.8 * min(extruderValues('machine_nozzle_size')))" }, "layer_height_0": {"maximum_value": "(0.8 * min(extruderValues('machine_nozzle_size')))" }, - "line_width": {"value": "(machine_nozzle_size + 0.2)" }, - "wall_line_width_0": {"value": "(machine_nozzle_size)" }, - "infill_line_width": {"value": "(line_width)" }, "initial_layer_line_width_factor": {"value": 110 }, "wall_thickness": {"value": "(line_width * 3) if infill_sparse_density < 95 else line_width" }, @@ -111,17 +108,24 @@ "infill_pattern": {"value": "'lines'"}, "infill_before_walls": {"value": true}, - "material_print_temperature_layer_0": {"value": "material_print_temperature"}, - "material_initial_print_temperature": {"value": "material_print_temperature", - "maximum_value_warning": "material_print_temperature + 15"}, - "material_final_print_temperature": {"value": "material_print_temperature"}, - "material_bed_temperature_layer_0": {"value": "material_bed_temperature"}, - "material_flow_layer_0": {"value": "material_flow"}, - "retraction_enable": {"value": true }, - "retract_at_layer_change": {"value": false }, - "retraction_min_travel": {"value": "(round(line_width * 10))"}, - "switch_extruder_retraction_speeds": {"value": "(retraction_speed)"}, - "switch_extruder_prime_speed": {"value": "(retraction_prime_speed)"}, + "default_material_print_temperature": {"maximum_value": "401" }, + "material_print_temperature": {"maximum_value": "401" }, + "material_print_temperature_layer_0": {"value": "material_print_temperature", + "maximum_value": "401" }, + "material_initial_print_temperature": {"value": "material_print_temperature", + "maximum_value_warning": "material_print_temperature + 15", + "maximum_value": "401" }, + "material_initial_print_temperature": {"maximum_value": "401" }, + "material_final_print_temperature": {"value": "material_print_temperature", + "maximum_value": "401" }, + "material_break_preparation_temperature": {"maximum_value": "401" }, + "material_bed_temperature_layer_0": {"value": "material_bed_temperature"}, + "material_flow_layer_0": {"value": "material_flow"}, + "retraction_enable": {"value": true }, + "retract_at_layer_change": {"value": false }, + "retraction_min_travel": {"value": "(round(line_width * 10))"}, + "switch_extruder_retraction_speeds": {"value": "(retraction_speed)"}, + "switch_extruder_prime_speed": {"value": "(retraction_prime_speed)"}, "speed_print": {"value": "50"}, "speed_infill": {"value": "speed_print"}, diff --git a/resources/extruders/beamup_l_extruder_0.def.json b/resources/extruders/beamup_l_extruder_0.def.json new file mode 100644 index 0000000000..b3ab9ed045 --- /dev/null +++ b/resources/extruders/beamup_l_extruder_0.def.json @@ -0,0 +1,15 @@ +{ + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "beamup_l", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.8 }, + "material_diameter": { "default_value": 1.75 } + } +} diff --git a/resources/extruders/hms434_tool_1.def.json b/resources/extruders/hms434_tool_1.def.json index 2a36dd4c2b..17f4cbc6f0 100644 --- a/resources/extruders/hms434_tool_1.def.json +++ b/resources/extruders/hms434_tool_1.def.json @@ -16,7 +16,7 @@ "machine_nozzle_offset_y": { "default_value": 0.0 }, "material_diameter": { "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;changing to tool1\nM83\nM109 T0 S{material_print_temperature}\nM114\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" + "default_value": "\n;changing to tool1\nT0\nM83\nM109 T0 S{material_print_temperature}\nM114\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" }, "machine_extruder_end_code": { "default_value": "\nG1 X10 Y40 F12000\nG1 X-25 F12000\nM109 T0 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool1\n\n" diff --git a/resources/extruders/hms434_tool_2.def.json b/resources/extruders/hms434_tool_2.def.json index 110695a8fc..00e92613e4 100644 --- a/resources/extruders/hms434_tool_2.def.json +++ b/resources/extruders/hms434_tool_2.def.json @@ -16,7 +16,7 @@ "machine_nozzle_offset_y": { "default_value": 0.0 }, "material_diameter": { "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;changing to tool2\nM83\nM109 T1 S{material_print_temperature}\nM114\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" + "default_value": "\n;changing to tool2\nT1\nM83\nM109 T1 S{material_print_temperature}\nM114\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" }, "machine_extruder_end_code": { "default_value": "\nG1 X10 Y40 F12000\nG1 X-25 F12000\nM109 T1 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool2\n\n" diff --git a/resources/meshes/beamup_l.3mf b/resources/meshes/beamup_l.3mf new file mode 100644 index 0000000000..ecbbfb8d7f Binary files /dev/null and b/resources/meshes/beamup_l.3mf differ diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 8839bf4a05..62768556e4 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -90,7 +90,7 @@ UM.PreferencesPage UM.Preferences.resetPreference("view/show_overhang"); showOverhangCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_overhang")) UM.Preferences.resetPreference("view/show_xray_warning"); - showXrayErrorCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_warning")) + showXrayErrorCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_xray_warning")) UM.Preferences.resetPreference("view/center_on_select"); centerOnSelectCheckbox.checked = boolCheck(UM.Preferences.getValue("view/center_on_select")) UM.Preferences.resetPreference("view/invert_zoom"); @@ -336,7 +336,7 @@ UM.PreferencesPage id: showOverhangCheckbox checked: boolCheck(UM.Preferences.getValue("view/show_overhang")) - onClicked: UM.Preferences.setValue("view/show_overhang", checked) + onClicked: UM.Preferences.setValue("view/show_overhang", checked) text: catalog.i18nc("@option:check", "Display overhang"); } diff --git a/resources/quality/beamup_l/beamup_l_coarse.inst.cfg b/resources/quality/beamup_l/beamup_l_coarse.inst.cfg new file mode 100644 index 0000000000..95a8098dbc --- /dev/null +++ b/resources/quality/beamup_l/beamup_l_coarse.inst.cfg @@ -0,0 +1,40 @@ +[general] +version = 4 +name = BeamUp L Coarse +definition = beamup_l + +[metadata] +setting_version = 15 +type = quality +quality_type = coarse +weight = -3 +material = generic_pla + +[values] +layer_height = 0.30 +adhesion_type = brim +brim_line_count = 8 +infill_before_walls = False +initial_layer_line_width_factor = 120.0 +material_print_temperature = 215 +material_print_temperature_layer_0 = 235 +retraction_amount = 2 +retraction_speed = 30 +speed_infill = 55 +speed_layer_0 = 25 +speed_print = 55 +speed_support_interface = 55 +speed_topbottom = 55 +speed_wall_0 = 45 +speed_wall_x = 55 +support_enable = True +support_angle = 60 +support_infill_rate = 20 +support_interface_enable = True +support_interface_height = 0.60 +support_interface_pattern = zigzag +support_interface_skip_height = 0.30 +support_offset = 0.8 +support_z_distance = 0.4 +wall_thickness = 1.6 +zig_zaggify_infill = True diff --git a/resources/quality/beamup_l/beamup_l_draft.inst.cfg b/resources/quality/beamup_l/beamup_l_draft.inst.cfg new file mode 100644 index 0000000000..ffd15ada90 --- /dev/null +++ b/resources/quality/beamup_l/beamup_l_draft.inst.cfg @@ -0,0 +1,40 @@ +[general] +version = 4 +name = BeamUp L Draft +definition = beamup_l + +[metadata] +setting_version = 15 +type = quality +quality_type = draft +weight = -2 +material = generic_pla + +[values] +layer_height = 0.2 +adhesion_type = brim +brim_line_count = 8 +infill_before_walls = False +initial_layer_line_width_factor = 120.0 +material_print_temperature = 210 +material_print_temperature_layer_0 = 235 +retraction_amount = 2 +retraction_speed = 30 +speed_infill = 55 +speed_layer_0 = 25 +speed_print = 55 +speed_support_interface = 55 +speed_topbottom = 55 +speed_wall_0 = 45 +speed_wall_x = 55 +support_enable = True +support_angle = 60 +support_infill_rate = 20 +support_interface_enable = True +support_interface_height = 0.60 +support_interface_pattern = zigzag +support_interface_skip_height = 0.20 +support_offset = 0.8 +support_z_distance = 0.3 +wall_thickness = 1.6 +zig_zaggify_infill = True diff --git a/resources/quality/beamup_l/beamup_l_extra_fine.inst.cfg b/resources/quality/beamup_l/beamup_l_extra_fine.inst.cfg new file mode 100644 index 0000000000..2ae9b31876 --- /dev/null +++ b/resources/quality/beamup_l/beamup_l_extra_fine.inst.cfg @@ -0,0 +1,40 @@ +[general] +version = 4 +name = BeamUp L Extra Fine +definition = beamup_l + +[metadata] +setting_version = 15 +type = quality +quality_type = high +weight = 1 +material = generic_pla + +[values] +layer_height = 0.06 +adhesion_type = brim +brim_line_count = 8 +infill_before_walls = False +initial_layer_line_width_factor = 120.0 +material_print_temperature = 195 +material_print_temperature_layer_0 = 235 +retraction_amount = 2 +retraction_speed = 30 +speed_infill = 45 +speed_layer_0 = 25 +speed_print = 45 +speed_support_interface = 45 +speed_topbottom = 45 +speed_wall_0 = 35 +speed_wall_x = 45 +support_enable = True +support_angle = 60 +support_infill_rate = 20 +support_interface_enable = True +support_interface_height = 0.30 +support_interface_pattern = zigzag +support_interface_skip_height = 0.06 +support_offset = 0.8 +support_z_distance = 0.12 +wall_thickness = 1.6 +zig_zaggify_infill = True diff --git a/resources/quality/beamup_l/beamup_l_fine.inst.cfg b/resources/quality/beamup_l/beamup_l_fine.inst.cfg new file mode 100644 index 0000000000..a63ee4af8f --- /dev/null +++ b/resources/quality/beamup_l/beamup_l_fine.inst.cfg @@ -0,0 +1,40 @@ +[general] +version = 4 +name = BeamUp L Fine +definition = beamup_l + +[metadata] +setting_version = 15 +type = quality +quality_type = normal +weight = 0 +material = generic_pla + +[values] +layer_height = 0.1 +adhesion_type = brim +brim_line_count = 8 +infill_before_walls = False +initial_layer_line_width_factor = 120.0 +material_print_temperature = 200 +material_print_temperature_layer_0 = 235 +retraction_amount = 2 +retraction_speed = 30 +speed_infill = 50 +speed_layer_0 = 25 +speed_print = 50 +speed_support_interface = 50 +speed_topbottom = 50 +speed_wall_0 = 40 +speed_wall_x = 50 +support_enable = True +support_angle = 60 +support_infill_rate = 20 +support_interface_enable = True +support_interface_height = 0.30 +support_interface_pattern = zigzag +support_interface_skip_height = 0.10 +support_offset = 0.8 +support_z_distance = 0.2 +wall_thickness = 1.6 +zig_zaggify_infill = True diff --git a/resources/quality/beamup_l/beamup_l_normal.inst.cfg b/resources/quality/beamup_l/beamup_l_normal.inst.cfg new file mode 100644 index 0000000000..c2ea70c63e --- /dev/null +++ b/resources/quality/beamup_l/beamup_l_normal.inst.cfg @@ -0,0 +1,40 @@ +[general] +version = 4 +name = BeamUp L Normal +definition = beamup_l + +[metadata] +setting_version = 15 +type = quality +quality_type = fast +weight = -1 +material = generic_pla + +[values] +layer_height = 0.15 +adhesion_type = brim +brim_line_count = 8 +infill_before_walls = False +initial_layer_line_width_factor = 120.0 +material_print_temperature = 205 +material_print_temperature_layer_0 = 235 +retraction_amount = 2 +retraction_speed = 30 +speed_infill = 50 +speed_layer_0 = 25 +speed_print = 50 +speed_support_interface = 50 +speed_topbottom = 50 +speed_wall_0 = 40 +speed_wall_x = 50 +support_enable = True +support_angle = 60 +support_infill_rate = 20 +support_interface_enable = True +support_interface_height = 0.45 +support_interface_pattern = zigzag +support_interface_skip_height = 0.15 +support_offset = 0.8 +support_z_distance = 0.25 +wall_thickness = 1.6 +zig_zaggify_infill = True diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg index 651308bfeb..598f5adb8b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg @@ -32,7 +32,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg index 267f56961f..afacf33523 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg @@ -32,7 +32,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg index d338bb6378..258e573b74 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg @@ -34,7 +34,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg index 4228dd7e38..420bad697f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg @@ -33,7 +33,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg index 4b5695e321..50ee345fa8 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg @@ -23,7 +23,6 @@ material_print_temperature = =default_material_print_temperature - 10 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg index 8211d73755..8b59be1f3f 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -24,7 +24,6 @@ material_print_temperature = =default_material_print_temperature - 5 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg index 5f1f663293..d08488c9eb 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -24,7 +24,6 @@ material_print_temperature = =default_material_print_temperature - 7 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/tests/TestBuildVolume.py b/tests/TestBuildVolume.py index fc49483e5a..c5d59b64d7 100644 --- a/tests/TestBuildVolume.py +++ b/tests/TestBuildVolume.py @@ -252,7 +252,7 @@ class TestCalculateExtraZClearance: return properties.get(args[2]) def test_noContainerStack(self, build_volume: BuildVolume): - assert build_volume._calculateExtraZClearance([]) is 0 + assert build_volume._calculateExtraZClearance([]) == 0 def test_withRetractionHop(self, build_volume: BuildVolume): mocked_global_stack = MagicMock(name="mocked_global_stack")