quaternion.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. from sympy.core.numbers import Rational
  2. from sympy.core.singleton import S
  3. from sympy.functions.elementary.complexes import (conjugate, im, re, sign)
  4. from sympy.functions.elementary.exponential import (exp, log as ln)
  5. from sympy.functions.elementary.miscellaneous import sqrt
  6. from sympy.functions.elementary.trigonometric import (acos, cos, sin)
  7. from sympy.simplify.trigsimp import trigsimp
  8. from sympy.integrals.integrals import integrate
  9. from sympy.matrices.dense import MutableDenseMatrix as Matrix
  10. from sympy.core.sympify import sympify
  11. from sympy.core.expr import Expr
  12. from mpmath.libmp.libmpf import prec_to_dps
  13. class Quaternion(Expr):
  14. """Provides basic quaternion operations.
  15. Quaternion objects can be instantiated as Quaternion(a, b, c, d)
  16. as in (a + b*i + c*j + d*k).
  17. Examples
  18. ========
  19. >>> from sympy import Quaternion
  20. >>> q = Quaternion(1, 2, 3, 4)
  21. >>> q
  22. 1 + 2*i + 3*j + 4*k
  23. Quaternions over complex fields can be defined as :
  24. >>> from sympy import Quaternion
  25. >>> from sympy import symbols, I
  26. >>> x = symbols('x')
  27. >>> q1 = Quaternion(x, x**3, x, x**2, real_field = False)
  28. >>> q2 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
  29. >>> q1
  30. x + x**3*i + x*j + x**2*k
  31. >>> q2
  32. (3 + 4*I) + (2 + 5*I)*i + 0*j + (7 + 8*I)*k
  33. References
  34. ==========
  35. .. [1] http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/
  36. .. [2] https://en.wikipedia.org/wiki/Quaternion
  37. """
  38. _op_priority = 11.0
  39. is_commutative = False
  40. def __new__(cls, a=0, b=0, c=0, d=0, real_field=True):
  41. a = sympify(a)
  42. b = sympify(b)
  43. c = sympify(c)
  44. d = sympify(d)
  45. if any(i.is_commutative is False for i in [a, b, c, d]):
  46. raise ValueError("arguments have to be commutative")
  47. else:
  48. obj = Expr.__new__(cls, a, b, c, d)
  49. obj._a = a
  50. obj._b = b
  51. obj._c = c
  52. obj._d = d
  53. obj._real_field = real_field
  54. return obj
  55. @property
  56. def a(self):
  57. return self._a
  58. @property
  59. def b(self):
  60. return self._b
  61. @property
  62. def c(self):
  63. return self._c
  64. @property
  65. def d(self):
  66. return self._d
  67. @property
  68. def real_field(self):
  69. return self._real_field
  70. @classmethod
  71. def from_axis_angle(cls, vector, angle):
  72. """Returns a rotation quaternion given the axis and the angle of rotation.
  73. Parameters
  74. ==========
  75. vector : tuple of three numbers
  76. The vector representation of the given axis.
  77. angle : number
  78. The angle by which axis is rotated (in radians).
  79. Returns
  80. =======
  81. Quaternion
  82. The normalized rotation quaternion calculated from the given axis and the angle of rotation.
  83. Examples
  84. ========
  85. >>> from sympy import Quaternion
  86. >>> from sympy import pi, sqrt
  87. >>> q = Quaternion.from_axis_angle((sqrt(3)/3, sqrt(3)/3, sqrt(3)/3), 2*pi/3)
  88. >>> q
  89. 1/2 + 1/2*i + 1/2*j + 1/2*k
  90. """
  91. (x, y, z) = vector
  92. norm = sqrt(x**2 + y**2 + z**2)
  93. (x, y, z) = (x / norm, y / norm, z / norm)
  94. s = sin(angle * S.Half)
  95. a = cos(angle * S.Half)
  96. b = x * s
  97. c = y * s
  98. d = z * s
  99. # note that this quaternion is already normalized by construction:
  100. # c^2 + (s*x)^2 + (s*y)^2 + (s*z)^2 = c^2 + s^2*(x^2 + y^2 + z^2) = c^2 + s^2 * 1 = c^2 + s^2 = 1
  101. # so, what we return is a normalized quaternion
  102. return cls(a, b, c, d)
  103. @classmethod
  104. def from_rotation_matrix(cls, M):
  105. """Returns the equivalent quaternion of a matrix. The quaternion will be normalized
  106. only if the matrix is special orthogonal (orthogonal and det(M) = 1).
  107. Parameters
  108. ==========
  109. M : Matrix
  110. Input matrix to be converted to equivalent quaternion. M must be special
  111. orthogonal (orthogonal and det(M) = 1) for the quaternion to be normalized.
  112. Returns
  113. =======
  114. Quaternion
  115. The quaternion equivalent to given matrix.
  116. Examples
  117. ========
  118. >>> from sympy import Quaternion
  119. >>> from sympy import Matrix, symbols, cos, sin, trigsimp
  120. >>> x = symbols('x')
  121. >>> M = Matrix([[cos(x), -sin(x), 0], [sin(x), cos(x), 0], [0, 0, 1]])
  122. >>> q = trigsimp(Quaternion.from_rotation_matrix(M))
  123. >>> q
  124. sqrt(2)*sqrt(cos(x) + 1)/2 + 0*i + 0*j + sqrt(2 - 2*cos(x))*sign(sin(x))/2*k
  125. """
  126. absQ = M.det()**Rational(1, 3)
  127. a = sqrt(absQ + M[0, 0] + M[1, 1] + M[2, 2]) / 2
  128. b = sqrt(absQ + M[0, 0] - M[1, 1] - M[2, 2]) / 2
  129. c = sqrt(absQ - M[0, 0] + M[1, 1] - M[2, 2]) / 2
  130. d = sqrt(absQ - M[0, 0] - M[1, 1] + M[2, 2]) / 2
  131. b = b * sign(M[2, 1] - M[1, 2])
  132. c = c * sign(M[0, 2] - M[2, 0])
  133. d = d * sign(M[1, 0] - M[0, 1])
  134. return Quaternion(a, b, c, d)
  135. def __add__(self, other):
  136. return self.add(other)
  137. def __radd__(self, other):
  138. return self.add(other)
  139. def __sub__(self, other):
  140. return self.add(other*-1)
  141. def __mul__(self, other):
  142. return self._generic_mul(self, other)
  143. def __rmul__(self, other):
  144. return self._generic_mul(other, self)
  145. def __pow__(self, p):
  146. return self.pow(p)
  147. def __neg__(self):
  148. return Quaternion(-self._a, -self._b, -self._c, -self.d)
  149. def __truediv__(self, other):
  150. return self * sympify(other)**-1
  151. def __rtruediv__(self, other):
  152. return sympify(other) * self**-1
  153. def _eval_Integral(self, *args):
  154. return self.integrate(*args)
  155. def diff(self, *symbols, **kwargs):
  156. kwargs.setdefault('evaluate', True)
  157. return self.func(*[a.diff(*symbols, **kwargs) for a in self.args])
  158. def add(self, other):
  159. """Adds quaternions.
  160. Parameters
  161. ==========
  162. other : Quaternion
  163. The quaternion to add to current (self) quaternion.
  164. Returns
  165. =======
  166. Quaternion
  167. The resultant quaternion after adding self to other
  168. Examples
  169. ========
  170. >>> from sympy import Quaternion
  171. >>> from sympy import symbols
  172. >>> q1 = Quaternion(1, 2, 3, 4)
  173. >>> q2 = Quaternion(5, 6, 7, 8)
  174. >>> q1.add(q2)
  175. 6 + 8*i + 10*j + 12*k
  176. >>> q1 + 5
  177. 6 + 2*i + 3*j + 4*k
  178. >>> x = symbols('x', real = True)
  179. >>> q1.add(x)
  180. (x + 1) + 2*i + 3*j + 4*k
  181. Quaternions over complex fields :
  182. >>> from sympy import Quaternion
  183. >>> from sympy import I
  184. >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
  185. >>> q3.add(2 + 3*I)
  186. (5 + 7*I) + (2 + 5*I)*i + 0*j + (7 + 8*I)*k
  187. """
  188. q1 = self
  189. q2 = sympify(other)
  190. # If q2 is a number or a SymPy expression instead of a quaternion
  191. if not isinstance(q2, Quaternion):
  192. if q1.real_field and q2.is_complex:
  193. return Quaternion(re(q2) + q1.a, im(q2) + q1.b, q1.c, q1.d)
  194. elif q2.is_commutative:
  195. return Quaternion(q1.a + q2, q1.b, q1.c, q1.d)
  196. else:
  197. raise ValueError("Only commutative expressions can be added with a Quaternion.")
  198. return Quaternion(q1.a + q2.a, q1.b + q2.b, q1.c + q2.c, q1.d
  199. + q2.d)
  200. def mul(self, other):
  201. """Multiplies quaternions.
  202. Parameters
  203. ==========
  204. other : Quaternion or symbol
  205. The quaternion to multiply to current (self) quaternion.
  206. Returns
  207. =======
  208. Quaternion
  209. The resultant quaternion after multiplying self with other
  210. Examples
  211. ========
  212. >>> from sympy import Quaternion
  213. >>> from sympy import symbols
  214. >>> q1 = Quaternion(1, 2, 3, 4)
  215. >>> q2 = Quaternion(5, 6, 7, 8)
  216. >>> q1.mul(q2)
  217. (-60) + 12*i + 30*j + 24*k
  218. >>> q1.mul(2)
  219. 2 + 4*i + 6*j + 8*k
  220. >>> x = symbols('x', real = True)
  221. >>> q1.mul(x)
  222. x + 2*x*i + 3*x*j + 4*x*k
  223. Quaternions over complex fields :
  224. >>> from sympy import Quaternion
  225. >>> from sympy import I
  226. >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
  227. >>> q3.mul(2 + 3*I)
  228. (2 + 3*I)*(3 + 4*I) + (2 + 3*I)*(2 + 5*I)*i + 0*j + (2 + 3*I)*(7 + 8*I)*k
  229. """
  230. return self._generic_mul(self, other)
  231. @staticmethod
  232. def _generic_mul(q1, q2):
  233. """Generic multiplication.
  234. Parameters
  235. ==========
  236. q1 : Quaternion or symbol
  237. q2 : Quaternion or symbol
  238. It's important to note that if neither q1 nor q2 is a Quaternion,
  239. this function simply returns q1 * q2.
  240. Returns
  241. =======
  242. Quaternion
  243. The resultant quaternion after multiplying q1 and q2
  244. Examples
  245. ========
  246. >>> from sympy import Quaternion
  247. >>> from sympy import Symbol
  248. >>> q1 = Quaternion(1, 2, 3, 4)
  249. >>> q2 = Quaternion(5, 6, 7, 8)
  250. >>> Quaternion._generic_mul(q1, q2)
  251. (-60) + 12*i + 30*j + 24*k
  252. >>> Quaternion._generic_mul(q1, 2)
  253. 2 + 4*i + 6*j + 8*k
  254. >>> x = Symbol('x', real = True)
  255. >>> Quaternion._generic_mul(q1, x)
  256. x + 2*x*i + 3*x*j + 4*x*k
  257. Quaternions over complex fields :
  258. >>> from sympy import Quaternion
  259. >>> from sympy import I
  260. >>> q3 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
  261. >>> Quaternion._generic_mul(q3, 2 + 3*I)
  262. (2 + 3*I)*(3 + 4*I) + (2 + 3*I)*(2 + 5*I)*i + 0*j + (2 + 3*I)*(7 + 8*I)*k
  263. """
  264. q1 = sympify(q1)
  265. q2 = sympify(q2)
  266. # None is a Quaternion:
  267. if not isinstance(q1, Quaternion) and not isinstance(q2, Quaternion):
  268. return q1 * q2
  269. # If q1 is a number or a SymPy expression instead of a quaternion
  270. if not isinstance(q1, Quaternion):
  271. if q2.real_field and q1.is_complex:
  272. return Quaternion(re(q1), im(q1), 0, 0) * q2
  273. elif q1.is_commutative:
  274. return Quaternion(q1 * q2.a, q1 * q2.b, q1 * q2.c, q1 * q2.d)
  275. else:
  276. raise ValueError("Only commutative expressions can be multiplied with a Quaternion.")
  277. # If q2 is a number or a SymPy expression instead of a quaternion
  278. if not isinstance(q2, Quaternion):
  279. if q1.real_field and q2.is_complex:
  280. return q1 * Quaternion(re(q2), im(q2), 0, 0)
  281. elif q2.is_commutative:
  282. return Quaternion(q2 * q1.a, q2 * q1.b, q2 * q1.c, q2 * q1.d)
  283. else:
  284. raise ValueError("Only commutative expressions can be multiplied with a Quaternion.")
  285. return Quaternion(-q1.b*q2.b - q1.c*q2.c - q1.d*q2.d + q1.a*q2.a,
  286. q1.b*q2.a + q1.c*q2.d - q1.d*q2.c + q1.a*q2.b,
  287. -q1.b*q2.d + q1.c*q2.a + q1.d*q2.b + q1.a*q2.c,
  288. q1.b*q2.c - q1.c*q2.b + q1.d*q2.a + q1.a * q2.d)
  289. def _eval_conjugate(self):
  290. """Returns the conjugate of the quaternion."""
  291. q = self
  292. return Quaternion(q.a, -q.b, -q.c, -q.d)
  293. def norm(self):
  294. """Returns the norm of the quaternion."""
  295. q = self
  296. # trigsimp is used to simplify sin(x)^2 + cos(x)^2 (these terms
  297. # arise when from_axis_angle is used).
  298. return sqrt(trigsimp(q.a**2 + q.b**2 + q.c**2 + q.d**2))
  299. def normalize(self):
  300. """Returns the normalized form of the quaternion."""
  301. q = self
  302. return q * (1/q.norm())
  303. def inverse(self):
  304. """Returns the inverse of the quaternion."""
  305. q = self
  306. if not q.norm():
  307. raise ValueError("Cannot compute inverse for a quaternion with zero norm")
  308. return conjugate(q) * (1/q.norm()**2)
  309. def pow(self, p):
  310. """Finds the pth power of the quaternion.
  311. Parameters
  312. ==========
  313. p : int
  314. Power to be applied on quaternion.
  315. Returns
  316. =======
  317. Quaternion
  318. Returns the p-th power of the current quaternion.
  319. Returns the inverse if p = -1.
  320. Examples
  321. ========
  322. >>> from sympy import Quaternion
  323. >>> q = Quaternion(1, 2, 3, 4)
  324. >>> q.pow(4)
  325. 668 + (-224)*i + (-336)*j + (-448)*k
  326. """
  327. p = sympify(p)
  328. q = self
  329. if p == -1:
  330. return q.inverse()
  331. res = 1
  332. if not p.is_Integer:
  333. return NotImplemented
  334. if p < 0:
  335. q, p = q.inverse(), -p
  336. while p > 0:
  337. if p % 2 == 1:
  338. res = q * res
  339. p = p//2
  340. q = q * q
  341. return res
  342. def exp(self):
  343. """Returns the exponential of q (e^q).
  344. Returns
  345. =======
  346. Quaternion
  347. Exponential of q (e^q).
  348. Examples
  349. ========
  350. >>> from sympy import Quaternion
  351. >>> q = Quaternion(1, 2, 3, 4)
  352. >>> q.exp()
  353. E*cos(sqrt(29))
  354. + 2*sqrt(29)*E*sin(sqrt(29))/29*i
  355. + 3*sqrt(29)*E*sin(sqrt(29))/29*j
  356. + 4*sqrt(29)*E*sin(sqrt(29))/29*k
  357. """
  358. # exp(q) = e^a(cos||v|| + v/||v||*sin||v||)
  359. q = self
  360. vector_norm = sqrt(q.b**2 + q.c**2 + q.d**2)
  361. a = exp(q.a) * cos(vector_norm)
  362. b = exp(q.a) * sin(vector_norm) * q.b / vector_norm
  363. c = exp(q.a) * sin(vector_norm) * q.c / vector_norm
  364. d = exp(q.a) * sin(vector_norm) * q.d / vector_norm
  365. return Quaternion(a, b, c, d)
  366. def _ln(self):
  367. """Returns the natural logarithm of the quaternion (_ln(q)).
  368. Examples
  369. ========
  370. >>> from sympy import Quaternion
  371. >>> q = Quaternion(1, 2, 3, 4)
  372. >>> q._ln()
  373. log(sqrt(30))
  374. + 2*sqrt(29)*acos(sqrt(30)/30)/29*i
  375. + 3*sqrt(29)*acos(sqrt(30)/30)/29*j
  376. + 4*sqrt(29)*acos(sqrt(30)/30)/29*k
  377. """
  378. # _ln(q) = _ln||q|| + v/||v||*arccos(a/||q||)
  379. q = self
  380. vector_norm = sqrt(q.b**2 + q.c**2 + q.d**2)
  381. q_norm = q.norm()
  382. a = ln(q_norm)
  383. b = q.b * acos(q.a / q_norm) / vector_norm
  384. c = q.c * acos(q.a / q_norm) / vector_norm
  385. d = q.d * acos(q.a / q_norm) / vector_norm
  386. return Quaternion(a, b, c, d)
  387. def _eval_evalf(self, prec):
  388. """Returns the floating point approximations (decimal numbers) of the quaternion.
  389. Returns
  390. =======
  391. Quaternion
  392. Floating point approximations of quaternion(self)
  393. Examples
  394. ========
  395. >>> from sympy import Quaternion
  396. >>> from sympy import sqrt
  397. >>> q = Quaternion(1/sqrt(1), 1/sqrt(2), 1/sqrt(3), 1/sqrt(4))
  398. >>> q.evalf()
  399. 1.00000000000000
  400. + 0.707106781186547*i
  401. + 0.577350269189626*j
  402. + 0.500000000000000*k
  403. """
  404. nprec = prec_to_dps(prec)
  405. return Quaternion(*[arg.evalf(n=nprec) for arg in self.args])
  406. def pow_cos_sin(self, p):
  407. """Computes the pth power in the cos-sin form.
  408. Parameters
  409. ==========
  410. p : int
  411. Power to be applied on quaternion.
  412. Returns
  413. =======
  414. Quaternion
  415. The p-th power in the cos-sin form.
  416. Examples
  417. ========
  418. >>> from sympy import Quaternion
  419. >>> q = Quaternion(1, 2, 3, 4)
  420. >>> q.pow_cos_sin(4)
  421. 900*cos(4*acos(sqrt(30)/30))
  422. + 1800*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*i
  423. + 2700*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*j
  424. + 3600*sqrt(29)*sin(4*acos(sqrt(30)/30))/29*k
  425. """
  426. # q = ||q||*(cos(a) + u*sin(a))
  427. # q^p = ||q||^p * (cos(p*a) + u*sin(p*a))
  428. q = self
  429. (v, angle) = q.to_axis_angle()
  430. q2 = Quaternion.from_axis_angle(v, p * angle)
  431. return q2 * (q.norm()**p)
  432. def integrate(self, *args):
  433. """Computes integration of quaternion.
  434. Returns
  435. =======
  436. Quaternion
  437. Integration of the quaternion(self) with the given variable.
  438. Examples
  439. ========
  440. Indefinite Integral of quaternion :
  441. >>> from sympy import Quaternion
  442. >>> from sympy.abc import x
  443. >>> q = Quaternion(1, 2, 3, 4)
  444. >>> q.integrate(x)
  445. x + 2*x*i + 3*x*j + 4*x*k
  446. Definite integral of quaternion :
  447. >>> from sympy import Quaternion
  448. >>> from sympy.abc import x
  449. >>> q = Quaternion(1, 2, 3, 4)
  450. >>> q.integrate((x, 1, 5))
  451. 4 + 8*i + 12*j + 16*k
  452. """
  453. # TODO: is this expression correct?
  454. return Quaternion(integrate(self.a, *args), integrate(self.b, *args),
  455. integrate(self.c, *args), integrate(self.d, *args))
  456. @staticmethod
  457. def rotate_point(pin, r):
  458. """Returns the coordinates of the point pin(a 3 tuple) after rotation.
  459. Parameters
  460. ==========
  461. pin : tuple
  462. A 3-element tuple of coordinates of a point which needs to be
  463. rotated.
  464. r : Quaternion or tuple
  465. Axis and angle of rotation.
  466. It's important to note that when r is a tuple, it must be of the form
  467. (axis, angle)
  468. Returns
  469. =======
  470. tuple
  471. The coordinates of the point after rotation.
  472. Examples
  473. ========
  474. >>> from sympy import Quaternion
  475. >>> from sympy import symbols, trigsimp, cos, sin
  476. >>> x = symbols('x')
  477. >>> q = Quaternion(cos(x/2), 0, 0, sin(x/2))
  478. >>> trigsimp(Quaternion.rotate_point((1, 1, 1), q))
  479. (sqrt(2)*cos(x + pi/4), sqrt(2)*sin(x + pi/4), 1)
  480. >>> (axis, angle) = q.to_axis_angle()
  481. >>> trigsimp(Quaternion.rotate_point((1, 1, 1), (axis, angle)))
  482. (sqrt(2)*cos(x + pi/4), sqrt(2)*sin(x + pi/4), 1)
  483. """
  484. if isinstance(r, tuple):
  485. # if r is of the form (vector, angle)
  486. q = Quaternion.from_axis_angle(r[0], r[1])
  487. else:
  488. # if r is a quaternion
  489. q = r.normalize()
  490. pout = q * Quaternion(0, pin[0], pin[1], pin[2]) * conjugate(q)
  491. return (pout.b, pout.c, pout.d)
  492. def to_axis_angle(self):
  493. """Returns the axis and angle of rotation of a quaternion
  494. Returns
  495. =======
  496. tuple
  497. Tuple of (axis, angle)
  498. Examples
  499. ========
  500. >>> from sympy import Quaternion
  501. >>> q = Quaternion(1, 1, 1, 1)
  502. >>> (axis, angle) = q.to_axis_angle()
  503. >>> axis
  504. (sqrt(3)/3, sqrt(3)/3, sqrt(3)/3)
  505. >>> angle
  506. 2*pi/3
  507. """
  508. q = self
  509. if q.a.is_negative:
  510. q = q * -1
  511. q = q.normalize()
  512. angle = trigsimp(2 * acos(q.a))
  513. # Since quaternion is normalised, q.a is less than 1.
  514. s = sqrt(1 - q.a*q.a)
  515. x = trigsimp(q.b / s)
  516. y = trigsimp(q.c / s)
  517. z = trigsimp(q.d / s)
  518. v = (x, y, z)
  519. t = (v, angle)
  520. return t
  521. def to_rotation_matrix(self, v=None):
  522. """Returns the equivalent rotation transformation matrix of the quaternion
  523. which represents rotation about the origin if v is not passed.
  524. Parameters
  525. ==========
  526. v : tuple or None
  527. Default value: None
  528. Returns
  529. =======
  530. tuple
  531. Returns the equivalent rotation transformation matrix of the quaternion
  532. which represents rotation about the origin if v is not passed.
  533. Examples
  534. ========
  535. >>> from sympy import Quaternion
  536. >>> from sympy import symbols, trigsimp, cos, sin
  537. >>> x = symbols('x')
  538. >>> q = Quaternion(cos(x/2), 0, 0, sin(x/2))
  539. >>> trigsimp(q.to_rotation_matrix())
  540. Matrix([
  541. [cos(x), -sin(x), 0],
  542. [sin(x), cos(x), 0],
  543. [ 0, 0, 1]])
  544. Generates a 4x4 transformation matrix (used for rotation about a point
  545. other than the origin) if the point(v) is passed as an argument.
  546. Examples
  547. ========
  548. >>> from sympy import Quaternion
  549. >>> from sympy import symbols, trigsimp, cos, sin
  550. >>> x = symbols('x')
  551. >>> q = Quaternion(cos(x/2), 0, 0, sin(x/2))
  552. >>> trigsimp(q.to_rotation_matrix((1, 1, 1)))
  553. Matrix([
  554. [cos(x), -sin(x), 0, sin(x) - cos(x) + 1],
  555. [sin(x), cos(x), 0, -sin(x) - cos(x) + 1],
  556. [ 0, 0, 1, 0],
  557. [ 0, 0, 0, 1]])
  558. """
  559. q = self
  560. s = q.norm()**-2
  561. m00 = 1 - 2*s*(q.c**2 + q.d**2)
  562. m01 = 2*s*(q.b*q.c - q.d*q.a)
  563. m02 = 2*s*(q.b*q.d + q.c*q.a)
  564. m10 = 2*s*(q.b*q.c + q.d*q.a)
  565. m11 = 1 - 2*s*(q.b**2 + q.d**2)
  566. m12 = 2*s*(q.c*q.d - q.b*q.a)
  567. m20 = 2*s*(q.b*q.d - q.c*q.a)
  568. m21 = 2*s*(q.c*q.d + q.b*q.a)
  569. m22 = 1 - 2*s*(q.b**2 + q.c**2)
  570. if not v:
  571. return Matrix([[m00, m01, m02], [m10, m11, m12], [m20, m21, m22]])
  572. else:
  573. (x, y, z) = v
  574. m03 = x - x*m00 - y*m01 - z*m02
  575. m13 = y - x*m10 - y*m11 - z*m12
  576. m23 = z - x*m20 - y*m21 - z*m22
  577. m30 = m31 = m32 = 0
  578. m33 = 1
  579. return Matrix([[m00, m01, m02, m03], [m10, m11, m12, m13],
  580. [m20, m21, m22, m23], [m30, m31, m32, m33]])