Blender V4.3
math_rotation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_math_base.h"
10#include "BLI_math_matrix.h"
11#include "BLI_math_matrix.hh"
12#include "BLI_math_rotation.h"
13#include "BLI_math_rotation.hh"
15#include "BLI_math_vector.h"
16#include "BLI_math_vector.hh"
17
18namespace blender::math {
19
20template EulerXYZ to_euler(const AxisAngle &);
21template EulerXYZ to_euler(const AxisAngleCartesian &);
22template EulerXYZ to_euler(const Quaternion &);
23template Euler3 to_euler(const AxisAngle &, EulerOrder);
25template Euler3 to_euler(const Quaternion &, EulerOrder);
26template Quaternion to_quaternion(const AxisAngle &);
27template Quaternion to_quaternion(const AxisAngleCartesian &);
28template Quaternion to_quaternion(const Euler3 &);
29template Quaternion to_quaternion(const EulerXYZ &);
32template AxisAngleCartesian to_axis_angle(const Quaternion &);
33template AxisAngle to_axis_angle(const Euler3 &);
34template AxisAngle to_axis_angle(const EulerXYZ &);
35template AxisAngle to_axis_angle(const Quaternion &);
36
37#if 0 /* Only for reference. */
38void generate_axes_to_quaternion_switch_cases()
39{
40 std::cout << "default: *this = identity(); break;" << std::endl;
41 /* Go through all 32 cases. Only 23 valid and 1 is identity. */
42 for (int i : IndexRange(6)) {
43 for (int j : IndexRange(6)) {
44 const AxisSigned forward = AxisSigned(i);
45 const AxisSigned up = AxisSigned(j);
46 /* Filter the 12 invalid cases. Fall inside the default case. */
47 if (Axis(forward) == Axis(up)) {
48 continue;
49 }
50 /* Filter the identity case. Fall inside the default case. */
51 if (forward == AxisSigned::Y_POS && up == AxisSigned::Z_POS) {
52 continue;
53 }
54
55 VecBase<AxisSigned, 3> axes{cross(forward, up), forward, up};
56
57 float3x3 mat;
58 mat.x_axis() = float3(axes.x);
59 mat.y_axis() = float3(axes.y);
60 mat.z_axis() = float3(axes.z);
61
63 /* Create a integer value out of the 4 possible component values (+sign). */
64 int4 p = int4(round(sign(float4(q)) * min(pow(float4(q), 2.0f), float4(0.75)) * 4.0));
65
66 auto format_component = [](int value) {
67 switch (abs(value)) {
68 default:
69 case 0:
70 return "T(0)";
71 case 1:
72 return (value > 0) ? "T(0.5)" : "T(-0.5)";
73 case 2:
74 return (value > 0) ? "T(M_SQRT1_2)" : "T(-M_SQRT1_2)";
75 case 3:
76 return (value > 0) ? "T(1)" : "T(-1)";
77 }
78 };
79 auto format_axis = [](AxisSigned axis) {
80 switch (axis) {
81 default:
83 return "AxisSigned::X_POS";
85 return "AxisSigned::Y_POS";
87 return "AxisSigned::Z_POS";
89 return "AxisSigned::X_NEG";
91 return "AxisSigned::Y_NEG";
93 return "AxisSigned::Z_NEG";
94 }
95 };
96 /* Use same code function as in the switch case. */
97 std::cout << "case ";
98 std::cout << format_axis(axes.x) << " << 16 | ";
99 std::cout << format_axis(axes.y) << " << 8 | ";
100 std::cout << format_axis(axes.z);
101 std::cout << ": *this = {";
102 std::cout << format_component(p.x) << ", ";
103 std::cout << format_component(p.y) << ", ";
104 std::cout << format_component(p.z) << ", ";
105 std::cout << format_component(p.w) << "}; break;";
106 std::cout << std::endl;
107 }
108 }
109}
110#endif
111
112float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, const float angle)
113{
115
116 if (UNLIKELY(angle == 0.0f || math::is_zero(direction, std::numeric_limits<float>::epsilon()))) {
117 return direction;
118 }
119
120 BLI_assert(math::is_unit(direction));
121
122 const float3 axis_scaled = axis * math::dot(direction, axis);
123 const float3 diff = direction - axis_scaled;
124 const float3 cross = math::cross(axis, diff);
125
126 return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
127}
128
130 const float3 &center,
131 const float3 &axis,
132 const float angle)
133
134{
135 float3 result = vector - center;
136 float mat[3][3];
137 axis_angle_normalized_to_mat3(mat, axis, angle);
138 mul_m3_v3(mat, result);
139 return result + center;
140}
141
142std::ostream &operator<<(std::ostream &stream, EulerOrder order)
143{
144 switch (order) {
145 default:
146 case XYZ:
147 return stream << "XYZ";
148 case XZY:
149 return stream << "XZY";
150 case YXZ:
151 return stream << "YXZ";
152 case YZX:
153 return stream << "YZX";
154 case ZXY:
155 return stream << "ZXY";
156 case ZYX:
157 return stream << "ZYX";
158 }
159}
160
161} // namespace blender::math
#define BLI_assert(a)
Definition BLI_assert.h:50
void mul_m3_v3(const float M[3][3], float r[3])
void axis_angle_normalized_to_mat3(float R[3][3], const float axis[3], float angle)
#define UNLIKELY(x)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
T pow(const T &x, const T &power)
QuaternionBase< T > to_quaternion(const AxisAngleBase< T, AngleT > &axis_angle)
T sign(const T &a)
std::ostream & operator<<(std::ostream &stream, EulerOrder order)
AxisAngleBase< float, AngleRadianBase< float > > AxisAngle
EulerXYZBase< float > EulerXYZ
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
bool is_zero(const T &a)
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
AxisAngleBase< T, AngleT > to_axis_angle(const EulerXYZBase< T > &euler)
AxisAngleBase< float, AngleCartesianBase< float > > AxisAngleCartesian
Euler3Base< float > Euler3
bool is_unit(const VecBase< T, Size > &a, const T epsilon=T(10) *std::numeric_limits< T >::epsilon())
T abs(const T &a)
T round(const T &a)
float3 rotate_around_axis(const float3 &vector, const float3 &center, const float3 &axis, float angle)
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
VecBase< int32_t, 4 > int4
VecBase< float, 4 > float4
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
#define min(a, b)
Definition sort.c:32