Changeset View
Standalone View
source/blender/blenlib/BLI_math_matrix_types.hh
- This file was added.
| /* SPDX-License-Identifier: GPL-2.0-or-later | |||||||||||||||||||||
| * Copyright 2022 Blender Foundation. */ | |||||||||||||||||||||
| #pragma once | |||||||||||||||||||||
| /** \file | |||||||||||||||||||||
| * \ingroup bli | |||||||||||||||||||||
| * | |||||||||||||||||||||
| * Template for matrix types. | |||||||||||||||||||||
| * | |||||||||||||||||||||
| * The `blender::MatBase<T, NumCol, NumRow>` is a Row x Col matrix (in mathematical notation) laid | |||||||||||||||||||||
HooglyBoogly: I understand you might not have the energy for this at this point, but I think it would be… | |||||||||||||||||||||
Done Inline ActionsCould the "this only works for tightly packed T" be checked with a static assert instead? HooglyBoogly: Could the "this only works for tightly packed T" be checked with a static assert instead? | |||||||||||||||||||||
| * out as column major in memory. | |||||||||||||||||||||
| * | |||||||||||||||||||||
| * This class overloads `+, -, *` and `+=, -=, *=` mathematical operators. | |||||||||||||||||||||
| * They are all using per component operation, except for a few: | |||||||||||||||||||||
| * `MatBase<C,R> * Vector<C>` the vector product with the matrix. | |||||||||||||||||||||
| * `Vector<R> * MatBase<C,R>` the vector product with the **transposed** matrix. | |||||||||||||||||||||
| * `MatBase<C,R> * MatBase<R,C>` and `MatBase<C,R> *= MatBase<R,C>` the matrix multiplication. | |||||||||||||||||||||
| * | |||||||||||||||||||||
| * The `blender::MatView` allows working on a subset of a matrix without having to move the data | |||||||||||||||||||||
Done Inline ActionsCould we use is_unit_scale instead, to avoid including this file? HooglyBoogly: Could we use `is_unit_scale` instead, to avoid including this file? | |||||||||||||||||||||
| * around. It can be obtained using the `MatBase.view<NumCol, NumRow>()`. It is const by default if | |||||||||||||||||||||
Done Inline ActionsThis compiles fine without the BLI_math_vector.hh include for me. HooglyBoogly: This compiles fine without the `BLI_math_vector.hh` include for me.
We should be careful about… | |||||||||||||||||||||
| * the matrix type is. Otherwise, a `blender::MutableMatView` is returned. | |||||||||||||||||||||
| * | |||||||||||||||||||||
| * A `blender::MutableMatView`. It is mostly the same as `blender::MatView`, but can to be | |||||||||||||||||||||
| * modified. | |||||||||||||||||||||
| * | |||||||||||||||||||||
| * This allow working with any number type `T` (float, double, mpq, ...) and to use these types in | |||||||||||||||||||||
Done Inline ActionsPersonally, I'd still prefer changing the order of NumCol and NumRow to be more consistent with mathematics and Eigen. Would like to hear the reasoning from other people as well. I'd also be in favor of not allowing mat[x][y] and to use an API like mat.row(x), mat.col(x, y), mat.col_row(x, y) and maybe mat.row_col(y, x). I think that way the intend is way more obvious. JacquesLucke: Personally, I'd still prefer changing the order of `NumCol` and `NumRow` to be more consistent… | |||||||||||||||||||||
Done Inline ActionsJust to clarify, I'm not talking about changing how the matrix is stored internally. Column-major makes sense to me. I just prefer to change the order of the template parameters. For example, float3x4 is a matrix with 3 rows and 4 columns to me. I think that makes sense because I think of float3 as a column vector, and I have 4 of those. JacquesLucke: Just to clarify, I'm not talking about changing how the matrix is stored internally. Column… | |||||||||||||||||||||
Done Inline ActionsFrom Eigen-only code perspective I agree with you. But in realities of Blender I am not convinced this is great . It opens the opportunities to make a lot of mistakes during conversion stages, or when one works with both C and C++ code: you'd constantly need to think whether it is [col][row] or (row, col) or (col, row) or something else. This is intrusive enough decision, and it at least needs to be supported by the Core module. Disallowing subscript operator goes agains the part of the design which talks about aligning GLSL syntax with C++. The amount of code duplication between C/C++ and GLSL is to be minimised. mat.col(x, y) is unclear to me what it is. sergey: From Eigen-only code perspective I agree with you. But in realities of Blender I am not… | |||||||||||||||||||||
Done Inline Actions
Oh oops, that was a typo. Yeah, I can see that changing [col][row] or so to something else can cause many bugs, hence my suggestion to not allow it at all. But yes, I forgot about the GLSL compatibility. Makes me wonder whether we should not use this kind of access in glsl either. JacquesLucke: > `mat.col(x, y)` is unclear to me what it is.
Oh oops, that was a typo.
Yeah, I can see… | |||||||||||||||||||||
| * shared shader files (code compiled in both C++ and Shader language). To this end, only low level | |||||||||||||||||||||
| * constructors are defined inside the class itself and every function working on matrices are | |||||||||||||||||||||
| * defined outside of the class in the `blender::math` namespace. | |||||||||||||||||||||
| */ | |||||||||||||||||||||
| #include <array> | |||||||||||||||||||||
| #include <cmath> | |||||||||||||||||||||
| #include <iostream> | |||||||||||||||||||||
| #include <type_traits> | |||||||||||||||||||||
| #include "BLI_math_vec_types.hh" | |||||||||||||||||||||
| #include "BLI_utildefines.h" | |||||||||||||||||||||
| #include "BLI_utility_mixins.hh" | |||||||||||||||||||||
| namespace blender { | |||||||||||||||||||||
| template<typename T, | |||||||||||||||||||||
| int NumCol, | |||||||||||||||||||||
| int NumRow, | |||||||||||||||||||||
| int SrcNumCol, | |||||||||||||||||||||
| int SrcNumRow, | |||||||||||||||||||||
| int SrcStartCol, | |||||||||||||||||||||
| int SrcStartRow, | |||||||||||||||||||||
| int Alignment> | |||||||||||||||||||||
| struct MatView; | |||||||||||||||||||||
| template<typename T, | |||||||||||||||||||||
| int NumCol, | |||||||||||||||||||||
| int NumRow, | |||||||||||||||||||||
| int SrcNumCol, | |||||||||||||||||||||
| int SrcNumRow, | |||||||||||||||||||||
| int SrcStartCol, | |||||||||||||||||||||
| int SrcStartRow, | |||||||||||||||||||||
| int Alignment> | |||||||||||||||||||||
| struct MutableMatView; | |||||||||||||||||||||
| template< | |||||||||||||||||||||
| /* Number type. */ | |||||||||||||||||||||
| typename T, | |||||||||||||||||||||
| /* Number of column in the matrix. */ | |||||||||||||||||||||
| int NumCol, | |||||||||||||||||||||
| /* Number of row in the matrix. */ | |||||||||||||||||||||
| int NumRow, | |||||||||||||||||||||
| /* Alignment in bytes. Do not align matrices whose size is not a multiple of 4 component. | |||||||||||||||||||||
| * This is in order to avoid padding when using arrays of matrices. */ | |||||||||||||||||||||
| int Alignment = (((NumCol * NumRow) % 4) ? 4 : 0) * sizeof(T)> | |||||||||||||||||||||
| struct alignas(Alignment) MatBase : public vec_struct_base<vec_base<T, NumRow>, NumCol> { | |||||||||||||||||||||
| using base_type = T; | |||||||||||||||||||||
| using vec3_type = vec_base<T, 3>; | |||||||||||||||||||||
| using col_type = vec_base<T, NumRow>; | |||||||||||||||||||||
| using row_type = vec_base<T, NumCol>; | |||||||||||||||||||||
Done Inline ActionsMy c++-foo is weak, do we also need ?: MatBase(MatBase &&) = default; MatBase& operator=(const MatBase &) = default; ` chrisbblend: My c++-foo is weak, do we also need ?:
```
MatBase(MatBase &&) = default;
MatBase&… | |||||||||||||||||||||
Done Inline ActionsThat isn't required since none of the copy / move constructor/assignment are defined. Thus these are automatically defined. fclem: That isn't required since none of the copy / move constructor/assignment are defined. Thus… | |||||||||||||||||||||
| static constexpr int min_dim = (NumRow < NumCol) ? NumRow : NumCol; | |||||||||||||||||||||
| static constexpr int col_len = NumCol; | |||||||||||||||||||||
| static constexpr int row_len = NumRow; | |||||||||||||||||||||
| MatBase() = default; | |||||||||||||||||||||
| /* Workaround issue with template BLI_ENABLE_IF((Size == 2)) not working. */ | |||||||||||||||||||||
| #define BLI_ENABLE_IF_MAT(_size, _test) int S = _size, BLI_ENABLE_IF((S _test)) | |||||||||||||||||||||
| template<BLI_ENABLE_IF_MAT(NumCol, == 2)> MatBase(col_type _x, col_type _y) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| (*this)[0] = _x; | |||||||||||||||||||||
| (*this)[1] = _y; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| template<BLI_ENABLE_IF_MAT(NumCol, == 3)> MatBase(col_type _x, col_type _y, col_type _z) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| (*this)[0] = _x; | |||||||||||||||||||||
| (*this)[1] = _y; | |||||||||||||||||||||
| (*this)[2] = _z; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| template<BLI_ENABLE_IF_MAT(NumCol, == 4)> | |||||||||||||||||||||
| MatBase(col_type _x, col_type _y, col_type _z, col_type _w) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| (*this)[0] = _x; | |||||||||||||||||||||
| (*this)[1] = _y; | |||||||||||||||||||||
| (*this)[2] = _z; | |||||||||||||||||||||
| (*this)[3] = _w; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Masking. */ | |||||||||||||||||||||
| template<typename U, int OtherNumCol, int OtherNumRow> | |||||||||||||||||||||
| explicit MatBase(const MatBase<U, OtherNumCol, OtherNumRow> &other) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| if constexpr ((OtherNumRow >= NumRow) && (OtherNumCol >= NumCol)) { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] = col_type(other[i]); }); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| else { | |||||||||||||||||||||
| /* Allow enlarging following GLSL standard (i.e: mat4x4(mat3x3())). */ | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { | |||||||||||||||||||||
| unroll<NumRow>([&](auto j) { | |||||||||||||||||||||
| if (i < OtherNumCol && j < OtherNumRow) { | |||||||||||||||||||||
| (*this)[i][j] = other[i][j]; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| else if (i == j) { | |||||||||||||||||||||
| (*this)[i][j] = T(1); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| else { | |||||||||||||||||||||
| (*this)[i][j] = T(0); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| } | |||||||||||||||||||||
Done Inline ActionsThink those should be removed even now already. JacquesLucke: Think those should be removed even now already. | |||||||||||||||||||||
| #undef BLI_ENABLE_IF_MAT | |||||||||||||||||||||
| /** Conversion from pointers (from C-style vectors). */ | |||||||||||||||||||||
| explicit MatBase(const T *ptr) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] = reinterpret_cast<const col_type *>(ptr)[i]; }); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| template<typename U, BLI_ENABLE_IF((std::is_convertible_v<U, T>))> explicit MatBase(const U *ptr) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] = ptr[i]; }); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| explicit MatBase(const T (*ptr)[NumCol]) : MatBase(static_cast<const T *>(ptr[0])) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Conversion from other matrix types. */ | |||||||||||||||||||||
| template<typename U> explicit MatBase(const MatBase<U, NumRow, NumCol> &vec) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] = col_type(vec[i]); }); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** C-style pointer dereference. */ | |||||||||||||||||||||
| using c_style_mat = T[NumCol][NumRow]; | |||||||||||||||||||||
| /** \note Prevent implicit cast to types that could fit other pointer constructor. */ | |||||||||||||||||||||
| const c_style_mat &ptr() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return *reinterpret_cast<const c_style_mat *>(this); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** \note Prevent implicit cast to types that could fit other pointer constructor. */ | |||||||||||||||||||||
| c_style_mat &ptr() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return *reinterpret_cast<c_style_mat *>(this); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** \note Prevent implicit cast to types that could fit other pointer constructor. */ | |||||||||||||||||||||
| const T *base_ptr() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
Done Inline ActionsWhat about using BLI_ENABLE_IF instead of these static asserts? It seems a bit better to me, and maybe it would integrate better with IDEs if they can check those conditions when using autocompletion. HooglyBoogly: What about using `BLI_ENABLE_IF` instead of these static asserts? It seems a bit better to me… | |||||||||||||||||||||
Done Inline Actionsenable_if will not give human understandable clues about why my_matrix.forward() is undefined. I would much rather see a clue "Hey, expecting number of rows to be at least 3" than "hey, undefined reference to forward, and here is a kilometer of unreadable substitution attempts". In general, I think majority of BLI_ENABLE_IF in BLI is just plain wrong. I would really suggest to stay away from it, and start thinking of a good flexible API. sergey: `enable_if` will not give human understandable clues about why `my_matrix.forward()` is… | |||||||||||||||||||||
| return reinterpret_cast<const T *>(this); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** \note Prevent implicit cast to types that could fit other pointer constructor. */ | |||||||||||||||||||||
| T *base_ptr() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return reinterpret_cast<T *>(this); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** View creation. */ | |||||||||||||||||||||
| template<int ViewNumCol = NumCol, | |||||||||||||||||||||
| int ViewNumRow = NumRow, | |||||||||||||||||||||
| int SrcStartCol = 0, | |||||||||||||||||||||
| int SrcStartRow = 0> | |||||||||||||||||||||
| const MatView<T, ViewNumCol, ViewNumRow, NumCol, NumRow, SrcStartCol, SrcStartRow, Alignment> | |||||||||||||||||||||
| view() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return MatView<T, ViewNumCol, ViewNumRow, NumCol, NumRow, SrcStartCol, SrcStartRow, Alignment>( | |||||||||||||||||||||
| const_cast<MatBase &>(*this)); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| template<int ViewNumCol = NumCol, | |||||||||||||||||||||
| int ViewNumRow = NumRow, | |||||||||||||||||||||
| int SrcStartCol = 0, | |||||||||||||||||||||
| int SrcStartRow = 0> | |||||||||||||||||||||
| MutableMatView<T, ViewNumCol, ViewNumRow, NumCol, NumRow, SrcStartCol, SrcStartRow, Alignment> | |||||||||||||||||||||
| view() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return MutableMatView<T, | |||||||||||||||||||||
| ViewNumCol, | |||||||||||||||||||||
| ViewNumRow, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| SrcStartCol, | |||||||||||||||||||||
| SrcStartRow, | |||||||||||||||||||||
| Alignment>(*this); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Array access. */ | |||||||||||||||||||||
| const col_type &operator[](int index) const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_assert(index >= 0); | |||||||||||||||||||||
| BLI_assert(index < NumCol); | |||||||||||||||||||||
| return reinterpret_cast<const col_type *>(this)[index]; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| col_type &operator[](int index) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_assert(index >= 0); | |||||||||||||||||||||
| BLI_assert(index < NumCol); | |||||||||||||||||||||
| return reinterpret_cast<col_type *>(this)[index]; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Access helpers. Using Blender coordinate system. */ | |||||||||||||||||||||
| vec3_type &x_axis() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 1, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<vec3_type *>(&(*this)[0]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| vec3_type &y_axis() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 2, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<vec3_type *>(&(*this)[1]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| vec3_type &z_axis() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<vec3_type *>(&(*this)[2]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| vec3_type &location() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 4, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<vec3_type *>(&(*this)[3]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| const vec3_type &x_axis() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 1, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<const vec3_type *>(&(*this)[0]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| const vec3_type &y_axis() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 2, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<const vec3_type *>(&(*this)[1]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| const vec3_type &z_axis() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<const vec3_type *>(&(*this)[2]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| const vec3_type &location() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumCol >= 4, "Wrong Matrix dimension"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension"); | |||||||||||||||||||||
| return *reinterpret_cast<const vec3_type *>(&(*this)[3]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Matrix operators. */ | |||||||||||||||||||||
| friend MatBase operator+(const MatBase &a, const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] + b[i]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatBase operator+(const MatBase &a, T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] + b; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatBase operator+(T a, const MatBase &b) | |||||||||||||||||||||
Done Inline ActionsIMO a more helpful comment would describe what the function does in a way that also makes it clear what it doesn't do. HooglyBoogly: IMO a more helpful comment would describe what the function does in a way that also makes it… | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return b + a; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MatBase &operator+=(const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] += b[i]; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MatBase &operator+=(T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] += b; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
Done Inline ActionsIf it only works for square matrices, add a static assert. JacquesLucke: If it only works for square matrices, add a static assert. | |||||||||||||||||||||
| friend MatBase operator-(const MatBase &a) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = -a[i]; }); | |||||||||||||||||||||
Done Inline ActionsThink it's better to define the binary operator functions as a friend function, or it doesn't even have to be inside the class. JacquesLucke: Think it's better to define the binary operator functions as a `friend` function, or it doesn't… | |||||||||||||||||||||
Done Inline ActionsThis is in order to provide template specialization for common types. I couldn't find a nice way for friend functions to work with this. fclem: This is in order to provide template specialization for common types. I couldn't find a nice… | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatBase operator-(const MatBase &a, const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] - b[i]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatBase operator-(const MatBase &a, T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] - b; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatBase operator-(T a, const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a - b[i]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MatBase &operator-=(const MatBase &b) | |||||||||||||||||||||
Done Inline ActionsTODO: The return type doesn't look quite right here, it should be possible to return a non-square matrix. chrisbblend: TODO: The return type doesn't look quite right here, it should be possible to return a non… | |||||||||||||||||||||
Done Inline ActionsI did it out of simplicity and avoiding to template this even more. I'll leave that as a TODO. fclem: I did it out of simplicity and avoiding to template this even more. I'll leave that as a TODO. | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] -= b[i]; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MatBase &operator-=(T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] -= b; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply two matrices using matrix multiplication. */ | |||||||||||||||||||||
| MatBase<T, NumRow, NumRow> operator*(const MatBase<T, NumRow, NumCol> &b) const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| const MatBase &a = *this; | |||||||||||||||||||||
| /* This is the reference implementation. | |||||||||||||||||||||
| * Might be overloaded with vectorized / optimized code. */ | |||||||||||||||||||||
| /* TODO(fclem): It should be possible to return non-square matrices when multiplying against | |||||||||||||||||||||
| * MatBase<T, NumRow, OtherNumRow>. */ | |||||||||||||||||||||
| MatBase<T, NumRow, NumRow> result{}; | |||||||||||||||||||||
| unroll<NumRow>([&](auto j) { | |||||||||||||||||||||
| unroll<NumRow>([&](auto i) { | |||||||||||||||||||||
| /* Same as dot product, but avoid dependency on vector math. */ | |||||||||||||||||||||
| unroll<NumCol>([&](auto k) { result[j][i] += a[k][i] * b[j][k]; }); | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply each component by a scalar. */ | |||||||||||||||||||||
| friend MatBase operator*(const MatBase &a, T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] * b; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply each component by a scalar. */ | |||||||||||||||||||||
| friend MatBase operator*(T a, const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return b * a; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply two matrices using matrix multiplication. */ | |||||||||||||||||||||
| MatBase &operator*=(const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| const MatBase &a = *this; | |||||||||||||||||||||
| *this = a * b; | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply each component by a scalar. */ | |||||||||||||||||||||
| MatBase &operator*=(T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] *= b; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Vector operators. */ | |||||||||||||||||||||
| friend col_type operator*(const MatBase &a, const row_type &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| /* This is the reference implementation. | |||||||||||||||||||||
| * Might be overloaded with vectorized / optimized code. */ | |||||||||||||||||||||
| col_type result(0); | |||||||||||||||||||||
| unroll<NumCol>([&](auto c) { result += b[c] * a[c]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply by the transposed. */ | |||||||||||||||||||||
| friend row_type operator*(const col_type &a, const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| /* This is the reference implementation. | |||||||||||||||||||||
| * Might be overloaded with vectorized / optimized code. */ | |||||||||||||||||||||
| row_type result(0); | |||||||||||||||||||||
| unroll<NumCol>([&](auto c) { unroll<NumRow>([&](auto r) { result[c] += b[c][r] * a[r]; }); }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
Done Inline Actions
We're already in namespace blender here, so I don't think the specification is necessary HooglyBoogly: We're already in `namespace blender` here, so I don't think the specification is necessary | |||||||||||||||||||||
| /** Compare. */ | |||||||||||||||||||||
| friend bool operator==(const MatBase &a, const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| for (int i = 0; i < NumCol; i++) { | |||||||||||||||||||||
| if (a[i] != b[i]) { | |||||||||||||||||||||
| return false; | |||||||||||||||||||||
Done Inline ActionsTo support non-square matrices, perhaps std::min(NumCol,NumRow) chrisbblend: To support non-square matrices, perhaps `std::min(NumCol,NumRow)` | |||||||||||||||||||||
Done Inline ActionsGood catch! fclem: Good catch! | |||||||||||||||||||||
| } | |||||||||||||||||||||
| } | |||||||||||||||||||||
| return true; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend bool operator!=(const MatBase &a, const MatBase &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return !(a == b); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Miscellaneous. */ | |||||||||||||||||||||
| static MatBase diagonal(T value) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result{}; | |||||||||||||||||||||
| unroll<min_dim>([&](auto i) { result[i][i] = value; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| static MatBase all(T value) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatBase result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = col_type(value); }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
Done Inline ActionsCan we make a slightly stronger hash by following FNV-hash and using XOR instead of ADD: uint64_t hash() const
{
uint64_t h = 14695981039346656037ULL;
unroll<NumCol * NumRow>([&](auto i) {
T value = (static_cast<const T *>(this))[i];
h *= 1099511628211ULL;
h ^= *reinterpret_cast<const as_uint_type<T> *>(&value);
});
return h;
}chrisbblend: Can we make a slightly stronger hash by following FNV-hash and using XOR instead of ADD:
```… | |||||||||||||||||||||
Done Inline ActionsThis patch uses the same hash as the old float4x4 library for compatibility. If we have to change it, it isn't in this patch. fclem: This patch uses the same hash as the old `float4x4` library for compatibility. If we have to… | |||||||||||||||||||||
| static MatBase identity() | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return diagonal(1); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| static MatBase zero() | |||||||||||||||||||||
| { | |||||||||||||||||||||
Done Inline Actions(!OPTIONAL!) As a personal preference, it's possible to write this slightly cleaner like so: friend std::ostream &operator<<(std::ostream &stream, const MatBase &mat)
{
const char *col_prefix = "(\n";
unroll<NumCol>([&](auto i) {
stream << col_prefix;
col_prefix = "),\n(";
const char *row_prefix = "(";
unroll<NumRow>([&](auto j) {
/** NOTE: j and i are swapped to follow mathematical convention. */
stream << row_prefix << mat[j][i];
row_prefix = ", ";
});
});
stream << ")\n)\n";
return stream;
}
};chrisbblend: (!!!OPTIONAL!!!)
As a personal preference, it's possible to write this slightly cleaner like… | |||||||||||||||||||||
Done Inline ActionsI find that's a little less obvious. fclem: I find that's a little less obvious. | |||||||||||||||||||||
| return all(0); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| uint64_t hash() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| uint64_t h = 435109; | |||||||||||||||||||||
| unroll<NumCol * NumRow>([&](auto i) { | |||||||||||||||||||||
| T value = (static_cast<const T *>(this))[i]; | |||||||||||||||||||||
| h = h * 33 + *reinterpret_cast<const as_uint_type<T> *>(&value); | |||||||||||||||||||||
| }); | |||||||||||||||||||||
Done Inline ActionsOne problem right now is that this breaks const correctness. See how x can be changed even though it is constant. void test(const float4x4 &x) {
auto view = x.view<4, 4>();
view += 5;
}Not sure what's the best way to deal with this here. Some options:
JacquesLucke: One problem right now is that this breaks const correctness. See how `x` can be changed even… | |||||||||||||||||||||
Done Inline ActionsYour example does not compile. (MacOS + Clang 13.1.6) You should get something like /Users/clement/Blender/blender/source/blender/blenlib/tests/BLI_math_matrix_types_test.cc:298:32: error: no viable overloaded '+=' mat_const.view<2, 2, 1, 1>() += 5; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~ [...] /Users/clement/Blender/blender/source/blender/blenlib/BLI_math_matrix_types.hh:566:12: note: candidate function not viable: 'this' argument has type 'const MatView<float, 2, 2, 4, 4, 1, 1>', but method is not marked const MatView &operator+=(T b) This is because the view() method return either a const MatView or a MatView depending on the object const type. So I am a bit hesitant to make the code way less understandable for something that should work. fclem: Your example does not compile. (MacOS + Clang 13.1.6)
You should get something like… | |||||||||||||||||||||
Done Inline ActionsThere is an important difference between: mat_const.view<2, 2, 1, 1>() += 5; and auto view = x.view<4, 4>(); view += 5; The example you provided indeed does not work, but my example does and it has the const correctness problem. JacquesLucke: There is an important difference between:
```
mat_const.view<2, 2, 1, 1>() += 5;
```
and
```… | |||||||||||||||||||||
| return h; | |||||||||||||||||||||
| } | |||||||||||||||||||||
Done Inline ActionsShould probably check SrcStartCol + NumCol <= SrcNumCol JacquesLucke: Should probably check `SrcStartCol + NumCol <= SrcNumCol` | |||||||||||||||||||||
Done Inline ActionsDoes not look like this has been addressed. JacquesLucke: Does not look like this has been addressed. | |||||||||||||||||||||
| friend std::ostream &operator<<(std::ostream &stream, const MatBase &mat) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| stream << "(\n"; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { | |||||||||||||||||||||
| stream << "("; | |||||||||||||||||||||
| unroll<NumRow>([&](auto j) { | |||||||||||||||||||||
Done Inline ActionsThese checks are not necessary since they are included in the checks below. JacquesLucke: These checks are not necessary since they are included in the checks below. | |||||||||||||||||||||
| /** NOTE: j and i are swapped to follow mathematical convention. */ | |||||||||||||||||||||
| stream << mat[j][i]; | |||||||||||||||||||||
| if (j < NumRow - 1) { | |||||||||||||||||||||
| stream << ", "; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| stream << ")"; | |||||||||||||||||||||
| if (i < NumCol - 1) { | |||||||||||||||||||||
| stream << ","; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| stream << "\n"; | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| stream << ")\n"; | |||||||||||||||||||||
| return stream; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| }; | |||||||||||||||||||||
| template<typename T, | |||||||||||||||||||||
| /** The view dimensions. */ | |||||||||||||||||||||
| int NumCol, | |||||||||||||||||||||
| int NumRow, | |||||||||||||||||||||
| /** The source matrix dimensions. */ | |||||||||||||||||||||
| int SrcNumCol, | |||||||||||||||||||||
| int SrcNumRow, | |||||||||||||||||||||
| /** The base offset inside the source matrix. */ | |||||||||||||||||||||
| int SrcStartCol, | |||||||||||||||||||||
| int SrcStartRow, | |||||||||||||||||||||
| /** The source matrix alignment. */ | |||||||||||||||||||||
| int SrcAlignment> | |||||||||||||||||||||
| struct MatView : NonCopyable, NonMovable { | |||||||||||||||||||||
| using MatT = MatBase<T, NumCol, NumRow>; | |||||||||||||||||||||
| using SrcMatT = MatBase<T, SrcNumCol, SrcNumRow, SrcAlignment>; | |||||||||||||||||||||
| using col_type = vec_base<T, NumRow>; | |||||||||||||||||||||
| using row_type = vec_base<T, NumCol>; | |||||||||||||||||||||
| const SrcMatT &mat; | |||||||||||||||||||||
| MatView() = delete; | |||||||||||||||||||||
| MatView(const SrcMatT &src) : mat(src) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_STATIC_ASSERT(SrcStartCol >= 0, "View does not fit source matrix dimensions"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(SrcStartRow >= 0, "View does not fit source matrix dimensions"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(SrcStartCol + NumCol <= SrcNumCol, | |||||||||||||||||||||
| "View does not fit source matrix dimensions"); | |||||||||||||||||||||
| BLI_STATIC_ASSERT(SrcStartRow + NumRow <= SrcNumRow, | |||||||||||||||||||||
| "View does not fit source matrix dimensions"); | |||||||||||||||||||||
| } | |||||||||||||||||||||
Done Inline ActionsAssert that the other matrix has the same size as this matrix. JacquesLucke: Assert that the other matrix has the same size as this matrix.
Also this should probably be… | |||||||||||||||||||||
Done Inline ActionsThis is already the case here. You were just confused by the long list of arguments. fclem: This is already the case here. You were just confused by the long list of arguments. | |||||||||||||||||||||
| /** Allow wrapping C-style matrices using view. IMPORTANT: Alignment of src needs to match. */ | |||||||||||||||||||||
| explicit MatView(const float (*src)[SrcNumRow]) | |||||||||||||||||||||
| : MatView(*reinterpret_cast<const SrcMatT *>(&src[0][0])){}; | |||||||||||||||||||||
| /** Array access. */ | |||||||||||||||||||||
| const col_type &operator[](int index) const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_assert(index >= 0); | |||||||||||||||||||||
| BLI_assert(index < NumCol); | |||||||||||||||||||||
| return *reinterpret_cast<const col_type *>(&mat[index + SrcStartCol][SrcStartRow]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Conversion back to matrix. */ | |||||||||||||||||||||
| operator MatT() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatT mat; | |||||||||||||||||||||
| unroll<NumCol>([&](auto c) { mat[c] = (*this)[c]; }); | |||||||||||||||||||||
| return mat; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Matrix operators. */ | |||||||||||||||||||||
| friend MatT operator+(const MatView &a, T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatT result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] + b; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatT operator+(T a, const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return b + a; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatT operator-(const MatView &a) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatT result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = -a[i]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| template<int OtherSrcNumCol, | |||||||||||||||||||||
| int OtherSrcNumRow, | |||||||||||||||||||||
| int OtherSrcStartCol, | |||||||||||||||||||||
| int OtherSrcStartRow, | |||||||||||||||||||||
| int OtherSrcAlignment> | |||||||||||||||||||||
| friend MatT operator-(const MatView &a, | |||||||||||||||||||||
| const MatView<T, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| OtherSrcNumCol, | |||||||||||||||||||||
| OtherSrcNumRow, | |||||||||||||||||||||
| OtherSrcStartCol, | |||||||||||||||||||||
| OtherSrcStartRow, | |||||||||||||||||||||
| OtherSrcAlignment> &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatT result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] - b[i]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatT operator-(const MatView &a, const MatT &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return a - b.template view(); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| template<int OtherSrcNumCol, | |||||||||||||||||||||
| int OtherSrcNumRow, | |||||||||||||||||||||
| int OtherSrcStartCol, | |||||||||||||||||||||
| int OtherSrcStartRow, | |||||||||||||||||||||
| int OtherSrcAlignment> | |||||||||||||||||||||
| friend MatT operator-(const MatView<T, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| OtherSrcNumCol, | |||||||||||||||||||||
| OtherSrcNumRow, | |||||||||||||||||||||
| OtherSrcStartCol, | |||||||||||||||||||||
| OtherSrcStartRow, | |||||||||||||||||||||
| OtherSrcAlignment> &a, | |||||||||||||||||||||
| const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatT result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] - b[i]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatT operator-(const MatT &a, const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return a.template view() - b; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatT operator-(const MatView &a, T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatT result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] - b; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend MatView operator-(T a, const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatView result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a - b[i]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply two matrices using matrix multiplication. */ | |||||||||||||||||||||
| template<int OtherSrcNumCol, | |||||||||||||||||||||
| int OtherSrcNumRow, | |||||||||||||||||||||
| int OtherSrcStartCol, | |||||||||||||||||||||
| int OtherSrcStartRow, | |||||||||||||||||||||
| int OtherSrcAlignment> | |||||||||||||||||||||
| MatBase<T, NumRow, NumRow> operator*(const MatView<T, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| OtherSrcNumCol, | |||||||||||||||||||||
| OtherSrcNumRow, | |||||||||||||||||||||
| OtherSrcStartCol, | |||||||||||||||||||||
| OtherSrcStartRow, | |||||||||||||||||||||
| OtherSrcAlignment> &b) const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| const MatView &a = *this; | |||||||||||||||||||||
| /* This is the reference implementation. | |||||||||||||||||||||
| * Might be overloaded with vectorized / optimized code. */ | |||||||||||||||||||||
| MatBase<T, NumRow, NumRow> result{}; | |||||||||||||||||||||
| unroll<NumRow>([&](auto j) { | |||||||||||||||||||||
| unroll<NumRow>([&](auto i) { | |||||||||||||||||||||
| /* Same as dot product, but avoid dependency on vector math. */ | |||||||||||||||||||||
| unroll<NumCol>([&](auto k) { result[j][i] += a[k][i] * b[j][k]; }); | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MatT operator*(const MatT &b) const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return *this * b.template view(); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply each component by a scalar. */ | |||||||||||||||||||||
| friend MatT operator*(const MatView &a, T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| MatT result; | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { result[i] = a[i] * b; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply each component by a scalar. */ | |||||||||||||||||||||
| friend MatT operator*(T a, const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return b * a; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Vector operators. */ | |||||||||||||||||||||
| friend col_type operator*(const MatView &a, const row_type &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| /* This is the reference implementation. | |||||||||||||||||||||
| * Might be overloaded with vectorized / optimized code. */ | |||||||||||||||||||||
| col_type result(0); | |||||||||||||||||||||
| unroll<NumCol>([&](auto c) { result += b[c] * a[c]; }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply by the transposed. */ | |||||||||||||||||||||
| friend row_type operator*(const col_type &a, const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| /* This is the reference implementation. | |||||||||||||||||||||
| * Might be overloaded with vectorized / optimized code. */ | |||||||||||||||||||||
| row_type result(0); | |||||||||||||||||||||
| unroll<NumCol>([&](auto c) { unroll<NumRow>([&](auto r) { result[c] += b[c][r] * a[r]; }); }); | |||||||||||||||||||||
| return result; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Compare. */ | |||||||||||||||||||||
| friend bool operator==(const MatView &a, const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| for (int i = 0; i < NumCol; i++) { | |||||||||||||||||||||
| if (a[i] != b[i]) { | |||||||||||||||||||||
| return false; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| } | |||||||||||||||||||||
| return true; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| friend bool operator!=(const MatView &a, const MatView &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return !(a == b); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Miscellaneous. */ | |||||||||||||||||||||
| friend std::ostream &operator<<(std::ostream &stream, const MatView &mat) | |||||||||||||||||||||
| { | |||||||||||||||||||||
Done Inline Actions
Grammar HooglyBoogly: Grammar | |||||||||||||||||||||
| return stream << mat->mat; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| }; | |||||||||||||||||||||
| template<typename T, | |||||||||||||||||||||
| /** The view dimensions. */ | |||||||||||||||||||||
| int NumCol, | |||||||||||||||||||||
| int NumRow, | |||||||||||||||||||||
| /** The source matrix dimensions. */ | |||||||||||||||||||||
| int SrcNumCol, | |||||||||||||||||||||
| int SrcNumRow, | |||||||||||||||||||||
| /** The base offset inside the source matrix. */ | |||||||||||||||||||||
| int SrcStartCol, | |||||||||||||||||||||
| int SrcStartRow, | |||||||||||||||||||||
| /** The source matrix alignment. */ | |||||||||||||||||||||
| int SrcAlignment> | |||||||||||||||||||||
| struct MutableMatView | |||||||||||||||||||||
| : MatView<T, NumCol, NumRow, SrcNumCol, SrcNumRow, SrcStartCol, SrcStartRow, SrcAlignment> { | |||||||||||||||||||||
| using MatT = MatBase<T, NumCol, NumRow, SrcAlignment>; | |||||||||||||||||||||
| using MatViewT = | |||||||||||||||||||||
| MatView<T, NumCol, NumRow, SrcNumCol, SrcNumRow, SrcStartCol, SrcStartRow, SrcAlignment>; | |||||||||||||||||||||
| using SrcMatT = MatBase<T, SrcNumCol, SrcNumRow, SrcAlignment>; | |||||||||||||||||||||
| using col_type = vec_base<T, NumRow>; | |||||||||||||||||||||
| using row_type = vec_base<T, NumCol>; | |||||||||||||||||||||
| public: | |||||||||||||||||||||
| MutableMatView() = delete; | |||||||||||||||||||||
| MutableMatView(SrcMatT &src) : MatViewT(const_cast<const SrcMatT &>(src)){}; | |||||||||||||||||||||
| /** Allow wrapping C-style matrices using view. IMPORTANT: Alignment of src needs to match. */ | |||||||||||||||||||||
| explicit MutableMatView(float src[SrcNumCol][SrcNumRow]) | |||||||||||||||||||||
| : MutableMatView(*reinterpret_cast<SrcMatT *>(&src[0][0])){}; | |||||||||||||||||||||
| /** Array access. */ | |||||||||||||||||||||
| col_type &operator[](int index) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return const_cast<col_type &>(static_cast<MatViewT &>(*this)[index]); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Conversion to immutable view. */ | |||||||||||||||||||||
| operator MatViewT() const | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return MatViewT(this->mat); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Copy Assignment. */ | |||||||||||||||||||||
| template<int OtherSrcNumCol, | |||||||||||||||||||||
| int OtherSrcNumRow, | |||||||||||||||||||||
| int OtherSrcStartCol, | |||||||||||||||||||||
| int OtherSrcStartRow, | |||||||||||||||||||||
| int OtherSrcAlignment> | |||||||||||||||||||||
| MutableMatView &operator=(const MatView<T, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| OtherSrcNumCol, | |||||||||||||||||||||
| OtherSrcNumRow, | |||||||||||||||||||||
| OtherSrcStartCol, | |||||||||||||||||||||
| OtherSrcStartRow, | |||||||||||||||||||||
| OtherSrcAlignment> &other) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| BLI_assert_msg( | |||||||||||||||||||||
| (reinterpret_cast<const void *>(&other.mat[0][0]) != | |||||||||||||||||||||
| reinterpret_cast<const void *>(&this->mat[0][0])) || | |||||||||||||||||||||
| /* Make sure assignment won't overwrite the source. OtherSrc* is the source. */ | |||||||||||||||||||||
| ((OtherSrcStartCol > SrcStartCol) || (OtherSrcStartCol + NumCol <= SrcStartCol) || | |||||||||||||||||||||
| (OtherSrcStartRow > SrcStartRow + NumRow) || | |||||||||||||||||||||
| (OtherSrcStartRow + NumRow <= SrcStartRow)), | |||||||||||||||||||||
| "Operation is undefined if views overlap."); | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] = other[i]; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MutableMatView &operator=(const MatT &other) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| *this = other.template view(); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Matrix operators. */ | |||||||||||||||||||||
| template<int OtherSrcNumCol, | |||||||||||||||||||||
| int OtherSrcNumRow, | |||||||||||||||||||||
| int OtherSrcStartCol, | |||||||||||||||||||||
| int OtherSrcStartRow, | |||||||||||||||||||||
| int OtherSrcAlignment> | |||||||||||||||||||||
| MutableMatView &operator+=(const MatView<T, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| OtherSrcNumCol, | |||||||||||||||||||||
| OtherSrcNumRow, | |||||||||||||||||||||
| OtherSrcStartCol, | |||||||||||||||||||||
| OtherSrcStartRow, | |||||||||||||||||||||
| OtherSrcAlignment> &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] += b[i]; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MutableMatView &operator+=(const MatT &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return *this += b.template view(); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MutableMatView &operator+=(T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] += b; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| template<int OtherSrcNumCol, | |||||||||||||||||||||
| int OtherSrcNumRow, | |||||||||||||||||||||
| int OtherSrcStartCol, | |||||||||||||||||||||
| int OtherSrcStartRow, | |||||||||||||||||||||
| int OtherSrcAlignment> | |||||||||||||||||||||
| MutableMatView &operator-=(const MatView<T, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| OtherSrcNumCol, | |||||||||||||||||||||
| OtherSrcNumRow, | |||||||||||||||||||||
| OtherSrcStartCol, | |||||||||||||||||||||
| OtherSrcStartRow, | |||||||||||||||||||||
| OtherSrcAlignment> &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] -= b[i]; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MutableMatView &operator-=(const MatT &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return *this -= b.template view(); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MutableMatView &operator-=(T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] -= b; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply two matrices using matrix multiplication. */ | |||||||||||||||||||||
| template<int OtherSrcNumCol, | |||||||||||||||||||||
| int OtherSrcNumRow, | |||||||||||||||||||||
| int OtherSrcStartCol, | |||||||||||||||||||||
| int OtherSrcStartRow, | |||||||||||||||||||||
| int OtherSrcAlignment> | |||||||||||||||||||||
| MutableMatView &operator*=(const MatView<T, | |||||||||||||||||||||
| NumCol, | |||||||||||||||||||||
| NumRow, | |||||||||||||||||||||
| OtherSrcNumCol, | |||||||||||||||||||||
| OtherSrcNumRow, | |||||||||||||||||||||
| OtherSrcStartCol, | |||||||||||||||||||||
| OtherSrcStartRow, | |||||||||||||||||||||
| OtherSrcAlignment> &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| *this = *static_cast<MatViewT *>(this) * b; | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| MutableMatView &operator*=(const MatT &b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| return *this *= b.template view(); | |||||||||||||||||||||
| } | |||||||||||||||||||||
| /** Multiply each component by a scalar. */ | |||||||||||||||||||||
| MutableMatView &operator*=(T b) | |||||||||||||||||||||
| { | |||||||||||||||||||||
| unroll<NumCol>([&](auto i) { (*this)[i] *= b; }); | |||||||||||||||||||||
| return *this; | |||||||||||||||||||||
| } | |||||||||||||||||||||
| }; | |||||||||||||||||||||
| using float2x2 = MatBase<float, 2, 2>; | |||||||||||||||||||||
| using float2x3 = MatBase<float, 2, 3>; | |||||||||||||||||||||
| using float2x4 = MatBase<float, 2, 4>; | |||||||||||||||||||||
| using float3x2 = MatBase<float, 3, 2>; | |||||||||||||||||||||
| using float3x3 = MatBase<float, 3, 3>; | |||||||||||||||||||||
| using float3x4 = MatBase<float, 3, 4>; | |||||||||||||||||||||
| using float4x2 = MatBase<float, 4, 2>; | |||||||||||||||||||||
| using float4x3 = MatBase<float, 4, 3>; | |||||||||||||||||||||
| using float4x4 = MatBase<float, 4, 4>; | |||||||||||||||||||||
| /* These types are reserved to wrap C matrices without copy. Note the un-alignment. */ | |||||||||||||||||||||
| /* TODO: It would be preferable to align all C matrices inside DNA structs. */ | |||||||||||||||||||||
| using float4x4_view = MatView<float, 4, 4, 4, 4, 0, 0, /* Alignment */ 0>; | |||||||||||||||||||||
| using float4x4_mutableview = MutableMatView<float, 4, 4, 4, 4, 0, 0, /* Alignment */ 0>; | |||||||||||||||||||||
| using double2x2 = MatBase<double, 2, 2>; | |||||||||||||||||||||
| using double2x3 = MatBase<double, 2, 3>; | |||||||||||||||||||||
| using double2x4 = MatBase<double, 2, 4>; | |||||||||||||||||||||
| using double3x2 = MatBase<double, 3, 2>; | |||||||||||||||||||||
| using double3x3 = MatBase<double, 3, 3>; | |||||||||||||||||||||
| using double3x4 = MatBase<double, 3, 4>; | |||||||||||||||||||||
| using double4x2 = MatBase<double, 4, 2>; | |||||||||||||||||||||
| using double4x3 = MatBase<double, 4, 3>; | |||||||||||||||||||||
| using double4x4 = MatBase<double, 4, 4>; | |||||||||||||||||||||
| /* Specialization for SSE optimization. */ | |||||||||||||||||||||
| template<> float4x4 float4x4::operator*(const float4x4 &b) const; | |||||||||||||||||||||
| template<> float3x3 float3x3::operator*(const float3x3 &b) const; | |||||||||||||||||||||
| extern template float2x2 float2x2::operator*(const float2x2 &b) const; | |||||||||||||||||||||
| extern template double2x2 double2x2::operator*(const double2x2 &b) const; | |||||||||||||||||||||
| extern template double3x3 double3x3::operator*(const double3x3 &b) const; | |||||||||||||||||||||
| extern template double4x4 double4x4::operator*(const double4x4 &b) const; | |||||||||||||||||||||
| } // namespace blender | |||||||||||||||||||||
I understand you might not have the energy for this at this point, but I think it would be great to see a description of the overall design in a comment at the top of the file like BLI_vector.hh.
This file will be looked at a lot, and that would probably save people lots of time in the long run