2023-08-15 16:20:26 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2023-03-09 18:15:22 +01:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
* \ingroup bli
|
|
|
|
*
|
|
|
|
* Orthonormal rotation and orientation.
|
|
|
|
*
|
|
|
|
* A practical reminder:
|
|
|
|
* - Forward is typically the positive Y direction in Blender.
|
|
|
|
* - Up is typically the positive Z direction in Blender.
|
|
|
|
* - Right is typically the positive X direction in Blender.
|
|
|
|
* - Blender uses right handedness.
|
|
|
|
* - For cross product, forward = thumb, up = index, right = middle finger.
|
|
|
|
*
|
|
|
|
* The basis changes for each space:
|
|
|
|
* - Object: X-right, Y-forward, Z-up
|
|
|
|
* - World: X-right, Y-forward, Z-up
|
|
|
|
* - Armature Bone: X-right, Y-forward, Z-up (with forward being the root to tip direction)
|
|
|
|
* - Curve Tangent-Space: X-left, Y-up, Z-forward
|
|
|
|
*/
|
|
|
|
|
Cleanup: fewer iostreams related includes from BLI/BKE headers
Including <iostream> or similar headers is quite expensive, since it
also pulls in things like <locale> and so on. In many BLI headers,
iostreams are only used to implement some sort of "debug print",
or an operator<< for ostream.
Change some of the commonly used places to instead include <iosfwd>,
which is the standard way of forward-declaring iostreams related
classes, and move the actual debug-print / operator<< implementations
into .cc files.
This is not done for templated classes though (it would be possible
to provide explicit operator<< instantiations somewhere in the
source file, but that would lead to hard-to-figure-out linker error
whenever someone would add a different template type). There, where
possible, I changed from full <iostream> include to only the needed
<ostream> part.
For Span<T>, I just removed print_as_lines since it's not used by
anything. It could be moved into a .cc file using a similar approach
as above if needed.
Doing full blender build changes include counts this way:
- <iostream> 1986 -> 978
- <sstream> 2880 -> 925
It does not affect the total build time much though, mostly because
towards the end of it there's just several CPU cores finishing
compiling OpenVDB related source files.
Pull Request: https://projects.blender.org/blender/blender/pulls/111046
2023-08-11 11:27:56 +02:00
|
|
|
#include <iosfwd>
|
2023-03-24 17:42:19 +01:00
|
|
|
|
2023-03-09 18:15:22 +01:00
|
|
|
#include "BLI_math_base.hh"
|
|
|
|
#include "BLI_math_vector_types.hh"
|
|
|
|
|
|
|
|
namespace blender::math {
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Axes
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An enum class representing one of the 3 basis axes.
|
|
|
|
* This is implemented using a class to allow operators and methods.
|
|
|
|
* NOTE: While this represents a 3D axis it can still be used to generate 2D basis vectors.
|
|
|
|
*/
|
|
|
|
class Axis {
|
|
|
|
public:
|
|
|
|
enum class Value : int8_t {
|
|
|
|
/* Must start at 0. Used as indices in tables and vectors. */
|
|
|
|
X = 0,
|
|
|
|
Y,
|
|
|
|
Z,
|
|
|
|
};
|
|
|
|
|
|
|
|
constexpr static Value X = Value::X;
|
|
|
|
constexpr static Value Y = Value::Y;
|
|
|
|
constexpr static Value Z = Value::Z;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Value axis_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
Axis() = default;
|
|
|
|
|
|
|
|
constexpr Axis(const Value axis) : axis_(axis){};
|
|
|
|
|
|
|
|
/** Convert an uppercase axis character 'X', 'Y' or 'Z' to an enum value. */
|
|
|
|
constexpr explicit Axis(char axis_char) : axis_(static_cast<Value>(axis_char - 'X'))
|
|
|
|
{
|
|
|
|
BLI_assert(Value::X <= axis_ && axis_ <= Value::Z);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Allow casting from DNA enums stored as short / int. */
|
|
|
|
constexpr static Axis from_int(const int axis_int)
|
|
|
|
{
|
|
|
|
const Axis axis = static_cast<Value>(axis_int);
|
|
|
|
BLI_assert(Axis::X <= axis && axis <= Axis::Z);
|
|
|
|
return axis;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allow usage in `switch()` statements and comparisons. */
|
|
|
|
constexpr operator Value() const
|
|
|
|
{
|
|
|
|
return axis_;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr int as_int() const
|
|
|
|
{
|
|
|
|
return int(axis_);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Avoid hell. */
|
|
|
|
explicit operator bool() const = delete;
|
|
|
|
|
Cleanup: fewer iostreams related includes from BLI/BKE headers
Including <iostream> or similar headers is quite expensive, since it
also pulls in things like <locale> and so on. In many BLI headers,
iostreams are only used to implement some sort of "debug print",
or an operator<< for ostream.
Change some of the commonly used places to instead include <iosfwd>,
which is the standard way of forward-declaring iostreams related
classes, and move the actual debug-print / operator<< implementations
into .cc files.
This is not done for templated classes though (it would be possible
to provide explicit operator<< instantiations somewhere in the
source file, but that would lead to hard-to-figure-out linker error
whenever someone would add a different template type). There, where
possible, I changed from full <iostream> include to only the needed
<ostream> part.
For Span<T>, I just removed print_as_lines since it's not used by
anything. It could be moved into a .cc file using a similar approach
as above if needed.
Doing full blender build changes include counts this way:
- <iostream> 1986 -> 978
- <sstream> 2880 -> 925
It does not affect the total build time much though, mostly because
towards the end of it there's just several CPU cores finishing
compiling OpenVDB related source files.
Pull Request: https://projects.blender.org/blender/blender/pulls/111046
2023-08-11 11:27:56 +02:00
|
|
|
friend std::ostream &operator<<(std::ostream &stream, const Axis axis);
|
2023-03-09 18:15:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An enum class representing one of the 6 axis aligned direction.
|
|
|
|
* This is implemented using a class to allow operators and methods.
|
|
|
|
* NOTE: While this represents a 3D axis it can still be used to generate 2D basis vectors.
|
|
|
|
*/
|
|
|
|
class AxisSigned {
|
|
|
|
public:
|
|
|
|
enum class Value : int8_t {
|
|
|
|
/* Match #eTrackToAxis_Modes */
|
|
|
|
/* Must start at 0. Used as indices in tables and vectors. */
|
|
|
|
X_POS = 0,
|
|
|
|
Y_POS = 1,
|
|
|
|
Z_POS = 2,
|
|
|
|
X_NEG = 3,
|
|
|
|
Y_NEG = 4,
|
|
|
|
Z_NEG = 5,
|
|
|
|
};
|
|
|
|
|
|
|
|
constexpr static Value X_POS = Value::X_POS;
|
|
|
|
constexpr static Value Y_POS = Value::Y_POS;
|
|
|
|
constexpr static Value Z_POS = Value::Z_POS;
|
|
|
|
constexpr static Value X_NEG = Value::X_NEG;
|
|
|
|
constexpr static Value Y_NEG = Value::Y_NEG;
|
|
|
|
constexpr static Value Z_NEG = Value::Z_NEG;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Value axis_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
AxisSigned() = default;
|
|
|
|
|
|
|
|
constexpr AxisSigned(Value axis) : axis_(axis){};
|
|
|
|
constexpr AxisSigned(Axis axis) : axis_(from_int(axis.as_int())){};
|
|
|
|
|
|
|
|
/** Allow casting from DNA enums stored as short / int. */
|
|
|
|
constexpr static AxisSigned from_int(int axis_int)
|
|
|
|
{
|
|
|
|
const AxisSigned axis = static_cast<Value>(axis_int);
|
|
|
|
BLI_assert(AxisSigned::X_POS <= axis && axis <= AxisSigned::Z_NEG);
|
|
|
|
return axis;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the axis without the sign. It changes the type whereas abs(axis) doesn't. */
|
|
|
|
constexpr Axis axis() const
|
|
|
|
{
|
|
|
|
return Axis::from_int(this->as_int() % 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the opposing axis. */
|
|
|
|
AxisSigned operator-() const
|
|
|
|
{
|
|
|
|
return from_int((this->as_int() + 3) % 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return next enum value. */
|
|
|
|
AxisSigned next_after() const
|
|
|
|
{
|
|
|
|
return from_int((this->as_int() + 1) % 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Allow usage in `switch()` statements and comparisons. */
|
|
|
|
constexpr operator Value() const
|
|
|
|
{
|
|
|
|
return axis_;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr int as_int() const
|
|
|
|
{
|
|
|
|
return int(axis_);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns -1 if axis is negative, 1 otherwise. */
|
|
|
|
constexpr int sign() const
|
|
|
|
{
|
|
|
|
return is_negative() ? -1 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Returns true if axis is negative, false otherwise. */
|
|
|
|
constexpr bool is_negative() const
|
|
|
|
{
|
|
|
|
return int(axis_) > int(Value::Z_POS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Avoid hell. */
|
|
|
|
explicit operator bool() const = delete;
|
|
|
|
|
Cleanup: fewer iostreams related includes from BLI/BKE headers
Including <iostream> or similar headers is quite expensive, since it
also pulls in things like <locale> and so on. In many BLI headers,
iostreams are only used to implement some sort of "debug print",
or an operator<< for ostream.
Change some of the commonly used places to instead include <iosfwd>,
which is the standard way of forward-declaring iostreams related
classes, and move the actual debug-print / operator<< implementations
into .cc files.
This is not done for templated classes though (it would be possible
to provide explicit operator<< instantiations somewhere in the
source file, but that would lead to hard-to-figure-out linker error
whenever someone would add a different template type). There, where
possible, I changed from full <iostream> include to only the needed
<ostream> part.
For Span<T>, I just removed print_as_lines since it's not used by
anything. It could be moved into a .cc file using a similar approach
as above if needed.
Doing full blender build changes include counts this way:
- <iostream> 1986 -> 978
- <sstream> 2880 -> 925
It does not affect the total build time much though, mostly because
towards the end of it there's just several CPU cores finishing
compiling OpenVDB related source files.
Pull Request: https://projects.blender.org/blender/blender/pulls/111046
2023-08-11 11:27:56 +02:00
|
|
|
friend std::ostream &operator<<(std::ostream &stream, const AxisSigned axis);
|
2023-03-09 18:15:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
constexpr static bool operator<=(const Axis::Value a, const Axis::Value b)
|
|
|
|
{
|
|
|
|
return int(a) <= int(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr static bool operator<=(const AxisSigned::Value a, const AxisSigned::Value b)
|
|
|
|
{
|
|
|
|
return int(a) <= int(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
2023-04-19 00:40:23 +02:00
|
|
|
/** \name Axes Utilities
|
2023-03-09 18:15:22 +01:00
|
|
|
* \{ */
|
|
|
|
|
|
|
|
template<> inline AxisSigned abs(const AxisSigned &axis)
|
|
|
|
{
|
|
|
|
return axis.axis();
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] inline int sign(const AxisSigned &axis)
|
|
|
|
{
|
|
|
|
return axis.sign();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the cross direction from two basis direction using the right hand rule.
|
|
|
|
* This is much faster than true cross product if the vectors are basis vectors.
|
|
|
|
* Any ill-formed case will return a orthogonal axis to \a a but will also trigger an assert. It is
|
|
|
|
* better to filter these cases upstream.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline AxisSigned cross(const AxisSigned a, const AxisSigned b)
|
|
|
|
{
|
|
|
|
BLI_assert_msg(abs(a) != abs(b), "Axes must not be colinear.");
|
|
|
|
switch (a) {
|
|
|
|
case AxisSigned::X_POS:
|
|
|
|
switch (b) {
|
|
|
|
case AxisSigned::X_POS:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Y_POS:
|
|
|
|
return AxisSigned::Z_POS;
|
|
|
|
case AxisSigned::Z_POS:
|
|
|
|
return AxisSigned::Y_NEG;
|
|
|
|
case AxisSigned::X_NEG:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Y_NEG:
|
|
|
|
return AxisSigned::Z_NEG;
|
|
|
|
case AxisSigned::Z_NEG:
|
|
|
|
return AxisSigned::Y_POS;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AxisSigned::Y_POS:
|
|
|
|
switch (b) {
|
|
|
|
case AxisSigned::X_POS:
|
|
|
|
return AxisSigned::Z_NEG;
|
|
|
|
case AxisSigned::Y_POS:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Z_POS:
|
|
|
|
return AxisSigned::X_POS;
|
|
|
|
case AxisSigned::X_NEG:
|
|
|
|
return AxisSigned::Z_POS;
|
|
|
|
case AxisSigned::Y_NEG:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Z_NEG:
|
|
|
|
return AxisSigned::X_NEG;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AxisSigned::Z_POS:
|
|
|
|
switch (b) {
|
|
|
|
case AxisSigned::X_POS:
|
|
|
|
return AxisSigned::Y_POS;
|
|
|
|
case AxisSigned::Y_POS:
|
|
|
|
return AxisSigned::X_NEG;
|
|
|
|
case AxisSigned::Z_POS:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::X_NEG:
|
|
|
|
return AxisSigned::Y_NEG;
|
|
|
|
case AxisSigned::Y_NEG:
|
|
|
|
return AxisSigned::X_POS;
|
|
|
|
case AxisSigned::Z_NEG:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AxisSigned::X_NEG:
|
|
|
|
switch (b) {
|
|
|
|
case AxisSigned::X_POS:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Y_POS:
|
|
|
|
return AxisSigned::Z_NEG;
|
|
|
|
case AxisSigned::Z_POS:
|
|
|
|
return AxisSigned::Y_POS;
|
|
|
|
case AxisSigned::X_NEG:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Y_NEG:
|
|
|
|
return AxisSigned::Z_POS;
|
|
|
|
case AxisSigned::Z_NEG:
|
|
|
|
return AxisSigned::Y_NEG;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AxisSigned::Y_NEG:
|
|
|
|
switch (b) {
|
|
|
|
case AxisSigned::X_POS:
|
|
|
|
return AxisSigned::Z_POS;
|
|
|
|
case AxisSigned::Y_POS:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Z_POS:
|
|
|
|
return AxisSigned::X_NEG;
|
|
|
|
case AxisSigned::X_NEG:
|
|
|
|
return AxisSigned::Z_NEG;
|
|
|
|
case AxisSigned::Y_NEG:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::Z_NEG:
|
|
|
|
return AxisSigned::X_POS;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AxisSigned::Z_NEG:
|
|
|
|
switch (b) {
|
|
|
|
case AxisSigned::X_POS:
|
|
|
|
return AxisSigned::Y_NEG;
|
|
|
|
case AxisSigned::Y_POS:
|
|
|
|
return AxisSigned::X_POS;
|
|
|
|
case AxisSigned::Z_POS:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
case AxisSigned::X_NEG:
|
|
|
|
return AxisSigned::Y_POS;
|
|
|
|
case AxisSigned::Y_NEG:
|
|
|
|
return AxisSigned::X_NEG;
|
|
|
|
case AxisSigned::Z_NEG:
|
|
|
|
break; /* Ill-defined. */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return a.next_after();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Create basis vector. */
|
|
|
|
template<typename T> T to_vector(const Axis axis)
|
|
|
|
{
|
|
|
|
BLI_assert(axis <= AxisSigned::from_int(T::type_length - 1));
|
|
|
|
T vec{};
|
|
|
|
vec[axis] = 1;
|
|
|
|
return vec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Create signed basis vector. */
|
|
|
|
template<typename T> T to_vector(const AxisSigned axis)
|
|
|
|
{
|
|
|
|
BLI_assert(abs(axis) <= AxisSigned::from_int(T::type_length - 1));
|
|
|
|
T vec{};
|
|
|
|
vec[abs(axis).as_int()] = axis.is_negative() ? -1 : 1;
|
|
|
|
return vec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name CartesianBasis
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An `blender::math::CartesianBasis` represents an orientation that is aligned with the basis
|
|
|
|
* axes. This type of rotation is fast, precise and adds more meaning to the code that uses it.
|
|
|
|
*/
|
|
|
|
struct CartesianBasis {
|
|
|
|
VecBase<AxisSigned, 3> axes = {AxisSigned::X_POS, AxisSigned::Y_POS, AxisSigned::Z_POS};
|
|
|
|
|
|
|
|
CartesianBasis() = default;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an arbitrary basis orientation.
|
|
|
|
* Handedness can be flipped but an axis cannot be present twice.
|
|
|
|
*/
|
|
|
|
CartesianBasis(const AxisSigned x, const AxisSigned y, const AxisSigned z) : axes(x, y, z)
|
|
|
|
{
|
|
|
|
BLI_assert(abs(x) != abs(y));
|
|
|
|
BLI_assert(abs(y) != abs(z));
|
|
|
|
BLI_assert(abs(z) != abs(x));
|
|
|
|
}
|
|
|
|
|
|
|
|
const AxisSigned &x() const
|
|
|
|
{
|
|
|
|
return axes.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AxisSigned &y() const
|
|
|
|
{
|
|
|
|
return axes.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AxisSigned &z() const
|
|
|
|
{
|
|
|
|
return axes.z;
|
|
|
|
}
|
|
|
|
|
|
|
|
AxisSigned &x()
|
|
|
|
{
|
|
|
|
return axes.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
AxisSigned &y()
|
|
|
|
{
|
|
|
|
return axes.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
AxisSigned &z()
|
|
|
|
{
|
|
|
|
return axes.z;
|
|
|
|
}
|
|
|
|
|
Cleanup: fewer iostreams related includes from BLI/BKE headers
Including <iostream> or similar headers is quite expensive, since it
also pulls in things like <locale> and so on. In many BLI headers,
iostreams are only used to implement some sort of "debug print",
or an operator<< for ostream.
Change some of the commonly used places to instead include <iosfwd>,
which is the standard way of forward-declaring iostreams related
classes, and move the actual debug-print / operator<< implementations
into .cc files.
This is not done for templated classes though (it would be possible
to provide explicit operator<< instantiations somewhere in the
source file, but that would lead to hard-to-figure-out linker error
whenever someone would add a different template type). There, where
possible, I changed from full <iostream> include to only the needed
<ostream> part.
For Span<T>, I just removed print_as_lines since it's not used by
anything. It could be moved into a .cc file using a similar approach
as above if needed.
Doing full blender build changes include counts this way:
- <iostream> 1986 -> 978
- <sstream> 2880 -> 925
It does not affect the total build time much though, mostly because
towards the end of it there's just several CPU cores finishing
compiling OpenVDB related source files.
Pull Request: https://projects.blender.org/blender/blender/pulls/111046
2023-08-11 11:27:56 +02:00
|
|
|
friend std::ostream &operator<<(std::ostream &stream, const CartesianBasis &rot);
|
2023-03-09 18:15:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2023-03-19 21:08:57 +01:00
|
|
|
* Create an CartesianBasis using two orthogonal axes.
|
2023-03-09 18:15:22 +01:00
|
|
|
* The third axis is chosen by right hand rule to follow blender coordinate system.
|
|
|
|
* \a forward is Y axis in blender coordinate system.
|
|
|
|
* \a up is Z axis in blender coordinate system.
|
|
|
|
* \note \a forward and \a up must be different axes.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline CartesianBasis from_orthonormal_axes(const AxisSigned forward,
|
|
|
|
const AxisSigned up)
|
|
|
|
{
|
|
|
|
BLI_assert(math::abs(forward) != math::abs(up));
|
|
|
|
return {cross(forward, up), forward, up};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an CartesianBasis for converting from \a a orientation to \a b orientation.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline CartesianBasis rotation_between(const CartesianBasis &a,
|
|
|
|
const CartesianBasis &b)
|
|
|
|
{
|
|
|
|
CartesianBasis basis;
|
|
|
|
basis.axes[abs(b.x()).as_int()] = (sign(b.x()) != sign(a.x())) ? -abs(a.x()) : abs(a.x());
|
|
|
|
basis.axes[abs(b.y()).as_int()] = (sign(b.y()) != sign(a.y())) ? -abs(a.y()) : abs(a.y());
|
|
|
|
basis.axes[abs(b.z()).as_int()] = (sign(b.z()) != sign(a.z())) ? -abs(a.z()) : abs(a.z());
|
|
|
|
return basis;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an CartesianBasis for converting from an \a a orientation defined only by its forward
|
|
|
|
* vector to a \a b orientation defined only by its forward vector.
|
|
|
|
* Rotation is given to be non flipped and deterministic.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline CartesianBasis rotation_between(const AxisSigned a_forward,
|
|
|
|
const AxisSigned b_forward)
|
|
|
|
{
|
|
|
|
/* Pick predictable next axis. */
|
2023-03-09 23:33:20 +01:00
|
|
|
AxisSigned a_up = abs(a_forward.next_after());
|
|
|
|
AxisSigned b_up = abs(b_forward.next_after());
|
2023-03-09 18:15:22 +01:00
|
|
|
|
|
|
|
if (sign(a_forward) != sign(b_forward)) {
|
|
|
|
/* Flip both axis (up and right) so resulting rotation matrix sign remains positive. */
|
|
|
|
b_up = -b_up;
|
|
|
|
}
|
|
|
|
return rotation_between(from_orthonormal_axes(a_forward, a_up),
|
|
|
|
from_orthonormal_axes(b_forward, b_up));
|
|
|
|
}
|
|
|
|
|
2023-04-01 12:00:33 +02:00
|
|
|
template<typename T>
|
|
|
|
[[nodiscard]] inline VecBase<T, 3> transform_point(const CartesianBasis &basis,
|
|
|
|
const VecBase<T, 3> &v)
|
|
|
|
{
|
|
|
|
VecBase<T, 3> result;
|
|
|
|
result[basis.x().axis().as_int()] = basis.x().is_negative() ? -v[0] : v[0];
|
|
|
|
result[basis.y().axis().as_int()] = basis.y().is_negative() ? -v[1] : v[1];
|
|
|
|
result[basis.z().axis().as_int()] = basis.z().is_negative() ? -v[2] : v[2];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the inverse transformation represented by the given basis.
|
|
|
|
* This is conceptually the equivalent to a rotation matrix transpose, but much faster.
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline CartesianBasis invert(const CartesianBasis &basis)
|
|
|
|
{
|
|
|
|
/* Returns the column where the `axis` is found in. The sign is taken from the axis value. */
|
|
|
|
auto search_axis = [](const CartesianBasis &basis, const Axis axis) {
|
|
|
|
if (basis.x().axis() == axis) {
|
|
|
|
return basis.x().is_negative() ? AxisSigned::X_NEG : AxisSigned::X_POS;
|
|
|
|
}
|
|
|
|
if (basis.y().axis() == axis) {
|
|
|
|
return basis.y().is_negative() ? AxisSigned::Y_NEG : AxisSigned::Y_POS;
|
|
|
|
}
|
|
|
|
return basis.z().is_negative() ? AxisSigned::Z_NEG : AxisSigned::Z_POS;
|
|
|
|
};
|
|
|
|
CartesianBasis result;
|
|
|
|
result.x() = search_axis(basis, Axis::X);
|
|
|
|
result.y() = search_axis(basis, Axis::Y);
|
|
|
|
result.z() = search_axis(basis, Axis::Z);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-03-09 18:15:22 +01:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
} // namespace blender::math
|