Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpQuaternionVector.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Quaternion vector.
32  */
33 
34 #include <algorithm>
35 #include <cassert>
36 #include <stdio.h>
37 #include <string.h>
38 #include <visp3/core/vpMath.h>
39 #include <visp3/core/vpQuaternionVector.h>
40 
42 // minimum value of sine
43 const double vpQuaternionVector::minimum = 0.0001;
44 
52 
55 
57 vpQuaternionVector::vpQuaternionVector(double x_, double y_, double z_, double w_) : vpRotationVector(4)
58 {
59  set(x_, y_, z_, w_);
60 }
61 
64 
66 vpQuaternionVector::vpQuaternionVector(const std::vector<double> &q) : vpRotationVector(4) { build(q); }
67 
74 
82 
90 void vpQuaternionVector::set(double qx, double qy, double qz, double qw)
91 {
92  const unsigned int index_0 = 0;
93  const unsigned int index_1 = 1;
94  const unsigned int index_2 = 2;
95  const unsigned int index_3 = 3;
96  data[index_0] = qx;
97  data[index_1] = qy;
98  data[index_2] = qz;
99  data[index_3] = qw;
100 }
101 
102 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
113 vpQuaternionVector vpQuaternionVector::buildFrom(const double qx, const double qy, const double qz, const double qw)
114 {
115  build(qx, qy, qz, qw);
116  return *this;
117 }
118 
127 {
128  build(tu);
129  return *this;
130 }
131 
137 {
138  build(q);
139  return *this;
140 }
141 
147 {
148  build(q);
149  return *this;
150 }
151 
159 {
160  build(R);
161  return *this;
162 }
163 #endif
164 
174 vpQuaternionVector &vpQuaternionVector::build(const double &qx, const double &qy, const double &qz, const double &qw)
175 {
176  set(qx, qy, qz, qw);
177  return *this;
178 }
179 
187 {
188  vpRotationMatrix R(tu);
189  build(R);
190 
191  return *this;
192 }
193 
198 {
199  if (q.size() != 4) {
201  "Cannot construct a quaternion vector from a %d-dimension col vector", q.size()));
202  }
203  const unsigned int val_4 = 4;
204  for (unsigned int i = 0; i < val_4; ++i) {
205  data[i] = q[i];
206  }
207 
208  return *this;
209 }
210 
214 vpQuaternionVector &vpQuaternionVector::build(const std::vector<double> &q)
215 {
216  if (q.size() != 4) {
218  "Cannot construct a quaternion vector from a %d-dimension std::vector", q.size()));
219  }
220 
221  const unsigned int val_4 = 4;
222  for (unsigned int i = 0; i < val_4; ++i) {
223  data[i] = q[i];
224  }
225 
226  return *this;
227 }
228 
235 {
236  vpThetaUVector tu(R);
237  vpColVector u;
238  double theta;
239  tu.extract(theta, u);
240 
241  theta *= 0.5;
242 
243  double sinTheta_2 = sin(theta);
244  const unsigned int index_0 = 0;
245  const unsigned int index_1 = 1;
246  const unsigned int index_2 = 2;
247  set(u[index_0] * sinTheta_2, u[index_1] * sinTheta_2, u[index_2] * sinTheta_2, cos(theta));
248  return *this;
249 }
250 
259 {
260  return vpQuaternionVector(x() + q.x(), y() + q.y(), z() + q.z(), w() + q.w());
261 }
270 {
271  return vpQuaternionVector(x() - q.x(), y() - q.y(), z() - q.z(), w() - q.w());
272 }
273 
276 
279 {
280  return vpQuaternionVector(l * x(), l * y(), l * z(), l * w());
281 }
282 
285 {
286  return vpQuaternionVector(((w() * rq.x()) + (x() * rq.w()) + (y() * rq.z())) - (z() * rq.y()),
287  ((w() * rq.y()) + (y() * rq.w()) + (z() * rq.x())) - (x() * rq.z()),
288  ((w() * rq.z()) + (z() * rq.w()) + (x() * rq.y())) - (y() * rq.x()),
289  ((w() * rq.w()) - (x() * rq.x()) - (y() * rq.y())) - (z() * rq.z()));
290 }
291 
294 {
295  if (vpMath::nul(l, std::numeric_limits<double>::epsilon())) {
296  throw vpException(vpException::fatalError, "Division by scalar l==0 !");
297  }
298 
299  return vpQuaternionVector(x() / l, y() / l, z() / l, w() / l);
300 }
328 {
329  if (q.size() != 4) {
330  throw(vpException(vpException::dimensionError, "Cannot set a quaternion vector from a %d-dimension col vector",
331  q.size()));
332  }
333  const unsigned int val_4 = 4;
334  for (unsigned int i = 0; i < val_4; ++i) {
335  data[i] = q[i];
336  }
337 
338  return *this;
339 }
340 
341 
342 
349 
356 {
357  vpQuaternionVector q_inv;
358 
359  double mag_square = (w() * w()) + (x() * x()) + (y() * y()) + (z() * z());
360  if (!vpMath::nul(mag_square, std::numeric_limits<double>::epsilon())) {
361  q_inv = this->conjugate() / mag_square;
362  }
363  else {
364  std::cerr << "The current quaternion is null ! The inverse cannot be computed !" << std::endl;
365  }
366 
367  return q_inv;
368 }
369 
375 double vpQuaternionVector::magnitude() const { return sqrt((w() * w()) + (x() * x()) + (y() * y()) + (z() * z())); }
376 
381 {
382  double mag = magnitude();
383  if (!vpMath::nul(mag, std::numeric_limits<double>::epsilon())) {
384  set(x() / mag, y() / mag, z() / mag, w() / mag);
385  }
386 }
387 
397 {
398  return (q0.x() * q1.x()) + (q0.y() * q1.y()) + (q0.z() * q1.z()) + (q0.w() * q1.w());
399 }
400 
402 const double &vpQuaternionVector::x() const { const unsigned int index_0 = 0; return data[index_0]; }
404 const double &vpQuaternionVector::y() const { const unsigned int index_1 = 1; return data[index_1]; }
406 const double &vpQuaternionVector::z() const { const unsigned int index_2 = 2; return data[index_2]; }
408 const double &vpQuaternionVector::w() const { const unsigned int index_3 = 3; return data[index_3]; }
409 
411 double &vpQuaternionVector::x() { const unsigned int index_0 = 0; return data[index_0]; }
413 double &vpQuaternionVector::y() { const unsigned int index_1 = 1; return data[index_1]; }
415 double &vpQuaternionVector::z() { const unsigned int index_2 = 2; return data[index_2]; }
417 double &vpQuaternionVector::w() { const unsigned int index_3 = 3; return data[index_3]; }
418 
419 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
441 vpQuaternionVector &vpQuaternionVector::operator=(const std::initializer_list<double> &list)
442 {
443  if (list.size() > size()) {
444  throw(vpException(
446  "Cannot set quaternion vector out of bounds. It has only %d values while you try to initialize with %d values",
447  size(), list.size()));
448  }
449  std::copy(list.begin(), list.end(), data);
450  return *this;
451 }
452 #endif
453 
469 {
470  assert(t >= 0 && t <= 1);
471 
472  double cosHalfTheta = dot(q0, q1);
473  vpQuaternionVector q1_ = q1;
474  if (cosHalfTheta < 0) {
475  cosHalfTheta = -cosHalfTheta;
476  q1_ = -q1;
477  }
478 
479  vpQuaternionVector qLerp;
480  qLerp.x() = q0.x() - (t * (q0.x() - q1.x()));
481  qLerp.y() = q0.y() - (t * (q0.y() - q1.y()));
482  qLerp.z() = q0.z() - (t * (q0.z() - q1.z()));
483  qLerp.w() = q0.w() - (t * (q0.w() - q1.w()));
484 
485  return qLerp;
486 }
487 
503 {
504  assert(t >= 0 && t <= 1);
505 
506  vpQuaternionVector qLerp = lerp(q0, q1, t);
507  qLerp.normalize();
508 
509  return qLerp;
510 }
511 
527 {
528  assert(t >= 0 && t <= 1);
529  // Some additional references:
530  // https://splines.readthedocs.io/en/latest/rotation/slerp.html
531  // https://zeux.io/2015/07/23/approximating-slerp/
532  // https://github.com/eigenteam/eigen-git-mirror/blob/36b95962756c1fce8e29b1f8bc45967f30773c00/Eigen/src/Geometry/Quaternion.h#L753-L790
533  // https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/index.htm
534  // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
535  // https://www.3dgep.com/understanding-quaternions/
536  // https://blog.magnum.graphics/backstage/the-unnecessarily-short-ways-to-do-a-quaternion-slerp/
537 
538  double cosHalfTheta = dot(q0, q1);
539  vpQuaternionVector q1_ = q1;
540  if (cosHalfTheta < 0) {
541  cosHalfTheta = -cosHalfTheta;
542  q1_ = -q1;
543  }
544 
545  double scale0 = 1 - t;
546  double scale1 = t;
547 
548  if ((1 - cosHalfTheta) > 0.1) {
549  double theta = std::acos(cosHalfTheta);
550  double invSinTheta = 1 / std::sin(theta);
551 
552  scale0 = std::sin((1 - t) * theta) * invSinTheta;
553  scale1 = std::sin(t * theta) * invSinTheta;
554  }
555 
556  vpQuaternionVector qSlerp;
557  qSlerp.x() = (scale0 * q0.x()) + (scale1 * q1_.x());
558  qSlerp.y() = (scale0 * q0.y()) + (scale1 * q1_.y());
559  qSlerp.z() = (scale0 * q0.z()) + (scale1 * q1_.z());
560  qSlerp.w() = (scale0 * q0.w()) + (scale1 * q1_.w());
561  qSlerp.normalize();
562 
563  return qSlerp;
564 }
565 END_VISP_NAMESPACE
double * data
Address of the first element of the data array.
Definition: vpArray2D.h:148
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:349
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ dimensionError
Bad dimension.
Definition: vpException.h:71
@ fatalError
Fatal error.
Definition: vpException.h:72
static bool nul(double x, double threshold=0.001)
Definition: vpMath.h:449
Implementation of a rotation vector as quaternion angle minimal representation.
vpQuaternionVector operator*(double l) const
Multiplication by scalar. Returns a quaternion defined by (lx,ly,lz,lw).
const double & z() const
Returns the z-component of the quaternion.
vpQuaternionVector conjugate() const
vpQuaternionVector inverse() const
vpQuaternionVector & operator=(const vpColVector &q)
void set(double x, double y, double z, double w)
static vpQuaternionVector slerp(const vpQuaternionVector &q0, const vpQuaternionVector &q1, double t)
static vpQuaternionVector nlerp(const vpQuaternionVector &q0, const vpQuaternionVector &q1, double t)
vpQuaternionVector operator-() const
Negate operator. Returns a quaternion defined by (-x,-y,-z-,-w).
vpQuaternionVector & build(const double &qx, const double &qy, const double &qz, const double &qw)
const double & x() const
Returns the x-component of the quaternion.
static double dot(const vpQuaternionVector &q0, const vpQuaternionVector &q1)
const double & y() const
Returns the y-component of the quaternion.
const double & w() const
Returns the w-component of the quaternion.
VP_DEPRECATED vpQuaternionVector buildFrom(const double qx, const double qy, const double qz, const double qw)
vpQuaternionVector operator+(const vpQuaternionVector &q) const
static vpQuaternionVector lerp(const vpQuaternionVector &q0, const vpQuaternionVector &q1, double t)
vpQuaternionVector operator/(double l) const
Division by scalar. Returns a quaternion defined by (x/l,y/l,z/l,w/l).
Implementation of a rotation matrix and operations on such kind of matrices.
Implementation of a generic rotation vector.
vpRowVector t() const
Implementation of a rotation vector as axis-angle minimal representation.
void extract(double &theta, vpColVector &u) const