From bf834a33e0bb39588dda84e4abfbb68f7867aff2 Mon Sep 17 00:00:00 2001 From: Hartmut Seichter Date: Mon, 21 Jan 2019 15:46:57 +0100 Subject: [PATCH] working a bit to get the constructor usage cleaned up for the whole matrix code --- src/core/CMakeLists.txt | 6 +- src/core/include/pw/core/matrix.hpp | 71 +++++++-- src/core/include/pw/core/matrixbase.hpp | 27 ++-- src/core/include/pw/core/quaternion.hpp | 173 +++++++++++++--------- src/core/include/pw/core/serialize.hpp | 2 +- src/core/include/pw/core/vector.hpp | 111 +++++++++----- src/core/tests/CMakeLists.txt | 20 +-- src/core/tests/pwcore_test_axisangle.cpp | 2 +- src/core/tests/pwcore_test_matrix.cpp | 9 +- src/core/tests/pwcore_test_quaternion.cpp | 18 ++- src/core/tests/pwcore_test_vector.cpp | 17 ++- 11 files changed, 293 insertions(+), 163 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2c4bdef..cafc464 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -7,8 +7,8 @@ set(hdrs include/pw/core/matrixbase.hpp include/pw/core/matrix.hpp include/pw/core/vector.hpp -# include/pw/core/quaternion.hpp -# include/pw/core/serialize.hpp + include/pw/core/quaternion.hpp + include/pw/core/serialize.hpp include/pw/core/image.hpp include/pw/core/point.hpp include/pw/core/rect.hpp @@ -25,7 +25,7 @@ set(srcs src/debug.cpp src/mesh.cpp src/core.cpp -# src/serialize.cpp + src/serialize.cpp src/timer.cpp src/image.cpp ${CMAKE_SOURCE_DIR}/README.md diff --git a/src/core/include/pw/core/matrix.hpp b/src/core/include/pw/core/matrix.hpp index 38323b4..85ceced 100644 --- a/src/core/include/pw/core/matrix.hpp +++ b/src/core/include/pw/core/matrix.hpp @@ -29,23 +29,27 @@ #include - namespace pw { - - template struct matrix_ : matrixbase_> { T data[R*C]; - matrix_() = default; + typedef matrixbase_> Base; + using Base::Base; - matrix_(const matrix_& other) + matrix_(const matrix_& other) { *this = other; } + explicit matrix_(std::initializer_list args) + { + typename std::initializer_list::iterator it = args.begin(); + for (;it != args.end();it++) data[it-args.begin()] = *it; + } + matrix_& operator = (const matrix_& other) { for (size_t i = 0; i < other.size();i++) (*this)[i] = other[i]; @@ -53,11 +57,11 @@ struct matrix_ : matrixbase_> { } template - matrix_(Arguments ...values) - : data {values... } + matrix_& set(Arguments ...values) { - static_assert(sizeof...(Arguments) == R*C, - "Incorrect number of arguments"); + static_assert(sizeof...(Arguments) == R*C, "Incorrect number of arguments"); + data = {values... }; + return *this; } //! rows @@ -81,12 +85,14 @@ struct matrix_ : matrixbase_> { return data[offset(r,c)]; } + inline const T* ptr() const { return &data[0]; } + //! set identity inline matrix_& set_identity() { for (unsigned int r = 0;r < rows(); r++) for (unsigned int c = 0; c < cols(); c++) - this->at(r,c) = (c == r) ? T(1) : T(0); + (*this)(r,c) = (c == r) ? T(1) : T(0); return *this; } @@ -167,6 +173,22 @@ struct matrix_ : matrixbase_> { inline bool square() const { return R == C; } + inline const matrix_ operator + (const matrix_ &other) const { + matrix_ res(*this); + for (size_t r = 0; r < R;r++) + for (size_t c = 0; c < C;c++) + res(r,c) += other(r,c); + return res; + } + + inline const matrix_ operator - (const matrix_ &other) const { + matrix_ res(*this); + for (size_t r = 0; r < R;r++) + for (size_t c = 0; c < C;c++) + res(r,c) -= other(r,c); + return res; + } + }; template <> inline @@ -218,6 +240,35 @@ using matrix4x4f = matrix_; using matrix4x4d = matrix_; using matrix4x4 = matrix_; +// +// +// + +template +struct matrix_tools { + + inline static + matrix4x4_ projection_from_frustum(T Left,T Right,T Bottom,T Top,T zNear,T zFar) + { + matrix4x4_ frustum; + + frustum.fill(0); + + frustum(0,0) = T(2) * zNear/(Right-Left); + frustum(1,1) = T(2) * zNear/(Top-Bottom); + + frustum(0,2) = (Right+Left)/(Right-Left); //A + frustum(1,2) = (Top+Bottom)/(Top-Bottom); //B + frustum(2,2) = - (zFar+zNear)/(zFar-zNear); //C + frustum(3,2) = -(T(2) * zFar*zNear)/(zFar-zNear); //D + + frustum(2,3) = -T(1); + + return frustum; + } +}; + + } diff --git a/src/core/include/pw/core/matrixbase.hpp b/src/core/include/pw/core/matrixbase.hpp index 7941fef..3db78fe 100644 --- a/src/core/include/pw/core/matrixbase.hpp +++ b/src/core/include/pw/core/matrixbase.hpp @@ -38,7 +38,10 @@ namespace pw { template struct matrixbase_ { - Derived& derived() { return static_cast(*this); } + + typedef T value_type; + + Derived& derived() { return static_cast(*this); } const Derived& derived() const { return static_cast(*this); } std::size_t size() const { @@ -54,11 +57,6 @@ struct matrixbase_ { return derived().fill(0); } - T trace() const { - return std::accumulate(std::begin(derived().data),std::end(derived().data),T(0)); - } - - inline T squared_norm() const { return std::accumulate(std::begin(derived().data),std::end(derived().data), T(0), [&](const T& a,const T& b){ @@ -70,9 +68,8 @@ struct matrixbase_ { return std::sqrt(squared_norm()); } - inline Derived& normalize() { - (*this) /= this->norm(); - return derived(); + inline const Derived normalized() const { + return *this / this->norm() ; } using iterator = T*; @@ -90,11 +87,15 @@ struct matrixbase_ { return derived().data[i]; } - inline Derived& operator *= (const T& b) { for (auto & e : *this) e *= b; return derived(); } - inline Derived& operator /= (const T& b) { for (auto & e : *this) e /= b; return derived(); } - inline Derived& operator += (const T& b) { for (auto & e : *this) e += b; return derived(); } - inline Derived& operator -= (const T& b) { for (auto & e : *this) e -= b; return derived(); } +// inline Derived& operator *= (const T& b) { for (auto & e : *this) e *= b; return derived(); } +// inline Derived& operator /= (const T& b) { for (auto & e : *this) e /= b; return derived(); } +// inline Derived& operator += (const T& b) { for (auto & e : *this) e += b; return derived(); } +// inline Derived& operator -= (const T& b) { for (auto & e : *this) e -= b; return derived(); } + inline const Derived operator * (const T& b) const { Derived r(derived()); for (auto & e : r) e *= b; return r; } + inline const Derived operator / (const T& b) const { Derived r(derived()); for (auto & e : r) e /= b; return r; } + inline const Derived operator + (const T& b) const { Derived r(derived()); for (auto & e : r) e += b; return r; } + inline const Derived operator - (const T& b) const { Derived r(derived()); for (auto & e : r) e -= b; return r; } }; diff --git a/src/core/include/pw/core/quaternion.hpp b/src/core/include/pw/core/quaternion.hpp index 22e7a9a..4948b00 100644 --- a/src/core/include/pw/core/quaternion.hpp +++ b/src/core/include/pw/core/quaternion.hpp @@ -20,14 +20,109 @@ * SOFTWARE. * */ -#ifndef TP_CORE_QUATERNION_HPP -#define TP_CORE_QUATERNION_HPP +#ifndef PW_CORE_QUATERNION_HPP +#define PW_CORE_QUATERNION_HPP #include -#include namespace pw { +/** + * simplified quaternion class + */ +template +struct quaternion_ : vector4_ { + + typedef vector4_ Base; + + using Base::Base; + using Base::x; + using Base::y; + using Base::z; + using Base::w; +// using Base::lerp; +// using Base::operator*; +// using Base::operator/; + + inline const quaternion_ operator * (const quaternion_& rhs) const { + return quaternion_( + rhs.w()*x() + rhs.x()*w() + rhs.y()*z() - rhs.z()*y(), + rhs.w()*y() - rhs.x()*z() + rhs.y()*w() + rhs.z()*x(), + rhs.w()*z() + rhs.x()*y() - rhs.y()*x() + rhs.z()*w(), + rhs.w()*w() - rhs.x()*x() - rhs.y()*y() - rhs.z()*z() + ); + } + + //! conjugate + inline quaternion_ conjugate() const { return quaternion_( { -x(),-y(),-z(),w() } ); } + + //! compute inverse + inline auto inverse() const { + return conjugate() / this->norm(); + } + + const matrix4x4_ to_matrix() const { + + matrix4x4_ m; m.set_identity(); + + T xx = x() * x(); + T xy = x() * y(); + T xz = x() * z(); + T xw = x() * w(); + + T yy = y() * y(); + T yz = y() * z(); + T yw = y() * w(); + + T zz = z() * z(); + T zw = z() * w(); + + m(0,0) = 1 - 2 * ( yy + zz ); + m(0,1) = 2 * ( xy - zw ); + m(0,2) = 2 * ( xz + yw ); + + m(1,0) = 2 * ( xy + zw ); + m(1,1) = 1 - 2 * ( xx + zz ); + m(1,2) = 2 * ( yz - xw ); + + m(2,0) = 2 * ( xz - yw ); + m(2,1) = 2 * ( yz + xw ); + m(2,2) = 1 - 2 * ( xx + yy ); + + return m; + } + + static quaternion_ from_matrix(const matrix_ &m) { + using std::sqrt; + const T wtemp = sqrt(T(1) + m(0,0) + m(1,1) + m(2,2)) / T(2); + const T w4 = T(4.0) * wtemp; + return quaternion_( + (m(2,1) - m(1,2)) / w4, + (m(0,2) - m(2,0)) / w4, + (m(1,0) - m(0,1)) / w4, + wtemp); + } + + static const quaternion_ normalized_lerp(const quaternion_ &a,const quaternion_ &b,const T &t) { + return quaternion_(lerp(a,b,t).normalized()); + } + +}; +// +// +// +typedef quaternion_ quaternion; +typedef quaternion_ quaternionf; +typedef quaternion_ quaterniond; + + + +} + + + + +#if 0 /** * simplified quaternion class */ @@ -104,16 +199,10 @@ public: inline const T norm() const { return _q.norm(); } //! dot product - inline const T dot(const quaternion_& other) const { return _q.dot(other._q); } + inline const T dot(const quaternion_& other) const { return dot(_q,other._q); } + - //! conjugate - inline quaternion_ conjugate() const { return quaternion_(-x(),-y(),-z(),w()); } - //! compute inverse - inline quaternion_ inverse() const { - const T one_over_squared_norm = T(1) / squared_norm(); - return conjugate() * one_over_squared_norm; - } //! compute normalized inline quaternion_ normalized() const { @@ -169,52 +258,9 @@ template const T quaternion_::_sqrt90 = std::sqrt(0.5); -template -const quaternion_ quaternion_::from_matrix(const matrix_ &m) { - using std::sqrt; - T wtemp = sqrt(T(1) + m.at(0,0) + m.at(1,1) + m.at(2,2)) / T(2.0); - const T w4 = T(4.0) * wtemp; - return quaternion_( - (m.at(2,1) - m.at(1,2)) / w4, - (m.at(0,2) - m.at(2,0)) / w4, - (m.at(1,0) - m.at(0,1)) / w4, - wtemp); -} - -template -const matrix_ quaternion_::to_matrix() const { - - matrix_ m; m.set_identity(); - - T xx = x() * x(); - T xy = x() * y(); - T xz = x() * z(); - T xw = x() * w(); - - T yy = y() * y(); - T yz = y() * z(); - T yw = y() * w(); - - T zz = z() * z(); - T zw = z() * w(); - - m.at(0,0) = 1 - 2 * ( yy + zz ); - m.at(0,1) = 2 * ( xy - zw ); - m.at(0,2) = 2 * ( xz + yw ); - - m.at(1,0) = 2 * ( xy + zw ); - m.at(1,1) = 1 - 2 * ( xx + zz ); - m.at(1,2) = 2 * ( yz - xw ); - - m.at(2,0) = 2 * ( xz - yw ); - m.at(2,1) = 2 * ( yz + xw ); - m.at(2,2) = 1 - 2 * ( xx + yy ); - - return m; -} template const quaternion_ quaternion_::identity() @@ -257,15 +303,7 @@ const quaternion_ quaternion_::rotate_90_degree_around_z(bool negative/* = return quaternion_(0,0,(negative) ? -_sqrt90 : _sqrt90, _sqrt90); } -template -const quaternion_ quaternion_::lerp(const quaternion_& qa,const quaternion_& qb,const T& t) { - return quaternion_(qa + (qb - qa) * t); -} -template -const quaternion_ quaternion_::normalized_lerp(const quaternion_& qa,const quaternion_& qb,const T& t) { - return quaternion_::lerp(qa,qb,t).normalized(); -} template const quaternion_ quaternion_::slerp(const quaternion_& qa,const quaternion_& qb,const T& t) @@ -306,14 +344,9 @@ const quaternion_ quaternion_::slerp(const quaternion_& qa,const quater return qm; } -// -// -// -typedef quaternion_ quaternion; -typedef quaternion_ quaternionf; -typedef quaternion_ quaterniond; - -} + + +#endif #endif diff --git a/src/core/include/pw/core/serialize.hpp b/src/core/include/pw/core/serialize.hpp index dd61b22..8395bb8 100644 --- a/src/core/include/pw/core/serialize.hpp +++ b/src/core/include/pw/core/serialize.hpp @@ -39,7 +39,7 @@ struct serialize { for (int r = 0; r < m.rows();r++) { for (int c = 0; c < m.cols();c++) { - ss << m.at(r,c) << " "; + ss << m(r,c) << " "; } ss << std::endl; } diff --git a/src/core/include/pw/core/vector.hpp b/src/core/include/pw/core/vector.hpp index cfac3f8..235bd5b 100644 --- a/src/core/include/pw/core/vector.hpp +++ b/src/core/include/pw/core/vector.hpp @@ -28,40 +28,44 @@ namespace pw { -template -struct vector_ : matrix_ +template +struct vector_ : matrix_ { - typedef matrix_ derived_type; + typedef matrix_ Base; - vector_() = default; - vector_(const derived_type& rhs) : derived_type(rhs) {} + using typename Base::value_type; + using Base::Base; - template - vector_(Arguments ...values) - : derived_type( { values...} ) - { - static_assert(sizeof...(Arguments) == N, - "Incorrect number of arguments"); + static T dot(const vector_ &a,const vector_ &b) { + vector_ r; for (size_t i = 0;i < N;i++) r[i] = a[i] * b[i]; + return std::accumulate(std::begin(r), std::end(r), T(0)); } + + static T angle_between(const vector_ &a,const vector_ &b) { + return std::acos( dot( a.normalized(), b.normalized() ) ); + } + + static const vector_ lerp(const vector_ &a,const vector_ &b,const T& t) { + return a + (b - a) * t; + } + }; -template -auto operator * (const vector_& a, const vector_& b) --> vector_ { - vector_ result; - for (std::size_t i = 0; i < N; ++i) { - result[i] = a[i] * b[i]; - } - return result; -} +template +struct vector2_ : vector_ { -template -auto dot(const vector_& a, const vector_& b) --> decltype(a[0] * b[0]) { - auto product = a * b; - using V = decltype(product.x); - return std::accumulate(std::begin(product), std::end(product), V(0)); -} + using vector_::vector_; + + + inline const T& x() const { return (*this)[0]; } + inline T& x() { return (*this)[0]; } + + inline const T& y() const { return (*this)[1]; } + + inline T& y() { return (*this)[1]; } + inline auto homogenous(T w = 1) const { return vector_(x(),y(),w); } + +}; template @@ -69,24 +73,55 @@ struct vector3_ : vector_ { using vector_::vector_; - inline static vector3_ forward() { return vector3_ ( T(0), T(0),-T(1) ); } - inline static vector3_ backward() { return vector3_( T(0), T(0), T(1) ); } - inline static vector3_ right() { return vector3_ ( T(1), T(0), T(0) ); } - inline static vector3_ left() { return vector3_ ( -T(1), T(0), T(0) ); } - inline static vector3_ up() { return vector3_ ( T(0), T(1), T(0) ); } - inline static vector3_ down() { return vector3_ ( T(0),-T(1), T(0) ); } + inline const T& x() const { return (*this)[0]; } + inline T& x() { return (*this)[0]; } + + inline const T& y() const { return (*this)[1]; } + inline T& y() { return (*this)[1]; } + + inline const T& z() const { return (*this)[2]; } + inline T& z() { return (*this)[2]; } + + inline auto xy() const { return vector2_( { x(),y() } ); } + inline auto homogenous(T w = 1) const { return vector_( { x(),y(),z(),w } ); } + + + inline static vector3_ forward() { return vector3_ ( { T(0), T(0),-T(1) } ); } + inline static vector3_ backward() { return vector3_( { T(0), T(0), T(1) } ); } + inline static vector3_ right() { return vector3_ ( { T(1), T(0), T(0) } ); } + inline static vector3_ left() { return vector3_ ( {-T(1), T(0), T(0) } ); } + inline static vector3_ up() { return vector3_ ( { T(0), T(1), T(0) } ); } + inline static vector3_ down() { return vector3_ ( { T(0),-T(1), T(0) } ); } }; +template +struct vector4_ : vector_ { + + using vector_::vector_; + + inline const T& x() const { return (*this)[0]; } + inline T& x() { return (*this)[0]; } + + inline const T& y() const { return (*this)[1]; } + inline T& y() { return (*this)[1]; } + + inline const T& z() const { return (*this)[2]; } + inline T& z() { return (*this)[2]; } + + inline const T& w() const { return (*this)[3]; } + inline T& w() { return (*this)[3]; } + + inline auto xyz() const { return vector3_({ x(),y(),z() } ); } + + inline auto project() const { return vector3_({ x()/w(),y()/w(),z()/w() } ); } + +}; // // // -template using vector2_ = vector_; -//template using vector3_ = vector_; -template using vector4_ = vector_; - using vector2f = vector2_; using vector2d = vector2_; using vector2 = vector2_; @@ -104,7 +139,7 @@ using vector4 = vector4_; -#if ___OLDSTUFF +#if defined(___OLDSTUFF) template class vector_ : public matrix_ { diff --git a/src/core/tests/CMakeLists.txt b/src/core/tests/CMakeLists.txt index b414871..89e795b 100644 --- a/src/core/tests/CMakeLists.txt +++ b/src/core/tests/CMakeLists.txt @@ -5,20 +5,20 @@ add_executable(pwcore_test_matrix target_link_libraries(pwcore_test_matrix pwcore) -#add_executable(pwcore_test_vector -# pwcore_test_vector.cpp -# ) +add_executable(pwcore_test_vector + pwcore_test_vector.cpp + ) -#target_link_libraries(pwcore_test_vector -# pwcore) +target_link_libraries(pwcore_test_vector + pwcore) -#add_executable(pwcore_test_quaternion -# pwcore_test_quaternion.cpp -# ) +add_executable(pwcore_test_quaternion + pwcore_test_quaternion.cpp + ) -#target_link_libraries(pwcore_test_quaternion -# pwcore) +target_link_libraries(pwcore_test_quaternion + pwcore) add_executable(pwcore_test_axisangle diff --git a/src/core/tests/pwcore_test_axisangle.cpp b/src/core/tests/pwcore_test_axisangle.cpp index 648d3b8..4445193 100644 --- a/src/core/tests/pwcore_test_axisangle.cpp +++ b/src/core/tests/pwcore_test_axisangle.cpp @@ -8,7 +8,7 @@ int main(int argc,char **argv) { pw::axisangle_ aa = pw::axisangle_(); - pw::quaternionf qf = pw::quaternionf::from_axisangle(aa); +// pw::quaternionf qf = pw::quaternionf::from_axisangle(aa); // std::cout << "aa as quaternion as vector = " << pw::serialize::matrix(qf.as_vector()) << std::endl; diff --git a/src/core/tests/pwcore_test_matrix.cpp b/src/core/tests/pwcore_test_matrix.cpp index b462ce6..1803ab6 100644 --- a/src/core/tests/pwcore_test_matrix.cpp +++ b/src/core/tests/pwcore_test_matrix.cpp @@ -37,7 +37,7 @@ int main(int argc,char **argv) { vector2f v2; v2[0] = 1; v2[1] = 3; - vector2f v3(1.f,2.f); + vector2f v3( { 1.f,2.f } ); auto m22_inv = m22.inverse(); auto m22_id = m22_inv * m22; @@ -65,8 +65,11 @@ int main(int argc,char **argv) { std::cout << "v2_b.norm " << v2_b.norm() << std::endl; - v2_b.normalize(); - std::cout << "v2_b.normalized " << v2_b << std::endl; +// v2_b.normalize(); + std::cout << "v2_b.normalized " << v2_b.normalized() << std::endl; + + std::cout << "v2_b~v3_t " << rad_to_deg(vector2f::angle_between(v2,v3)) << std::endl; + diff --git a/src/core/tests/pwcore_test_quaternion.cpp b/src/core/tests/pwcore_test_quaternion.cpp index 6e4b97f..9aa30fa 100644 --- a/src/core/tests/pwcore_test_quaternion.cpp +++ b/src/core/tests/pwcore_test_quaternion.cpp @@ -5,22 +5,24 @@ int main(int argc,char **argv) { - pw::quaternion_ qf = pw::quaternionf::rotate_90_degree_around_x(); + pw::quaternion_ qf; - std::cout << "qf = " << pw::serialize::matrix(qf.as_vector()) << std::endl; + std::cout << "qf = " << pw::serialize::matrix(qf) << std::endl; std::cout << "qf.matrix() = " << pw::serialize::matrix(qf.to_matrix()) << std::endl; std::cout << "qf.squared_norm() = " << qf.squared_norm() << std::endl; - std::cout << "qf.dot(qf) = " << qf.dot(qf) << std::endl; - std::cout << "qf.conjugate() = " << pw::serialize::matrix(qf.conjugate().as_vector()) << std::endl; +// std::cout << "qf.dot(qf) = " << qf.dot(qf) << std::endl; + std::cout << "qf.conjugate() = " << pw::serialize::matrix(qf.conjugate()) << std::endl; - pw::quaternionf qi = qf.inverse(); - std::cout << "qf.inverse() (qi) = " << pw::serialize::matrix(qi.as_vector()) << std::endl; + pw::quaternionf qc = qf.conjugate(); + std::cout << "qf.conjugate() (qc) = " << pw::serialize::matrix(qc) << std::endl; + pw::quaternionf qi = qf.inverse(); + std::cout << "qf.inverse() (qi) = " << pw::serialize::matrix(qi) << std::endl; - pw::quaternionf qmid = pw::quaternionf::normalized_lerp(qi,qf,0.5f); +// pw::quaternionf qmid = pw::quaternionf::normalized_lerp(qi,qf,0.5f); - std::cout << "qmid.dot() (half between qf and qi) = " << pw::rad_to_deg(std::acos(qmid.dot(qf))) << std::endl; +// std::cout << "qmid.dot() (half between qf and qi) = " << pw::rad_to_deg(std::acos(qmid.dot(qf))) << std::endl; diff --git a/src/core/tests/pwcore_test_vector.cpp b/src/core/tests/pwcore_test_vector.cpp index 1cff586..d0ae64c 100644 --- a/src/core/tests/pwcore_test_vector.cpp +++ b/src/core/tests/pwcore_test_vector.cpp @@ -7,20 +7,25 @@ int main(int argc,char **argv) { pw::vector4_ v4; + pw::vector3f v; v4.fill(1.5); std::cout << "v4 = " << pw::serialize::matrix(v4) << std::endl; - std::cout << "row_stride() : " << v4.row_stride() << std::endl; - std::cout << "col_stride() : " << v4.col_stride() << std::endl; std::cout << "rows() : " << v4.rows() << std::endl; std::cout << "cols() : " << v4.cols() << std::endl; - std::cout << "data() : " << v4.data() << std::endl; - std::cout << "data()[0] : " << v4.data()[0] << std::endl; - std::cout << "at(0,0) : " << v4.at(0,0) << std::endl; + std::cout << "ptr() : " << v4.ptr() << std::endl; + std::cout << "ptr()[0] : " << v4.ptr()[0] << std::endl; + std::cout << "(0,0) : " << v4(0,0) << std::endl; - pw::vector3f v3 = v4.xyz(); + auto v3 = v4.xyz(); + + auto v3_p = v4.project(); + + auto v3_h = v.homogenous(); + +// auto v3_lerp = vector4f::lerp() std::cout << "v3 = " << pw::serialize::matrix(v3) << std::endl;