Fix typing for Buildvolume

This commit is contained in:
Jaime van Kessel 2019-06-07 13:34:57 +02:00
parent 2d4fe5c128
commit a6ca49d68c
2 changed files with 72 additions and 56 deletions

View file

@ -1,6 +1,6 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 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 UM.Mesh.MeshData import MeshData
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.Application import Application #To modify the maximum zoom level. from UM.Application import Application #To modify the maximum zoom level.
@ -20,13 +20,18 @@ from UM.Signal import Signal
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
from UM.View.RenderBatch import RenderBatch from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGL import OpenGL
from cura.Settings.GlobalStack import GlobalStack
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
import numpy import numpy
import math import math
import copy import copy
from typing import List, Optional from typing import List, Optional, TYPE_CHECKING, Any, Set, cast, Iterable
if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication
# Radius of disallowed area in mm around prime. I.e. how much distance to keep from prime position. # Radius of disallowed area in mm around prime. I.e. how much distance to keep from prime position.
PRIME_CLEARANCE = 6.5 PRIME_CLEARANCE = 6.5
@ -36,45 +41,45 @@ PRIME_CLEARANCE = 6.5
class BuildVolume(SceneNode): class BuildVolume(SceneNode):
raftThicknessChanged = Signal() raftThicknessChanged = Signal()
def __init__(self, application, parent = None): def __init__(self, application: "CuraApplication", parent: Optional[SceneNode] = None) -> None:
super().__init__(parent) super().__init__(parent)
self._application = application self._application = application
self._machine_manager = self._application.getMachineManager() self._machine_manager = self._application.getMachineManager()
self._volume_outline_color = None self._volume_outline_color = None # type: Optional[Color]
self._x_axis_color = None self._x_axis_color = None # type: Optional[Color]
self._y_axis_color = None self._y_axis_color = None # type: Optional[Color]
self._z_axis_color = None self._z_axis_color = None # type: Optional[Color]
self._disallowed_area_color = None self._disallowed_area_color = None # type: Optional[Color]
self._error_area_color = None self._error_area_color = None # type: Optional[Color]
self._width = 0 #type: float self._width = 0 # type: float
self._height = 0 #type: float self._height = 0 # type: float
self._depth = 0 #type: float self._depth = 0 # type: float
self._shape = "" #type: str self._shape = "" # type: str
self._shader = None self._shader = None
self._origin_mesh = None self._origin_mesh = None # type: Optional[MeshData]
self._origin_line_length = 20 self._origin_line_length = 20
self._origin_line_width = 0.5 self._origin_line_width = 0.5
self._grid_mesh = None self._grid_mesh = None # type: Optional[MeshData]
self._grid_shader = None self._grid_shader = None
self._disallowed_areas = [] self._disallowed_areas = [] # type: List[Polygon]
self._disallowed_areas_no_brim = [] self._disallowed_areas_no_brim = [] # type: List[Polygon]
self._disallowed_area_mesh = None self._disallowed_area_mesh = None # type: Optional[MeshData]
self._error_areas = [] self._error_areas = [] # type: List[Polygon]
self._error_mesh = None self._error_mesh = None # type: Optional[MeshData]
self.setCalculateBoundingBox(False) self.setCalculateBoundingBox(False)
self._volume_aabb = None self._volume_aabb = None # type: Optional[AxisAlignedBox]
self._raft_thickness = 0.0 self._raft_thickness = 0.0
self._extra_z_clearance = 0.0 self._extra_z_clearance = 0.0
self._adhesion_type = None self._adhesion_type = None # type: Any
self._platform = Platform(self) self._platform = Platform(self)
self._build_volume_message = Message(catalog.i18nc("@info:status", self._build_volume_message = Message(catalog.i18nc("@info:status",
@ -82,7 +87,7 @@ class BuildVolume(SceneNode):
" \"Print Sequence\" setting to prevent the gantry from colliding" " \"Print Sequence\" setting to prevent the gantry from colliding"
" with printed models."), title = catalog.i18nc("@info:title", "Build Volume")) " with printed models."), title = catalog.i18nc("@info:title", "Build Volume"))
self._global_container_stack = None self._global_container_stack = None # type: Optional[GlobalStack]
self._stack_change_timer = QTimer() self._stack_change_timer = QTimer()
self._stack_change_timer.setInterval(100) self._stack_change_timer.setInterval(100)
@ -100,7 +105,7 @@ class BuildVolume(SceneNode):
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged) self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
#Objects loaded at the moment. We are connected to the property changed events of these objects. #Objects loaded at the moment. We are connected to the property changed events of these objects.
self._scene_objects = set() self._scene_objects = set() # type: Set[SceneNode]
self._scene_change_timer = QTimer() self._scene_change_timer = QTimer()
self._scene_change_timer.setInterval(100) self._scene_change_timer.setInterval(100)
@ -124,8 +129,8 @@ class BuildVolume(SceneNode):
# Enable and disable extruder # Enable and disable extruder
self._machine_manager.extruderChanged.connect(self.updateNodeBoundaryCheck) self._machine_manager.extruderChanged.connect(self.updateNodeBoundaryCheck)
# list of settings which were updated # List of settings which were updated
self._changed_settings_since_last_rebuild = [] self._changed_settings_since_last_rebuild = [] # type: List[str]
def _onSceneChanged(self, source): def _onSceneChanged(self, source):
if self._global_container_stack: if self._global_container_stack:
@ -219,9 +224,12 @@ class BuildVolume(SceneNode):
## For every sliceable node, update node._outside_buildarea ## For every sliceable node, update node._outside_buildarea
# #
def updateNodeBoundaryCheck(self): def updateNodeBoundaryCheck(self):
if not self._global_container_stack:
return
root = self._application.getController().getScene().getRoot() root = self._application.getController().getScene().getRoot()
nodes = list(BreadthFirstIterator(root)) nodes = cast(List[SceneNode], list(cast(Iterable, BreadthFirstIterator(root))))
group_nodes = [] group_nodes = [] # type: List[SceneNode]
build_volume_bounding_box = self.getBoundingBox() build_volume_bounding_box = self.getBoundingBox()
if build_volume_bounding_box: if build_volume_bounding_box:
@ -240,6 +248,9 @@ class BuildVolume(SceneNode):
group_nodes.append(node) # Keep list of affected group_nodes group_nodes.append(node) # Keep list of affected group_nodes
if node.callDecoration("isSliceable") or node.callDecoration("isGroup"): if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
if not isinstance(node, CuraSceneNode):
continue
if node.collidesWithBbox(build_volume_bounding_box): if node.collidesWithBbox(build_volume_bounding_box):
node.setOutsideBuildArea(True) node.setOutsideBuildArea(True)
continue continue
@ -277,8 +288,8 @@ class BuildVolume(SceneNode):
child_node.setOutsideBuildArea(group_node.isOutsideBuildArea()) child_node.setOutsideBuildArea(group_node.isOutsideBuildArea())
## 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) -> None:
if not isinstance(node, CuraSceneNode): if not isinstance(node, CuraSceneNode) or self._global_container_stack is None:
return return
if bounds is None: if bounds is None:
@ -310,7 +321,7 @@ class BuildVolume(SceneNode):
node.setOutsideBuildArea(False) node.setOutsideBuildArea(False)
def _buildGridMesh(self, min_w, max_w, min_h, max_h, min_d, max_d, z_fight_distance): def _buildGridMesh(self, min_w: float, max_w: float, min_h: float, max_h: float, min_d: float, max_d:float, z_fight_distance: float) -> MeshData:
mb = MeshBuilder() mb = MeshBuilder()
if self._shape != "elliptic": if self._shape != "elliptic":
# Build plate grid mesh # Build plate grid mesh
@ -346,7 +357,7 @@ class BuildVolume(SceneNode):
mb.setVertexUVCoordinates(n, v[0], v[2] * aspect) mb.setVertexUVCoordinates(n, v[0], v[2] * aspect)
return mb.build().getTransformed(scale_matrix) return mb.build().getTransformed(scale_matrix)
def _buildMesh(self, min_w, max_w, min_h, max_h, min_d, max_d, z_fight_distance): def _buildMesh(self, min_w: float, max_w: float, min_h: float, max_h: float, min_d: float, max_d:float, z_fight_distance: float) -> MeshData:
if self._shape != "elliptic": if self._shape != "elliptic":
# Outline 'cube' of the build volume # Outline 'cube' of the build volume
mb = MeshBuilder() mb = MeshBuilder()
@ -379,7 +390,7 @@ class BuildVolume(SceneNode):
mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self._volume_outline_color) mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self._volume_outline_color)
return mb.build().getTransformed(scale_matrix) return mb.build().getTransformed(scale_matrix)
def _buildOriginMesh(self, origin): def _buildOriginMesh(self, origin: Vector) -> MeshData:
mb = MeshBuilder() mb = MeshBuilder()
mb.addCube( mb.addCube(
width=self._origin_line_length, width=self._origin_line_length,
@ -405,15 +416,20 @@ class BuildVolume(SceneNode):
return mb.build() return mb.build()
## Recalculates the build volume & disallowed areas. ## Recalculates the build volume & disallowed areas.
def rebuild(self): def rebuild(self) -> None:
if not self._width or not self._height or not self._depth: if not self._width or not self._height or not self._depth:
return return
if not self._engine_ready: if not self._engine_ready:
return return
if not self._global_container_stack:
return
if not self._volume_outline_color: if not self._volume_outline_color:
theme = self._application.getTheme() theme = self._application.getTheme()
if theme is None:
return
self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb()) self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb())
self._x_axis_color = Color(*theme.getColor("x_axis").getRgb()) self._x_axis_color = Color(*theme.getColor("x_axis").getRgb())
self._y_axis_color = Color(*theme.getColor("y_axis").getRgb()) self._y_axis_color = Color(*theme.getColor("y_axis").getRgb())
@ -503,17 +519,20 @@ class BuildVolume(SceneNode):
maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1) maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1)
) )
self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds # type: ignore
self.updateNodeBoundaryCheck() self.updateNodeBoundaryCheck()
def getBoundingBox(self) -> AxisAlignedBox: def getBoundingBox(self):
return self._volume_aabb return self._volume_aabb
def getRaftThickness(self) -> float: def getRaftThickness(self) -> float:
return self._raft_thickness return self._raft_thickness
def _updateRaftThickness(self): def _updateRaftThickness(self) -> None:
if not self._global_container_stack:
return
old_raft_thickness = self._raft_thickness old_raft_thickness = self._raft_thickness
if self._global_container_stack.extruders: if self._global_container_stack.extruders:
# This might be called before the extruder stacks have initialised, in which case getting the adhesion_type fails # This might be called before the extruder stacks have initialised, in which case getting the adhesion_type fails
@ -524,7 +543,7 @@ class BuildVolume(SceneNode):
self._global_container_stack.getProperty("raft_base_thickness", "value") + self._global_container_stack.getProperty("raft_base_thickness", "value") +
self._global_container_stack.getProperty("raft_interface_thickness", "value") + self._global_container_stack.getProperty("raft_interface_thickness", "value") +
self._global_container_stack.getProperty("raft_surface_layers", "value") * self._global_container_stack.getProperty("raft_surface_layers", "value") *
self._global_container_stack.getProperty("raft_surface_thickness", "value") + self._global_container_stack.getProperty("raft_surface_thickness", "value") +
self._global_container_stack.getProperty("raft_airgap", "value") - self._global_container_stack.getProperty("raft_airgap", "value") -
self._global_container_stack.getProperty("layer_0_z_overlap", "value")) self._global_container_stack.getProperty("layer_0_z_overlap", "value"))
@ -534,6 +553,9 @@ class BuildVolume(SceneNode):
self.raftThicknessChanged.emit() self.raftThicknessChanged.emit()
def _updateExtraZClearance(self) -> None: def _updateExtraZClearance(self) -> None:
if not self._global_container_stack:
return
extra_z = 0.0 extra_z = 0.0
extruders = ExtruderManager.getInstance().getUsedExtruderStacks() extruders = ExtruderManager.getInstance().getUsedExtruderStacks()
use_extruders = False use_extruders = False
@ -554,7 +576,7 @@ class BuildVolume(SceneNode):
self._stack_change_timer.start() self._stack_change_timer.start()
## Update the build volume visualization ## Update the build volume visualization
def _onStackChangeTimerFinished(self): def _onStackChangeTimerFinished(self) -> None:
if self._global_container_stack: if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
extruders = ExtruderManager.getInstance().getActiveExtruderStacks() extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
@ -594,20 +616,23 @@ class BuildVolume(SceneNode):
if camera: if camera:
diagonal = self.getDiagonalSize() diagonal = self.getDiagonalSize()
if diagonal > 1: if diagonal > 1:
camera.setZoomRange(min = 0.1, max = diagonal * 5) #You can zoom out up to 5 times the diagonal. This gives some space around the volume. # You can zoom out up to 5 times the diagonal. This gives some space around the volume.
camera.setZoomRange(min = 0.1, max = diagonal * 5) # type: ignore
def _onEngineCreated(self): def _onEngineCreated(self) -> None:
self._engine_ready = True self._engine_ready = True
self.rebuild() self.rebuild()
def _onSettingChangeTimerFinished(self): def _onSettingChangeTimerFinished(self) -> None:
if not self._global_container_stack:
return
rebuild_me = False rebuild_me = False
update_disallowed_areas = False update_disallowed_areas = False
update_raft_thickness = False update_raft_thickness = False
update_extra_z_clearance = True update_extra_z_clearance = True
for setting_key in self._changed_settings_since_last_rebuild: for setting_key in self._changed_settings_since_last_rebuild:
if setting_key == "print_sequence": if setting_key == "print_sequence":
machine_height = self._global_container_stack.getProperty("machine_height", "value") machine_height = self._global_container_stack.getProperty("machine_height", "value")
if self._application.getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1: if self._application.getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1:
@ -664,7 +689,7 @@ class BuildVolume(SceneNode):
# We just did a rebuild, reset the list. # We just did a rebuild, reset the list.
self._changed_settings_since_last_rebuild = [] self._changed_settings_since_last_rebuild = []
def _onSettingPropertyChanged(self, setting_key: str, property_name: str): def _onSettingPropertyChanged(self, setting_key: str, property_name: str) -> None:
if property_name != "value": if property_name != "value":
return return
@ -689,7 +714,7 @@ class BuildVolume(SceneNode):
self._updateExtraZClearance() self._updateExtraZClearance()
self.rebuild() self.rebuild()
def _updateDisallowedAreas(self): def _updateDisallowedAreas(self) -> None:
if not self._global_container_stack: if not self._global_container_stack:
return return

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2019 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 copy import deepcopy from copy import deepcopy
@ -14,6 +14,7 @@ import cura.CuraApplication #To get the build plate.
from cura.Settings.ExtruderStack import ExtruderStack #For typing. from cura.Settings.ExtruderStack import ExtruderStack #For typing.
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator #For per-object settings. from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator #For per-object settings.
## Scene nodes that are models are only seen when selecting the corresponding build plate ## Scene nodes that are models are only seen when selecting the corresponding build plate
# Note that many other nodes can just be UM SceneNode objects. # Note that many other nodes can just be UM SceneNode objects.
class CuraSceneNode(SceneNode): class CuraSceneNode(SceneNode):
@ -85,16 +86,6 @@ class CuraSceneNode(SceneNode):
1.0 1.0
] ]
## Return if the provided bbox collides with the bbox of this scene node
def collidesWithBbox(self, check_bbox: AxisAlignedBox) -> bool:
bbox = self.getBoundingBox()
if bbox is not None:
# Mark the node as outside the build volume if the bounding box test fails.
if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
return True
return False
## Return if any area collides with the convex hull of this scene node ## Return if any area collides with the convex hull of this scene node
def collidesWithArea(self, areas: List[Polygon]) -> bool: def collidesWithArea(self, areas: List[Polygon]) -> bool:
convex_hull = self.callDecoration("getConvexHull") convex_hull = self.callDecoration("getConvexHull")