Merge branch '3.2'

This commit is contained in:
Jack Ha 2018-01-23 14:25:11 +01:00
commit b0d0b75662
7 changed files with 66 additions and 58 deletions

View file

@ -1,8 +1,9 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Scene.Platform import Platform from UM.Scene.Platform import Platform
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
@ -194,51 +195,52 @@ class BuildVolume(SceneNode):
return True return True
## For every sliceable node, update node._outside_buildarea. ## 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.
# #
# If the node is a group node, the child nodes will also get updated. def updateNodeBoundaryCheck(self):
# \param node The node to update the boundary checks of. root = Application.getInstance().getController().getScene().getRoot()
def updateNodeBoundaryCheck(self, node: SceneNode): nodes = list(BreadthFirstIterator(root))
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"): group_nodes = []
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.
#Mark the node as outside the build volume if the bounding box test fails. build_volume_bounding_box = self.getBoundingBox()
build_volume = self.getBoundingBox() if build_volume_bounding_box:
if build_volume is None: # It's over 9000!
#No bounding box. This is triggered when running Cura from command line with a model for the first time. build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
#In that situation there is a model, but no machine (and therefore no build volume). 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 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. for node in nodes:
convex_hull = node.callDecoration("getConvexHull") # Need to check group nodes later
if not convex_hull or not convex_hull.isValid(): if node.callDecoration("isGroup"):
return group_nodes.append(node) # Keep list of affected group_nodes
for area in self.getDisallowedAreas():
overlap = convex_hull.intersectsPolygon(area) if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
if overlap is not None:
node._outside_buildarea = True
break
else:
node._outside_buildarea = False node._outside_buildarea = False
bbox = node.getBoundingBox()
#Group nodes should override the _outside_buildarea property of their children. # Mark the node as outside the build volume if the bounding box test fails.
if node.callDecoration("isGroup"): if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
for child in node.getAllChildren(): node._outside_buildarea = True
child._outside_buildarea = node._outside_buildarea continue
else:
for child in node.getChildren(): convex_hull = node.callDecoration("getConvexHull")
self.updateNodeBoundaryCheck(child) 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
## Update the outsideBuildArea of a single node, given bounds or current build volume ## Update the outsideBuildArea of a single node, given bounds or current build volume
def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None): def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None):
@ -461,7 +463,7 @@ class BuildVolume(SceneNode):
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
self.updateAllBoundaryChecks() self.updateNodeBoundaryCheck()
def getBoundingBox(self) -> AxisAlignedBox: def getBoundingBox(self) -> AxisAlignedBox:
return self._volume_aabb return self._volume_aabb

View file

@ -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. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
@ -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. # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
nodes = list(BreadthFirstIterator(root)) nodes = list(BreadthFirstIterator(root))
# Only check nodes inside build area.
nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea)]
random.shuffle(nodes) random.shuffle(nodes)
for node in nodes: for node in nodes:
if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None: if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None:
continue 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() bbox = node.getBoundingBox()
# Move it downwards if bottom is above platform # 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 # After moving, we have to evaluate the boundary checks for nodes
build_volume = Application.getInstance().getBuildVolume() build_volume = Application.getInstance().getBuildVolume()
build_volume.updateAllBoundaryChecks() build_volume.updateNodeBoundaryCheck()
def _onToolOperationStarted(self, tool): def _onToolOperationStarted(self, tool):
self._enabled = False self._enabled = False

View file

@ -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.Application import Application
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from copy import deepcopy from copy import deepcopy

View file

@ -31,6 +31,7 @@ class GlobalStack(CuraContainerStack):
# and if so, to bypass the resolve to prevent an infinite recursion that would occur # 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. # if the resolve function tried to access the same property it is a resolve for.
self._resolving_settings = set() self._resolving_settings = set()
self._resolving_settings2 = [] # For debugging CURA-4848, if it happens
## Get the list of extruders of this stack. ## Get the list of extruders of this stack.
# #
@ -91,9 +92,17 @@ class GlobalStack(CuraContainerStack):
# Handle the "resolve" property. # Handle the "resolve" property.
if self._shouldResolve(key, property_name, context): if self._shouldResolve(key, property_name, context):
self._resolving_settings2.append(key)
self._resolving_settings.add(key) self._resolving_settings.add(key)
resolve = super().getProperty(key, "resolve", context) 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_settings.remove(key)
self._resolving_settings2.pop()
if resolve is not None: if resolve is not None:
return resolve return resolve

View file

@ -911,7 +911,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# We will first find the correct quality profile for the extruder, then apply the same # We will first find the correct quality profile for the extruder, then apply the same
# quality profile for the global stack. # 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] extruder_stack = extruder_stacks[0]
search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")} search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")}

View file

@ -98,11 +98,14 @@ class SimulationView(View):
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) 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._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"), 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")) 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): 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._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed, 3 is layer thickness
self._extruder_count = 0 self._extruder_count = 0
@ -127,7 +130,7 @@ class SimulationView(View):
# Currently the RenderPass constructor requires a size > 0 # Currently the RenderPass constructor requires a size > 0
# This should be fixed in RenderPass's constructor. # This should be fixed in RenderPass's constructor.
self._layer_pass = SimulationPass(1, 1) 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) self._layer_pass.setSimulationView(self)
return self._layer_pass return self._layer_pass
@ -534,8 +537,7 @@ class SimulationView(View):
def _updateWithPreferences(self): def _updateWithPreferences(self):
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) 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._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool( self._compatibility_mode = self._evaluateCompatibilityMode()
Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type")))); self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type"))));

View file

@ -5339,7 +5339,7 @@
}, },
"infill_enable_travel_optimization": "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.", "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", "type": "bool",
"default_value": false, "default_value": false,