123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- from sympy.core.basic import Basic
- from sympy.core.sympify import sympify
- from sympy.functions.elementary.trigonometric import (cos, sin)
- from sympy.matrices.dense import (eye, rot_axis1, rot_axis2, rot_axis3)
- from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
- from sympy.core.cache import cacheit
- from sympy.core.symbol import Str
- import sympy.vector
- class Orienter(Basic):
- """
- Super-class for all orienter classes.
- """
- def rotation_matrix(self):
- """
- The rotation matrix corresponding to this orienter
- instance.
- """
- return self._parent_orient
- class AxisOrienter(Orienter):
- """
- Class to denote an axis orienter.
- """
- def __new__(cls, angle, axis):
- if not isinstance(axis, sympy.vector.Vector):
- raise TypeError("axis should be a Vector")
- angle = sympify(angle)
- obj = super().__new__(cls, angle, axis)
- obj._angle = angle
- obj._axis = axis
- return obj
- def __init__(self, angle, axis):
- """
- Axis rotation is a rotation about an arbitrary axis by
- some angle. The angle is supplied as a SymPy expr scalar, and
- the axis is supplied as a Vector.
- Parameters
- ==========
- angle : Expr
- The angle by which the new system is to be rotated
- axis : Vector
- The axis around which the rotation has to be performed
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> from sympy import symbols
- >>> q1 = symbols('q1')
- >>> N = CoordSys3D('N')
- >>> from sympy.vector import AxisOrienter
- >>> orienter = AxisOrienter(q1, N.i + 2 * N.j)
- >>> B = N.orient_new('B', (orienter, ))
- """
- # Dummy initializer for docstrings
- pass
- @cacheit
- def rotation_matrix(self, system):
- """
- The rotation matrix corresponding to this orienter
- instance.
- Parameters
- ==========
- system : CoordSys3D
- The coordinate system wrt which the rotation matrix
- is to be computed
- """
- axis = sympy.vector.express(self.axis, system).normalize()
- axis = axis.to_matrix(system)
- theta = self.angle
- parent_orient = ((eye(3) - axis * axis.T) * cos(theta) +
- Matrix([[0, -axis[2], axis[1]],
- [axis[2], 0, -axis[0]],
- [-axis[1], axis[0], 0]]) * sin(theta) +
- axis * axis.T)
- parent_orient = parent_orient.T
- return parent_orient
- @property
- def angle(self):
- return self._angle
- @property
- def axis(self):
- return self._axis
- class ThreeAngleOrienter(Orienter):
- """
- Super-class for Body and Space orienters.
- """
- def __new__(cls, angle1, angle2, angle3, rot_order):
- if isinstance(rot_order, Str):
- rot_order = rot_order.name
- approved_orders = ('123', '231', '312', '132', '213',
- '321', '121', '131', '212', '232',
- '313', '323', '')
- original_rot_order = rot_order
- rot_order = str(rot_order).upper()
- if not (len(rot_order) == 3):
- raise TypeError('rot_order should be a str of length 3')
- rot_order = [i.replace('X', '1') for i in rot_order]
- rot_order = [i.replace('Y', '2') for i in rot_order]
- rot_order = [i.replace('Z', '3') for i in rot_order]
- rot_order = ''.join(rot_order)
- if rot_order not in approved_orders:
- raise TypeError('Invalid rot_type parameter')
- a1 = int(rot_order[0])
- a2 = int(rot_order[1])
- a3 = int(rot_order[2])
- angle1 = sympify(angle1)
- angle2 = sympify(angle2)
- angle3 = sympify(angle3)
- if cls._in_order:
- parent_orient = (_rot(a1, angle1) *
- _rot(a2, angle2) *
- _rot(a3, angle3))
- else:
- parent_orient = (_rot(a3, angle3) *
- _rot(a2, angle2) *
- _rot(a1, angle1))
- parent_orient = parent_orient.T
- obj = super().__new__(
- cls, angle1, angle2, angle3, Str(rot_order))
- obj._angle1 = angle1
- obj._angle2 = angle2
- obj._angle3 = angle3
- obj._rot_order = original_rot_order
- obj._parent_orient = parent_orient
- return obj
- @property
- def angle1(self):
- return self._angle1
- @property
- def angle2(self):
- return self._angle2
- @property
- def angle3(self):
- return self._angle3
- @property
- def rot_order(self):
- return self._rot_order
- class BodyOrienter(ThreeAngleOrienter):
- """
- Class to denote a body-orienter.
- """
- _in_order = True
- def __new__(cls, angle1, angle2, angle3, rot_order):
- obj = ThreeAngleOrienter.__new__(cls, angle1, angle2, angle3,
- rot_order)
- return obj
- def __init__(self, angle1, angle2, angle3, rot_order):
- """
- Body orientation takes this coordinate system through three
- successive simple rotations.
- Body fixed rotations include both Euler Angles and
- Tait-Bryan Angles, see https://en.wikipedia.org/wiki/Euler_angles.
- Parameters
- ==========
- angle1, angle2, angle3 : Expr
- Three successive angles to rotate the coordinate system by
- rotation_order : string
- String defining the order of axes for rotation
- Examples
- ========
- >>> from sympy.vector import CoordSys3D, BodyOrienter
- >>> from sympy import symbols
- >>> q1, q2, q3 = symbols('q1 q2 q3')
- >>> N = CoordSys3D('N')
- A 'Body' fixed rotation is described by three angles and
- three body-fixed rotation axes. To orient a coordinate system D
- with respect to N, each sequential rotation is always about
- the orthogonal unit vectors fixed to D. For example, a '123'
- rotation will specify rotations about N.i, then D.j, then
- D.k. (Initially, D.i is same as N.i)
- Therefore,
- >>> body_orienter = BodyOrienter(q1, q2, q3, '123')
- >>> D = N.orient_new('D', (body_orienter, ))
- is same as
- >>> from sympy.vector import AxisOrienter
- >>> axis_orienter1 = AxisOrienter(q1, N.i)
- >>> D = N.orient_new('D', (axis_orienter1, ))
- >>> axis_orienter2 = AxisOrienter(q2, D.j)
- >>> D = D.orient_new('D', (axis_orienter2, ))
- >>> axis_orienter3 = AxisOrienter(q3, D.k)
- >>> D = D.orient_new('D', (axis_orienter3, ))
- Acceptable rotation orders are of length 3, expressed in XYZ or
- 123, and cannot have a rotation about about an axis twice in a row.
- >>> body_orienter1 = BodyOrienter(q1, q2, q3, '123')
- >>> body_orienter2 = BodyOrienter(q1, q2, 0, 'ZXZ')
- >>> body_orienter3 = BodyOrienter(0, 0, 0, 'XYX')
- """
- # Dummy initializer for docstrings
- pass
- class SpaceOrienter(ThreeAngleOrienter):
- """
- Class to denote a space-orienter.
- """
- _in_order = False
- def __new__(cls, angle1, angle2, angle3, rot_order):
- obj = ThreeAngleOrienter.__new__(cls, angle1, angle2, angle3,
- rot_order)
- return obj
- def __init__(self, angle1, angle2, angle3, rot_order):
- """
- Space rotation is similar to Body rotation, but the rotations
- are applied in the opposite order.
- Parameters
- ==========
- angle1, angle2, angle3 : Expr
- Three successive angles to rotate the coordinate system by
- rotation_order : string
- String defining the order of axes for rotation
- See Also
- ========
- BodyOrienter : Orienter to orient systems wrt Euler angles.
- Examples
- ========
- >>> from sympy.vector import CoordSys3D, SpaceOrienter
- >>> from sympy import symbols
- >>> q1, q2, q3 = symbols('q1 q2 q3')
- >>> N = CoordSys3D('N')
- To orient a coordinate system D with respect to N, each
- sequential rotation is always about N's orthogonal unit vectors.
- For example, a '123' rotation will specify rotations about
- N.i, then N.j, then N.k.
- Therefore,
- >>> space_orienter = SpaceOrienter(q1, q2, q3, '312')
- >>> D = N.orient_new('D', (space_orienter, ))
- is same as
- >>> from sympy.vector import AxisOrienter
- >>> axis_orienter1 = AxisOrienter(q1, N.i)
- >>> B = N.orient_new('B', (axis_orienter1, ))
- >>> axis_orienter2 = AxisOrienter(q2, N.j)
- >>> C = B.orient_new('C', (axis_orienter2, ))
- >>> axis_orienter3 = AxisOrienter(q3, N.k)
- >>> D = C.orient_new('C', (axis_orienter3, ))
- """
- # Dummy initializer for docstrings
- pass
- class QuaternionOrienter(Orienter):
- """
- Class to denote a quaternion-orienter.
- """
- def __new__(cls, q0, q1, q2, q3):
- q0 = sympify(q0)
- q1 = sympify(q1)
- q2 = sympify(q2)
- q3 = sympify(q3)
- parent_orient = (Matrix([[q0 ** 2 + q1 ** 2 - q2 ** 2 -
- q3 ** 2,
- 2 * (q1 * q2 - q0 * q3),
- 2 * (q0 * q2 + q1 * q3)],
- [2 * (q1 * q2 + q0 * q3),
- q0 ** 2 - q1 ** 2 +
- q2 ** 2 - q3 ** 2,
- 2 * (q2 * q3 - q0 * q1)],
- [2 * (q1 * q3 - q0 * q2),
- 2 * (q0 * q1 + q2 * q3),
- q0 ** 2 - q1 ** 2 -
- q2 ** 2 + q3 ** 2]]))
- parent_orient = parent_orient.T
- obj = super().__new__(cls, q0, q1, q2, q3)
- obj._q0 = q0
- obj._q1 = q1
- obj._q2 = q2
- obj._q3 = q3
- obj._parent_orient = parent_orient
- return obj
- def __init__(self, angle1, angle2, angle3, rot_order):
- """
- Quaternion orientation orients the new CoordSys3D with
- Quaternions, defined as a finite rotation about lambda, a unit
- vector, by some amount theta.
- This orientation is described by four parameters:
- q0 = cos(theta/2)
- q1 = lambda_x sin(theta/2)
- q2 = lambda_y sin(theta/2)
- q3 = lambda_z sin(theta/2)
- Quaternion does not take in a rotation order.
- Parameters
- ==========
- q0, q1, q2, q3 : Expr
- The quaternions to rotate the coordinate system by
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> from sympy import symbols
- >>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
- >>> N = CoordSys3D('N')
- >>> from sympy.vector import QuaternionOrienter
- >>> q_orienter = QuaternionOrienter(q0, q1, q2, q3)
- >>> B = N.orient_new('B', (q_orienter, ))
- """
- # Dummy initializer for docstrings
- pass
- @property
- def q0(self):
- return self._q0
- @property
- def q1(self):
- return self._q1
- @property
- def q2(self):
- return self._q2
- @property
- def q3(self):
- return self._q3
- def _rot(axis, angle):
- """DCM for simple axis 1, 2 or 3 rotations. """
- if axis == 1:
- return Matrix(rot_axis1(angle).T)
- elif axis == 2:
- return Matrix(rot_axis2(angle).T)
- elif axis == 3:
- return Matrix(rot_axis3(angle).T)
|