def test_create(self): p = Plane() self.assertEqual(Vector(), p.normal) self.assertEqual(0.0, p.distance) p = Plane(Vector.Unit_Y, 1.0) self.assertEqual(Vector.Unit_Y, p.normal) self.assertEqual(1.0, p.distance)
def test_getDragVector(): test_tool_1 = createTool("test_tool_1") # No drag plane set assert test_tool_1.getDragVector(0, 0) is None test_tool_1.setDragPlane(Plane()) # No drag start assert test_tool_1.getDragVector(0, 0) is None
def test_intersects(self): p = Plane(Vector.Unit_Y, 0.0) r = Ray(Vector(0, 10, 0), -Vector.Unit_Y) result = p.intersectsRay(r) self.assertNotEqual(False, result) self.assertEqual(10.0, result) r = Ray(Vector(0, -10, 0), Vector.Unit_Y) result = p.intersectsRay(r) self.assertNotEqual(False, result) self.assertEqual(10.0, result) r = Ray(Vector(0, 10, 0), Vector.Unit_Y) self.assertEqual(False, p.intersectsRay(r)) r = Ray(Vector(0, 0, 0), Vector.Unit_X) self.assertEqual(False, p.intersectsRay(r))
def test_getDragVector(): test_tool_1 = Tool() test_tool_1.setPluginId("test_tool_1") # No drag plane set assert test_tool_1.getDragVector(0, 0) is None test_tool_1.setDragPlane(Plane()) # No drag start assert test_tool_1.getDragVector(0, 0) is None
def event(self, event: Event) -> bool: super().event(event) # Make sure the displayed values are updated if the bounding box of the selected mesh(es) changes if event.type == Event.ToolActivateEvent: for node in self._getSelectedObjectsWithoutSelectedAncestors(): node.boundingBoxChanged.connect(self.propertyChanged) if event.type == Event.ToolDeactivateEvent: for node in self._getSelectedObjectsWithoutSelectedAncestors(): node.boundingBoxChanged.disconnect(self.propertyChanged) if event.type == Event.KeyPressEvent and cast( KeyEvent, event).key == KeyEvent.ShiftKey: return False if event.type == Event.MousePressEvent and self._controller.getToolsEnabled( ): # Start a translate operation if MouseEvent.LeftButton not in cast(MouseEvent, event).buttons: return False if not self._selection_pass: return False id = self._selection_pass.getIdAtPosition( cast(MouseEvent, event).x, cast(MouseEvent, event).y) if not id: return False if id in self._enabled_axis: self.setLockedAxis(id) elif self._handle.isAxis(id): return False self._moved = False camera = self._controller.getScene().getActiveCamera() if not camera: return False camera_direction = camera.getPosition().normalized() abs_x = abs(camera_direction.x) abs_y = abs(camera_direction.y) # We have to define a plane vector that is suitable for the selected toolhandle axis # and at the same time the camera direction should not be exactly perpendicular to the plane vector if id == ToolHandle.XAxis: plane_vector = Vector(0, camera_direction.y, camera_direction.z).normalized() elif id == ToolHandle.YAxis: plane_vector = Vector(camera_direction.x, 0, camera_direction.z).normalized() elif id == ToolHandle.ZAxis: plane_vector = Vector(camera_direction.x, camera_direction.y, 0).normalized() else: if abs_y > DIRECTION_TOLERANCE: plane_vector = Vector(0, 1, 0) elif abs_x > DIRECTION_TOLERANCE: plane_vector = Vector(1, 0, 0) self.setLockedAxis( ToolHandle.ZAxis) # Do not move y / vertical else: plane_vector = Vector(0, 0, 1) self.setLockedAxis( ToolHandle.XAxis) # Do not move y / vertical self.setDragPlane(Plane(plane_vector, 0)) return True if event.type == Event.MouseMoveEvent: # Perform a translate operation if not self.getDragPlane(): return False x = cast(MouseEvent, event).x y = cast(MouseEvent, event).y if not self.getDragStart(): self.setDragStart(x, y) return False drag = self.getDragVector(x, y) if drag: if self._grid_snap and drag.length() < self._grid_size: return False if self.getLockedAxis() == ToolHandle.XAxis: drag = drag.set(y=0, z=0) elif self.getLockedAxis() == ToolHandle.YAxis: drag = drag.set(x=0, z=0) elif self.getLockedAxis() == ToolHandle.ZAxis: drag = drag.set(x=0, y=0) if not self._moved: self._moved = True self._distance = Vector(0, 0, 0) self.operationStarted.emit(self) selected_nodes = self._getSelectedObjectsWithoutSelectedAncestors( ) if len(selected_nodes) > 1: op = GroupedOperation() for node in selected_nodes: if node.getSetting(SceneNodeSettings.LockPosition, "False") == "False": op.addOperation(TranslateOperation(node, drag)) op.push() else: for node in selected_nodes: if node.getSetting(SceneNodeSettings.LockPosition, "False") == "False": TranslateOperation(node, drag).push() if not self._distance: self._distance = Vector(0, 0, 0) self._distance += drag self.setDragStart(x, y) # Rate-limit the angle change notification # This is done to prevent the UI from being flooded with property change notifications, # which in turn would trigger constant repaints. new_time = time.monotonic() if not self._distance_update_time or new_time - self._distance_update_time > 0.1: self.propertyChanged.emit() self._distance_update_time = new_time return True if event.type == Event.MouseReleaseEvent: # Finish a translate operation if self.getDragPlane(): self.operationStopped.emit(self) self._distance = None self.propertyChanged.emit() self.setLockedAxis(ToolHandle.NoAxis) self.setDragPlane(None) self.setDragStart( cast(MouseEvent, event).x, cast(MouseEvent, event).y) return True return False
def event(self, event): super().event(event) # Make sure the displayed values are updated if the bounding box of the selected mesh(es) changes if event.type == Event.ToolActivateEvent: 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 and event.key == KeyEvent.ShiftKey: return False if event.type == Event.MousePressEvent and self._controller.getToolsEnabled( ): # Start a translate 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 id in self._enabled_axis: self.setLockedAxis(id) elif self._handle.isAxis(id): return False self._moved = False if id == ToolHandle.XAxis: self.setDragPlane(Plane(Vector(0, 0, 1), 0)) elif id == ToolHandle.YAxis: self.setDragPlane(Plane(Vector(0, 0, 1), 0)) elif id == ToolHandle.ZAxis: self.setDragPlane(Plane(Vector(0, 1, 0), 0)) else: self.setDragPlane(Plane(Vector(0, 1, 0), 0)) if event.type == Event.MouseMoveEvent: # Perform a translate operation if not self.getDragPlane(): return False if not self.getDragStart(): self.setDragStart(event.x, event.y) return False drag = self.getDragVector(event.x, event.y) if drag: if self._grid_snap and drag.length() < self._grid_size: return False if self.getLockedAxis() == ToolHandle.XAxis: drag = drag.set(y=0, z=0) elif self.getLockedAxis() == ToolHandle.YAxis: drag = drag.set(x=0, z=0) elif self.getLockedAxis() == ToolHandle.ZAxis: drag = drag.set(x=0, y=0) if not self._moved: self._moved = True self._distance = Vector(0, 0, 0) self.operationStarted.emit(self) op = GroupedOperation() for node in Selection.getAllSelectedObjects(): op.addOperation(TranslateOperation(node, drag)) op.push() self._distance += drag self.setDragStart(event.x, event.y) # Rate-limit the angle change notification # This is done to prevent the UI from being flooded with property change notifications, # which in turn would trigger constant repaints. new_time = time.monotonic() if not self._distance_update_time or new_time - self._distance_update_time > 0.1: self.propertyChanged.emit() self._distance_update_time = new_time return True if event.type == Event.MouseReleaseEvent: # Finish a translate operation if self.getDragPlane(): self.operationStopped.emit(self) self._distance = None self.propertyChanged.emit() self.setLockedAxis(None) self.setDragPlane(None) self.setDragStart(None, None) return True return False
def event(self, event): """Handle mouse and keyboard events :param event: type(Event) """ super().event(event) if event.type == Event.ToolActivateEvent: for node in self._getSelectedObjectsWithoutSelectedAncestors(): node.boundingBoxChanged.connect(self.propertyChanged) if event.type == Event.ToolDeactivateEvent: for node in self._getSelectedObjectsWithoutSelectedAncestors(): node.boundingBoxChanged.disconnect(self.propertyChanged) # Handle modifier keys: Shift toggles snap, Control toggles uniform scaling if event.type == Event.KeyPressEvent: if event.key == KeyEvent.ShiftKey: self.setScaleSnap(not self._snap_scale) elif event.key == KeyEvent.ControlKey: self.setNonUniformScale(not self._non_uniform_scale) if event.type == Event.KeyReleaseEvent: if event.key == KeyEvent.ShiftKey: self.setScaleSnap(not self._snap_scale) elif event.key == KeyEvent.ControlKey: self.setNonUniformScale(not self._non_uniform_scale) if event.type == Event.MousePressEvent and self._controller.getToolsEnabled( ): # Initialise a scale 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._saved_handle_position = self._handle.getWorldPosition() # Save the current positions of the node, as we want to scale arround their current centres self._saved_node_positions = [] for node in self._getSelectedObjectsWithoutSelectedAncestors(): self._saved_node_positions.append((node, node.getPosition())) self._scale_sum = 0.0 self._last_event = event 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) return True if event.type == Event.MouseMoveEvent: # Perform a scale operation if not self.getDragPlane(): return False drag_position = self.getDragPosition(event.x, event.y) if drag_position: if self.getLockedAxis() == ToolHandle.XAxis: drag_position = drag_position.set(y=0, z=0) elif self.getLockedAxis() == ToolHandle.YAxis: drag_position = drag_position.set(x=0, z=0) elif self.getLockedAxis() == ToolHandle.ZAxis: drag_position = drag_position.set(x=0, y=0) 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 if self.getLockedAxis() in [ ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis ]: # drag the handle, axis is already determined if self._snap_scale: scale_factor = round(drag_change, 1) else: scale_factor = drag_change else: # uniform scaling; because we use central cube, we use the screen x, y for scaling. # upper right is scale up, lower left is scale down scale_factor_delta = ( (self._last_event.y - event.y) - (self._last_event.x - event.x)) * self._scale_speed self._scale_sum += scale_factor_delta if self._snap_scale: scale_factor = round(self._scale_sum, 1) # remember the decimals when snap scaling self._scale_sum -= scale_factor else: scale_factor = self._scale_sum self._scale_sum = 0.0 if scale_factor: scale_change = Vector(0.0, 0.0, 0.0) if self._non_uniform_scale: if self.getLockedAxis() == ToolHandle.XAxis: scale_change = scale_change.set(x=scale_factor) elif self.getLockedAxis() == ToolHandle.YAxis: scale_change = scale_change.set(y=scale_factor) elif self.getLockedAxis() == ToolHandle.ZAxis: scale_change = scale_change.set(z=scale_factor) else: # Middle handle scale_change = scale_change.set(x=scale_factor, y=scale_factor, z=scale_factor) else: scale_change = scale_change.set(x=scale_factor, y=scale_factor, z=scale_factor) # Scale around the saved centers of all selected nodes if len(self._saved_node_positions) > 1: op = GroupedOperation() for node, position in self._saved_node_positions: op.addOperation( ScaleOperation( node, scale_change, relative_scale=True, scale_around_point=position)) op.push() else: for node, position in self._saved_node_positions: ScaleOperation( node, scale_change, relative_scale=True, scale_around_point=position).push() self._drag_length = (self._saved_handle_position - drag_position).length() else: self.operationStarted.emit(self) self._drag_length = ( self._saved_handle_position - drag_position ).length() #First move, do nothing but set right length. self._last_event = event # remember for uniform drag return True if event.type == Event.MouseReleaseEvent: # Finish a scale operation if self.getDragPlane(): self.setDragPlane(None) self.setLockedAxis(ToolHandle.NoAxis) self._drag_length = 0 self.operationStopped.emit(self) return True
def event(self, event): super().event(event) if event.type == Event.KeyPressEvent and event.key == KeyEvent.ShiftKey: # Snap is toggled when pressing the shift button self.setRotationSnap(not self._snap_rotation) if event.type == Event.KeyReleaseEvent and event.key == KeyEvent.ShiftKey: # Snap is "toggled back" when releasing the shift button self.setRotationSnap(not self._snap_rotation) if event.type == Event.MousePressEvent and self._controller.getToolsEnabled( ): # Start a rotate 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) else: # Not clicked on an axis: do nothing. return False handle_position = self._handle.getWorldPosition() # Save the current positions of the node, as we want to rotate around their current centres self._saved_node_positions = [] for node in self._getSelectedObjectsWithoutSelectedAncestors(): self._saved_node_positions.append((node, node.getPosition())) if id == ToolHandle.XAxis: self.setDragPlane(Plane(Vector(1, 0, 0), handle_position.x)) elif id == ToolHandle.YAxis: self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y)) elif self._locked_axis == ToolHandle.ZAxis: self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z)) else: self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y)) self.setDragStart(event.x, event.y) self._rotating = False self._angle = 0 return True if event.type == Event.MouseMoveEvent: # Perform a rotate operation if not self.getDragPlane(): return False if not self.getDragStart(): self.setDragStart(event.x, event.y) if not self.getDragStart(): #May have set it to None. return False if not self._rotating: self._rotating = True self.operationStarted.emit(self) handle_position = self._handle.getWorldPosition() drag_start = (self.getDragStart() - handle_position).normalized() drag_position = self.getDragPosition(event.x, event.y) if not drag_position: return False drag_end = (drag_position - handle_position).normalized() try: angle = math.acos(drag_start.dot(drag_end)) except ValueError: angle = 0 if self._snap_rotation: angle = int(angle / self._snap_angle) * self._snap_angle if angle == 0: return False rotation = None if self.getLockedAxis() == ToolHandle.XAxis: direction = 1 if Vector.Unit_X.dot( drag_start.cross(drag_end)) > 0 else -1 rotation = Quaternion.fromAngleAxis(direction * angle, Vector.Unit_X) elif self.getLockedAxis() == ToolHandle.YAxis: direction = 1 if Vector.Unit_Y.dot( drag_start.cross(drag_end)) > 0 else -1 rotation = Quaternion.fromAngleAxis(direction * angle, Vector.Unit_Y) elif self.getLockedAxis() == ToolHandle.ZAxis: direction = 1 if Vector.Unit_Z.dot( drag_start.cross(drag_end)) > 0 else -1 rotation = Quaternion.fromAngleAxis(direction * angle, Vector.Unit_Z) else: direction = -1 # Rate-limit the angle change notification # This is done to prevent the UI from being flooded with property change notifications, # which in turn would trigger constant repaints. new_time = time.monotonic() if not self._angle_update_time or new_time - self._angle_update_time > 0.1: self._angle_update_time = new_time self._angle += direction * angle self.propertyChanged.emit() # Rotate around the saved centeres of all selected nodes if len(self._saved_node_positions) > 1: op = GroupedOperation() for node, position in self._saved_node_positions: op.addOperation( RotateOperation(node, rotation, rotate_around_point=position)) op.push() else: for node, position in self._saved_node_positions: RotateOperation(node, rotation, rotate_around_point=position).push() self.setDragStart(event.x, event.y) return True if event.type == Event.MouseReleaseEvent: # Finish a rotate operation if self.getDragPlane(): self.setDragPlane(None) self.setLockedAxis(ToolHandle.NoAxis) self._angle = None self.propertyChanged.emit() if self._rotating: self.operationStopped.emit(self) return True
from UM.Scene.ToolHandle import ToolHandle from UM.Tool import Tool def test_exposedProperties(): test_tool_1 = Tool() test_tool_1.setPluginId("test_tool_1") test_tool_1.setExposedProperties("bla", "omg", "zomg") assert test_tool_1.getExposedProperties() == ["bla", "omg", "zomg"] test_validate_data = [{ "attribute": "DragPlane", "value": Plane() }, { "attribute": "Handle", "value": None }] @pytest.mark.parametrize("data", test_validate_data) def test_getAndSet(data): test_tool = Tool() # Attempt to set the value getattr(test_tool, "set" + data["attribute"])(data["value"]) # Ensure that the value got set assert getattr(test_tool, "get" + data["attribute"])() == data["value"]
def event(self, event): super().event(event) if event.type == Event.KeyPressEvent and event.key == KeyEvent.ShiftKey: self._snap_rotation = (not self._snap_rotation) self.propertyChanged.emit() if event.type == Event.KeyReleaseEvent and event.key == KeyEvent.ShiftKey: self._snap_rotation = (not self._snap_rotation) self.propertyChanged.emit() 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 if ToolHandle.isAxis(id): self.setLockedAxis(id) handle_position = self._handle.getWorldPosition() if id == ToolHandle.XAxis: self.setDragPlane(Plane(Vector(1, 0, 0), handle_position.x)) elif id == ToolHandle.YAxis: self.setDragPlane(Plane(Vector(0, 1, 0), handle_position.y)) elif self._locked_axis == ToolHandle.ZAxis: self.setDragPlane(Plane(Vector(0, 0, 1), handle_position.z)) self.setDragStart(event.x, event.y) self._angle = 0 self.operationStarted.emit(self) if event.type == Event.MouseMoveEvent: if not self.getDragPlane(): return False if not self.getDragStart(): self.setDragStart(event.x, event.y) handle_position = self._handle.getWorldPosition() drag_start = (self.getDragStart() - handle_position).normalize() drag_position = self.getDragPosition(event.x, event.y) if not drag_position: return drag_end = (drag_position - handle_position).normalize() try: angle = math.acos(drag_start.dot(drag_end)) except ValueError: angle = 0 if self._snap_rotation: angle = int(angle / self._snap_angle) * self._snap_angle if angle == 0: return rotation = None if self.getLockedAxis() == ToolHandle.XAxis: direction = 1 if Vector.Unit_X.dot( drag_start.cross(drag_end)) > 0 else -1 rotation = Quaternion.fromAngleAxis(direction * angle, Vector.Unit_X) elif self.getLockedAxis() == ToolHandle.YAxis: direction = 1 if Vector.Unit_Y.dot( drag_start.cross(drag_end)) > 0 else -1 rotation = Quaternion.fromAngleAxis(direction * angle, Vector.Unit_Y) elif self.getLockedAxis() == ToolHandle.ZAxis: direction = 1 if Vector.Unit_Z.dot( drag_start.cross(drag_end)) > 0 else -1 rotation = Quaternion.fromAngleAxis(direction * angle, Vector.Unit_Z) self._angle += direction * angle # Rate-limit the angle change notification # This is done to prevent the UI from being flooded with property change notifications, # which in turn would trigger constant repaints. new_time = time.monotonic() if not self._angle_update_time or new_time - self._angle_update_time > 0.01: self.propertyChanged.emit() self._angle_update_time = new_time Selection.applyOperation(RotateOperation, rotation) self.setDragStart(event.x, event.y) if event.type == Event.MouseReleaseEvent: if self.getDragPlane(): self.setDragPlane(None) self.setLockedAxis(None) self._angle = None self.propertyChanged.emit() self.operationStopped.emit(self) return True
def event(self, event): super().event(event) if event.type == Event.KeyPressEvent and event.key == KeyEvent.ShiftKey: self._grid_snap = True if event.type == Event.KeyReleaseEvent and event.key == KeyEvent.ShiftKey: self._grid_snap = False 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 id in self._enabled_axis: self.setLockedAxis(id) elif self._handle.isAxis(id): return False self._moved = False if id == ToolHandle.XAxis: self.setDragPlane(Plane(Vector(0, 0, 1), 0)) elif id == ToolHandle.YAxis: self.setDragPlane(Plane(Vector(0, 0, 1), 0)) elif id == ToolHandle.ZAxis: self.setDragPlane(Plane(Vector(0, 1, 0), 0)) else: self.setDragPlane(Plane(Vector(0, 1, 0), 0)) if event.type == Event.MouseMoveEvent: if not self.getDragPlane(): return False if not self.getDragStart(): self.setDragStart(event.x, event.y) return False drag = self.getDragVector(event.x, event.y) if drag: if self._grid_snap and drag.length() < self._grid_size: return False if self.getLockedAxis() == ToolHandle.XAxis: drag.setY(0) drag.setZ(0) elif self.getLockedAxis() == ToolHandle.YAxis: drag.setX(0) drag.setZ(0) elif self.getLockedAxis() == ToolHandle.ZAxis: drag.setX(0) drag.setY(0) if not self._moved: self._moved = True self.operationStarted.emit(self) Selection.applyOperation(TranslateOperation, drag) self.setDragStart(event.x, event.y) return True if event.type == Event.MouseReleaseEvent: if self.getDragPlane(): self.operationStopped.emit(self) self.setLockedAxis(None) self.setDragPlane(None) self.setDragStart(None, None) return True return False
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) # Handle modifier keys: Shift toggles snap, Control toggles uniform scaling 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(): # Initialise a scale 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 ToolHandle.isAxis(id): self.setLockedAxis(id) # Save the current positions of the node, as we want to scale arround their current centres self._saved_node_positions = [] for node in Selection.getAllSelectedObjects(): self._saved_node_positions.append((node, node.getWorldPosition())) 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: # Perform a scale operation if not self.getDragPlane(): return False 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 if self._snap_scale: scale_factor = round(drag_change, 1) else: scale_factor = drag_change if scale_factor: 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) # Scale around the saved centeres of all selected nodes op = GroupedOperation() for node, position in self._saved_node_positions: op.addOperation(ScaleOperation(node, scale_change, relative_scale = True, scale_around_point = position)) op.push() self._drag_length = (self._saved_handle_position - drag_position).length() else: self._drag_length = (self._saved_handle_position - drag_position).length() #First move, do nothing but set right length. return True if event.type == Event.MouseReleaseEvent: # Finish a scale operation if self.getDragPlane(): self.setDragPlane(None) self.setLockedAxis(None) self._drag_length = 0 self.operationStopped.emit(self) return True
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._selection_pass.getIdAtPosition(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 event(self, event: Event) -> bool: if not self._selection_pass: self._selection_pass = cast( SelectionPass, Application.getInstance().getRenderer().getRenderPass( "selection")) if not self._selection_pass: return False # Tool activated - make sure we render the faces if event.type == Event.ToolActivateEvent: self._changeRenderMode(faces=True) self._enabled = True if self._bc_list and self._bc_list.getActiveNode(): self._controller.getScene().sceneChanged.emit( self._bc_list.getActiveNode()) return False # Tool deactivated - make sure we render the faces if event.type == Event.ToolDeactivateEvent: self._changeRenderMode(faces=False) self._enabled = False if self._bc_list and self._bc_list.getActiveNode(): self._controller.getScene().sceneChanged.emit( self._bc_list.getActiveNode()) self._bc_list = None return False if not self.getEnabled(): return False # Not a load face - make sure we render faces if not self._bc_list or not self._bc_list.getActiveNode( ) or isinstance(self._bc_list.getActiveNode(), SmartSliceScene.AnchorFace): self._changeRenderMode(faces=True) return False active_node = self._bc_list.getActiveNode() # Load face rotator = active_node.getRotator() # Rotator on the load face arrow = active_node.activeArrow # Active arrow on the load face if event.type == Event.MousePressEvent: # Must be a left mouse event to select or rotate if MouseEvent.LeftButton not in event.buttons: return False pixel_color = self._selection_pass.getIdAtPosition( event.x, event.y) # We did not click the tool - we need to select the surface under it if it exists # TODO - This is a little hacky.... we should implement a SelectionPass just for this Tool if not pixel_color or not arrow.isAxis(pixel_color): if Selection.hasSelection( ) and not Selection.getFaceSelectMode(): self._changeRenderMode(faces=True) select_tool = PluginRegistry.getInstance().getPluginObject( "SelectionTool") return select_tool.event(event) # Rotator isn't enabled - we don't need to do anything if not rotator.isEnabled(): return False # If we made it here, we have clicked the tool. Set the locked color to our tool color, and set the plane # the user will be constrained to drag in self.setLockedAxis(pixel_color) self.setDragPlane(Plane(rotator.rotation_axis)) self.setDragStart(event.x, event.y) self._rotating = True self._angle = 0 return True if event.type == Event.MouseMoveEvent: # Rotator isn't enabled - we don't need to do anything if not rotator.isEnabled(): return False event = cast(MouseEvent, event) # Turn the shader on for the rotator and arrow if the mouse is hovered on them # in the above, pixel_color is the color of the solid mesh of the pixekl the mouse is on # For some reason, "ActiveAxis" means the color of the tool we are interested in if not self._rotating: self._changeRenderMode(faces=False) pixel_color = self._selection_pass.getIdAtPosition( event.x, event.y) if rotator.isAxis(pixel_color): rotator.setActiveAxis(pixel_color) arrow.setActiveAxis(pixel_color) else: rotator.setActiveAxis(None) arrow.setActiveAxis(None) return False # We are rotating. Check to ensure we have a starting position for the mouse if not self.getDragStart(): self.setDragStart(event.x, event.y) if not self.getDragStart(): #May have set it to None. return False self.operationStarted.emit(self) drag_start = self.getDragStart() - rotator.center drag_position = self.getDragPosition(event.x, event.y) if not drag_position: return False drag_end = drag_position - rotator.center # Project the vectors back to the plane of the rotator drag_start = drag_start - drag_start.dot( rotator.rotation_axis) * rotator.rotation_axis drag_end = drag_end - drag_end.dot( rotator.rotation_axis) * rotator.rotation_axis angle = angleBetweenVectors(drag_start, drag_end) axes_length = (rotator.rotation_axis.normalized() - drag_end.cross(drag_start).normalized()).length() angle = -angle if axes_length < 1.e-2 else angle rotation = Quaternion.fromAngleAxis(angle, rotator.rotation_axis) self._angle += angle active_node.rotateArrow(angle) self.setDragStart(event.x, event.y) return True # Finished the rotation - reset everything and update the arrow direction if event.type == Event.MouseReleaseEvent: if self._rotating: self.setDragPlane(None) self.setLockedAxis(ToolHandle.NoAxis) self._angle = None self._rotating = False self.propertyChanged.emit() active_node.facePropertyChanged.emit(active_node) # self._changeRenderMode(faces=True) self.operationStopped.emit(self) return True return False
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