From 1efc92ddbe5136c2f3d39ffd766c664e9ffaf0f1 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 23 Jan 2018 11:46:59 +0100 Subject: [PATCH 1/7] CURA-4853 evaluate layer view compatibility mode in init, therefore not binding some signals and fixing the bug --- plugins/SimulationView/SimulationView.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index dfecda06bb..253ece315e 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -98,11 +98,14 @@ class SimulationView(View): self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) - self._compatibility_mode = True # for safety + self._compatibility_mode = self._evaluateCompatibilityMode() self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"), title = catalog.i18nc("@info:title", "Simulation View")) + def _evaluateCompatibilityMode(self): + return OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")) + def _resetSettings(self): self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed, 3 is layer thickness self._extruder_count = 0 @@ -127,7 +130,7 @@ class SimulationView(View): # Currently the RenderPass constructor requires a size > 0 # This should be fixed in RenderPass's constructor. self._layer_pass = SimulationPass(1, 1) - self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")) + self._compatibility_mode = self._evaluateCompatibilityMode() self._layer_pass.setSimulationView(self) return self._layer_pass @@ -534,8 +537,7 @@ class SimulationView(View): def _updateWithPreferences(self): self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) - self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool( - Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode")) + self._compatibility_mode = self._evaluateCompatibilityMode() self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type")))); From 7a84712fc9b6957e242168a5e75ad51c87c15f69 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 23 Jan 2018 12:13:26 +0100 Subject: [PATCH 2/7] CURA-4854 Revert some changes in 619a8cc that make that issue not working --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 3267bf486f..f1a4caea12 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -911,7 +911,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # We will first find the correct quality profile for the extruder, then apply the same # quality profile for the global stack. # - if has_extruder_stack_files and len(extruder_stacks) == 1: + if len(extruder_stacks) == 1: extruder_stack = extruder_stacks[0] search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")} From 3ec4cc6a0ba466df7d454084b51a0becbc3d7d32 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 23 Jan 2018 12:38:51 +0100 Subject: [PATCH 3/7] Revert "Rename Enable Travel Optimization to Infill Travel Optimization" This reverts commit a31d65786b0c8a0a62c1a3637db914d88bbcc6b4. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 60ede2c3b0..0451bd2539 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5308,7 +5308,7 @@ }, "infill_enable_travel_optimization": { - "label": "Infill Travel Optimization", + "label": "Enable Travel Optimization", "description": "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased.", "type": "bool", "default_value": false, From d3e85e63709d43e8c989c396c8b8d42bc1cc3284 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 23 Jan 2018 12:38:59 +0100 Subject: [PATCH 4/7] Revert "Do boundary checks on nodes for which the boundary check is unknown" This reverts commit 27e441ecd9642530b5b8f8fe949cff152d54dcba. --- cura/BuildVolume.py | 84 +++++++++++++++++++++-------------------- cura/PlatformPhysics.py | 11 ++---- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index f7e748ba4e..2567641cc9 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -1,7 +1,8 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from cura.Settings.ExtruderManager import ExtruderManager +from UM.Settings.ContainerRegistry import ContainerRegistry from UM.i18n import i18nCatalog from UM.Scene.Platform import Platform from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator @@ -193,51 +194,52 @@ class BuildVolume(SceneNode): return True - ## For every sliceable node, update node._outside_buildarea. - def updateAllBoundaryChecks(self): - self.updateNodeBoundaryCheck(Application.getInstance().getController().getScene().getRoot()) - - ## For a single node, update _outside_buildarea. + ## For every sliceable node, update node._outside_buildarea # - # If the node is a group node, the child nodes will also get updated. - # \param node The node to update the boundary checks of. - def updateNodeBoundaryCheck(self, node: SceneNode): - if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"): - for child in node.getChildren(): #Still update the children! For instance, the root is not sliceable. - self.updateNodeBoundaryCheck(child) - return #Don't compute for non-sliceable nodes. + def updateNodeBoundaryCheck(self): + root = Application.getInstance().getController().getScene().getRoot() + nodes = list(BreadthFirstIterator(root)) + group_nodes = [] - #Mark the node as outside the build volume if the bounding box test fails. - build_volume = self.getBoundingBox() - if build_volume is None: - #No bounding box. This is triggered when running Cura from command line with a model for the first time. - #In that situation there is a model, but no machine (and therefore no build volume). + build_volume_bounding_box = self.getBoundingBox() + if build_volume_bounding_box: + # It's over 9000! + build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001) + else: + # No bounding box. This is triggered when running Cura from command line with a model for the first time + # In that situation there is a model, but no machine (and therefore no build volume. return - build_volume = build_volume.set(bottom = -999999) #Allow models to clip the build plate. This should allow printing but remove the bottom side of the model underneath the build plate. - bounding_box = node.getBoundingBox() - if build_volume.intersectsBox(bounding_box) != AxisAlignedBox.IntersectionResult.FullIntersection: - node._outside_buildarea = True - else: - #Check for collisions between disallowed areas and the object. - convex_hull = node.callDecoration("getConvexHull") - if not convex_hull or not convex_hull.isValid(): - return - for area in self.getDisallowedAreas(): - overlap = convex_hull.intersectsPolygon(area) - if overlap is not None: - node._outside_buildarea = True - break - else: + for node in nodes: + # Need to check group nodes later + if node.callDecoration("isGroup"): + group_nodes.append(node) # Keep list of affected group_nodes + + if node.callDecoration("isSliceable") or node.callDecoration("isGroup"): node._outside_buildarea = False + bbox = node.getBoundingBox() - #Group nodes should override the _outside_buildarea property of their children. - if node.callDecoration("isGroup"): - for child in node.getAllChildren(): - child._outside_buildarea = node._outside_buildarea - else: - for child in node.getChildren(): - self.updateNodeBoundaryCheck(child) + # Mark the node as outside the build volume if the bounding box test fails. + if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: + node._outside_buildarea = True + continue + + convex_hull = node.callDecoration("getConvexHull") + if convex_hull: + if not convex_hull.isValid(): + return + # Check for collisions between disallowed areas and the object + for area in self.getDisallowedAreas(): + overlap = convex_hull.intersectsPolygon(area) + if overlap is None: + continue + node._outside_buildarea = True + continue + + # Group nodes should override the _outside_buildarea property of their children. + for group_node in group_nodes: + for child_node in group_node.getAllChildren(): + child_node._outside_buildarea = group_node._outside_buildarea ## Recalculates the build volume & disallowed areas. def rebuild(self): @@ -422,7 +424,7 @@ class BuildVolume(SceneNode): Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds - self.updateAllBoundaryChecks() + self.updateNodeBoundaryCheck() def getBoundingBox(self) -> AxisAlignedBox: return self._volume_aabb diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 05385d7c71..cf4dd83fef 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -56,17 +56,14 @@ class PlatformPhysics: # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve. nodes = list(BreadthFirstIterator(root)) + # Only check nodes inside build area. + nodes = [node for node in nodes if getattr(node, "_outside_buildarea", False)] + random.shuffle(nodes) for node in nodes: if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None: continue - #Only check nodes inside the build area. - if not hasattr(node, "_outside_buildarea"): - self._build_volume.updateNodeBoundaryCheck(node) - if getattr(node, "_outside_buildarea", True): - continue - bbox = node.getBoundingBox() # Move it downwards if bottom is above platform @@ -158,7 +155,7 @@ class PlatformPhysics: # After moving, we have to evaluate the boundary checks for nodes build_volume = Application.getInstance().getBuildVolume() - build_volume.updateAllBoundaryChecks() + build_volume.updateNodeBoundaryCheck() def _onToolOperationStarted(self, tool): self._enabled = False From f45de9654b04544c3cb34dda37ea4a578de2772d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 23 Jan 2018 12:39:07 +0100 Subject: [PATCH 5/7] Revert "Remove unused import" This reverts commit cf556ccf8ff7116af0014ffc91c019b26cf4f364. --- cura/Scene/CuraSceneNode.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 597dfedf7a..9df2931f0b 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -1,7 +1,5 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - from UM.Application import Application +from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode from copy import deepcopy From 710e3c162990e3c02958dfdb04ece18170bbcc89 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 23 Jan 2018 12:39:15 +0100 Subject: [PATCH 6/7] Revert "Simplify check for _outside_buildarea" This reverts commit 5c8d46b5c2ff27ff32bfc8e27451d3f8764accf8. --- cura/PlatformPhysics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index cf4dd83fef..69890178e4 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import QTimer @@ -57,7 +57,7 @@ class PlatformPhysics: nodes = list(BreadthFirstIterator(root)) # Only check nodes inside build area. - nodes = [node for node in nodes if getattr(node, "_outside_buildarea", False)] + nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea)] random.shuffle(nodes) for node in nodes: From 07e687519968378ebc443421a6b55f476ce72a58 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 23 Jan 2018 14:24:00 +0100 Subject: [PATCH 7/7] CURA-4848 added logging for when the crash occurs --- cura/Settings/GlobalStack.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index dae1c103b4..cc3a29aa8b 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -31,6 +31,7 @@ class GlobalStack(CuraContainerStack): # and if so, to bypass the resolve to prevent an infinite recursion that would occur # if the resolve function tried to access the same property it is a resolve for. self._resolving_settings = set() + self._resolving_settings2 = [] # For debugging CURA-4848, if it happens ## Get the list of extruders of this stack. # @@ -91,9 +92,17 @@ class GlobalStack(CuraContainerStack): # Handle the "resolve" property. if self._shouldResolve(key, property_name, context): + self._resolving_settings2.append(key) self._resolving_settings.add(key) resolve = super().getProperty(key, "resolve", context) + if key not in self._resolving_settings: + Logger.log("e", "Key [%s] should really have been in set(%s) and [%s]. Now I'm gonna crash", key, str(self._resolving_settings), str(self._resolving_settings2)) + Logger.log("d", "------ context ------") + for stack in context.stack_of_containers: + Logger.log("d", "Context: %s", stack.getId()) + Logger.log("d", "------ context end ------") self._resolving_settings.remove(key) + self._resolving_settings2.pop() if resolve is not None: return resolve