Blender V5.0
BLI_math_rotation.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
10
12
14#include "BLI_math_euler.hh"
15#include "BLI_math_matrix.hh"
17#include "BLI_math_vector.hh"
18
19namespace blender::math {
20
21/* -------------------------------------------------------------------- */
24
31template<typename T, typename RotT>
32[[nodiscard]] QuaternionBase<T> rotate(const QuaternionBase<T> &a, const RotT &b);
33
40template<typename T, typename RotT, typename AngleT>
41[[nodiscard]] AxisAngleBase<T, AngleT> rotate(const AxisAngleBase<T, AngleT> &a, const RotT &b);
42
49template<typename T, typename RotT>
50[[nodiscard]] EulerXYZBase<T> rotate(const EulerXYZBase<T> &a, const RotT &b);
51
58template<typename T, typename RotT>
59[[nodiscard]] Euler3Base<T> rotate(const Euler3Base<T> &a, const RotT &b);
60
64template<typename T>
66 const QuaternionBase<T> &b);
67
73template<typename T>
74[[nodiscard]] QuaternionBase<T> from_triangle(const VecBase<T, 3> &v1,
75 const VecBase<T, 3> &v2,
76 const VecBase<T, 3> &v3,
77 const VecBase<T, 3> &normal);
78
82template<typename T>
83[[nodiscard]] QuaternionBase<T> from_triangle(const VecBase<T, 3> &v1,
84 const VecBase<T, 3> &v2,
85 const VecBase<T, 3> &v3);
86
93template<typename T>
94[[nodiscard]] QuaternionBase<T> from_vector(const VecBase<T, 3> &vector,
95 const AxisSigned track_flag,
96 const Axis up_flag);
97
102template<typename T>
103[[nodiscard]] QuaternionBase<T> from_tracking(AxisSigned forward_axis, Axis up_axis);
104
108template<typename T> [[nodiscard]] MatBase<T, 3, 3> to_gimbal_axis(const Euler3Base<T> &rotation);
109
111
112/* -------------------------------------------------------------------- */
115
123template<typename T> [[nodiscard]] AngleRadianBase<T> angle_of(const QuaternionBase<T> &q)
124{
126 return T(2) * math::safe_acos(q.w);
127}
128
137template<typename T> [[nodiscard]] AngleRadianBase<T> angle_of_signed(const QuaternionBase<T> &q)
138{
140 return T(2) * ((q.w >= T(0)) ? math::safe_acos(q.w) : -math::safe_acos(-q.w));
141}
142
149template<typename T>
151 const QuaternionBase<T> &b)
152{
153 return angle_of(rotation_between(a, b));
154}
155template<typename T>
157{
160 return math::safe_acos(dot(a, b));
161}
162template<typename T>
164{
165 if (a == b) {
167 }
168 if (abs(a) == abs(b)) {
169 return AngleFraction<T>::pi();
170 }
171 return AngleFraction<T>::pi() / 2;
172}
173
179template<typename T>
185
187
188/* -------------------------------------------------------------------- */
191
192template<typename T, typename RotT>
193[[nodiscard]] QuaternionBase<T> rotate(const QuaternionBase<T> &a, const RotT &b)
194{
195 return a * QuaternionBase<T>(b);
196}
197
198template<typename T, typename RotT, typename AngleT>
203
204template<typename T, typename RotT>
205[[nodiscard]] EulerXYZBase<T> rotate(const EulerXYZBase<T> &a, const RotT &b)
206{
208 return to_euler(tmp);
209}
210
211template<typename T, typename RotT>
212[[nodiscard]] Euler3Base<T> rotate(const Euler3Base<T> &a, const RotT &b)
213{
216 return to_euler(tmp, a.order());
217}
218
219template<typename T>
221 const QuaternionBase<T> &b)
222{
223 return invert(a) * b;
224}
225
226template<typename T>
228 const VecBase<T, 3> &v2,
229 const VecBase<T, 3> &v3,
230 const VecBase<T, 3> &normal)
231{
232 /* Force to used an unused var to avoid the same function signature as the version without
233 * `normal` argument. */
234 UNUSED_VARS(v3);
235
236 using Vec3T = VecBase<T, 3>;
237
238 /* Move z-axis to face-normal. */
239 const Vec3T z_axis = normal;
240 Vec3T nor = normalize(Vec3T(z_axis.y, -z_axis.x, T(0)));
241 if (is_zero(nor.xy())) {
242 nor.x = T(1);
243 }
244
245 T angle = T(-0.5) * math::safe_acos(z_axis.z);
246 T si = math::sin(angle);
247 QuaternionBase<T> q1(math::cos(angle), nor.x * si, nor.y * si, T(0));
248
249 /* Rotate back line v1-v2. */
250 Vec3T line = transform_point(conjugate(q1), (v2 - v1));
251 /* What angle has this line with x-axis? */
252 line = normalize(Vec3T(line.x, line.y, T(0)));
253
254 angle = T(0.5) * math::atan2(line.y, line.x);
256
257 return q1 * q2;
258}
259
260template<typename T>
262 const VecBase<T, 3> &v2,
263 const VecBase<T, 3> &v3)
264{
265 return from_triangle(v1, v2, v3, normal_tri(v1, v2, v3));
266}
267
268template<typename T>
270 const AxisSigned track_flag,
271 const Axis up_flag)
272{
273 using Vec2T = VecBase<T, 2>;
274 using Vec3T = VecBase<T, 3>;
275 using Vec4T = VecBase<T, 4>;
276
277 const T vec_len = length(vector);
278
279 if (UNLIKELY(vec_len == 0.0f)) {
281 }
282
283 const Axis axis = track_flag.axis();
284 const Vec3T vec = track_flag.is_negative() ? vector : -vector;
285
286 Vec3T rotation_axis;
287 constexpr T eps = T(1e-4);
288 T axis_len;
289 switch (axis) {
290 case Axis::X:
291 rotation_axis = normalize_and_get_length(Vec3T(T(0), -vec.z, vec.y), axis_len);
292 if (axis_len < eps) {
293 rotation_axis = Vec3T(0, 1, 0);
294 }
295 break;
296 case Axis::Y:
297 rotation_axis = normalize_and_get_length(Vec3T(vec.z, T(0), -vec.x), axis_len);
298 if (axis_len < eps) {
299 rotation_axis = Vec3T(0, 0, 1);
300 }
301 break;
302 default:
303 case Axis::Z:
304 rotation_axis = normalize_and_get_length(Vec3T(-vec.y, vec.x, T(0)), axis_len);
305 if (axis_len < eps) {
306 rotation_axis = Vec3T(1, 0, 0);
307 }
308 break;
309 }
310 /* TODO(fclem): Can optimize here by initializing AxisAngle using the cos an sin directly.
311 * Avoiding the need for safe_acos and deriving sin from cos. */
312 const T rotation_angle = math::safe_acos(vec[axis.as_int()] / vec_len);
313
315 AxisAngleBase<T, AngleRadianBase<T>>(rotation_axis, rotation_angle));
316
317 if (axis == up_flag) {
318 /* Nothing else to do. */
319 return q1;
320 }
321
322 /* Extract rotation between the up axis of the rotated space and the up axis. */
323 /* There might be an easier way to get this angle directly from the quaternion representation. */
324 const Vec3T rotated_up = transform_point(q1, Vec3T(0, 0, 1));
325
326 /* Project using axes index instead of arithmetic. It's much faster and more precise. */
327 const AxisSigned y_axis_signed = math::cross(AxisSigned(axis), AxisSigned(up_flag));
328 const Axis x_axis = up_flag;
329 const Axis y_axis = y_axis_signed.axis();
330
331 Vec2T projected = normalize(Vec2T(rotated_up[x_axis.as_int()], rotated_up[y_axis.as_int()]));
332 /* Flip sign for flipped axis. */
333 if (y_axis_signed.is_negative()) {
334 projected.y = -projected.y;
335 }
336 /* Not sure if this was a bug or not in the previous implementation.
337 * Carry over this weird behavior to avoid regressions. */
338 if (axis == Axis::Z) {
339 projected = -projected;
340 }
341
342 const AngleCartesianBase<T> angle(projected.x, projected.y);
343 const AngleCartesianBase<T> half_angle = angle / T(2);
344
345 const QuaternionBase<T> q2(Vec4T(half_angle.cos(), vec * (half_angle.sin() / vec_len)));
346
347 return q2 * q1;
348}
349
350template<typename T>
351[[nodiscard]] QuaternionBase<T> from_tracking(AxisSigned forward_axis, Axis up_axis)
352{
353 BLI_assert(forward_axis.axis() != up_axis);
354
355 /* Curve have Z forward, Y up, X left. */
356 return QuaternionBase<T>(
358 from_orthonormal_axes(forward_axis, AxisSigned(up_axis))));
359}
360
361template<typename T> [[nodiscard]] MatBase<T, 3, 3> to_gimbal_axis(const Euler3Base<T> &rotation)
362{
363 using Mat3T = MatBase<T, 3, 3>;
364 using Vec3T = VecBase<T, 3>;
365 const int i_index = rotation.i_index();
366 const int j_index = rotation.j_index();
367 const int k_index = rotation.k_index();
368
369 Mat3T result;
370 /* First axis is local. */
371 result[i_index] = from_rotation<Mat3T>(rotation)[i_index];
372 /* Second axis is local minus first rotation. */
373 Euler3Base<T> tmp_rot = rotation;
374 tmp_rot.i() = T(0);
375 result[j_index] = from_rotation<Mat3T>(tmp_rot)[j_index];
376 /* Last axis is global. */
377 result[k_index] = Vec3T(0);
378 result[k_index][k_index] = T(1);
379
380 return result;
381}
382
384
385/* -------------------------------------------------------------------- */
388
393template<typename T> QuaternionBase<T> to_quaternion(const CartesianBasis &rotation)
394{
400 constexpr auto map = [](AxisSigned x, AxisSigned y, AxisSigned z) {
401 return x.as_int() << 16 | y.as_int() << 8 | z.as_int();
402 };
403
404 switch (map(rotation.axes.x, rotation.axes.y, rotation.axes.z)) {
405 default:
408 return QuaternionBase<T>{T(0.5), T(-0.5), T(-0.5), T(-0.5)};
412 return QuaternionBase<T>{T(0.5), T(0.5), T(0.5), T(-0.5)};
420 return QuaternionBase<T>{T(0), T(0), T(1), T(0)};
422 return QuaternionBase<T>{T(0.5), T(0.5), T(0.5), T(0.5)};
426 return QuaternionBase<T>{T(0.5), T(0.5), T(-0.5), T(-0.5)};
430 return QuaternionBase<T>{T(0.5), T(-0.5), T(0.5), T(0.5)};
434 return QuaternionBase<T>{T(0.5), T(0.5), T(-0.5), T(0.5)};
440 return QuaternionBase<T>{T(0), T(0), T(0), T(1)};
444 return QuaternionBase<T>{T(0), T(1), T(0), T(0)};
446 return QuaternionBase<T>{T(0.5), T(-0.5), T(0.5), T(-0.5)};
450 return QuaternionBase<T>{T(0.5), T(-0.5), T(-0.5), T(0.5)};
453 }
454}
455
457
458} // namespace blender::math
459
460namespace blender::math {
461
462/* -------------------------------------------------------------------- */
465
466/* Using explicit template instantiations in order to reduce compilation time. */
467extern template EulerXYZ to_euler(const AxisAngle &);
468extern template EulerXYZ to_euler(const AxisAngleCartesian &);
469extern template EulerXYZ to_euler(const Quaternion &);
470extern template Euler3 to_euler(const AxisAngle &, EulerOrder);
472extern template Euler3 to_euler(const Quaternion &, EulerOrder);
473extern template Quaternion to_quaternion(const AxisAngle &);
475extern template Quaternion to_quaternion(const Euler3 &);
476extern template Quaternion to_quaternion(const EulerXYZ &);
477extern template AxisAngleCartesian to_axis_angle(const Euler3 &);
480extern template AxisAngle to_axis_angle(const Euler3 &);
481extern template AxisAngle to_axis_angle(const EulerXYZ &);
482extern template AxisAngle to_axis_angle(const Quaternion &);
483
485
486} // namespace blender::math
#define BLI_assert(a)
Definition BLI_assert.h:46
#define UNUSED_VARS(...)
#define UNLIKELY(x)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
constexpr bool is_negative() const
constexpr int as_int() const
static constexpr Value Z
static constexpr Value X
static constexpr Value Y
uint nor
VecBase< float, D > normalize(VecOp< float, D >) RET
#define abs
#define T
QuaternionBase< T > from_triangle(const VecBase< T, 3 > &v1, const VecBase< T, 3 > &v2, const VecBase< T, 3 > &v3, const VecBase< T, 3 > &normal)
QuaternionBase< T > conjugate(const QuaternionBase< T > &a)
QuaternionBase< float > Quaternion
T cos(const AngleRadianBase< T > &a)
VecBase< T, 3 > normal_tri(const VecBase< T, 3 > &v1, const VecBase< T, 3 > &v2, const VecBase< T, 3 > &v3)
QuaternionBase< T > from_vector(const VecBase< T, 3 > &vector, const AxisSigned track_flag, const Axis up_flag)
QuaternionBase< T > to_quaternion(const AxisAngleBase< T, AngleT > &axis_angle)
QuaternionBase< T > from_tracking(AxisSigned forward_axis, Axis up_axis)
CartesianBasis rotation_between(const CartesianBasis &a, const CartesianBasis &b)
AngleRadianBase< T > angle_between(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
AxisAngleBase< float, AngleRadianBase< float > > AxisAngle
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
bool is_unit_scale(const MatBase< T, NumCol, NumRow > &m)
AngleRadianBase< T > angle_between_signed(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
EulerXYZBase< float > EulerXYZ
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
MatBase< T, 3, 3 > to_gimbal_axis(const Euler3Base< T > &rotation)
bool is_zero(const T &a)
CartesianBasis invert(const CartesianBasis &basis)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
AngleRadianBase< T > angle_of(const QuaternionBase< T > &q)
T atan2(const T &y, const T &x)
T rcp(const T &a)
T safe_acos(const T &a)
AxisAngleBase< T, AngleT > to_axis_angle(const EulerXYZBase< T > &euler)
AxisAngleBase< float, AngleCartesianBase< float > > AxisAngleCartesian
T sin(const AngleRadianBase< T > &a)
Euler3Base< float > Euler3
AngleRadianBase< T > angle_of_signed(const QuaternionBase< T > &q)
MatBase< T, NumCol, NumRow > rotate(const MatBase< T, NumCol, NumRow > &mat, const RotationT &rotation)
CartesianBasis from_orthonormal_axes(const AxisSigned forward, const AxisSigned up)
MatT from_rotation(const RotationT &rotation)
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
const btScalar eps
Definition poly34.cpp:11
const EulerOrder & order() const