tornavis/source/blender/blenlib/BLI_math_matrix.h

644 lines
26 KiB
C

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*/
#include "BLI_compiler_attrs.h"
#include "BLI_sys_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/* -------------------------------------------------------------------- */
/** \name Init
* \{ */
void zero_m2(float m[2][2]);
void zero_m3(float m[3][3]);
void zero_m4(float m[4][4]);
void unit_m2(float m[2][2]);
void unit_m3(float m[3][3]);
void unit_m4(float m[4][4]);
void unit_m4_db(double m[4][4]);
void copy_m2_m2(float m1[2][2], const float m2[2][2]);
void copy_m3_m3(float m1[3][3], const float m2[3][3]);
void copy_m4_m4(float m1[4][4], const float m2[4][4]);
void copy_m3_m4(float m1[3][3], const float m2[4][4]);
void copy_m4_m3(float m1[4][4], const float m2[3][3]);
void copy_m3_m2(float m1[3][3], const float m2[2][2]);
void copy_m4_m2(float m1[4][4], const float m2[2][2]);
void copy_m4_m4_db(double m1[4][4], const double m2[4][4]);
/* double->float */
void copy_m3_m3d(float m1[3][3], const double m2[3][3]);
/* float->double */
void copy_m3d_m3(double m1[3][3], const float m2[3][3]);
void copy_m4d_m4(double m1[4][4], const float m2[4][4]);
void swap_m3m3(float m1[3][3], float m2[3][3]);
void swap_m4m4(float m1[4][4], float m2[4][4]);
/** Build index shuffle matrix. */
void shuffle_m4(float R[4][4], const int index[4]);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Arithmetic
* \{ */
void add_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3]);
void add_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4]);
void madd_m3_m3m3fl(float R[3][3], const float A[3][3], const float B[3][3], float f);
void madd_m4_m4m4fl(float R[4][4], const float A[4][4], const float B[4][4], float f);
void sub_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3]);
void sub_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4]);
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3]);
void mul_m4_m3m4(float R[4][4], const float A[3][3], const float B[4][4]);
void mul_m4_m4m3(float R[4][4], const float A[4][4], const float B[3][3]);
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4]);
/**
* `R = A * B`, ignore the elements on the 4th row/column of A.
*/
void mul_m3_m3m4(float R[3][3], const float A[3][3], const float B[4][4]);
/**
* `R = A * B`, ignore the elements on the 4th row/column of B.
*/
void mul_m3_m4m3(float R[3][3], const float A[4][4], const float B[3][3]);
void mul_m3_m4m4(float R[3][3], const float A[4][4], const float B[4][4]);
/**
* Special matrix multiplies
* - pre: `R <-- AR`
* - post: `R <-- RB`.
*/
void mul_m3_m3_pre(float R[3][3], const float A[3][3]);
void mul_m3_m3_post(float R[3][3], const float B[3][3]);
void mul_m4db_m4db_m4fl(double R[4][4], const double A[4][4], const float B[4][4]);
void mul_m4_m4_pre(float R[4][4], const float A[4][4]);
void mul_m4_m4_post(float R[4][4], const float B[4][4]);
/* Implement #mul_m3_series macro. */
void _va_mul_m3_series_3(float r[3][3], const float m1[3][3], const float m2[3][3]) ATTR_NONNULL();
void _va_mul_m3_series_4(float r[3][3],
const float m1[3][3],
const float m2[3][3],
const float m3[3][3]) ATTR_NONNULL();
void _va_mul_m3_series_5(float r[3][3],
const float m1[3][3],
const float m2[3][3],
const float m3[3][3],
const float m4[3][3]) ATTR_NONNULL();
void _va_mul_m3_series_6(float r[3][3],
const float m1[3][3],
const float m2[3][3],
const float m3[3][3],
const float m4[3][3],
const float m5[3][3]) ATTR_NONNULL();
void _va_mul_m3_series_7(float r[3][3],
const float m1[3][3],
const float m2[3][3],
const float m3[3][3],
const float m4[3][3],
const float m5[3][3],
const float m6[3][3]) ATTR_NONNULL();
void _va_mul_m3_series_8(float r[3][3],
const float m1[3][3],
const float m2[3][3],
const float m3[3][3],
const float m4[3][3],
const float m5[3][3],
const float m6[3][3],
const float m7[3][3]) ATTR_NONNULL();
void _va_mul_m3_series_9(float r[3][3],
const float m1[3][3],
const float m2[3][3],
const float m3[3][3],
const float m4[3][3],
const float m5[3][3],
const float m6[3][3],
const float m7[3][3],
const float m8[3][3]) ATTR_NONNULL();
/* Implement #mul_m4_series macro. */
void _va_mul_m4_series_3(float r[4][4], const float m1[4][4], const float m2[4][4]) ATTR_NONNULL();
void _va_mul_m4_series_4(float r[4][4],
const float m1[4][4],
const float m2[4][4],
const float m3[4][4]) ATTR_NONNULL();
void _va_mul_m4_series_5(float r[4][4],
const float m1[4][4],
const float m2[4][4],
const float m3[4][4],
const float m4[4][4]) ATTR_NONNULL();
void _va_mul_m4_series_6(float r[4][4],
const float m1[4][4],
const float m2[4][4],
const float m3[4][4],
const float m4[4][4],
const float m5[4][4]) ATTR_NONNULL();
void _va_mul_m4_series_7(float r[4][4],
const float m1[4][4],
const float m2[4][4],
const float m3[4][4],
const float m4[4][4],
const float m5[4][4],
const float m6[4][4]) ATTR_NONNULL();
void _va_mul_m4_series_8(float r[4][4],
const float m1[4][4],
const float m2[4][4],
const float m3[4][4],
const float m4[4][4],
const float m5[4][4],
const float m6[4][4],
const float m7[4][4]) ATTR_NONNULL();
void _va_mul_m4_series_9(float r[4][4],
const float m1[4][4],
const float m2[4][4],
const float m3[4][4],
const float m4[4][4],
const float m5[4][4],
const float m6[4][4],
const float m7[4][4],
const float m8[4][4]) ATTR_NONNULL();
#define mul_m3_series(...) VA_NARGS_CALL_OVERLOAD(_va_mul_m3_series_, __VA_ARGS__)
#define mul_m4_series(...) VA_NARGS_CALL_OVERLOAD(_va_mul_m4_series_, __VA_ARGS__)
void mul_m4_v3(const float M[4][4], float r[3]);
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3]);
void mul_v3_m4v3_db(double r[3], const double mat[4][4], const double vec[3]);
void mul_v4_m4v3_db(double r[4], const double mat[4][4], const double vec[3]);
void mul_v2_m4v3(float r[2], const float mat[4][4], const float vec[3]);
void mul_v2_m2v2(float r[2], const float mat[2][2], const float vec[2]);
void mul_m2_v2(const float mat[2][2], float vec[2]);
/** Same as #mul_m4_v3() but doesn't apply translation component. */
void mul_mat3_m4_v3(const float mat[4][4], float r[3]);
void mul_v3_mat3_m4v3(float r[3], const float mat[4][4], const float vec[3]);
void mul_v3_mat3_m4v3_db(double r[3], const double mat[4][4], const double vec[3]);
void mul_m4_v4(const float mat[4][4], float r[4]);
void mul_v4_m4v4(float r[4], const float mat[4][4], const float v[4]);
void mul_v4_m4v3(float r[4], const float M[4][4], const float v[3]); /* v has implicit w = 1.0f */
void mul_project_m4_v3(const float mat[4][4], float vec[3]);
void mul_v3_project_m4_v3(float r[3], const float mat[4][4], const float vec[3]);
void mul_v2_project_m4_v3(float r[2], const float mat[4][4], const float vec[3]);
void mul_m3_v2(const float m[3][3], float r[2]);
void mul_v2_m3v2(float r[2], const float m[3][3], const float v[2]);
void mul_m3_v3(const float M[3][3], float r[3]);
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3]);
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3]);
void mul_transposed_m3_v3(const float M[3][3], float r[3]);
void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3]);
void mul_m3_v3_double(const float M[3][3], double r[3]);
/**
* Combines transformations, handling scale separately in a manner equivalent
* to the Aligned Inherit Scale mode, in order to avoid creating shear.
* If A scale is uniform, the result is equivalent to ordinary multiplication.
*
* NOTE: this effectively takes output location from simple multiplication,
* and uses mul_m4_m4m4_split_channels for rotation and scale.
*/
void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4]);
/**
* Separately combines location, rotation and scale of the input matrices.
*/
void mul_m4_m4m4_split_channels(float R[4][4], const float A[4][4], const float B[4][4]);
void mul_m3_fl(float R[3][3], float f);
void mul_m4_fl(float R[4][4], float f);
void mul_mat3_m4_fl(float R[4][4], float f);
void negate_m3(float R[3][3]);
void negate_mat3_m4(float R[4][4]);
void negate_m4(float R[4][4]);
bool invert_m3_ex(float mat[3][3], float epsilon);
bool invert_m3_m3_ex(float inverse[3][3], const float mat[3][3], float epsilon);
bool invert_m3(float mat[3][3]);
bool invert_m2_m2(float inverse[2][2], const float mat[2][2]);
bool invert_m3_m3(float inverse[3][3], const float mat[3][3]);
bool invert_m4(float mat[4][4]);
bool invert_m4_m4(float inverse[4][4], const float mat[4][4]);
/**
* Computes the inverse of mat and puts it in inverse.
* Uses Gaussian Elimination with partial (maximal column) pivoting.
* \return true on success (i.e. can always find a pivot) and false on failure.
* Mark Segal - 1992.
*
* \note this has worse performance than #EIG_invert_m4_m4 (Eigen), but e.g.
* for non-invertible scale matrices, finding a partial solution can
* be useful to have a valid local transform center, see #57767.
*/
bool invert_m4_m4_fallback(float inverse[4][4], const float mat[4][4]);
/* Double arithmetic (mixed float/double). */
void mul_m4_v4d(const float mat[4][4], double r[4]);
void mul_v4d_m4v4d(double r[4], const float mat[4][4], const double v[4]);
/* Double matrix functions (no mixing types). */
void mul_v3_m3v3_db(double r[3], const double M[3][3], const double a[3]);
void mul_m3_v3_db(const double M[3][3], double r[3]);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Linear Algebra
* \{ */
void transpose_m3(float R[3][3]);
void transpose_m3_m3(float R[3][3], const float M[3][3]);
/**
* \note Seems obscure but in-fact a common operation.
*/
void transpose_m3_m4(float R[3][3], const float M[4][4]);
void transpose_m4(float R[4][4]);
void transpose_m4_m4(float R[4][4], const float M[4][4]);
bool compare_m4m4(const float mat1[4][4], const float mat2[4][4], float limit);
void normalize_m2_ex(float R[2][2], float r_scale[2]) ATTR_NONNULL();
void normalize_m2(float R[2][2]) ATTR_NONNULL();
void normalize_m2_m2_ex(float R[2][2], const float M[2][2], float r_scale[2]) ATTR_NONNULL();
void normalize_m2_m2(float R[2][2], const float M[2][2]) ATTR_NONNULL();
void normalize_m3_ex(float R[3][3], float r_scale[3]) ATTR_NONNULL();
void normalize_m3(float R[3][3]) ATTR_NONNULL();
void normalize_m3_m3_ex(float R[3][3], const float M[3][3], float r_scale[3]) ATTR_NONNULL();
void normalize_m3_m3(float R[3][3], const float M[3][3]) ATTR_NONNULL();
void normalize_m4_ex(float R[4][4], float r_scale[3]) ATTR_NONNULL();
void normalize_m4(float R[4][4]) ATTR_NONNULL();
void normalize_m4_m4_ex(float rmat[4][4], const float mat[4][4], float r_scale[3]) ATTR_NONNULL();
void normalize_m4_m4(float rmat[4][4], const float mat[4][4]) ATTR_NONNULL();
/**
* Make an orthonormal matrix around the selected axis of the given matrix.
*
* \param axis: Axis to build the orthonormal basis around.
*/
void orthogonalize_m3(float R[3][3], int axis);
/**
* Make an orthonormal matrix around the selected axis of the given matrix.
*
* \param axis: Axis to build the orthonormal basis around.
*/
void orthogonalize_m4(float R[4][4], int axis);
/**
* Make an orthonormal matrix around the selected axis of the given matrix,
* in a way that is symmetric and stable to variations in the input, and
* preserving the value of the determinant, i.e. the overall volume change.
*
* \param axis: Axis to build the orthonormal basis around.
* \param normalize: Normalize the matrix instead of preserving volume.
*/
void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize);
/**
* Make an orthonormal matrix around the selected axis of the given matrix,
* in a way that is symmetric and stable to variations in the input, and
* preserving the value of the determinant, i.e. the overall volume change.
*
* \param axis: Axis to build the orthonormal basis around.
* \param normalize: Normalize the matrix instead of preserving volume.
*/
void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize);
bool orthogonalize_m3_zero_axes(float m[3][3], float unit_length);
bool orthogonalize_m4_zero_axes(float m[4][4], float unit_length);
bool is_orthogonal_m3(const float m[3][3]);
bool is_orthogonal_m4(const float m[4][4]);
bool is_orthonormal_m3(const float m[3][3]);
bool is_orthonormal_m4(const float m[4][4]);
bool is_uniform_scaled_m3(const float m[3][3]);
bool is_uniform_scaled_m4(const float m[4][4]);
/* NOTE: 'adjoint' here means the adjugate (adjunct, "classical adjoint") matrix!
* Nowadays 'adjoint' usually refers to the conjugate transpose,
* which for real-valued matrices is simply the transpose. */
void adjoint_m2_m2(float R[2][2], const float M[2][2]);
void adjoint_m3_m3(float R[3][3], const float M[3][3]);
void adjoint_m4_m4(float R[4][4], const float M[4][4]);
float determinant_m2(float a, float b, float c, float d);
float determinant_m3(
float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3);
float determinant_m3_array(const float m[3][3]);
float determinant_m4_mat3_array(const float m[4][4]);
double determinant_m3_array_db(const double m[3][3]);
float determinant_m4(const float m[4][4]);
#define PSEUDOINVERSE_EPSILON 1e-8f
/**
* Compute the Single Value Decomposition of an arbitrary matrix A
* That is compute the 3 matrices U,W,V with U column orthogonal (m,n)
* ,W a diagonal matrix and V an orthogonal square matrix `s.t.A = U.W.Vt`.
* From this decomposition it is trivial to compute the (pseudo-inverse)
* of `A` as `Ainv = V.Winv.transpose(U)`.
*/
void svd_m4(float U[4][4], float s[4], float V[4][4], float A_[4][4]);
void pseudoinverse_m4_m4(float inverse[4][4], const float mat[4][4], float epsilon);
void pseudoinverse_m3_m3(float inverse[3][3], const float mat[3][3], float epsilon);
bool has_zero_axis_m4(const float matrix[4][4]);
/** Fix any zero scale axis adding a small bias orthogonal to the other valid axis. */
void zero_axis_bias_m4(float mat[4][4]);
void invert_m4_m4_safe(float inverse[4][4], const float mat[4][4]);
void invert_m3_m3_safe_ortho(float inverse[3][3], const float mat[3][3]);
/**
* A safe version of invert that uses valid axes, calculating the zero'd axis
* based on the non-zero ones.
*
* This works well for transformation matrices, when a single axis is zeroed.
*/
void invert_m4_m4_safe_ortho(float inverse[4][4], const float mat[4][4]);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Transformations
* \{ */
void scale_m3_fl(float R[3][3], float scale);
void scale_m4_fl(float R[4][4], float scale);
void scale_m4_v2(float R[4][4], const float scale[2]);
/**
* This computes the overall volume scale factor of a transformation matrix.
* For an orthogonal matrix, it is the product of all three scale values.
* Returns a negative value if the transform is flipped by negative scale.
*/
float mat3_to_volume_scale(const float mat[3][3]);
float mat4_to_volume_scale(const float mat[4][4]);
/**
* This gets the average scale of a matrix, only use when your scaling
* data that has no idea of scale axis, examples are bone-envelope-radius
* and curve radius.
*/
float mat3_to_scale(const float mat[3][3]);
float mat4_to_scale(const float mat[4][4]);
/** Return 2D scale (in XY plane) of given mat4. */
float mat4_to_xy_scale(const float mat[4][4]);
void size_to_mat3(float R[3][3], const float size[3]);
void size_to_mat4(float R[4][4], const float size[3]);
/** Return 2D size assuming the given matrix is a 2D affine matrix. */
void mat3_to_size_2d(float size[2], const float M[3][3]);
void mat3_to_size(float size[3], const float M[3][3]);
void mat4_to_size(float size[3], const float M[4][4]);
/**
* Return the largest scale on any axis, the equivalent of calling:
* \code{.c}
* mat3_to_size(size_v3, mat);
* size = size_v3[max_axis_v3(size_v3)];
* \endcode
* .. without 2x unnecessary `sqrtf` calls.
*/
float mat3_to_size_max_axis(const float M[3][3]);
/**
* Only the first 3 axes are used.
*/
float mat4_to_size_max_axis(const float M[4][4]);
/**
* Extract scale factors from the matrix, with correction to ensure
* exact volume in case of a sheared matrix.
*/
void mat4_to_size_fix_shear(float size[3], const float M[4][4]);
void translate_m4(float mat[4][4], float Tx, float Ty, float Tz);
/**
* Rotate a matrix in-place.
*
* \note To create a new rotation matrix see:
* #axis_angle_to_mat4_single, #axis_angle_to_mat3_single, #angle_to_mat2
* (axis & angle args are compatible).
*/
void rotate_m4(float mat[4][4], char axis, float angle);
/** Scale a matrix in-place. */
void rescale_m4(float mat[4][4], const float scale[3]);
/**
* Scale or rotate around a pivot point,
* a convenience function to avoid having to do inline.
*
* Since its common to make a scale/rotation matrix that pivots around an arbitrary point.
*
* Typical use case is to make 3x3 matrix, copy to 4x4, then pass to this function.
*/
void transform_pivot_set_m4(float mat[4][4], const float pivot[3]);
/**
* \param rot: A 3x3 rotation matrix, normalized never negative.
*/
void mat4_to_rot(float rot[3][3], const float wmat[4][4]);
/**
* \param rot: A 3x3 rotation matrix, normalized never negative.
* \param size: The scale, negative if `mat3` is negative.
*/
void mat3_to_rot_size(float rot[3][3], float size[3], const float mat3[3][3]);
/**
* \param rot: A 3x3 rotation matrix, normalized never negative.
* \param size: The scale, negative if `mat3` is negative.
*/
void mat4_to_loc_rot_size(float loc[3], float rot[3][3], float size[3], const float wmat[4][4]);
void mat4_to_loc_quat(float loc[3], float quat[4], const float wmat[4][4]);
void mat4_decompose(float loc[3], float quat[4], float size[3], const float wmat[4][4]);
void mat3_polar_decompose(const float mat3[3][3], float r_U[3][3], float r_P[3][3]);
/**
* Make a 4x4 matrix out of 3 transform components.
* Matrices are made in the order: `scale * rot * loc`
*/
void loc_rot_size_to_mat4(float R[4][4],
const float loc[3],
const float rot[3][3],
const float size[3]);
/**
* Make a 4x4 matrix out of 3 transform components.
* Matrices are made in the order: `scale * rot * loc`
*
* TODO: need to have a version that allows for rotation order.
*/
void loc_eul_size_to_mat4(float R[4][4],
const float loc[3],
const float eul[3],
const float size[3]);
/**
* Make a 4x4 matrix out of 3 transform components.
* Matrices are made in the order: `scale * rot * loc`
*/
void loc_eulO_size_to_mat4(
float R[4][4], const float loc[3], const float eul[3], const float size[3], short order);
/**
* Make a 4x4 matrix out of 3 transform components.
* Matrices are made in the order: `scale * rot * loc`
*/
void loc_quat_size_to_mat4(float R[4][4],
const float loc[3],
const float quat[4],
const float size[3]);
void loc_axisangle_size_to_mat4(
float R[4][4], const float loc[3], const float axis[3], float angle, const float size[3]);
void blend_m3_m3m3(float out[3][3], const float dst[3][3], const float src[3][3], float srcweight);
void blend_m4_m4m4(float out[4][4], const float dst[4][4], const float src[4][4], float srcweight);
/**
* A polar-decomposition-based interpolation between matrix A and matrix B.
*
* \note This code is about five times slower as the 'naive' interpolation done by #blend_m3_m3m3
* (it typically remains below 2 usec on an average i74700,
* while #blend_m3_m3m3 remains below 0.4 usec).
* However, it gives expected results even with non-uniformly scaled matrices,
* see #46418 for an example.
*
* Based on "Matrix Animation and Polar Decomposition", by Ken Shoemake & Tom Duff
*
* \param R: Resulting interpolated matrix.
* \param A: Input matrix which is totally effective with `t = 0.0`.
* \param B: Input matrix which is totally effective with `t = 1.0`.
* \param t: Interpolation factor.
*/
void interp_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3], float t);
/**
* Complete transform matrix interpolation,
* based on polar-decomposition-based interpolation from #interp_m3_m3m3.
*
* \param R: Resulting interpolated matrix.
* \param A: Input matrix which is totally effective with `t = 0.0`.
* \param B: Input matrix which is totally effective with `t = 1.0`.
* \param t: Interpolation factor.
*/
void interp_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4], float t);
/**
* Return true when the matrices determinant is less than zero.
*
* \note This is often used to check if a matrix flips content in 3D space,
* where transforming geometry (for example) would flip the direction of polygon normals
* from pointing outside a closed volume, to pointing inside (or the reverse).
*
* When the matrix is constructed from location, rotation & scale
* as matrix will be negative when it has an odd number of negative scales.
*/
bool is_negative_m3(const float mat[3][3]);
/** A version of #is_negative_m3 that takes a 4x4 matrix. */
bool is_negative_m4(const float mat[4][4]);
bool is_zero_m3(const float mat[3][3]);
bool is_zero_m4(const float mat[4][4]);
bool equals_m3m3(const float mat1[3][3], const float mat2[3][3]);
bool equals_m4m4(const float mat1[4][4], const float mat2[4][4]);
/**
* #SpaceTransform struct encapsulates all needed data to convert between two coordinate spaces
* (where conversion can be represented by a matrix multiplication).
*
* A #SpaceTransform is initialized using:
* - #BLI_SPACE_TRANSFORM_SETUP(&data, ob1, ob2)
*
* After that the following calls can be used:
* - Converts a coordinate in ob1 space to the corresponding ob2 space:
* #BLI_space_transform_apply(&data, co);
* - Converts a coordinate in ob2 space to the corresponding ob1 space:
* #BLI_space_transform_invert(&data, co);
*
* Same concept as #BLI_space_transform_apply and #BLI_space_transform_invert,
* but no is normalized after conversion (and not translated at all!):
* - #BLI_space_transform_apply_normal(&data, no);
* - #BLI_space_transform_invert_normal(&data, no);
*/
typedef struct SpaceTransform {
float local2target[4][4];
float target2local[4][4];
} SpaceTransform;
/**
* Global-invariant transform.
*
* This defines a matrix transforming a point in local space to a point in target space
* such that its global coordinates remain unchanged.
*
* In other words, if we have a global point P with local coordinates (x, y, z)
* and global coordinates (X, Y, Z),
* this defines a transform matrix TM such that (x', y', z') = TM * (x, y, z)
* where (x', y', z') are the coordinates of P' in target space
* such that it keeps (X, Y, Z) coordinates in global space.
*/
void BLI_space_transform_from_matrices(struct SpaceTransform *data,
const float local[4][4],
const float target[4][4]);
/**
* Local-invariant transform.
*
* This defines a matrix transforming a point in global space
* such that its local coordinates (from local space to target space) remain unchanged.
*
* In other words, if we have a local point p with local coordinates (x, y, z)
* and global coordinates (X, Y, Z),
* this defines a transform matrix TM such that (X', Y', Z') = TM * (X, Y, Z)
* where (X', Y', Z') are the coordinates of p' in global space
* such that it keeps (x, y, z) coordinates in target space.
*/
void BLI_space_transform_global_from_matrices(struct SpaceTransform *data,
const float local[4][4],
const float target[4][4]);
void BLI_space_transform_apply(const struct SpaceTransform *data, float co[3]);
void BLI_space_transform_invert(const struct SpaceTransform *data, float co[3]);
void BLI_space_transform_apply_normal(const struct SpaceTransform *data, float no[3]);
void BLI_space_transform_invert_normal(const struct SpaceTransform *data, float no[3]);
#define BLI_SPACE_TRANSFORM_SETUP(data, local, target) \
BLI_space_transform_from_matrices( \
(data), (local)->object_to_world().ptr(), (target)->object_to_world().ptr())
/** \} */
/* -------------------------------------------------------------------- */
/** \name Other
* \{ */
void print_m3(const char *str, const float m[3][3]);
void print_m4(const char *str, const float m[4][4]);
#define print_m3_id(M) print_m3(STRINGIFY(M), M)
#define print_m4_id(M) print_m4(STRINGIFY(M), M)
/** \} */
#ifdef __cplusplus
}
#endif