def test_invert(self): q1 = Quaternion() q1.setByAngleAxis(math.pi, Vector.Unit_Z) q1.invert() q2 = Quaternion() q2.setByAngleAxis(math.pi, -Vector.Unit_Z) self.assertEqual(q1, q2)
def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, name: str = "") -> None: super().__init__() # Call super to make multiple inheritance work. self._children = [] # type: List[SceneNode] self._mesh_data = None # type: Optional[MeshData] # Local transformation (from parent to local) self._transformation = Matrix() # type: Matrix # Convenience "components" of the transformation self._position = Vector() # type: Vector self._scale = Vector(1.0, 1.0, 1.0) # type: Vector self._shear = Vector(0.0, 0.0, 0.0) # type: Vector self._mirror = Vector(1.0, 1.0, 1.0) # type: Vector self._orientation = Quaternion() # type: Quaternion # World transformation (from root to local) self._world_transformation = Matrix() # type: Matrix # Convenience "components" of the world_transformation self._derived_position = Vector() # type: Vector self._derived_orientation = Quaternion() # type: Quaternion self._derived_scale = Vector() # type: Vector self._parent = parent # type: Optional[SceneNode] # Can this SceneNode be modified in any way? self._enabled = True # type: bool # Can this SceneNode be selected in any way? self._selectable = False # type: bool # Should the AxisAlignedBoundingBox be re-calculated? self._calculate_aabb = False # type: bool # The AxisAligned bounding box. self._aabb = None # type: Optional[AxisAlignedBox] self._bounding_box_mesh = None # type: Optional[MeshData] self._visible = visible # type: bool self._name = name # type: str self._decorators = [] # type: List[SceneNodeDecorator] # Store custom settings to be compatible with Savitar SceneNode self._settings = {} #type: Dict[str, Any] ## Signals self.boundingBoxChanged.connect(self.calculateBoundingBoxMesh) self.parentChanged.connect(self._onParentChanged) if parent: parent.addChild(self)
def test_multiply(self): q1 = Quaternion() q1.setByAngleAxis(math.pi / 2, Vector.Unit_Z) q2 = Quaternion() q2.setByAngleAxis(math.pi / 2, Vector.Unit_Z) q3 = q1 * q2 q4 = Quaternion() q4.setByAngleAxis(math.pi, Vector.Unit_Z) self.assertEqual(q3, q4)
def test_toMatrix(self): q1 = Quaternion() q1.setByAngleAxis(math.pi / 2, Vector.Unit_Z) m1 = q1.toMatrix() m2 = Matrix() m2.setByRotationAxis(math.pi / 2, Vector.Unit_Z) self.assertTrue(Float.fuzzyCompare(m1.at(0, 0), m2.at(0, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 1), m2.at(0, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 2), m2.at(0, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(0, 3), m2.at(0, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 0), m2.at(1, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 1), m2.at(1, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 2), m2.at(1, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(1, 3), m2.at(1, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 0), m2.at(2, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 1), m2.at(2, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 2), m2.at(2, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(2, 3), m2.at(2, 3), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 0), m2.at(3, 0), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 1), m2.at(3, 1), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 2), m2.at(3, 2), 1e-6)) self.assertTrue(Float.fuzzyCompare(m1.at(3, 3), m2.at(3, 3), 1e-6))
def resetRotation(self): for node in Selection.getAllSelectedObjects(): node.setMirror(Vector(1, 1, 1)) Selection.applyOperation(SetTransformOperation, None, Quaternion(), None)
def resetAll(self): Logger.log("i", "Resetting all scene transformations") nodes = [] 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) nodes.append(node) if nodes: op = GroupedOperation() for node in nodes: # Ensure that the object is above the build platform node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) center_y = 0 if node.callDecoration("isGroup"): center_y = node.getWorldPosition().y - node.getBoundingBox( ).bottom else: center_y = node.getMeshData().getCenterPosition().y op.addOperation( SetTransformOperation(node, Vector(0, center_y, 0), Quaternion(), Vector(1, 1, 1))) op.push()
def _updateTransformation(self): scale, shear, euler_angles, translation, mirror = self._transformation.decompose( ) self._position = translation self._scale = scale self._shear = shear self._mirror = mirror orientation = Quaternion() euler_angle_matrix = Matrix() euler_angle_matrix.setByEuler(euler_angles.x, euler_angles.y, euler_angles.z) orientation.setByMatrix(euler_angle_matrix) self._orientation = orientation if self._parent: self._world_transformation = self._parent.getWorldTransformation( ).multiply(self._transformation, copy=True) else: self._world_transformation = self._transformation world_scale, world_shear, world_euler_angles, world_translation, world_mirror = self._world_transformation.decompose( ) self._derived_position = world_translation self._derived_scale = world_scale world_euler_angle_matrix = Matrix() world_euler_angle_matrix.setByEuler(world_euler_angles.x, world_euler_angles.y, world_euler_angles.z) self._derived_orientation.setByMatrix(world_euler_angle_matrix)
def test_create(self): q = Quaternion() self.assertEqual(q.x, 0.0) self.assertEqual(q.y, 0.0) self.assertEqual(q.z, 0.0) self.assertEqual(q.w, 1.0)
def resetRotation(self): """Reset the orientation of the mesh(es) to their original orientation(s)""" for node in self._getSelectedObjectsWithoutSelectedAncestors(): node.setMirror(Vector(1, 1, 1)) Selection.applyOperation(SetTransformOperation, None, Quaternion(), None)
def _updateTransformation(self): self._transformation = Matrix.fromPositionOrientationScale( self._position, self._orientation, self._scale) if self._parent: parent_orientation = self._parent._getDerivedOrientation() if self._inherit_orientation: self._derived_orientation = parent_orientation * self._orientation else: self._derived_orientation = self._orientation # Sometimes the derived orientation can be None. # I've not been able to locate the cause of this, but this prevents it being an issue. if not self._derived_orientation: self._derived_orientation = Quaternion() parent_scale = self._parent._getDerivedScale() if self._inherit_scale: self._derived_scale = parent_scale.scale(self._scale) else: self._derived_scale = self._scale self._derived_position = parent_orientation.rotate( parent_scale.scale(self._position)) self._derived_position += self._parent._getDerivedPosition() self._world_transformation = Matrix.fromPositionOrientationScale( self._derived_position, self._derived_orientation, self._derived_scale) else: self._derived_position = self._position self._derived_orientation = self._orientation self._derived_scale = self._scale self._world_transformation = self._transformation
def __init__(self, parent=None): super().__init__() # Call super to make multiple inheritence work. self._children = [] self._mesh_data = None self._position = Vector() self._scale = Vector(1.0, 1.0, 1.0) self._orientation = Quaternion() self._transformation = None self._world_transformation = None self._derived_position = None self._derived_orientation = None self._derived_scale = None self._inherit_orientation = True self._inherit_scale = True self._parent = parent self._enabled = True self._selectable = False self._calculate_aabb = True self._aabb = None self._aabb_job = None self._visible = True self._name = "" self._decorators = [] self._bounding_box_mesh = None self.boundingBoxChanged.connect(self.calculateBoundingBoxMesh) self.parentChanged.connect(self._onParentChanged) if parent: parent.addChild(self)
def resetAll(self): nodes = [] 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) nodes.append(node) if nodes: op = GroupedOperation() for node in nodes: # Ensure that the object is above the build platform move_distance = node.getBoundingBox().center.y if move_distance <= 0: move_distance = -node.getBoundingBox().bottom op.addOperation( SetTransformOperation(node, Vector(0, move_distance, 0), Quaternion(), Vector(1, 1, 1))) op.push()
def setZ(self, Z): if float(Z) != self._Z_angle: self._angle = ((float(Z) % 360) - (self._Z_angle % 360)) % 360 self._Z_angle = float(Z) #rotation = Quaternion.fromAngleAxis(math.radians( self._angle ), Vector.Unit_Z) rotation = Quaternion() rotation.setByAngleAxis(math.radians(self._angle), Vector.Unit_Z) # Save the current positions of the node, as we want to rotate around their current centres self._saved_node_positions = [] for node in Selection.getAllSelectedObjects(): self._saved_node_positions.append((node, node.getPosition())) node._rotationZ = self._Z_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.1: self._angle_update_time = new_time # Rotate around the saved centeres of all selected nodes op = GroupedOperation() for node, position in self._saved_node_positions: op.addOperation( RotateOperation(node, rotation, rotate_around_point=position)) op.push() self._angle = 0 self.propertyChanged.emit()
def resetRotation(self): for node in self._getSelectedObjectsWithoutSelectedAncestors(): node.setMirror(Vector(1, 1, 1)) Selection.applyOperation(SetTransformOperation, None, Quaternion(), None)
def test_getData(self): q = Quaternion(1, 2, 3, 4) data = q.getData() assert data[0] == 1 assert data[1] == 2 assert data[2] == 3 assert data[3] == 4
def _getDerivedOrientation(self): if not self._derived_orientation: self._updateTransformation() # Sometimes the derived orientation can be None. # I've not been able to locate the cause of this, but this prevents it being an issue. if not self._derived_orientation: self._derived_orientation = Quaternion() return self._derived_orientation
def test_setByAxis(self): q = Quaternion() q.setByAngleAxis(math.pi / 2, Vector.Unit_Z) self.assertEqual(q.x, 0.0) self.assertEqual(q.y, 0.0) self.assertTrue(Float.fuzzyCompare(q.z, math.sqrt(2.0) / 2.0, 1e-6)) self.assertTrue(Float.fuzzyCompare(q.w, math.sqrt(2.0) / 2.0, 1e-6))
def _updateLocalTransformation(self): translation, euler_angle_matrix, scale, shear = self._transformation.decompose() self._position = translation self._scale = scale self._shear = shear orientation = Quaternion() orientation.setByMatrix(euler_angle_matrix) self._orientation = orientation
def __init__(self, parent = None, **kwargs): super().__init__() # Call super to make multiple inheritance work. self._children = [] self._mesh_data = None # Local transformation (from parent to local) self._transformation = Matrix() # Convenience "components" of the transformation self._position = Vector() self._scale = Vector(1.0, 1.0, 1.0) self._shear = Vector(0.0, 0.0, 0.0) self._mirror = Vector(1.0, 1.0, 1.0) self._orientation = Quaternion() # World transformation (from root to local) self._world_transformation = Matrix() # Convenience "components" of the world_transformation self._derived_position = Vector() self._derived_orientation = Quaternion() self._derived_scale = Vector() self._parent = parent self._enabled = True # Can this SceneNode be modified in any way? self._selectable = False # Can this SceneNode be selected in any way? self._calculate_aabb = True # Should the AxisAlignedBounxingBox be re-calculated? self._aabb = None # The AxisAligned bounding box. self._original_aabb = None # The AxisAligned bounding box, without transformations. self._bounding_box_mesh = None self._visible = kwargs.get("visible", True) self._name = kwargs.get("name", "") self._decorators = [] ## Signals self.boundingBoxChanged.connect(self.calculateBoundingBoxMesh) self.parentChanged.connect(self._onParentChanged) if parent: parent.addChild(self)
def test_rotateVector(self): q1 = Quaternion() q1.setByAngleAxis(math.pi / 2, Vector.Unit_Z) v = Vector(0, 1, 0) v = q1.rotate(v) self.assertTrue(Float.fuzzyCompare(v.x, -1.0, 1e-6)) self.assertTrue(Float.fuzzyCompare(v.y, 0.0, 1e-6)) self.assertTrue(Float.fuzzyCompare(v.z, 0.0, 1e-6))
def updateFromODE(self): self._update_from_ode = True self.getNode().setPosition(Helpers.fromODE(self._body.getPosition()), SceneNode.TransformSpace.World) body_orientation = self._body.getQuaternion() self.getNode().setOrientation( Quaternion(body_orientation[3], body_orientation[0], body_orientation[1], body_orientation[2])) self._update_from_ode = False
def test_rotate(self): node = SceneNode() self.assertEqual(node.getOrientation(), Quaternion()) node.rotate(Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) self.assertEqual(node.getOrientation(), Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) node.rotate(Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) self.assertEqual(node.getOrientation(), Quaternion.fromAngleAxis(math.pi / 2, Vector.Unit_Z))
def __init__(self, parent=None, **kwargs): super().__init__() # Call super to make multiple inheritance work. self._children = [] self._mesh_data = None # Local transformation (from parent to local) self._transformation = Matrix() # Convenience "components" of the transformation self._position = Vector() self._scale = Vector(1.0, 1.0, 1.0) self._shear = Vector(0.0, 0.0, 0.0) self._orientation = Quaternion() # World transformation (from root to local) self._world_transformation = Matrix() # Convenience "components" of the world_transformation self._derived_position = Vector() self._derived_orientation = Quaternion() self._derived_scale = Vector() self._parent = parent self._enabled = True self._selectable = False self._calculate_aabb = True self._aabb = None self._original_aabb = None self._aabb_job = None self._visible = kwargs.get("visible", True) self._name = kwargs.get("name", "") self._decorators = [] ## Signals self.boundingBoxChanged.connect(self.calculateBoundingBoxMesh) self.parentChanged.connect(self._onParentChanged) if parent: parent.addChild(self)
def test_fromMatrix(self): m = Matrix() m.setByRotationAxis(math.pi / 2, Vector.Unit_Z) q1 = Quaternion.fromMatrix(m) q2 = Quaternion() q2.setByAngleAxis(math.pi / 2, Vector.Unit_Z) self.assertTrue(Float.fuzzyCompare(q1.x, q2.x, 1e-6)) self.assertTrue(Float.fuzzyCompare(q1.y, q2.y, 1e-6)) self.assertTrue(Float.fuzzyCompare(q1.z, q2.z, 1e-6)) self.assertTrue(Float.fuzzyCompare(q1.w, q2.w, 1e-6))
def resetRotation(self): for node in Selection.getAllSelectedObjects(): node.setMirror(Vector(1, 1, 1)) node._rotationX = 0.0 node._rotationY = 0.0 node._rotationZ = 0.0 Selection.applyOperation(SetTransformOperation, None, Quaternion(), None) self._X_angle = 0 self._Z_angle = 0 self._Y_angle = 0 self.propertyChanged.emit()
def resetAll(self): nodes = [] for node in DepthFirstIterator(self.getController().getScene().getRoot()): if type(node) is not SceneNode or not node.getMeshData(): continue nodes.append(node) if nodes: op = GroupedOperation() for node in nodes: op.addOperation(SetTransformOperation(node, Vector(), Quaternion(), Vector(1, 1, 1))) op.push()
def test_rotate(self): node = SceneNode() self.assertEqual(node.getOrientation(), Quaternion()) node.rotate(Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) node_orientation = deepcopy(node.getOrientation()) node_orientation.normalize() #For fair comparison. self.assertEqual(node_orientation, Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) node.rotate(Quaternion.fromAngleAxis(math.pi / 4, Vector.Unit_Z)) node_orientation = deepcopy(node.getOrientation()) node_orientation.normalize() self.assertEqual(node_orientation, Quaternion.fromAngleAxis(math.pi / 2, Vector.Unit_Z))
def resetAll(self): nodes = [] 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) nodes.append(node) if nodes: op = GroupedOperation() for node in nodes: op.addOperation( SetTransformOperation(node, Vector(), Quaternion(), Vector(1, 1, 1))) op.push()
def test_slerp(self): q1 = Quaternion() q1.setByAngleAxis(0, Vector.Unit_Z) q2 = Quaternion() q2.setByAngleAxis(math.pi / 2, Vector.Unit_Z) c = Quaternion(0.0, 0.0, 0.0, 1.0) self.assertEqual(c, Quaternion.slerp(q1, q2, 0.0)) c = Quaternion(0.0, 0.0, 0.19509033858776093, 0.9807853102684021) self.assertEqual(c, Quaternion.slerp(q1, q2, 0.25)) c = Quaternion(0.0, 0.0, 0.38268348574638367, 0.9238795638084412) self.assertEqual(c, Quaternion.slerp(q1, q2, 0.5)) c = Quaternion(0.0, 0.0, 0.5555703043937683, 0.8314696550369263) self.assertEqual(c, Quaternion.slerp(q1, q2, 0.75)) c = Quaternion(0.0, 0.0, 0.7071068286895752, 0.7071068286895752) self.assertEqual(c, Quaternion.slerp(q1, q2, 1.0))
def resetRotation(self): Selection.applyOperation(SetTransformOperation, None, Quaternion(), None)