From 2b57e3e69679a71746f548526a20e221a7ca5481 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 29 Sep 2020 19:15:53 +0300 Subject: [PATCH] Add generic affine transform class & unify transform interfaces --- .../include/psemek/geom/affine_transform.hpp | 147 +++++++++++ libs/geom/include/psemek/geom/permutation.hpp | 111 +++++--- libs/geom/include/psemek/geom/rotation.hpp | 245 +++++++++++------- libs/geom/include/psemek/geom/scale.hpp | 134 ++++++---- libs/geom/include/psemek/geom/translation.hpp | 99 +++---- libs/geom/source/camera.cpp | 17 +- 6 files changed, 522 insertions(+), 231 deletions(-) create mode 100644 libs/geom/include/psemek/geom/affine_transform.hpp diff --git a/libs/geom/include/psemek/geom/affine_transform.hpp b/libs/geom/include/psemek/geom/affine_transform.hpp new file mode 100644 index 00000000..2cff7be7 --- /dev/null +++ b/libs/geom/include/psemek/geom/affine_transform.hpp @@ -0,0 +1,147 @@ +#pragma once + +#include +#include +#include +#include + +namespace psemek::geom +{ + + // Affine transformation from M-dimensional to N-dimensional affine space + template + struct affine_transform + { + matrix m; + + static affine_transform zero(); + + affine_transform(); + affine_transform(matrix const & matrix); + affine_transform(matrix const & linear, vector const & translation); + affine_transform(affine_transform const &) = default; + + matrix affine_matrix() const; + matrix linear_matrix() const; + vector translation_vector() const; + matrix homogeneous_matrix() const; + + vector operator()(vector const & v) const; + point operator()(point const & p) const; + }; + + template + affine_transform affine_transform::zero() + { + return affine_transform{matrix::zero()}; + } + + template + affine_transform::affine_transform() + : m{m.zero()} + { + for (std::size_t i = 0; i < std::min(N, M); ++i) + m[i][i] = T{1}; + } + + template + affine_transform::affine_transform(matrix const & matrix) + : m{matrix} + {} + + template + affine_transform::affine_transform(matrix const & linear, vector const & translation) + { + for (std::size_t r = 0; r < N; ++r) + { + for (std::size_t c = 0; c < M; ++c) + m[r][c] = linear[r][c]; + m[r][M] = translation[r]; + } + } + + template + matrix affine_transform::affine_matrix() const + { + return m; + } + + template + matrix affine_transform::linear_matrix() const + { + matrix result; + for (std::size_t r = 0; r < N; ++r) + { + for (std::size_t c = 0; c < M; ++c) + result[r][c] = m[r][c]; + } + return result; + } + + template + vector affine_transform::translation_vector() const + { + vector result; + for (std::size_t r = 0; r < N; ++r) + result[r] = m[r][M]; + return result; + } + + template + matrix affine_transform::homogeneous_matrix() const + { + matrix result; + for (std::size_t r = 0; r < N; ++r) + { + for (std::size_t c = 0; c <= M; ++c) + result[r][c] = m[r][c]; + } + for (std::size_t c = 0; c < M; ++c) + result[N][c] = T{0}; + result[N][M] = T{1}; + return result; + } + + template + vector affine_transform::operator()(vector const & v) const + { + vector result = vector::zero(); + for (std::size_t r = 0; r < N; ++r) + { + for (std::size_t c = 0; c < M; ++c) + result[r] += m[r][c] * v[c]; + } + return result; + } + + template + point affine_transform::operator()(point const & p) const + { + point result = point::zero(); + for (std::size_t r = 0; r < N; ++r) + { + for (std::size_t c = 0; c < M; ++c) + result[r] += m[r][c] * p[c]; + result[r] += m[r][M]; + } + return result; + } + + template + affine_transform operator * (affine_transform const & t1, affine_transform const & t2) + { + auto const t1_lin = t1.linear_matrix(); + return {t1_lin * t2.linear_matrix(), t1_lin * t2.translation_vector() + t1.translation_vector()}; + } + + template + std::optional> inverse(affine_transform const & t) + { + auto lin_inv = inverse(t.linear_matrix()); + if (!lin_inv) + return std::nullopt; + + return affine_transform{lin_inv, - lin_inv * t.translation()}; + } + +} diff --git a/libs/geom/include/psemek/geom/permutation.hpp b/libs/geom/include/psemek/geom/permutation.hpp index a12f0efe..4189f765 100644 --- a/libs/geom/include/psemek/geom/permutation.hpp +++ b/libs/geom/include/psemek/geom/permutation.hpp @@ -1,59 +1,108 @@ #pragma once -#include -#include -#include +#include + +#include namespace psemek::geom { - template + template struct swap { - using vector_type = geom::vector; - using point_type = geom::point; - using matrix_type = geom::matrix; + std::size_t i, j; swap(std::size_t i, std::size_t j); + swap(swap const &) = default; - vector_type operator()(vector_type v) const; + matrix affine_matrix() const; + matrix linear_matrix() const; + vector translation_vector() const; + matrix homogeneous_matrix() const; - matrix_type matrix() const; + affine_transform transform() const; - swap inverse() const; + vector operator()(vector v) const; + point operator()(point p) const; private: - std::size_t i_, j_; + template + void fill_matrix(Matrix & m) const; }; - template - swap::swap(std::size_t i, std::size_t j) - : i_(i) - , j_(j) - {} - - template - vector swap::operator()(vector_type v) const + template + swap::swap(std::size_t i, std::size_t j) + : i{i} + , j{j} { - std::swap(v[i_], v[j_]); + assert(i < N); + assert(j < N); + } + + template + vector swap::operator()(vector v) const + { + std::swap(v[i], v[j]); return v; } - template - matrix swap::matrix() const + template + point swap::operator()(point p) const { - matrix_type m = matrix_type::identity(); - m[i_][i_] = 0; - m[j_][j_] = 0; - m[i_][j_] = 1; - m[j_][i_] = 1; - return m; + std::swap(p[i], p[j]); + return p; } - template - swap swap::inverse() const + template + matrix swap::affine_matrix() const { - return *this; + auto result = matrix::identity(); + fill_matrix(result); + return result; + } + + template + matrix swap::linear_matrix() const + { + auto result = matrix::identity(); + fill_matrix(result); + return result; + } + + template + vector swap::translation_vector() const + { + return vector::zero(); + } + + template + matrix swap::homogeneous_matrix() const + { + auto result = matrix::identity(); + fill_matrix(result); + return result; + } + + template + affine_transform swap::transform() const + { + return {affine_matrix()}; + } + + template + template + void swap::fill_matrix(Matrix & m) const + { + m[i][i] = T{0}; + m[j][j] = T{0}; + m[i][j] = T{1}; + m[j][i] = T{1}; + } + + template + swap inverse(swap const & s) + { + return s; } } diff --git a/libs/geom/include/psemek/geom/rotation.hpp b/libs/geom/include/psemek/geom/rotation.hpp index 82e3324a..cae065e6 100644 --- a/libs/geom/include/psemek/geom/rotation.hpp +++ b/libs/geom/include/psemek/geom/rotation.hpp @@ -1,168 +1,224 @@ #pragma once -#include -#include -#include -#include +#include + +#include namespace psemek::geom { // Rotation in oriented plane (i,j) - template + template struct plane_rotation { - using scalar_type = T; - using vector_type = geom::vector; - using point_type = geom::point; - using matrix_type = geom::matrix; + std::size_t i, j; + T angle; - plane_rotation(std::size_t i, std::size_t j, T angle = T(0)); + plane_rotation(); + plane_rotation(std::size_t i, std::size_t j, T angle); + plane_rotation(plane_rotation const &) = default; - scalar_type angle() const; - scalar_type angle(scalar_type a); + matrix affine_matrix() const; + matrix linear_matrix() const; + vector translation_vector() const; + matrix homogeneous_matrix() const; - vector_type operator()(vector_type v) const; + affine_transform transform() const; - matrix_type matrix() const; - - plane_rotation inverse() const; + vector operator()(vector const & v) const; + point operator()(point const & p) const; private: - std::size_t const i_; - std::size_t const j_; - T angle_; + template + void fill_matrix(Matrix & m) const; }; - // 3D-rotation around an axis + // 3N-rotation around an axis template struct axis_rotation { - using scalar_type = T; - using vector_type = geom::vector; - using point_type = geom::point; - using matrix_type = geom::matrix; + vector axis; + T angle; axis_rotation(); - axis_rotation(vector_type axis, scalar_type angle); + axis_rotation(vector const & axis, T angle); + axis_rotation(axis_rotation const &) = default; - vector_type axis() const; - vector_type axis(vector_type a); + matrix affine_matrix() const; + matrix linear_matrix() const; + vector translation_vector() const; + matrix homogeneous_matrix() const; - scalar_type angle() const; - scalar_type angle(scalar_type a); + affine_transform transform() const; - vector_type operator()(vector_type v) const; - - matrix_type matrix() const; - - axis_rotation inverse() const; + vector operator()(vector const & v) const; + point operator()(point const & p) const; private: - vector_type axis_; - scalar_type angle_; + template + void fill_matrix(Matrix & m) const; }; - template - plane_rotation::plane_rotation(std::size_t i, std::size_t j, T angle) - : i_(i), j_(j), angle_(angle) - {} - - template - T plane_rotation::angle() const + template + plane_rotation::plane_rotation() + : i{0} + , j{1} + , angle{0} { - return angle_; + assert(i < N); + assert(j < N); } - template - T plane_rotation::angle(T a) + template + plane_rotation::plane_rotation(std::size_t i, std::size_t j, T angle) + : i{i} + , j{j} + , angle{angle} { - T t = angle_; - angle_ = a; - return t; + assert(i < N); + assert(j < N); } - template - vector plane_rotation::operator()(vector_type v) const + template + matrix plane_rotation::affine_matrix() const { - T vi = v[i_] * std::cos(angle_) - v[j_] * std::sin(angle_); - T vj = v[i_] * std::sin(angle_) + v[j_] * std::cos(angle_); - v[i_] = vi; - v[j_] = vj; - return v; + auto result = matrix::identity(); + fill_matrix(result); + return result; } - template - matrix plane_rotation::matrix() const + template + matrix plane_rotation::linear_matrix() const { - matrix_type m = matrix_type::identity(); - m[i_][i_] = std::cos(angle_); - m[i_][j_] = -std::sin(angle_); - m[j_][i_] = std::sin(angle_); - m[j_][j_] = std::cos(angle_); - return m; + auto result = matrix::identity(); + fill_matrix(result); + return result; } - template - plane_rotation plane_rotation::inverse() const + template + vector plane_rotation::translation_vector() const { - return {i_, j_, -angle_}; + return vector::zero(); + } + + template + matrix plane_rotation::homogeneous_matrix() const + { + auto result = matrix::identity(); + fill_matrix(result); + return result; + } + + template + affine_transform plane_rotation::transform() const + { + return {affine_matrix()}; + } + + template + vector plane_rotation::operator()(vector const & v) const + { + auto result = v; + result[i] = v[i] * std::cos(angle) - v[j] * std::sin(angle); + result[j] = v[i] * std::sin(angle) + v[j] * std::cos(angle); + return result; + } + + template + point plane_rotation::operator()(point const & p) const + { + auto result = p; + result[i] = p[i] * std::cos(angle) - p[j] * std::sin(angle); + result[j] = p[i] * std::sin(angle) + p[j] * std::cos(angle); + return result; + } + + template + template + void plane_rotation::fill_matrix(Matrix & m) const + { + m[i][i] = std::cos(angle); + m[i][j] = -std::sin(angle); + m[j][i] = std::sin(angle); + m[j][j] = std::cos(angle); + } + + template + plane_rotation inverse(plane_rotation const & r) + { + return {r.j, r.i, r.angle}; } template axis_rotation::axis_rotation() - : axis_rotation(vector_type{T(0), T(0), T(1)}, T(0)) + : axis{T{0}, T{0}, T{1}} + , angle{T{0}} {} template - axis_rotation::axis_rotation(vector_type axis, scalar_type angle) - : axis_(axis) - , angle_(angle) + axis_rotation::axis_rotation(vector const & axis, T angle) + : axis{axis} + , angle{angle} {} template - vector axis_rotation::axis() const + matrix axis_rotation::affine_matrix() const { - return axis_; + auto result = matrix::identity(); + fill_matrix(result); + return result; } template - vector axis_rotation::axis(vector_type a) + matrix axis_rotation::linear_matrix() const { - auto t = axis_; - axis_ = a; - return t; + auto result = matrix::identity(); + fill_matrix(result); + return result; } template - T axis_rotation::angle() const + vector axis_rotation::translation_vector() const { - return angle_; + return vector::zero(); } template - T axis_rotation::angle(scalar_type a) + matrix axis_rotation::homogeneous_matrix() const { - auto t = angle_; - angle_ = a; - return t; + auto result = matrix::identity(); + fill_matrix(result); + return result; } template - vector axis_rotation::operator()(vector_type v) const + affine_transform axis_rotation::transform() const { - return matrix() * v; + return {affine_matrix()}; } template - matrix axis_rotation::matrix() const + vector axis_rotation::operator()(vector const & v) const { - matrix_type m = matrix_type::identity(); - T c = std::cos(angle_); - T s = std::sin(angle_); - T x = axis_[0]; - T y = axis_[1]; - T z = axis_[2]; + return linear_matrix() * v; + } + + template + point axis_rotation::operator()(point const & p) const + { + auto const o = geom::point::zero(); + return linear_matrix() * (p - o) + o; + } + + template + template + void axis_rotation::fill_matrix(Matrix & m) const + { + T const c = std::cos(angle); + T const s = std::sin(angle); + T const x = axis[0]; + T const y = axis[1]; + T const z = axis[2]; m[0][0] = c + x * x * (1 - c); m[0][1] = x * y * (1 - c) - z * s; m[0][2] = x * z * (1 - c) + y * s; @@ -172,13 +228,12 @@ namespace psemek::geom m[2][0] = z * x * (1 - c) - y * s; m[2][1] = z * y * (1 - c) + x * s; m[2][2] = c + z * z * (1 - c); - return m; } template - axis_rotation axis_rotation::inverse() const + axis_rotation inverse(axis_rotation const & r) { - return {axis_, -angle_}; + return {r.axis, -r.angle}; } } diff --git a/libs/geom/include/psemek/geom/scale.hpp b/libs/geom/include/psemek/geom/scale.hpp index 4a5feadb..9f56212d 100644 --- a/libs/geom/include/psemek/geom/scale.hpp +++ b/libs/geom/include/psemek/geom/scale.hpp @@ -1,93 +1,125 @@ #pragma once -#include -#include -#include +#include namespace psemek::geom { - template + template struct scale { - using vector_type = geom::vector; - using point_type = geom::point; - using matrix_type = geom::matrix; + vector s; scale(); - scale(T v); - scale(vector_type v); + scale(T s); + scale(vector const & s); + scale(scale const &) = default; - vector_type vector() const; - vector_type vector(vector_type v); + matrix affine_matrix() const; + matrix linear_matrix() const; + vector translation_vector() const; + matrix homogeneous_matrix() const; - vector_type operator()(vector_type v) const; + affine_transform transform() const; - matrix_type matrix() const; - - scale inverse() const; + vector operator()(vector v) const; + point operator()(point p) const; private: - vector_type v_; + template + void fill_matrix(Matrix & m) const; }; - template - scale::scale() + template + scale::scale() { - for (std::size_t i = 0; i < D; ++i) - v_[i] = T(1); + for (std::size_t i = 0; i < N; ++i) + s[i] = T{1}; } - template - scale::scale(T v) + template + scale::scale(T s) { - for (std::size_t i = 0; i < D; ++i) - v_[i] = v; + for (std::size_t i = 0; i < N; ++i) + this->s[i] = s; } - template - scale::scale(vector_type v) - : v_(v) + template + scale::scale(vector const & s) + : s{s} {} - template - vector scale::vector() const + template + matrix scale::affine_matrix() const { - return v_; + auto result = matrix::zero(); + fill_matrix(result); + return result; } - template - vector scale::vector(vector_type v) + template + matrix scale::linear_matrix() const { - auto t = v_; - v_ = v; - return t; + auto result = matrix::zero(); + fill_matrix(result); + return result; } - template - vector scale::operator()(vector_type v) const + template + vector scale::translation_vector() const { - for (std::size_t i = 0; i < D; ++i) - v[i] *= v_[i]; + return vector::zero(); + } + + template + matrix scale::homogeneous_matrix() const + { + auto result = matrix::zero(); + fill_matrix(result); + return result; + } + + template + affine_transform scale::transform() const + { + return {affine_matrix()}; + } + + template + vector scale::operator()(vector v) const + { + for (std::size_t i = 0; i < N; ++i) + v[i] *= s[i]; return v; } - template - matrix scale::matrix() const + template + point scale::operator()(point p) const { - matrix_type m = matrix_type::identity(); - for (std::size_t i = 0; i < D; ++i) - m[i][i] = v_[i]; - return m; + for (std::size_t i = 0; i < N; ++i) + p[i] *= s[i]; + return p; } - template - scale scale::inverse() const + template + template + void scale::fill_matrix(Matrix & m) const { - vector_type v; - for (std::size_t i = 0; i < D; ++i) - v[i] = T(1) / v_[i]; - return {v}; + for (std::size_t i = 0; i < N; ++i) + m[i][i] = s[i]; + } + + template + std::optional> inverse(scale const & s) + { + vector result; + for (std::size_t i = 0; i < N; ++i) + { + if (s.s[i] == T{0}) + return std::nullopt; + result[i] = T{1} / s.s[i]; + } + return scale{result}; } } diff --git a/libs/geom/include/psemek/geom/translation.hpp b/libs/geom/include/psemek/geom/translation.hpp index 7fbebe7f..9a3b0c0e 100644 --- a/libs/geom/include/psemek/geom/translation.hpp +++ b/libs/geom/include/psemek/geom/translation.hpp @@ -1,85 +1,92 @@ #pragma once -#include -#include -#include +#include namespace psemek::geom { - template + template struct translation { - using vector_type = geom::vector; - using point_type = geom::point; - using homogeneous_matrix_type = geom::matrix; + vector v; translation(); - translation(vector_type v); + translation(vector const & v); + translation(translation const &) = default; - vector_type vector() const; - vector_type vector(vector_type v); + matrix affine_matrix() const; + matrix linear_matrix() const; + vector translation_vector() const; + matrix homogeneous_matrix() const; - vector_type operator()(vector_type v) const; - point_type operator()(point_type p) const; + affine_transform transform() const; - homogeneous_matrix_type homogeneous_matrix() const; - - translation inverse() const; - - private: - vector_type v_; + vector operator()(vector const & v) const; + point operator()(point const & p) const; }; - template - translation::translation() - : translation(vector_type::zero()) + template + translation::translation() + : v{vector::zero()} {} - template - translation::translation(vector_type v) - : v_(v) + template + translation::translation(vector const & v) + : v{v} {} - template - vector translation::vector() const + template + matrix translation::affine_matrix() const { - return v_; + auto result = matrix::identity(); + for (std::size_t i = 0; i < N; ++i) + result[i][N] = v[i]; + return result; } - template - vector translation::vector(vector_type v) + template + matrix translation::linear_matrix() const { - auto t = v_; - v_ = v; - return t; + return matrix::identity(); } - template - vector translation::operator()(vector_type v) const + template + vector translation::translation_vector() const { return v; } - template - point translation::operator()(point_type p) const + template + matrix translation::homogeneous_matrix() const { - return p + v_; + auto result = matrix::identity(); + for (std::size_t i = 0; i < N; ++i) + result[i][N] = v[i]; + return result; } - template - matrix translation::homogeneous_matrix() const + template + affine_transform translation::transform() const { - homogeneous_matrix_type m = homogeneous_matrix_type::identity(); - for (std::size_t i = 0; i < D; ++i) - m[i][D] = v_[i]; - return m; + return {affine_matrix()}; } - template - translation translation::inverse() const + template + vector translation::operator()(vector const & v) const { - return {-v_}; + return v; + } + + template + point translation::operator()(point const & p) const + { + return p + v; + } + + template + translation inverse(translation const & t) + { + return {-t.v}; } } diff --git a/libs/geom/source/camera.cpp b/libs/geom/source/camera.cpp index 45e31eb2..2dee85a7 100644 --- a/libs/geom/source/camera.cpp +++ b/libs/geom/source/camera.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include @@ -109,14 +108,16 @@ namespace psemek::geom matrix spherical_camera::view() const { - return - translation({0.f, 0.f, -distance}).homogeneous_matrix() - * homogeneous(swap(1, 2).matrix()) - * homogeneous(scale({1.f, -1.f, 1.f}).matrix()) - * homogeneous(plane_rotation(1, 2, elevation_angle).matrix()) - * homogeneous(plane_rotation(1, 0, azimuthal_angle).matrix()) - * translation({ -target[0], -target[1], -target[2] }).homogeneous_matrix() + auto tr = + translation({0.f, 0.f, -distance}).transform() + * swap(1, 2).transform() + * scale({1.f, -1.f, 1.f}).transform() + * plane_rotation(1, 2, elevation_angle).transform() + * plane_rotation(1, 0, azimuthal_angle).transform() + * translation({ -target[0], -target[1], -target[2] }).transform() ; + + return tr.homogeneous_matrix(); } }