From a6ca49d68c1f03608de1f39787992c2d05801b23 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Jun 2019 13:34:57 +0200 Subject: [PATCH] Fix typing for Buildvolume --- cura/BuildVolume.py | 115 ++++++++++++++++++++++-------------- cura/Scene/CuraSceneNode.py | 13 +--- 2 files changed, 72 insertions(+), 56 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 8e74a81842..a14d314804 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -1,6 +1,6 @@ # Copyright (c) 2019 Ultimaker B.V. # 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.Settings.ExtruderManager import ExtruderManager 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 UM.View.RenderBatch import RenderBatch from UM.View.GL.OpenGL import OpenGL +from cura.Settings.GlobalStack import GlobalStack + catalog = i18nCatalog("cura") import numpy import math 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. PRIME_CLEARANCE = 6.5 @@ -36,45 +41,45 @@ PRIME_CLEARANCE = 6.5 class BuildVolume(SceneNode): raftThicknessChanged = Signal() - def __init__(self, application, parent = None): + def __init__(self, application: "CuraApplication", parent: Optional[SceneNode] = None) -> None: super().__init__(parent) self._application = application self._machine_manager = self._application.getMachineManager() - self._volume_outline_color = None - self._x_axis_color = None - self._y_axis_color = None - self._z_axis_color = None - self._disallowed_area_color = None - self._error_area_color = None + self._volume_outline_color = None # type: Optional[Color] + self._x_axis_color = None # type: Optional[Color] + self._y_axis_color = None # type: Optional[Color] + self._z_axis_color = None # type: Optional[Color] + self._disallowed_area_color = None # type: Optional[Color] + self._error_area_color = None # type: Optional[Color] - self._width = 0 #type: float - self._height = 0 #type: float - self._depth = 0 #type: float - self._shape = "" #type: str + self._width = 0 # type: float + self._height = 0 # type: float + self._depth = 0 # type: float + self._shape = "" # type: str self._shader = None - self._origin_mesh = None + self._origin_mesh = None # type: Optional[MeshData] self._origin_line_length = 20 self._origin_line_width = 0.5 - self._grid_mesh = None + self._grid_mesh = None # type: Optional[MeshData] self._grid_shader = None - self._disallowed_areas = [] - self._disallowed_areas_no_brim = [] - self._disallowed_area_mesh = None + self._disallowed_areas = [] # type: List[Polygon] + self._disallowed_areas_no_brim = [] # type: List[Polygon] + self._disallowed_area_mesh = None # type: Optional[MeshData] - self._error_areas = [] - self._error_mesh = None + self._error_areas = [] # type: List[Polygon] + self._error_mesh = None # type: Optional[MeshData] self.setCalculateBoundingBox(False) - self._volume_aabb = None + self._volume_aabb = None # type: Optional[AxisAlignedBox] self._raft_thickness = 0.0 self._extra_z_clearance = 0.0 - self._adhesion_type = None + self._adhesion_type = None # type: Any self._platform = Platform(self) 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" " 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.setInterval(100) @@ -100,7 +105,7 @@ class BuildVolume(SceneNode): self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged) #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.setInterval(100) @@ -124,8 +129,8 @@ class BuildVolume(SceneNode): # Enable and disable extruder self._machine_manager.extruderChanged.connect(self.updateNodeBoundaryCheck) - # list of settings which were updated - self._changed_settings_since_last_rebuild = [] + # List of settings which were updated + self._changed_settings_since_last_rebuild = [] # type: List[str] def _onSceneChanged(self, source): if self._global_container_stack: @@ -219,9 +224,12 @@ class BuildVolume(SceneNode): ## For every sliceable node, update node._outside_buildarea # def updateNodeBoundaryCheck(self): + if not self._global_container_stack: + return + root = self._application.getController().getScene().getRoot() - nodes = list(BreadthFirstIterator(root)) - group_nodes = [] + nodes = cast(List[SceneNode], list(cast(Iterable, BreadthFirstIterator(root)))) + group_nodes = [] # type: List[SceneNode] build_volume_bounding_box = self.getBoundingBox() if build_volume_bounding_box: @@ -240,6 +248,9 @@ class BuildVolume(SceneNode): group_nodes.append(node) # Keep list of affected group_nodes if node.callDecoration("isSliceable") or node.callDecoration("isGroup"): + if not isinstance(node, CuraSceneNode): + continue + if node.collidesWithBbox(build_volume_bounding_box): node.setOutsideBuildArea(True) continue @@ -277,8 +288,8 @@ class BuildVolume(SceneNode): child_node.setOutsideBuildArea(group_node.isOutsideBuildArea()) ## Update the outsideBuildArea of a single node, given bounds or current build volume - def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None): - if not isinstance(node, CuraSceneNode): + def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None) -> None: + if not isinstance(node, CuraSceneNode) or self._global_container_stack is None: return if bounds is None: @@ -310,7 +321,7 @@ class BuildVolume(SceneNode): 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() if self._shape != "elliptic": # Build plate grid mesh @@ -346,7 +357,7 @@ class BuildVolume(SceneNode): mb.setVertexUVCoordinates(n, v[0], v[2] * aspect) 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": # Outline 'cube' of the build volume 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) return mb.build().getTransformed(scale_matrix) - def _buildOriginMesh(self, origin): + def _buildOriginMesh(self, origin: Vector) -> MeshData: mb = MeshBuilder() mb.addCube( width=self._origin_line_length, @@ -405,15 +416,20 @@ class BuildVolume(SceneNode): return mb.build() ## 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: return if not self._engine_ready: return + if not self._global_container_stack: + return + if not self._volume_outline_color: theme = self._application.getTheme() + if theme is None: + return self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb()) self._x_axis_color = Color(*theme.getColor("x_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) ) - self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds + self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds # type: ignore self.updateNodeBoundaryCheck() - def getBoundingBox(self) -> AxisAlignedBox: + def getBoundingBox(self): return self._volume_aabb def getRaftThickness(self) -> float: return self._raft_thickness - def _updateRaftThickness(self): + def _updateRaftThickness(self) -> None: + if not self._global_container_stack: + return + old_raft_thickness = self._raft_thickness if self._global_container_stack.extruders: # 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_interface_thickness", "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("layer_0_z_overlap", "value")) @@ -534,6 +553,9 @@ class BuildVolume(SceneNode): self.raftThicknessChanged.emit() def _updateExtraZClearance(self) -> None: + if not self._global_container_stack: + return + extra_z = 0.0 extruders = ExtruderManager.getInstance().getUsedExtruderStacks() use_extruders = False @@ -554,7 +576,7 @@ class BuildVolume(SceneNode): self._stack_change_timer.start() ## Update the build volume visualization - def _onStackChangeTimerFinished(self): + def _onStackChangeTimerFinished(self) -> None: if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) extruders = ExtruderManager.getInstance().getActiveExtruderStacks() @@ -594,20 +616,23 @@ class BuildVolume(SceneNode): if camera: diagonal = self.getDiagonalSize() 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.rebuild() - def _onSettingChangeTimerFinished(self): + def _onSettingChangeTimerFinished(self) -> None: + if not self._global_container_stack: + return + rebuild_me = False update_disallowed_areas = False update_raft_thickness = False update_extra_z_clearance = True for setting_key in self._changed_settings_since_last_rebuild: - if setting_key == "print_sequence": 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: @@ -664,7 +689,7 @@ class BuildVolume(SceneNode): # We just did a rebuild, reset the list. 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": return @@ -689,7 +714,7 @@ class BuildVolume(SceneNode): self._updateExtraZClearance() self.rebuild() - def _updateDisallowedAreas(self): + def _updateDisallowedAreas(self) -> None: if not self._global_container_stack: return diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 9038c12b2c..38a988f982 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -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. 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.SettingOverrideDecorator import SettingOverrideDecorator #For per-object settings. + ## 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. class CuraSceneNode(SceneNode): @@ -85,16 +86,6 @@ class CuraSceneNode(SceneNode): 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 def collidesWithArea(self, areas: List[Polygon]) -> bool: convex_hull = self.callDecoration("getConvexHull")