Files
omath/docs/linear_algebra/mat.md
Orange 95c0873b8c Documents view angle struct and related API
Adds documentation for the `omath::ViewAngles` struct,
clarifying its purpose, common usage patterns,
and the definition of the types of pitch, yaw and roll.

Also, adds short explanations of how to use ViewAngles and what tradeoffs exist
between using raw float types and strongly typed Angle<> types.
2025-11-01 09:12:04 +03:00

12 KiB
Raw Permalink Blame History

omath::Mat — Matrix class (C++20/23)

Header: your projects mat.hpp (requires vector3.hpp) Namespace: omath Requires: C++23 (uses multi-parameter operator[]) SIMD (optional): define OMATH_USE_AVX2 to enable AVX2-accelerated multiplication for float/double.


Overview

omath::Mat<Rows, Columns, Type, StoreType> is a compile-time, fixed-size matrix with:

  • Row/column counts as template parameters (no heap allocations).
  • Row-major or column-major storage (compile-time via MatStoreType).
  • Arithmetic and linear algebra: matrix × matrix, scalar ops, transpose, determinant, inverse (optional), etc.
  • Transform helpers: translation, axis rotations, look-at, perspective & orthographic projections.
  • I/O helpers: to_string/to_wstring/to_u8string and std::formatter specializations.

Template parameters

Parameter Description Default
Rows Number of rows (size_t, compile-time)
Columns Number of columns (size_t, compile-time)
Type Element type (arithmetic) float
StoreType Storage order: MatStoreType::ROW_MAJOR or MatStoreType::COLUMN_MAJOR ROW_MAJOR
enum class MatStoreType : uint8_t { ROW_MAJOR = 0, COLUMN_MAJOR };

Quick start

#include "mat.hpp"
using omath::Mat;

// 4x4 float, row-major
Mat<4,4> I = {
  {1,0,0,0},
  {0,1,0,0},
  {0,0,1,0},
  {0,0,0,1},
};

// Multiply 4x4 transforms
Mat<4,4> A = { {1,2,3,0},{0,1,4,0},{5,6,0,0},{0,0,0,1} };
Mat<4,4> B = { {2,0,0,0},{0,2,0,0},{0,0,2,0},{0,0,0,1} };
Mat<4,4> C = A * B;                   // matrix × matrix

// Scalar ops
auto D = C * 0.5f;                    // scale all entries

// Indexing (C++23 multi-parameter operator[])
float a03 = A[0,3];                   // same as A.at(0,3)
A[1,2] = 42.0f;

// Transpose, determinant, inverse
auto AT = A.transposed();
float det = A.determinant();          // only for square matrices
auto inv = A.inverted();              // std::optional<Mat>; std::nullopt if non-invertible

Note

Multiplication requires the same StoreType and Type on both operands, and dimensions must match at compile time.


Construction

Mat();                                 // zero-initialized
Mat(std::initializer_list<std::initializer_list<Type>> rows);
explicit Mat(const Type* raw_data);    // copies Rows*Columns elements
Mat(const Mat&); Mat(Mat&&);
  • Zeroing/setting

    m.clear();            // set all entries to 0
    m.set(3.14f);         // set all entries to a value
    
  • Shape & metadata

    Mat<>::row_count();             // constexpr size_t
    Mat<>::columns_count();         // constexpr size_t
    Mat<>::size();                  // constexpr MatSize {rows, columns}
    Mat<>::get_store_ordering();    // constexpr MatStoreType
    using ContainedType = Type;     // alias
    

Element access

T&       at(size_t r, size_t c);
T const& at(size_t r, size_t c) const;

T&       operator[](size_t r, size_t c);       // C++23
T const& operator[](size_t r, size_t c) const; // C++23

Bounds checking In debug builds you may enable/disable range checks via your compile-time macros (see the source guard around at()).


Arithmetic

  • Matrix × matrix

    // (Rows x Columns) * (Columns x OtherColumns) -> (Rows x OtherColumns)
    template<size_t OtherColumns>
    Mat<Rows, OtherColumns, Type, StoreType>
    operator*(const Mat<Columns, OtherColumns, Type, StoreType>&) const;
    
    • Complexity: O(Rows * Columns * OtherColumns).
    • AVX2-accelerated when OMATH_USE_AVX2 is defined and Type is float or double.
  • Scalars

    Mat  operator*(const Type& s) const;  Mat& operator*=(const Type& s);
    Mat  operator/(const Type& s) const;  Mat& operator/=(const Type& s);
    
  • Transpose

    Mat<Columns, Rows, Type, StoreType> transposed() const noexcept;
    
  • Determinant (square only)

    Type determinant() const;  // 1x1, 2x2 fast path; larger uses Laplace expansion
    
  • Inverse (square only)

    std::optional<Mat> inverted() const;  // nullopt if det == 0
    
  • Minors & cofactors (square only)

    Mat<Rows-1, Columns-1, Type, StoreType> strip(size_t r, size_t c) const;
    Type minor(size_t r, size_t c) const;
    Type alg_complement(size_t r, size_t c) const; // cofactor
    
  • Utilities

    Type sum() const noexcept;
    auto& raw_array();               // std::array<Type, Rows*Columns>&
    auto const& raw_array() const;
    
  • Comparison / formatting

    bool operator==(const Mat&) const;
    bool operator!=(const Mat&) const;
    
    std::string  to_string()  const noexcept;
    std::wstring to_wstring() const noexcept;
    std::u8string to_u8string() const noexcept;
    

// std::formatter specialization provided for char, wchar_t, char8_t


---

## Storage order notes

- **Row-major**: `index = row * Columns + column`
- **Column-major**: `index = row + column * Rows`

Choose one **consistently** across your math types and shader conventions. Mixed orders are supported by the type system but not for cross-multiplying (store types must match).

---

## Transform helpers

### From vectors

```cpp
template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<1,4,T,St> mat_row_from_vector(const Vector3<T>& v);

template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,1,T,St> mat_column_from_vector(const Vector3<T>& v);

Translation

template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_translation(const Vector3<T>& d) noexcept;

Axis rotations

// Angle type must provide angle.cos() and angle.sin()
template<class T=float, MatStoreType St=ROW_MAJOR, class Angle>
Mat<4,4,T,St> mat_rotation_axis_x(const Angle& a) noexcept;

template<class T=float, MatStoreType St=ROW_MAJOR, class Angle>
Mat<4,4,T,St> mat_rotation_axis_y(const Angle& a) noexcept;

template<class T=float, MatStoreType St=ROW_MAJOR, class Angle>
Mat<4,4,T,St> mat_rotation_axis_z(const Angle& a) noexcept;

Camera/view

template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_camera_view(const Vector3<T>& forward,
                              const Vector3<T>& right,
                              const Vector3<T>& up,
                              const Vector3<T>& camera_origin) noexcept;

Perspective projections

template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_perspective_left_handed (float fov_deg, float aspect, float near, float far) noexcept;

template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_perspective_right_handed(float fov_deg, float aspect, float near, float far) noexcept;

Orthographic projections

template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_ortho_left_handed (T left, T right, T bottom, T top, T near, T far) noexcept;

template<class T=float, MatStoreType St=ROW_MAJOR>
Mat<4,4,T,St> mat_ortho_right_handed(T left, T right, T bottom, T top, T near, T far) noexcept;

Look-at matrices

template<class T=float, MatStoreType St=COLUMN_MAJOR>
Mat<4,4,T,St> mat_look_at_left_handed (const Vector3<T>& eye,
                                       const Vector3<T>& center,
                                       const Vector3<T>& up);

template<class T=float, MatStoreType St=COLUMN_MAJOR>
Mat<4,4,T,St> mat_look_at_right_handed(const Vector3<T>& eye,
                                       const Vector3<T>& center,
                                       const Vector3<T>& up);

Screen-space helper

template<class Type=float>
static constexpr Mat<4,4> to_screen_mat(const Type& screen_w, const Type& screen_h) noexcept;
// Maps NDC to screen space (origin top-left, y down)

Examples

1) Building a left-handed camera and perspective

using V3 = omath::Vector3<float>;
using M4 = omath::Mat<4,4,float, omath::MatStoreType::COLUMN_MAJOR>;

V3 eye{0, 1, -5}, center{0, 0, 0}, up{0, 1, 0};
M4 view = omath::mat_look_at_left_handed<float, omath::MatStoreType::COLUMN_MAJOR>(eye, center, up);

float fov = 60.f, aspect = 16.f/9.f, n = 0.1f, f = 100.f;
M4 proj = omath::mat_perspective_left_handed<float, omath::MatStoreType::COLUMN_MAJOR>(fov, aspect, n, f);

// final VP
M4 vp = proj * view;

2) Inverting a transform safely

omath::Mat<4,4> T = omath::mat_translation(omath::Vector3<float>{2,3,4});
if (auto inv = T.inverted()) {
  // use *inv
} else {
  // handle non-invertible
}

3) Formatting for logs

omath::Mat<2,2> A = { {1,2},{3,4} };
std::string s = A.to_string();       // "[[    1.000,     2.000]\n [    3.000,     4.000]]"
std::string f = std::format("A = {}", A);  // uses std::formatter

Performance

  • Cache-friendly kernels per storage order when AVX2 is not enabled.
  • AVX2 path (OMATH_USE_AVX2) for float/double implements FMAs with 256-bit vectors for both row-major and column-major multiplication.
  • Complexity for A(R×K) * B(K×C): O(RKC) regardless of storage order.

Constraints & concepts

template<typename M1, typename M2>
concept MatTemplateEqual =
  (M1::rows == M2::rows) &&
  (M1::columns == M2::columns) &&
  std::is_same_v<typename M1::value_type, typename M2::value_type> &&
  (M1::store_type == M2::store_type);

Use this concept to constrain generic functions that operate on like-shaped matrices.


Exceptions

  • std::invalid_argument — initializer list dimensions mismatch.
  • std::out_of_range — out-of-bounds in at() when bounds checking is active (see source guard).
  • inverted() does not throw; returns std::nullopt if determinant() == 0.

Build switches

  • OMATH_USE_AVX2 — enable AVX2 vectorized multiplication paths (<immintrin.h> required).
  • Debug checks — the at() method contains a conditional range check; refer to the preprocessor guard in the code to enable/disable in your configuration.

Known requirements & interoperability

  • C++23 is required for multi-parameter operator[]. If you target pre-C++23, use at(r,c) instead.
  • All binary operations require matching Type and StoreType. Convert explicitly if needed.

See also

  • omath::Vector3<T>
  • Projection helpers: mat_perspective_*, mat_ortho_*
  • View helpers: mat_look_at_*, mat_camera_view
  • Construction helpers: mat_row_from_vector, mat_column_from_vector, mat_translation, mat_rotation_axis_*

Appendix: API summary (signatures)

// Core
Mat(); Mat(const Mat&); Mat(Mat&&);
Mat(std::initializer_list<std::initializer_list<Type>>);
explicit Mat(const Type* raw);
Mat& operator=(const Mat&); Mat& operator=(Mat&&);

static constexpr size_t row_count();
static constexpr size_t columns_count();
static consteval MatSize size();
static constexpr MatStoreType get_store_ordering();

T& at(size_t r, size_t c);
T const& at(size_t r, size_t c) const;
T& operator[](size_t r, size_t c);
T const& operator[](size_t r, size_t c) const;

void clear();
void set(const Type& v);
Type sum() const noexcept;

template<size_t OC> Mat<Rows,OC,Type,StoreType> operator*(const Mat<Columns,OC,Type,StoreType>&) const;
Mat& operator*=(const Type&); Mat operator*(const Type&) const;
Mat& operator/=(const Type&); Mat operator/(const Type&) const;

Mat<Columns,Rows,Type,StoreType> transposed() const noexcept;
Type determinant() const;                              // square only
std::optional<Mat> inverted() const;                   // square only

Mat<Rows-1,Columns-1,Type,StoreType> strip(size_t r, size_t c) const;
Type minor(size_t r, size_t c) const;
Type alg_complement(size_t r, size_t c) const;

auto& raw_array(); auto const& raw_array() const;
std::string  to_string() const noexcept;
std::wstring to_wstring() const noexcept;
std::u8string to_u8string() const noexcept;

bool operator==(const Mat&) const;
bool operator!=(const Mat&) const;

// Helpers (see sections above)

Last updated: 31 Oct 2025