def deleteAll(self, only_selectable = True) -> None: Logger.log("i", "Clearing scene") if not self.getController().getToolsEnabled(): return nodes = [] for node in DepthFirstIterator(self.getController().getScene().getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if not isinstance(node, SceneNode): continue if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"): continue # Node that doesnt have a mesh and is not a group. if only_selectable and not node.isSelectable(): continue if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"): continue # Only remove nodes that are selectable. if node.getParent() and cast(SceneNode, node.getParent()).callDecoration("isGroup"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) nodes.append(node) if nodes: op = GroupedOperation() for node in nodes: op.addOperation(RemoveSceneNodeOperation(node)) # Reset the print information self.getController().getScene().sceneChanged.emit(node) op.push() Selection.clear()
def getZ(self): # We want to display based on the bottom instead of the actual coordinate. if Selection.hasSelection(): # Note; The switching of z & y is intentional. We display z as up for the user, # But store the data in openGL space. return float(Selection.getBoundingBox().bottom) return 0.0
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() renderer.setRenderSelection(False) if not self._material: self._material = renderer.createMaterial(Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "vertexcolor.frag")) self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0]) self._selection_material = renderer.createMaterial(Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "color.frag")) self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128)) for node in DepthFirstIterator(scene.getRoot()): # We do not want to render ConvexHullNode as it conflicts with the bottom layers. # However, it is somewhat relevant when the node is selected, so do render it then. if type(node) is ConvexHullNode and not Selection.isSelected(node.getWatchedNode()): continue if not node.render(renderer): if node.getMeshData() and node.isVisible(): if Selection.isSelected(node): renderer.queueNode(node, material = self._selection_material, transparent = True) layer_data = node.callDecoration("getLayerData") if not layer_data: continue # Render all layers below a certain number as line mesh instead of vertices. if self._current_layer_num - self._solid_layers > -1: start = 0 end = 0 element_counts = layer_data.getElementCounts() for layer, counts in element_counts.items(): if layer + self._solid_layers > self._current_layer_num: break end += counts # This uses glDrawRangeElements internally to only draw a certain range of lines. renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) # We currently recreate the current "solid" layers every time a if not self._current_layer_mesh: self._current_layer_mesh = MeshData() for i in range(self._solid_layers): layer = self._current_layer_num - i if layer < 0: continue layer_mesh = layer_data.getLayer(layer).createMesh() if not layer_mesh or layer_mesh.getVertices() is None: continue self._current_layer_mesh.addVertices(layer_mesh.getVertices()) # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. brightness = (2.0 - (i / self._solid_layers)) / 2.0 self._current_layer_mesh.addColors(layer_mesh.getColors() * brightness) renderer.queueNode(node, mesh = self._current_layer_mesh, material = self._material)
def requestWriteSelectionToDevice(self, device_id, file_name): if not Selection.hasSelection(): return # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. event = CallFunctionEvent(self._writeToDevice, [Selection.getSelectedObject(0), device_id, file_name], {}) Application.getInstance().functionEvent(event)
def event(self, event): super().event(event) if event.type == Event.MousePressEvent and self._controller.getToolsEnabled(): # Initialise a mirror operation if MouseEvent.LeftButton not in event.buttons: return False id = self._selection_pass.getIdAtPosition(event.x, event.y) if not id: return False if self._handle.isAxis(id): self.setLockedAxis(id) self._operation_started = True self.operationStarted.emit(self) return True if event.type == Event.MouseReleaseEvent: if self._operation_started: self._operation_started = False self.operationStopped.emit(self) # Perform a mirror operation if self.getLockedAxis() != ToolHandle.NoAxis: if Selection.getCount() == 1: node = Selection.getSelectedObject(0) if self.getLockedAxis() == ToolHandle.XAxis: mirror = Vector(-1, 1, 1) elif self.getLockedAxis() == ToolHandle.YAxis: mirror = Vector(1, -1, 1) elif self.getLockedAxis() == ToolHandle.ZAxis: mirror = Vector(1, 1, -1) else: mirror = Vector(1, 1, 1) op = MirrorOperation(node, mirror, mirror_around_center = True) else: op = GroupedOperation() for node in self._getSelectedObjectsWithoutSelectedAncestors(): if self.getLockedAxis() == ToolHandle.XAxis: mirror = Vector(-1, 1, 1) elif self.getLockedAxis() == ToolHandle.YAxis: mirror = Vector(1, -1, 1) elif self.getLockedAxis() == ToolHandle.ZAxis: mirror = Vector(1, 1, -1) else: mirror = Vector(1, 1, 1) op.addOperation(MirrorOperation(node, mirror, mirror_around_center = True)) op.push() self.setLockedAxis(ToolHandle.NoAxis) return True return False
def requestWriteSelectionToDevice(self, device_id, file_name, kwargs): if not Selection.hasSelection(): return filter_by_machine = kwargs.get("filter_by_machine", False) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. Application.getInstance().callLater(self._writeToDevice, Selection.getSelectedObject(0), device_id, file_name, filter_by_machine)
def removeSelection(self): if not Selection.hasSelection(): return op = GroupedOperation() for node in Selection.getAllSelectedObjects(): op.addOperation(RemoveSceneNodeOperation(node)) op.push() Selection.clear()
def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: Mapping[str, str]) -> None: if not Selection.hasSelection(): return limit_mimetypes = kwargs.get("limit_mimetypes", False) preferred_mimetypes = kwargs.get("preferred_mimetypes", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetypes = preferred_mimetypes)
def setActiveBuildPlate(self, nr): if nr == self._active_build_plate: return Logger.log("d", "Select build plate: %s" % nr) self._active_build_plate = nr Selection.clear() self._multi_build_plate_model.setActiveBuildPlate(nr) self._objects_model.setActiveBuildPlate(nr) self.activeBuildPlateChanged.emit()
def test_selectionCount(self): assert self.proxy.selectionCount == 0 node_1 = SceneNode() Selection.add(node_1) assert self.proxy.selectionCount == 1 node_2 = SceneNode() Selection.add(node_2) assert self.proxy.selectionCount == 2
def setX(self, x): bounding_box = Selection.getBoundingBox() op = GroupedOperation() for selected_node in Selection.getAllSelectedObjects(): world_position = selected_node.getWorldPosition() new_position = world_position.set(x=float(x) + (world_position.x - bounding_box.center.x)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() self.operationStopped.emit(self)
def test_hasSelection(self): # Nothing is selected by default assert not self.proxy.hasSelection node_1 = SceneNode() Selection.add(node_1) assert self.proxy.hasSelection Selection.remove(node_1) assert not self.proxy.hasSelection
def _onChildrenChanged(self, node): if not self.getNode().hasChildren(): # A group that no longer has children may remove itself from the scene self._old_parent = self.getNode().getParent() self.getNode().setParent(None) Selection.remove(self.getNode()) else: # A group that has removed itself from the scene because it had no children may add itself back to the scene when a child is added to it if not self.getNode().getParent() and self._old_parent: self.getNode().setParent(self._old_parent) self._old_parent = None
def setScaleX(self, scale): obj = Selection.getSelectedObject(0) if obj: obj_scale = self._getScaleInWorldCoordinates(obj) if round(float(obj_scale.x), 4) != scale: scale_factor = abs(scale / obj_scale.x) if self._non_uniform_scale: scale_vector = Vector(scale_factor, 1, 1) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector, scale_around_point = obj.getWorldPosition())
def setScaleZ(self, scale): obj = Selection.getSelectedObject(0) if obj: obj_scale = self._getScaleInWorldCoordinates(obj) if round(float(obj_scale.z), 4) != scale: scale_factor = abs(scale / obj_scale.z) if self._non_uniform_scale: scale_vector = Vector(1, 1, scale_factor) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector)
def onSelectionChanged(self): if Selection.hasSelection(): if not self.getController().getActiveTool(): self.getController().setActiveTool("TranslateTool") self._camera_animation.setStart(self.getController().getTool("CameraTool").getOrigin()) self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition()) self._camera_animation.start() else: if self.getController().getActiveTool(): self.getController().setActiveTool(None)
def test_UndoRedoWithSelection(): node = SceneNode() parent_node = SceneNode() Selection.add(node) operation = AddSceneNodeOperation(node, parent_node) operation.undo() assert not Selection.isSelected(node) operation.redo() assert Selection.isSelected(node)
def setObjectWidth(self, width): obj = Selection.getSelectedObject(0) if obj: width = float(width) obj_width = obj.getBoundingBox().width if not Float.fuzzyCompare(obj_width, width, DIMENSION_TOLERANCE): scale_factor = width / obj_width if self._non_uniform_scale: scale_vector = Vector(scale_factor, 1, 1) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector, scale_around_point = obj.getWorldPosition())
def setObjectHeight(self, height): obj = Selection.getSelectedObject(0) if obj: height = float(height) obj_height = obj.getBoundingBox().height if not Float.fuzzyCompare(obj_height, height, DIMENSION_TOLERANCE): scale_factor = height / obj_height if self._non_uniform_scale: scale_vector = Vector(1, scale_factor, 1) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector)
def setObjectDepth(self, depth): obj = Selection.getSelectedObject(0) if obj: depth = float(depth) obj_depth = obj.getBoundingBox().depth if not Float.fuzzyCompare(obj_depth, depth, DIMENSION_TOLERANCE): scale_factor = depth / obj_depth if self._non_uniform_scale: scale_vector = Vector(1, 1, scale_factor) else: scale_vector = Vector(scale_factor, scale_factor, scale_factor) Selection.applyOperation(ScaleOperation, scale_vector)
def _removeEraserMesh(self, node: CuraSceneNode): parent = node.getParent() if parent == self._controller.getScene().getRoot(): parent = None op = RemoveSceneNodeOperation(node) op.push() if parent and not Selection.isSelected(parent): Selection.add(parent) CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def setZ(self, z): bounding_box = Selection.getBoundingBox() op = GroupedOperation() for selected_node in Selection.getAllSelectedObjects(): # Note: The switching of z & y is intentional. We display z as up for the user, # But store the data in openGL space. world_position = selected_node.getWorldPosition() new_position = world_position.set(y=float(z) + (world_position.y - bounding_box.bottom)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() self.operationStopped.emit(self)
def groupSelected(self): group_node = SceneNode() group_decorator = GroupDecorator() group_node.addDecorator(group_decorator) group_node.setParent(self.getController().getScene().getRoot()) for node in Selection.getAllSelectedObjects(): node.setParent(group_node) for node in group_node.getChildren(): Selection.remove(node) Selection.add(group_node)
def setX(self, x): parsed_x = self._parseInt(x) bounding_box = Selection.getBoundingBox() op = GroupedOperation() if not Float.fuzzyCompare(parsed_x, float(bounding_box.center.x), DIMENSION_TOLERANCE): for selected_node in Selection.getAllSelectedObjects(): world_position = selected_node.getWorldPosition() new_position = world_position.set(x=parsed_x + (world_position.x - bounding_box.center.x)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() self._controller.toolOperationStopped.emit(self)
def selectAll(self): if not self.getController().getToolsEnabled(): return Selection.clear() for node in DepthFirstIterator(self.getController().getScene().getRoot()): if type(node) is not SceneNode: continue if not node.getMeshData() and not node.callDecoration("isGroup"): continue # Node that doesnt have a mesh and is not a group. if node.getParent() and node.getParent().callDecoration("isGroup"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) Selection.add(node)
def event(self, event): super().event(event) if event.type == Event.MousePressEvent: if MouseEvent.LeftButton not in event.buttons: return False id = self._selection_pass.getIdAtPosition(event.x, event.y) if not id: return False if ToolHandle.isAxis(id): self.setLockedAxis(id) return True if event.type == Event.MouseReleaseEvent: if self.getLockedAxis(): op = None if Selection.getCount() == 1: node = Selection.getSelectedObject(0) mirror = node.getMirror() if self.getLockedAxis() == ToolHandle.XAxis: mirror.setX(-mirror.x) elif self.getLockedAxis() == ToolHandle.YAxis: mirror.setY(-mirror.y) elif self.getLockedAxis() == ToolHandle.ZAxis: mirror.setZ(-mirror.z) op = MirrorOperation(node, mirror, set_mirror=True) else: op = GroupedOperation() for node in Selection.getAllSelectedObjects(): mirror = node.getMirror() if self.getLockedAxis() == ToolHandle.XAxis: mirror.setX(-mirror.x) elif self.getLockedAxis() == ToolHandle.YAxis: mirror.setY(-mirror.y) elif self.getLockedAxis() == ToolHandle.ZAxis: mirror.setZ(-mirror.z) op.addOperation(MirrorOperation(node, mirror, set_mirror = True)) op.push() self.setLockedAxis(None) return True return False
def event(self, event): super().event(event) if event.type == Event.MousePressEvent: if MouseEvent.LeftButton not in event.buttons: return False id = self._renderer.getIdAtCoordinate(event.x, event.y) if not id: return False if ToolHandle.isAxis(id): self.setLockedAxis(id) return True if event.type == Event.MouseReleaseEvent: if self.getLockedAxis(): op = None if Selection.getCount() == 1: node = Selection.getSelectedObject(0) scale = node.getScale() if self.getLockedAxis() == ToolHandle.XAxis: scale.setX(-scale.x) elif self.getLockedAxis() == ToolHandle.YAxis: scale.setY(-scale.y) elif self.getLockedAxis() == ToolHandle.ZAxis: scale.setZ(-scale.z) op = ScaleOperation(node, scale, set_scale=True) else: op = GroupedOperation() for node in Selection.getAllSelectedObjects(): scale = node.getScale() if self.getLockedAxis() == ToolHandle.XAxis: scale.setX(-scale.x) elif self.getLockedAxis() == ToolHandle.YAxis: scale.setY(-scale.y) elif self.getLockedAxis() == ToolHandle.ZAxis: scale.setZ(-scale.z) op.addOperation(ScaleOperation(node, scale, set_scale = True)) op.push() self.setLockedAxis(None) return True return False
def setZ(self, z): parsed_z = self._parseInt(z) bounding_box = Selection.getBoundingBox() op = GroupedOperation() if not Float.fuzzyCompare(parsed_z, float(bounding_box.center.y), DIMENSION_TOLERANCE): for selected_node in Selection.getAllSelectedObjects(): # Note: The switching of z & y is intentional. We display z as up for the user, # But store the data in openGL space. world_position = selected_node.getWorldPosition() new_position = world_position.set(y=parsed_z + (world_position.y - bounding_box.bottom)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() self._controller.toolOperationStopped.emit(self)
def groupSelected(self): group_node = SceneNode() group_decorator = GroupDecorator() group_node.addDecorator(group_decorator) group_node.setParent(self.getController().getScene().getRoot()) for node in Selection.getAllSelectedObjects(): node.setParent(group_node) group_node.setCenterPosition(group_node.getBoundingBox().center) #group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0)) group_node.translate(group_node.getBoundingBox().center) for node in group_node.getChildren(): Selection.remove(node) Selection.add(group_node)
def updateList(self, trigger_node): self.clear() for root_child in self._scene.getRoot().getChildren(): if root_child.callDecoration("isGroup"): # Check if its a group node parent_key = id(root_child) for node in DepthFirstIterator(root_child): if root_child in self._collapsed_nodes: self._collapsed_nodes.append(node) data = {"name":node.getName(), "visibility": node.isVisible(), "key": (id(node)), "selected": Selection.isSelected(node), "collapsed": node in self._collapsed_nodes, "parent_key": parent_key, "is_group":bool(node.callDecoration("isGroup")), "is_dummy" : False } self.appendItem(data) data = { "name":"Dummy", "visibility": True, "key": 0, "selected": Selection.isSelected(node), "collapsed": root_child in self._collapsed_nodes, "parent_key": parent_key, "is_group":False, "is_dummy" : True } self.appendItem(data) elif type(root_child) is SceneNode or type(root_child) is PointCloudNode: # Item is not a group node. data = {"name":root_child.getName(), "visibility": root_child.isVisible(), "key": (id(root_child)), "selected": Selection.isSelected(root_child), "collapsed": root_child in self._collapsed_nodes, "parent_key": 0, "is_group":bool(root_child.callDecoration("isGroup")), "is_dummy" : False } # Check if data exists, if yes, remove old and re-add. index = self.find("key",(id(root_child))) if index is not None and index >= 0: self.removeItem(index) self.insertItem(index,data) else: self.appendItem(data)
def multiplySelection(self, count: int) -> None: job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset=4) job.start()
def _onChangeTimerFinished(self): if not self._enabled: return root = self._controller.getScene().getRoot() for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode: continue bbox = node.getBoundingBox() if not bbox or not bbox.isValid(): self._change_timer.start() continue build_volume_bounding_box = copy.deepcopy( self._build_volume.getBoundingBox()) build_volume_bounding_box.setBottom( -9001) # Ignore intersections with the bottom # 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 else: node._outside_buildarea = False # Move it downwards if bottom is above platform move_vector = Vector() if bbox.bottom > 0: move_vector.setY(-bbox.bottom) #if not Float.fuzzyCompare(bbox.bottom, 0.0): # pass#move_vector.setY(-bbox.bottom) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) if not node.callDecoration("getConvexHull"): if not node.callDecoration("getConvexHullJob"): job = ConvexHullJob.ConvexHullJob(node) job.start() node.callDecoration("setConvexHullJob", job) elif Selection.isSelected(node): pass elif Preferences.getInstance().getValue( "physics/automatic_push_free"): # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. if other_node is root or type( other_node) is not SceneNode or other_node is node: continue # Ignore colissions of a group with it's own children if other_node in node.getAllChildren( ) or node in other_node.getAllChildren(): continue # Ignore colissions within a group if other_node.getParent().callDecoration( "isGroup") is not None: if node.getParent().callDecoration( "isGroup") is other_node.getParent( ).callDecoration("isGroup"): continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration( "getConvexHull") or not other_node.getBoundingBox( ): continue # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. #if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: # continue # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. try: overlap = node.callDecoration( "getConvexHull").intersectsPolygon( other_node.callDecoration("getConvexHull")) except: overlap = None #It can sometimes occur that the caclulated convex hull has no size, in which case there is no overlap. if overlap is None: continue move_vector.setX(overlap[0] * 1.1) move_vector.setZ(overlap[1] * 1.1) 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._build_volume.getDisallowedAreas(): overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue node._outside_buildarea = True if move_vector != Vector(): op = PlatformPhysicsOperation.PlatformPhysicsOperation( node, move_vector) op.push()
def redo(self) -> None: self._node.setParent(self._parent) if self._selected: # It was selected while the operation was undone. We should restore that selection. Selection.add(self._node)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() self._checkSetup() global_container_stack = Application.getInstance( ).getGlobalContainerStack() 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 >= 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))) self._enabled_shader.setUniformValue("u_lowestPrintableHeight", self._lowest_printable_height) 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 node.render(renderer): continue if node.getMeshData() and node.isVisible(): uniforms = {} shade_factor = 1.0 per_mesh_stack = node.callDecoration("getStack") 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] 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 ] # 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 ( 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._non_printing_shader, transparent=True) elif getattr(node, "_outside_buildarea", False): disabled_batch.addItem( node.getWorldTransformation(copy=False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix()) 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, normal_transformation=node.getCachedNormalMatrix()) if node.callDecoration("isGroup") and Selection.isSelected(node): renderer.queueNode(scene.getRoot(), mesh=node.getBoundingBoxMesh(), mode=RenderBatch.RenderMode.LineLoop)
def getX(self): if Selection.hasSelection(): return float(Selection.getBoundingBox().center.x) return 0.0
def getSelectedIndex(self): selected_object_id = id(Selection.getSelectedObject(0)) index = self.getModel().find("id", selected_object_id) return index
def getObjectHeight(self): if Selection.hasSelection(): return float(Selection.getSelectedObject(0).getBoundingBox().height) return 0.0
def getScaleZ(self): if Selection.hasSelection(): return round(float(Selection.getSelectedObject(0).getScale().z), 4) return 1.0
def getSelectedActiveExtruder(self): selected_object = Selection.getSelectedObject(0) return selected_object.callDecoration("getActiveExtruder")
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() if not self._extruders_model: self._extruders_model = Application.getInstance( ).getExtrudersModel() if not self._theme: self._theme = Application.getInstance().getTheme() 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())) if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._disabled_shader.setUniformValue( "u_diffuseColor1", Color(*self._theme.getColor("model_unslicable").getRgb())) self._disabled_shader.setUniformValue( "u_diffuseColor2", Color(*self._theme.getColor("model_unslicable_alt").getRgb())) self._disabled_shader.setUniformValue("u_width", 50.0) if not self._non_printing_shader: self._non_printing_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "transparent_object.shader")) self._non_printing_shader.setUniformValue( "u_diffuseColor", Color(*self._theme.getColor("model_non_printing").getRgb())) self._non_printing_shader.setUniformValue("u_opacity", 0.6) if not self._support_mesh_shader: self._support_mesh_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._support_mesh_shader.setUniformValue("u_vertical_stripes", True) self._support_mesh_shader.setUniformValue("u_width", 5.0) global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault( "support_extruder_nr") support_angle_stack = Application.getInstance().getExtruderManager( ).getExtruderStack(support_extruder_nr) if support_angle_stack is not None and Application.getInstance( ).getPreferences().getValue("view/show_overhang"): angle = support_angle_stack.getProperty( "support_angle", "value") # Make sure the overhang angle is valid before passing it to the shader # Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None) if angle is not None and global_container_stack.getProperty( "support_angle", "validationState") in [None, ValidatorState.Valid]: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(90 - 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))) 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 per_mesh_stack = node.callDecoration("getStack") extruder_index = node.callDecoration( "getActiveExtruderPosition") if extruder_index is None: extruder_index = "0" extruder_index = int(extruder_index) # 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")) 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 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 ] 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) 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)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() if not self._enabled_shader: self._enabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "overhang.shader")) if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._disabled_shader.setUniformValue("u_diffuseColor1", [0.48, 0.48, 0.48, 1.0]) self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) self._disabled_shader.setUniformValue("u_width", 50.0) multi_extrusion = False global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if Preferences.getInstance().getValue("view/show_overhang"): angle = global_container_stack.getProperty( "support_angle", "value") if angle is not None and global_container_stack.getProperty( "support_angle", "validationState") == ValidatorState.Valid: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(90 - 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))) multi_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): uniforms = {} if not multi_extrusion: if global_container_stack: material = global_container_stack.findContainer( {"type": "material"}) material_color = material.getMetaDataEntry( "color_code", default=self._extruders_model.defaultColors[0] ) if material else self._extruders_model.defaultColors[ 0] else: material_color = self._extruders_model.defaultColors[ 0] else: # Get color to render this mesh in from ExtrudersModel extruder_index = 0 extruder_id = node.callDecoration("getActiveExtruder") if extruder_id: extruder_index = max( 0, self._extruders_model.find("id", extruder_id)) material_color = self._extruders_model.getItem( extruder_index)["color"] 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"] = [ int(material_color[1:3], 16) / 255, int(material_color[3:5], 16) / 255, int(material_color[5:7], 16) / 255, 1.0 ] except ValueError: pass if hasattr(node, "_outside_buildarea"): if node._outside_buildarea: renderer.queueNode(node, shader=self._disabled_shader) else: renderer.queueNode(node, shader=self._enabled_shader, uniforms=uniforms) else: renderer.queueNode(node, material=self._enabled_shader, uniforms=uniforms) if node.callDecoration("isGroup") and Selection.isSelected( node): renderer.queueNode(scene.getRoot(), mesh=node.getBoundingBoxMesh(), mode=Renderer.RenderLines)
def event(self, event): super().event(event) if event.type == Event.ToolActivateEvent: self._old_scale = Selection.getSelectedObject(0).getScale() for node in Selection.getAllSelectedObjects(): node.boundingBoxChanged.connect(self.propertyChanged) if event.type == Event.ToolDeactivateEvent: for node in Selection.getAllSelectedObjects(): node.boundingBoxChanged.disconnect(self.propertyChanged) if event.type == Event.KeyPressEvent: if event.key == KeyEvent.ShiftKey: self._snap_scale = False self.propertyChanged.emit() elif event.key == KeyEvent.ControlKey: self._non_uniform_scale = True self.propertyChanged.emit() if event.type == Event.KeyReleaseEvent: if event.key == KeyEvent.ShiftKey: self._snap_scale = True self.propertyChanged.emit() elif event.key == KeyEvent.ControlKey: self._non_uniform_scale = False self.propertyChanged.emit() if event.type == Event.MousePressEvent and self._controller.getToolsEnabled(): if MouseEvent.LeftButton not in event.buttons: return False id = self._selection_pass.getIdAtPosition(event.x, event.y) if not id: return False if ToolHandle.isAxis(id): self.setLockedAxis(id) self._saved_handle_position = self._handle.getWorldPosition() if id == ToolHandle.XAxis: self.setDragPlane(Plane(Vector(0, 0, 1), self._saved_handle_position.z)) elif id == ToolHandle.YAxis: self.setDragPlane(Plane(Vector(0, 0, 1), self._saved_handle_position.z)) elif id == ToolHandle.ZAxis: self.setDragPlane(Plane(Vector(0, 1, 0), self._saved_handle_position.y)) else: self.setDragPlane(Plane(Vector(0, 1, 0), self._saved_handle_position.y)) self.setDragStart(event.x, event.y) self.operationStarted.emit(self) if event.type == Event.MouseMoveEvent: if not self.getDragPlane(): return False #handle_position = self._handle.getWorldPosition() drag_position = self.getDragPosition(event.x, event.y) if drag_position: drag_length = (drag_position - self._saved_handle_position).length() if self._drag_length > 0: drag_change = (drag_length - self._drag_length) / 100 * self._scale_speed scale_factor = drag_change scale_change = Vector(0.0, 0.0, 0.0) if self._non_uniform_scale: if self.getLockedAxis() == ToolHandle.XAxis: scale_change.setX(scale_factor) elif self.getLockedAxis() == ToolHandle.YAxis: scale_change.setY(scale_factor) elif self.getLockedAxis() == ToolHandle.ZAxis: scale_change.setZ(scale_factor) else: scale_change.setX(scale_factor) scale_change.setY(scale_factor) scale_change.setZ(scale_factor) Selection.applyOperation(ScaleOperation, scale_change, relative_scale = True, snap = self._snap_scale) self._drag_length = (self._saved_handle_position - drag_position).length() return True if event.type == Event.MouseReleaseEvent: if self.getDragPlane(): self.setDragPlane(None) self.setLockedAxis(None) self._drag_length = 0 self.operationStopped.emit(self) return True
def getScaleZ(self): if Selection.hasSelection(): ## Ensure that the returned value is positive (mirror causes scale to be negative) return abs(round(float(Selection.getSelectedObject(0).getScale().z), 4)) return 1.0
def getObjectDepth(self): if Selection.hasSelection(): return float(Selection.getSelectedObject(0).getBoundingBox().depth) return 0.0
def undo(self) -> None: self._node.setParent(None) self._selected = Selection.isSelected(self._node) if self._selected: Selection.remove(self._node) # Also remove the node from the selection.
def getY(self): if Selection.hasSelection(): # Note; The switching of z & y is intentional. We display z as up for the user, # But store the data in openGL space. return float(Selection.getBoundingBox().center.z) return 0.0
def _update(self, *args) -> None: nodes = [] filter_current_build_plate = Application.getInstance().getPreferences().getValue("view/filter_current_build_plate") active_build_plate_number = self._build_plate_number naming_regex = re.compile("^(.+)\(([0-9]+)\)$") NodeInfo = namedtuple("NodeInfo", ["index_to_node", "nodes_to_rename", "is_group"]) name_to_node_info_dict = {} # type: Dict[str, NodeInfo] group_name_template = catalog.i18nc("@label", "Group #{group_nr}") group_name_prefix = group_name_template.split("#")[0] for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): # type: ignore if not isinstance(node, SceneNode): continue if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"): continue parent = node.getParent() if parent and parent.callDecoration("isGroup"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"): continue node_build_plate_number = node.callDecoration("getBuildPlateNumber") if filter_current_build_plate and node_build_plate_number != active_build_plate_number: continue is_group = bool(node.callDecoration("isGroup")) force_rename = False if not is_group: # Handle names for individual nodes name = node.getName() name_match = naming_regex.fullmatch(name) if name_match is None: original_name = name name_index = 0 else: original_name = name_match.groups()[0] name_index = int(name_match.groups()[1]) else: # Handle names for grouped nodes original_name = group_name_prefix current_name = node.getName() if current_name.startswith(group_name_prefix): name_index = int(current_name.split("#")[-1]) else: # Force rename this group because this node has not been named as a group yet, probably because # it's a newly created group. name_index = 0 force_rename = True if original_name not in name_to_node_info_dict: # Keep track of 2 things: # - known indices for nodes which doesn't need to be renamed # - a list of nodes that need to be renamed. When renaming then, we should avoid using the known indices. name_to_node_info_dict[original_name] = NodeInfo(index_to_node = {}, nodes_to_rename = [], is_group = is_group) node_info_dict = name_to_node_info_dict[original_name] if not force_rename and name_index not in node_info_dict.index_to_node: node_info_dict.index_to_node[name_index] = node else: node_info_dict.nodes_to_rename.append(node) # Go through all names and rename the nodes that need to be renamed. node_rename_list = [] # type: List[Dict[str, Any]] for name, node_info_dict in name_to_node_info_dict.items(): # First add the ones that do not need to be renamed. for node in node_info_dict.index_to_node.values(): node_rename_list.append({"node": node}) # Generate new names for the nodes that need to be renamed current_index = 0 for node in node_info_dict.nodes_to_rename: current_index += 1 while current_index in node_info_dict.index_to_node: current_index += 1 if not node_info_dict.is_group: new_group_name = "{0}({1})".format(name, current_index) else: new_group_name = "{0}#{1}".format(name, current_index) node_rename_list.append({"node": node, "new_name": new_group_name}) for node_info in node_rename_list: node = node_info["node"] new_name = node_info.get("new_name") if hasattr(node, "isOutsideBuildArea"): is_outside_build_area = node.isOutsideBuildArea() # type: ignore else: is_outside_build_area = False node_build_plate_number = node.callDecoration("getBuildPlateNumber") from UM.Logger import Logger if new_name is not None: old_name = node.getName() node.setName(new_name) Logger.log("d", "Node [%s] renamed to [%s]", old_name, new_name) nodes.append({ "name": node.getName(), "selected": Selection.isSelected(node), "outside_build_area": is_outside_build_area, "buildplate_number": node_build_plate_number, "node": node }) nodes = sorted(nodes, key=lambda n: n["name"]) self.setItems(nodes) self.itemsChanged.emit()
def resetScale(self): """Reset scale of the selected objects""" Selection.applyOperation(SetTransformOperation, None, None, Vector(1.0, 1.0, 1.0), Vector(0, 0, 0))
def changeSelection(self, index): modifiers = QApplication.keyboardModifiers() ctrl_is_active = modifiers & Qt.ControlModifier shift_is_active = modifiers & Qt.ShiftModifier if ctrl_is_active: item = self._objects_model.getItem(index) node = item["node"] if Selection.isSelected(node): Selection.remove(node) else: Selection.add(node) elif shift_is_active: polarity = 1 if index + 1 > self._last_selected_index else -1 for i in range(self._last_selected_index, index + polarity, polarity): item = self._objects_model.getItem(i) node = item["node"] Selection.add(node) else: # Single select item = self._objects_model.getItem(index) node = item["node"] build_plate_number = node.callDecoration("getBuildPlateNumber") if build_plate_number is not None and build_plate_number != -1: self.setActiveBuildPlate(build_plate_number) Selection.clear() Selection.add(node) self._last_selected_index = index
def resetScale(self): Selection.applyOperation(SetTransformOperation, None, None, Vector(1.0, 1.0, 1.0))
def event(self, event): super().event(event) if event.type == Event.ToolActivateEvent: self._old_scale = Selection.getSelectedObject(0).getScale() for node in Selection.getAllSelectedObjects(): node.boundingBoxChanged.connect(self.propertyChanged) if event.type == Event.ToolDeactivateEvent: for node in Selection.getAllSelectedObjects(): node.boundingBoxChanged.disconnect(self.propertyChanged) if event.type == Event.KeyPressEvent: if event.key == KeyEvent.ShiftKey: self._snap_scale = False self.propertyChanged.emit() elif event.key == KeyEvent.ControlKey: self._non_uniform_scale = True self.propertyChanged.emit() if event.type == Event.KeyReleaseEvent: if event.key == KeyEvent.ShiftKey: self._snap_scale = True self.propertyChanged.emit() elif event.key == KeyEvent.ControlKey: self._non_uniform_scale = False self.propertyChanged.emit() if event.type == Event.MousePressEvent: if MouseEvent.LeftButton not in event.buttons: return False id = self._renderer.getIdAtCoordinate(event.x, event.y) if not id: return False if ToolHandle.isAxis(id): self.setLockedAxis(id) handle_position = self._handle.getWorldPosition() if id == ToolHandle.XAxis: self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z)) elif id == ToolHandle.YAxis: self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z)) elif id == ToolHandle.ZAxis: self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y)) else: self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y)) self.setDragStart(event.x, event.y) self.operationStarted.emit(self) if event.type == Event.MouseMoveEvent: if not self.getDragPlane(): return False handle_position = self._handle.getWorldPosition() drag_position = self.getDragPosition(event.x, event.y) if drag_position: drag_length = (drag_position - handle_position).length() if self._drag_length > 0: drag_change = (drag_length - self._drag_length) / 100 * self._scale_speed if self._snap_scale: scaleFactor = round(drag_change, 1) else: scaleFactor = drag_change scale = Vector(0.0, 0.0, 0.0) if self._non_uniform_scale: if self.getLockedAxis() == ToolHandle.XAxis: scale.setX(scaleFactor) elif self.getLockedAxis() == ToolHandle.YAxis: scale.setY(scaleFactor) elif self.getLockedAxis() == ToolHandle.ZAxis: scale.setZ(scaleFactor) else: scale.setX(scaleFactor) scale.setY(scaleFactor) scale.setZ(scaleFactor) Selection.applyOperation(ScaleOperation, scale, add_scale=True) #this part prevents the mesh being scaled to a size < 0. #This cannot be done before the operation (even though that would be more efficient) #because then the operation can distract more of the mesh then is remaining of its size realWorldMeshScale = Selection.getSelectedObject( 0).getScale() if realWorldMeshScale.x <= 0 or realWorldMeshScale.y <= 0 or realWorldMeshScale.z <= 0: minimumScale = 0.01 #1% so the mesh never completely disapears for the user if self._snap_scale == True: minimumScale = 0.1 #10% same reason as above if realWorldMeshScale.x <= 0: realWorldMeshScale.setX(minimumScale) if realWorldMeshScale.y <= 0: realWorldMeshScale.setY(minimumScale) if realWorldMeshScale.z <= 0: realWorldMeshScale.setZ(minimumScale) Selection.applyOperation(SetTransformOperation, None, None, realWorldMeshScale) self._drag_length = (handle_position - drag_position).length() return True if event.type == Event.MouseReleaseEvent: if self.getDragPlane(): self.setDragPlane(None) self.setLockedAxis(None) self._drag_length = 0 self.operationStopped.emit(self) return True
def test_selectionNames(self): node_1 = SceneNode(name="TestNode1") node_2 = SceneNode(name="TestNode2") Selection.add(node_2) Selection.add(node_1) assert self.proxy.selectionNames == ["TestNode2", "TestNode1"]
def setSelectedActiveExtruder(self, extruder_stack_id): selected_object = Selection.getSelectedObject(0) stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. if not stack: selected_object.addDecorator(SettingOverrideDecorator()) selected_object.callDecoration("setActiveExtruder", extruder_stack_id)
def tearDown(self): Selection.clear()
def getContainerID(self): selected_object = Selection.getSelectedObject(0) try: return selected_object.callDecoration("getStack").getId() except AttributeError: return ""
def setUp(self): Selection.clear() self.proxy = SelectionProxy()
def getSelectedObjectId(self): selected_object = Selection.getSelectedObject(0) selected_object_id = id(selected_object) return selected_object_id
def _updateSelectedObjectBuildPlateNumbers(self, *args): result = set() for node in Selection.getAllSelectedObjects(): result.add(node.callDecoration("getBuildPlateNumber")) self._selection_build_plates = list(result) self.selectionChanged.emit()
def scaleToMax(self): if hasattr(self.getController().getScene(), "_maximum_bounds"): Selection.applyOperation(ScaleToBoundsOperation, self.getController().getScene()._maximum_bounds)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() renderer.setRenderSelection(False) if not self._material: self._material = renderer.createMaterial( Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "vertexcolor.frag")) self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0]) self._selection_material = renderer.createMaterial( Resources.getPath(Resources.Shaders, "basic.vert"), Resources.getPath(Resources.Shaders, "color.frag")) self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128)) for node in DepthFirstIterator(scene.getRoot()): # We do not want to render ConvexHullNode as it conflicts with the bottom layers. # However, it is somewhat relevant when the node is selected, so do render it then. if type(node) is ConvexHullNode and not Selection.isSelected( node.getWatchedNode()): continue if not node.render(renderer): if node.getMeshData() and node.isVisible(): if Selection.isSelected(node): renderer.queueNode(node, material=self._selection_material, transparent=True) layer_data = node.callDecoration("getLayerData") if not layer_data: continue # Render all layers below a certain number as line mesh instead of vertices. if self._current_layer_num - self._solid_layers > -1: start = 0 end = 0 element_counts = layer_data.getElementCounts() for layer, counts in element_counts.items(): if layer + self._solid_layers > self._current_layer_num: break end += counts # This uses glDrawRangeElements internally to only draw a certain range of lines. renderer.queueNode(node, mesh=layer_data, material=self._material, mode=Renderer.RenderLines, start=start, end=end) # We currently recreate the current "solid" layers every time a if not self._current_layer_mesh: self._current_layer_mesh = MeshData() for i in range(self._solid_layers): layer = self._current_layer_num - i if layer < 0: continue try: layer_mesh = layer_data.getLayer( layer).createMesh() if not layer_mesh or layer_mesh.getVertices( ) is None: continue except: continue if self._current_layer_mesh: #Threading thing; Switching between views can cause the current layer mesh to be deleted. self._current_layer_mesh.addVertices( layer_mesh.getVertices()) # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. brightness = (2.0 - (i / self._solid_layers)) / 2.0 if self._current_layer_mesh: self._current_layer_mesh.addColors( layer_mesh.getColors() * brightness) if self._current_layer_mesh: renderer.queueNode(node, mesh=self._current_layer_mesh, material=self._material) if not self._current_layer_jumps: self._current_layer_jumps = MeshData() for i in range(1): layer = self._current_layer_num - i if layer < 0: continue try: layer_mesh = layer_data.getLayer( layer).createJumps() if not layer_mesh or layer_mesh.getVertices( ) is None: continue except: continue self._current_layer_jumps.addVertices( layer_mesh.getVertices()) # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. brightness = (2.0 - (i / self._solid_layers)) / 2.0 self._current_layer_jumps.addColors( layer_mesh.getColors() * brightness) renderer.queueNode(node, mesh=self._current_layer_jumps, material=self._material)