Blender V4.3
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
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/* -------------------------------------------------------------------- */
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>
65[[nodiscard]] QuaternionBase<T> rotation_between(const QuaternionBase<T> &a,
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
112/* -------------------------------------------------------------------- */
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
188/* -------------------------------------------------------------------- */
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);
255 QuaternionBase<T> q2(math::cos(angle), 0.0, 0.0, math::sin(angle));
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
385/* -------------------------------------------------------------------- */
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
458} // namespace blender::math
459
460namespace blender::math {
461
462/* -------------------------------------------------------------------- */
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
486} // namespace blender::math
#define BLI_assert(a)
Definition BLI_assert.h:50
#define UNUSED_VARS(...)
#define UNLIKELY(x)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a vector
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 btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
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
local_group_size(16, 16) .push_constant(Type b
#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)
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)
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)
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)
T sin(const AngleRadianBase< T > &a)
T abs(const T &a)
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)
Frequency::GEOMETRY nor[]
const btScalar eps
Definition poly34.cpp:11