https://github.com/nurunue/3dcg-learnings

 

//! @file vmmath.hpp
//! @brief 3DCGに使用する基本的な数学
//! @author suzulang.com
//! @date 令和三年六月二十五日

#pragma once

#include <cmath>

namespace szl {
  namespace maths {

    //! @brief 入力が浮動小数点の時はそのまま返し、それ以外の時はfloatを返す
    template<typename T, bool isint>struct IsRealT;
    template<typename T>struct IsRealT<T, true> { using type = T; };
    template<typename T>struct IsRealT<T, false> { using type = float; };

    //! @brief 指定した型が整数ならfloatを返すテンプレート
    template<typename T>
    struct RealIfInt {
      using type = typename IsRealT<T, std::is_floating_point<T>::value>::type;
    };

    //! @brief 誤差の定義
    template<typename T>
    struct real_error { static constexpr float value = (float)1e-7; };
    template<>
    struct real_error<double> { static constexpr double value = 1e-15; };


  }
  namespace maths {

    //! @brief 誤差の閾値未満かをチェック
    //! @param [in] value 検査する値
    //! @retval true 値の絶対値は閾値より小さい(valueは0にとても近い)
    //! @retval false 値の絶対値は閾値より大きい(valueは0ではない)
    template<typename T>
    inline bool is_error(const T value) {
      return
        abs(value) < real_error<T>::value;
    }

    //! @brief 与えたラジアンを度に変換する
    //! @param [in] radian ラジアンの値
    //! @return 度の値
    template<typename ScalarT, typename ReturnT = typename maths::RealIfInt<ScalarT>::type>
    inline auto to_degree(const ScalarT radian) {
      return static_cast<ReturnT>(radian * 180.0 / 3.1415926535897932384626);
    }

    //! @brief 与えた度をラジアンに変換する
    //! @param [in] degree 度の値
    //! @return ラジアンの値
    template<typename ScalarT, typename ReturnT = typename maths::RealIfInt<ScalarT>::type>
    inline auto to_radian(const ScalarT degree) {
      return static_cast<ReturnT>(degree * 3.1415926535897932384626 / 180.0);
    }


    //! @brief 値を指定した範囲に収める
    //! @param [in] value
    //! @param [in] low valueが取り得る最小値
    //! @param [in] high valueが取り得る最大値
    //! @return low <= value <= high が保証されたvalue
    template<typename VALUE, typename LOW, typename HIGH>
    inline VALUE clamp(const VALUE value, const LOW low, const HIGH high) {
      if (value < low)
        return static_cast<VALUE>(low);
      if (value > high)
        return static_cast<VALUE>(high);
      return value;
    }

    //! @brief cotangent
    template<typename ScalarT, typename ReturnT = typename maths::RealIfInt<ScalarT>::type>
    inline ReturnT cot(const ScalarT theta) {
      return (ReturnT)(1.0 / tan(theta));
    }

  }

  //ベクトル
  namespace mathv {

    //! @brief 三次元ベクトルの長さを求める
    //! @param [in] v 三次元ベクトル
    //! @return ベクトルの長さ
    template<typename VectorT3>
    inline auto length3(const VectorT3& v) {
      return std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    }

    //! @brief ベクトルの足し算
    //! @param [in] vd 結果の格納先
    //! @param [in] va 三次元ベクトル
    //! @param [in] vb 三次元ベクトル
    //! @return vd
    template<typename VectorTD3, typename VectorTA3, typename VectorTB3>
    inline VectorTD3& add3(VectorTD3& vd, const VectorTA3& va, const VectorTB3& vb) {

      using TDScalarT = typename std::remove_reference<decltype(vd[0])>::type;

      const auto x1 = va[0];
      const auto y1 = va[1];
      const auto z1 = va[2];
      const auto x2 = vb[0];
      const auto y2 = vb[1];
      const auto z2 = vb[2];

      vd[0] = static_cast<TDScalarT>(va[0] + vb[0]);
      vd[1] = static_cast<TDScalarT>(va[1] + vb[1]);
      vd[2] = static_cast<TDScalarT>(va[2] + vb[2]);

      return vd;

    }
    //ベクトルの引き算
    //! @param [out] vd 結果の格納先
    //! @param [in] va 三次元ベクトル
    //! @param [in] vb 三次元ベクトル
    //! @return vd
    template<typename VectorTD3, typename VectorTA3, typename VectorTB3>
    inline VectorTD3& sub3(VectorTD3& vd, const VectorTA3& va, const VectorTB3& vb) {

      using TDScalarT = typename std::remove_reference<decltype(vd[0])>::type;

      vd[0] = static_cast<TDScalarT>(va[0] - vb[0]);
      vd[1] = static_cast<TDScalarT>(va[1] - vb[1]);
      vd[2] = static_cast<TDScalarT>(va[2] - vb[2]);

      return vd;

    }

    //! @brief 三次元ベクトルの内積を求める
    //! @param [in] va 三次元ベクトル
    //! @param [in] vb 三次元ベクトル
    //! @return 内積
    template<typename VectorTA3, typename VectorTB3>
    inline auto inner3(const VectorTA3& va, const VectorTB3& vb) {
      return ((va[0])*(vb[0]) + (va[1])*(vb[1]) + (va[2])*(vb[2]));
    }

    //! @brief 三次元ベクトルの外積を求める
    //! @param [out] vd 結果の格納先
    //! @param [in] va 三次元ベクトル
    //! @param [in] vb 三次元ベクトル
    //! @return vd
    template<typename VectorTD3, typename VectorTA3, typename VectorTB3>
    inline VectorTD3& outer3(VectorTD3& vd, const VectorTA3& va, const VectorTB3& vb) {

      using TDScalarT = typename std::remove_reference<decltype(vd[0])>::type;

      const auto x1 = va[0];
      const auto y1 = va[1];
      const auto z1 = va[2];
      const auto x2 = vb[0];
      const auto y2 = vb[1];
      const auto z2 = vb[2];

      vd[0] = static_cast<TDScalarT>(y1 * z2 - z1 * y2);
      vd[1] = static_cast<TDScalarT>(z1 * x2 - x1 * z2);
      vd[2] = static_cast<TDScalarT>(x1 * y2 - y1 * x2);

      return vd;

    }

    //! @brief 三次元ベクトルを正規化する
    //! @param [in] v 三次元ベクトル
    //! @retval true 計算成功
    //! @retval false ベクトルの長さが短すぎて失敗した
    template<typename VectorT3>
    inline bool normalize3(VectorT3& v)
    {
      auto& x = v[0];
      auto& y = v[1];
      auto& z = v[2];

      auto len = std::sqrt(x * x + y * y + z * z);

      if (maths::is_error(len) == true)
        return false;

      len = static_cast<decltype(len)>(1.0) / len;
      x *= len;
      y *= len;
      z *= len;

      return true;
    }

    //! @brief 三次元ベクトルの角度を計算する
    //! @param [in] va 三次元ベクトル
    //! @param [in] vb 三次元ベクトル
    //! @return ラジアンの角度
    template<typename VectorTA3, typename VectorTB3>
    inline auto angle3(const VectorTA3& va, const VectorTB3 vb) {
      auto ac = inner3(va, vb) / (length3(va) * length3(vb));
      using scalarT = decltype(ac);
      constexpr auto low = static_cast<scalarT>(-1.0) + maths::real_error<scalarT>::value;
      constexpr auto high = static_cast<scalarT>(1.0) - maths::real_error<scalarT>::value;
      return std::acos(clamp(ac, low, high));
    }


    //! @brief 直線の始点・終点から、始点→終点のベクトルを求める
    //! @param [out] vd 結果の格納先
    //! @param [in] from 直線の始点
    //! @param [in] to 直線の終点
    //! @return vd
    template<typename VectorTD3, typename VectorFrom3, typename VectorTo3>
    inline VectorTD3& vector_from_line3(VectorTD3& vd, const VectorFrom3& from, const VectorTo3& to) {
      vd[0] = to[0] - from[0];
      vd[1] = to[1] - from[1];
      vd[2] = to[2] - from[2];
      return vd;
    }
    //! @brief 二つの座標の距離
    //! @param [in] va 三次元座標1
    //! @param [in] vb 三次元座標2
    //! return 距離
    template<typename VectorTA3, typename VectorTB3>
    inline auto distance3(const VectorTA3& va, const VectorTB3& vb) {
      return sqrt(
        pow((vb[0] - va[0]), 2) +
        pow((vb[1] - va[1]), 2) +
        pow((vb[2] - va[2]), 2));
    }

    //! @brief ベクトルにスカラーをかける
    //! @param [in,out] vd 元となるベクトル及び計算結果
    //! @param [in] scalar 掛けるスカラー値
    //! @return vd
    template<typename VectorT3, typename ScalarT>
    inline VectorT3& multi3(VectorT3& vd, const ScalarT scalar) {
      vd[0] *= scalar;
      vd[1] *= scalar;
      vd[2] *= scalar;
      return vd;
    }

    //! @brief 配列にNANが入っているか確認
    //! @param [in] v 評価する配列
    //! @param [in] count 行列の要素数
    //! @retval true 配列の中にnanがある
    //! @retval false 配列の中にnanはない
    template<typename ScalarT>
    bool chek_array_nan(const ScalarT& v, const size_t count) {
      for (size_t i = 0; i < count; i++) {
        if (isnan(v[i]) == true)
          return true;
      }
      return false;
    }

  }


  //行列(補助関数)
  namespace mathm {
    ////////////////////////////////////////////////
    // 要素番号計算
    ////////////////////////////////////////////////

    //! @brief 与えられた番号が正方行列の対角線の要素かどうかを判定する
    //! @param [in] index 一次元配列の要素番号
    //! @param [in] Row 行列の一行の長さ
    //! @retval indexが一行Rowの行列上でi==j
    //! @retval i != j
    inline bool is_identity_matrix_diagonal(const int index, const int Row) {
      int row = index / Row;
      int col = index % Row;
      return row == col;
    }

    //! @brief IxJ行列の i,j の要素にアクセスする
    //! @param [in] i 行番号
    //! @param [in] j 列番号
    //! @param [in] I 行数
    //! @return i,jの一次元配列におけるインデクス
    inline int matijI(const int i, const int j, const int I) {
      return i + I * j;
    }
  }

  // 行列演算用関数群
  namespace mathm {

    ////////////////////////////////////////////////
    // 零行列
    ////////////////////////////////////////////////
    //! @brief 行列の要素を全て0にする
    //! @param [in] _mat operator[]で一次元配列としてアクセス可能な行列
    //! @param [in] size size一次元配列としての要素数
    //! @return なし
    template<typename Matrix>
    void zero_matrix(Matrix& _mat, const int size) {
      using TT = typename std::remove_reference<decltype(_mat[0])>::type;
      for (size_t i = 0; i < size; i++) {
        _mat[i] = TT(0);
      }
    }

    //! @brief 行列の要素を全て0にする(4x4行列用)
    //! @return なし
    template<typename Matrix>
    inline void zero_matrix4(Matrix& mat44) {
      zero_matrix(mat44, 4 * 4);
    }
    //! @brief 行列の要素を全て0にする(3x3行列用)
    //! @return なし
    template<typename Matrix>
    inline void zero_matrix3(Matrix& mat44) {
      zero_matrix(mat44, 3 * 3);
    }

    ////////////////////////////////////////////////
    // 単位行列
    ////////////////////////////////////////////////

    //! @brief 正方行列を単位行列にする
    //! @param [in,out] _mat 正方行列
    //! @param [in] Row 一行の要素数
    //! @return _mat
    template<typename Matrix>
    Matrix& load_identity(Matrix& _mat, const int Row) {
      int size = Row * Row;
      for (int i = 0; i < size; i++) {
        if (is_identity_matrix_diagonal(i, Row)) {
          _mat[i] = 1;
        }
        else {
          _mat[i] = 0;
        }
      }
      return _mat;
    }

    //! @brief 正方行列を単位行列にする(4x4)
    //! @param [in,out] _mat 正方行列
    //! @return _mat
    template<typename Matrix>
    Matrix& load_identity4(Matrix& _mat) {
      return load_identity(_mat, 4);
    }

    //! @brief 正方行列を単位行列にする(3x3)
    //! @param [in,out] _mat 正方行列
    //! @return _mat
    template<typename Matrix>
    Matrix& load_identity3(Matrix& _mat) {
      return load_identity(_mat, 3);
    }


    ////////////////////////////////////////////////
    // 逆行列
    ////////////////////////////////////////////////

    //! @brief 4x4行列の行列式を取得
    //! @param [in] dm16 4x4行列
    //! @return 行列式
    template<typename Matrix>
    inline auto determinant44(const Matrix& dm16) {

      using scalar_t = typename std::remove_reference<decltype(dm16[0])>::type;

      const scalar_t a11 = dm16[0];
      const scalar_t a12 = dm16[1];
      const scalar_t a13 = dm16[2];
      const scalar_t a14 = dm16[3];

      const scalar_t a21 = dm16[4];
      const scalar_t a22 = dm16[5];
      const scalar_t a23 = dm16[6];
      const scalar_t a24 = dm16[7];

      const scalar_t a31 = dm16[8];
      const scalar_t a32 = dm16[9];
      const scalar_t a33 = dm16[10];
      const scalar_t a34 = dm16[11];

      const scalar_t a41 = dm16[12];
      const scalar_t a42 = dm16[13];
      const scalar_t a43 = dm16[14];
      const scalar_t a44 = dm16[15];


      return a11 * a22* a33* a44 + a11 * a23 * a34 * a42 + a11 * a24 * a32 * a43
        + a12 * a21 * a34 * a43 + a12 * a23 * a31 * a44 + a12 * a24 * a33 * a41
        + a13 * a21 * a32 * a44 + a13 * a22 * a34 * a41 + a13 * a24 * a31 * a42
        + a14 * a21 * a33 * a42 + a14 * a22 * a31 * a43 + a14 * a23 * a32 * a41
        - a11 * a22 * a34 * a43 - a11 * a23 * a32 * a44 - a11 * a24 * a33 * a42
        - a12 * a21 * a33 * a44 - a12 * a23 * a34 * a41 - a12 * a24 * a31 * a43
        - a13 * a21 * a34 * a42 - a13 * a22 * a31 * a44 - a13 * a24 * a32 * a41
        - a14 * a21 * a32 * a43 - a14 * a22 * a33 * a41 - a14 * a23 * a31 * a42;
    }





    //! @brief 4x4行列の逆行列を取得
    //! @param [out] dst_dm16 結果を格納する一次元配列
    //! @param [in] src_dm16 元の行列
    //! @retval true 成功した
    //! @retval false 行列式の大きさが小さすぎて失敗した
    template<typename MatrixD, typename MatrixS>
    inline bool inverse44(MatrixD& dst_dm16, const MatrixS& src_dm16) {

      using d_scalar_t = typename std::remove_reference<decltype(dst_dm16[0])>::type;
      using s_scalar_t = typename std::remove_reference<decltype(src_dm16[0])>::type;

      s_scalar_t a11 = src_dm16[0];
      s_scalar_t a12 = src_dm16[1];
      s_scalar_t a13 = src_dm16[2];
      s_scalar_t a14 = src_dm16[3];

      s_scalar_t a21 = src_dm16[4];
      s_scalar_t a22 = src_dm16[5];
      s_scalar_t a23 = src_dm16[6];
      s_scalar_t a24 = src_dm16[7];

      s_scalar_t a31 = src_dm16[8];
      s_scalar_t a32 = src_dm16[9];
      s_scalar_t a33 = src_dm16[10];
      s_scalar_t a34 = src_dm16[11];

      s_scalar_t a41 = src_dm16[12];
      s_scalar_t a42 = src_dm16[13];
      s_scalar_t a43 = src_dm16[14];
      s_scalar_t a44 = src_dm16[15];

      d_scalar_t& b11 = dst_dm16[0];
      d_scalar_t& b12 = dst_dm16[1];
      d_scalar_t& b13 = dst_dm16[2];
      d_scalar_t& b14 = dst_dm16[3];
      d_scalar_t& b21 = dst_dm16[4];
      d_scalar_t& b22 = dst_dm16[5];

      d_scalar_t& b23 = dst_dm16[6];
      d_scalar_t& b24 = dst_dm16[7];
      d_scalar_t& b31 = dst_dm16[8];
      d_scalar_t& b32 = dst_dm16[9];
      d_scalar_t& b33 = dst_dm16[10];
      d_scalar_t& b34 = dst_dm16[11];

      d_scalar_t& b41 = dst_dm16[12];
      d_scalar_t& b42 = dst_dm16[13];
      d_scalar_t& b43 = dst_dm16[14];
      d_scalar_t& b44 = dst_dm16[15];

      b11 = static_cast<d_scalar_t>(a22 * a33 * a44 + a23 * a34 * a42 + a24 * a32 * a43 - a22 * a34 * a43 - a23 * a32 * a44 - a24 * a33 * a42);
      b12 = static_cast<d_scalar_t>(a12 * a34 * a43 + a13 * a32 * a44 + a14 * a33 * a42 - a12 * a33 * a44 - a13 * a34 * a42 - a14 * a32 * a43);
      b13 = static_cast<d_scalar_t>(a12 * a23 * a44 + a13 * a24 * a42 + a14 * a22 * a43 - a12 * a24 * a43 - a13 * a22 * a44 - a14 * a23 * a42);
      b14 = static_cast<d_scalar_t>(a12 * a24 * a33 + a13 * a22 * a34 + a14 * a23 * a32 - a12 * a23 * a34 - a13 * a24 * a32 - a14 * a22 * a33);

      b21 = static_cast<d_scalar_t>(a21 * a34 * a43 + a23 * a31 * a44 + a24 * a33 * a41 - a21 * a33 * a44 - a23 * a34 * a41 - a24 * a31 * a43);
      b22 = static_cast<d_scalar_t>(a11 * a33 * a44 + a13 * a34 * a41 + a14 * a31 * a43 - a11 * a34 * a43 - a13 * a31 * a44 - a14 * a33 * a41);
      b23 = static_cast<d_scalar_t>(a11 * a24 * a43 + a13 * a21 * a44 + a14 * a23 * a41 - a11 * a23 * a44 - a13 * a24 * a41 - a14 * a21 * a43);
      b24 = static_cast<d_scalar_t>(a11 * a23 * a34 + a13 * a24 * a31 + a14 * a21 * a33 - a11 * a24 * a33 - a13 * a21 * a34 - a14 * a23 * a31);
      b31 = static_cast<d_scalar_t>(a21 * a32 * a44 + a22 * a34 * a41 + a24 * a31 * a42 - a21 * a34 * a42 - a22 * a31 * a44 - a24 * a32 * a41);
      b32 = static_cast<d_scalar_t>(a11 * a34 * a42 + a12 * a31 * a44 + a14 * a32 * a41 - a11 * a32 * a44 - a12 * a34 * a41 - a14 * a31 * a42);
      b33 = static_cast<d_scalar_t>(a11 * a22 * a44 + a12 * a24 * a41 + a14 * a21 * a42 - a11 * a24 * a42 - a12 * a21 * a44 - a14 * a22 * a41);
      b34 = static_cast<d_scalar_t>(a11 * a24 * a32 + a12 * a21 * a34 + a14 * a22 * a31 - a11 * a22 * a34 - a12 * a24 * a31 - a14 * a21 * a32);
      b41 = static_cast<d_scalar_t>(a21 * a33 * a42 + a22 * a31 * a43 + a23 * a32 * a41 - a21 * a32 * a43 - a22 * a33 * a41 - a23 * a31 * a42);
      b42 = static_cast<d_scalar_t>(a11 * a32 * a43 + a12 * a33 * a41 + a13 * a31 * a42 - a11 * a33 * a42 - a12 * a31 * a43 - a13 * a32 * a41);
      b43 = static_cast<d_scalar_t>(a11 * a23 * a42 + a12 * a21 * a43 + a13 * a22 * a41 - a11 * a22 * a43 - a12 * a23 * a41 - a13 * a21 * a42);
      b44 = static_cast<d_scalar_t>(a11 * a22 * a33 + a12 * a23 * a31 + a13 * a21 * a32 - a11 * a23 * a32 - a12 * a21 * a33 - a13 * a22 * a31);

      auto detA = determinant44(src_dm16);

      if (maths::is_error(detA) == true) {
        load_identity4(dst_dm16);
        return false;
      }

      detA = static_cast<decltype(detA)>(1.0) / detA;

      for (int i = 0; i < 16; i++) {
        dst_dm16[i] *= static_cast<d_scalar_t>(detA);
      }
      return true;
    }

    ////////////////////////////////////////////////
    // 行列の転置
    ////////////////////////////////////////////////
    //! @brief IxJ行列を転置しJxI行列に格納する
    //! @param [out] dstJI JxI行列
    //! @param [in] srcIJ IxJ行列
    //! @param [in] I srcIJ行列の行数
    //! @param [in] J srcIJ行列の列数
    //! @return dstJI
    template<typename MatrixD, typename MatrixS>
    inline MatrixD& transpose(MatrixD& dstJI, const MatrixS& srcIJ, const int I, const int J) {
      //i...行
      //j...列
      for (int i = 0; i < I; i++) {
        for (int j = 0; j < J; j++) {
          dstJI[matijI(j, i, J)] = srcIJ[matijI(i, j, I)];
        }
      }
      return dstJI;
    }


    //! @brief 16個のスカラーの一次元配列からなる4*4行列を転置する
    //! @param [in,out] src16 データ real_t[16]
    //! @return src16と同じもの
    template<typename Matrix>
    inline Matrix& transpose44(Matrix& src16) {
      //i...行
      //j...列
      for (int i = 1; i < 4; i++) {
        for (int j = i; j > 0; j--) {
          std::swap(src16[i * 4 + i - j], src16[i * 4 + i - 4 * j]);
        }
      }
      return src16;
    }

    ////////////////////////////////////////////////
    // 行列の積
    ////////////////////////////////////////////////
    //! @brief IxJ行列 × JxK行列 の積
    //! @param [out] C Cik 計算結果格納先
    //! @param [in] A Aijの行列
    //! @param [in] B Bjkの行列
    //! @param [in] I Aの行列の行数
    //! @param [in] J Aの行列の列数
    //! @param [in] K Bの行列の列数
    //! @return C
    template<typename MatrixC, typename MatrixA, typename MatrixB>
    MatrixC& mult_matrixIJK(MatrixC & C, const MatrixA & A, const MatrixB & B, const int I, const int J, const int K) {

      using scalar_t = typename std::remove_reference<decltype(C[0])>::type;


      for (int i = 0; i < I; i++)
        for (int k = 0; k < K; k++) {
          C[matijI(i, k, I)] = static_cast<scalar_t>(0.0);
          for (int j = 0; j < J; j++) {
            C[matijI(i, k, I)] += static_cast<scalar_t>(A[matijI(i, j, I)] * B[matijI(j, k, J)]);
          }
        }

      return C;
    }


    //! @brief 4x4行列の積
    //! @param [out] mr 結果の格納先。4x4行列
    //! @param [in] ma 4x4行列1
    //! @param [in] mb 4x4行列2
    //! @return mr
    template<typename MatrixD, typename MatrixA, typename MatrixB>
    inline MatrixD& mult_matrix44(MatrixD& mr, const MatrixA& ma, const MatrixB& mb) {
      return mult_matrixIJK(mr, ma, mb, 4, 4, 4);
    }

    //! @brief 4x4行列で四次元ベクトルを変換
    //! @param [out] out4 結果を格納する四次元ベクトル
    //! @param [in] matrix 4x4行列
    //! @param [in] in4 変換する四次元ベクトル
    //! @return out4
    template<typename VectorD4, typename Matrix4, typename VectorS4>
    inline VectorD4& mult_m4_v4(VectorD4& out4, const Matrix4& matrix, const VectorS4& in4)
    {
      int i;

      for (i = 0; i < 4; i++) {
        out4[i] =
          in4[0] * matrix[0 * 4 + i] +
          in4[1] * matrix[1 * 4 + i] +
          in4[2] * matrix[2 * 4 + i] +
          in4[3] * matrix[3 * 4 + i];
      }
      return out4;
    }
  }

}