Program Listing for File unit_vector.hpp¶
↰ Return to documentation for file (/home/runner/work/wave_tracer/wave_tracer/include/wt/math/unit_vector/unit_vector.hpp)
/*
*
* wave tracer
* Copyright Shlomi Steinberg
*
* LICENSE: Creative Commons Attribution-NonCommercial 4.0 International
*
*/
#pragma once
#include <cstddef>
#include <wt/util/unreachable.hpp>
#include <wt/util/assert.hpp>
#include <wt/util/concepts.hpp>
#include <wt/math/defs.hpp>
#include <wt/math/quantity/quantity_vector.hpp>
namespace wt {
template <std::size_t N, FloatingPoint Q>
struct unit_vector_storage;
template <FloatingPoint T>
struct unit_vector_storage<1,T> { T x; };
template <FloatingPoint T>
struct unit_vector_storage<2,T> { T x,y; };
template <FloatingPoint T>
struct unit_vector_storage<3,T> { T x,y,z; };
template <std::size_t N, FloatingPoint T>
requires (N>=1 && N<=3)
struct unit_vector final : public unit_vector_storage<N, T> {
public:
static constexpr auto element_count = N;
using element_type = T;
private:
inline T check_unit_vector_runtime(const glm::vec<N,T>& v) const {
// allow some tolerance, letting implementation avoid extensive renormalizations
static constexpr T check_unit_vector_tolerance_scale = 5;
assert_unit_vector<T>(v, check_unit_vector_tolerance_scale);
return v[0];
}
// consteval inline T check_unit_vector_static(const glm::vec<N,T>& v) const {
// double norm;
// if constexpr (N==1) norm = double(v.x)*v.x;
// if constexpr (N==2) norm = double(v.x)*v.x + double(v.y)*v.y;
// if constexpr (N==3) norm = double(v.x)*v.x + double(v.y)*v.y + double(v.z)*v.z;
// int checker = std::abs(1-norm)<1e-10 ? 1 : 0;
// // this will fail to compile when norm is not ~1
// return v[0] / (checker==1); // unit_vector initialized with a non-unit vector!
// }
constexpr inline T check_unit_vector(const glm::vec<N,T>& v) const {
// if consteval {
// return check_unit_vector_static(v);
// } else {
return check_unit_vector_runtime(v);
// }
}
public:
constexpr unit_vector() noexcept = delete;
constexpr inline unit_vector(T x) requires (N==1)
{
this->x=x;
check_unit_vector({ x });
}
constexpr inline unit_vector(T x, T y) requires (N==2)
{
this->x=x;
this->y=y;
check_unit_vector({ x,y });
}
constexpr inline unit_vector(T x, T y, T z) requires (N==3)
{
this->x=x;
this->y=y;
this->z=z;
check_unit_vector({ x,y,z });
}
constexpr inline unit_vector(const unit_vector<2, T>& xy, T z) requires (N==3)
{
this->x=xy.x;
this->y=xy.y;
this->z=z;
check_unit_vector({ xy.x,xy.y,z });
}
constexpr inline unit_vector(const glm::vec<2, T>& xy, T z) requires (N==3)
{
this->x=xy.x;
this->y=xy.y;
this->z=z;
check_unit_vector({ xy.x,xy.y,z });
}
constexpr inline unit_vector(T x, const unit_vector<2, T>& yz) requires (N==3)
{
this->x=x;
this->y=yz.x;
this->z=yz.y;
check_unit_vector({ x,yz.x,yz.y });
}
constexpr inline unit_vector(T x, const glm::vec<2, T>& yz) requires (N==3)
{
this->x=x;
this->y=yz.x;
this->z=yz.y;
check_unit_vector({ x,yz.x,yz.y });
}
template <FloatingPoint S>
requires std::constructible_from<T, S>
explicit constexpr inline unit_vector(const vec<N, S>& v) {
this->x = T{ v[0] };
if constexpr (N>=2) this->y = T{ v[1] };
if constexpr (N>=3) this->z = T{ v[2] };
check_unit_vector(glm::vec<N,T>{ v });
}
template <Quantity Q>
requires std::constructible_from<T, typename quantity_vector<N,Q>::R>
explicit constexpr inline unit_vector(const quantity_vector<N, Q>& qv) {
this->x = T{ qv[0].numerical_value_in(u::one) };
if constexpr (N>=2) this->y = T{ qv[1].numerical_value_in(u::one) };
if constexpr (N>=3) this->z = T{ qv[2].numerical_value_in(u::one) };
check_unit_vector(glm::vec<N,T>{ qv });
}
constexpr unit_vector(unit_vector&&) noexcept = default;
constexpr unit_vector(const unit_vector&) noexcept = default;
constexpr unit_vector& operator=(unit_vector&&) noexcept = default;
constexpr unit_vector& operator=(const unit_vector&) noexcept = default;
constexpr inline const auto& operator[](std::size_t i) const noexcept {
assert(i<N);
if constexpr (N>=1) if (i==0) return this->x;
if constexpr (N>=2) if (i==1) return this->y;
if constexpr (N>=3) if (i==2) return this->z;
unreachable();
}
template <std::size_t M, typename S>
requires (M<=N) && std::convertible_to<S, T>
explicit constexpr inline operator vec<M,S>() const noexcept {
vec<M,S> ret;
for (auto i=0ul;i<M;++i)
ret[i] = static_cast<S>((*this)[i]);
return ret;
}
template <FloatingPoint S>
requires std::convertible_to<S, T>
explicit constexpr inline operator unit_vector<N, S>() const noexcept {
vec<N, S> ret{};
for (auto i=0ul;i<N;++i)
ret[i] = static_cast<S>((*this)[i]);
return unit_vector<N, S>{ ret };
}
};
namespace detail {
template <std::size_t N, typename T>
void to_base_specialization_of_unit_vector(const unit_vector<N, T>*);
template<typename T>
constexpr bool is_derived_from_specialization_of_unit_vector =
requires(T* type) { to_base_specialization_of_unit_vector(type); };
}
template <typename T>
concept UnitVector = detail::is_derived_from_specialization_of_unit_vector<T>;
template<typename T>
concept VectorOrUnitVector = NumericVector<T> || UnitVector<T>;
template<typename T>
concept Vector = NumericVector<T> || UnitVector<T> || QuantityVector<T>;
template <std::size_t N, FloatingPoint T>
using dirvec = unit_vector<N,T>;
template <FloatingPoint T>
using dirvec1 = unit_vector<1,T>;
template <FloatingPoint T>
using dirvec2 = unit_vector<2,T>;
template <FloatingPoint T>
using dirvec3 = unit_vector<3,T>;
using dir1_t = dirvec1<f_t>;
using dir2_t = dirvec2<f_t>;
using dir3_t = dirvec3<f_t>;
using dir1d_t = dirvec1<double>;
using dir2d_t = dirvec2<double>;
using dir3d_t = dirvec3<double>;
}