12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142 |
- // TODO: this file is getting huge, need to break it up somehow
- #ifndef SIMPLE_GEOM_VECTOR_HPP
- #define SIMPLE_GEOM_VECTOR_HPP
- #include <algorithm>
- #include <type_traits>
- #include <ostream>
- #include <numeric>
- #include <tuple>
- #include <cassert>
- #include "simple/support/carcdr.hpp"
- #include "simple/support/array.hpp"
- #include "simple/support/array_utils.hpp"
- #include "simple/support/array_operators.hpp"
- #include "simple/support/function_utils.hpp"
- #include "simple/support/meta/integer_sequence.hpp"
- #include "simple/support/tuple_utils/transform.hpp"
- #include "simple/support/algorithm/traits.hpp"
- #include "simple/support/math/abs.hpp"
- namespace simple::geom
- {
- template <typename Type, size_t Size>
- struct vector_array
- {
- using type = support::array<Type, Size>;
- };
- template <typename Type, size_t Size>
- using vector_array_t = typename vector_array<Type, Size>::type;
- // TODO; in an optional header
- // template <size_t Size>
- // struct vector_array<unsigned int, Size>
- // {
- // using value_type = unsigned int;
- // typedef value_type type __attribute__ ((vector_size (Size * sizeof(value_type))));
- // };
- //
- // template <size_t Size>
- // struct vector_array<int, Size>
- // {
- // using value_type = int;
- // typedef value_type type __attribute__ ((vector_size (Size * sizeof(value_type))));
- // };
- template <typename Coordinate, size_t Dimensions,
- typename Order = std::make_index_sequence<Dimensions>,
- std::enable_if_t<Order::size() == Dimensions>* = nullptr>
- class vector
- {
- public:
- using array = vector_array_t<Coordinate, Dimensions>;
- using coordinate_type = Coordinate;
- using order = Order;
- using value_type = Coordinate;
- static constexpr size_t dimensions = Dimensions;
- class meta
- {
- template <typename T, typename = std::nullptr_t>
- struct has_dimesntions_s { constexpr static bool value = false; };
- template <typename T>
- struct has_dimesntions_s<T, decltype(void(T::dimensions), nullptr)> { constexpr static bool value = true; };
- template <typename T>
- constexpr static bool has_dimesntions = has_dimesntions_s<T>::value;
- public:
- template <typename C = coordinate_type,
- std::enable_if_t<has_dimesntions<C>>* = nullptr>
- constexpr static size_t depth()
- {
- return depth<typename C::coordinate_type>() + 1;
- }
- template <typename C = coordinate_type,
- std::enable_if_t<not has_dimesntions<C>>* = nullptr>
- constexpr static size_t depth()
- {
- return 1;
- }
- private:
- template <size_t DepthIndex = depth(), typename Vector = vector>
- static constexpr size_t get_sub_dimentions()
- {
- static_assert( DepthIndex < depth(), "Invalid depth" );
- if constexpr (DepthIndex == 0)
- return Vector::dimensions;
- else return get_sub_dimentions<DepthIndex-1, typename Vector::coordinate_type>();
- }
- template <typename O, typename SizeType = size_t, size_t DepthIndex = 0, size_t Depth = depth()>
- static constexpr void set_sub_dimentions(vector<SizeType,Depth,O>& out)
- {
- out[Depth - 1 - DepthIndex] = get_sub_dimentions<DepthIndex>();
- if constexpr (DepthIndex+1 < Depth)
- set_sub_dimentions<O,SizeType,DepthIndex+1>(out);
- }
- template <typename SizeType = size_t, size_t Depth = depth()>
- static constexpr auto get_dimensions()
- {
- vector<SizeType,Depth> ret{};
- set_sub_dimentions(ret);
- return ret;
- }
- public:
- template <typename SizeType = size_t, size_t Depth = meta::depth()> // have to explicitly qualify for depth gcc-7
- static constexpr vector<SizeType,Depth> dimensions = get_dimensions<SizeType,Depth>();
- // this little convenience crashes all clangs currently supporting c++17 (5,6,7,8,9,10)
- // update: clang 11.1.0 - complains about duplicate member "dimensions", previous declared on the same line -_-
- // seems like it's caused by the recursion of vector::meta::size::meta::size::meta::size... which are all vector<size_t,1>s,
- // but gcc is ok with it, and why wouldn't it be??
- // static constexpr auto size = dimensions<>;
- // unused typename to work around gcc bug
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282
- template <typename, size_t Depth = depth(), typename Enabled = void>
- struct get_coordinate;
- template <typename U>
- struct get_coordinate<U, 1, void>
- { using type = vector::coordinate_type; };
- template <typename U, size_t Depth>
- struct get_coordinate<U, Depth, std::enable_if_t<(Depth > 1)>>
- { using type = typename vector::coordinate_type::meta:: template get_coordinate<U, Depth -1>::type; };
- using coordinate_type = typename get_coordinate<void, depth()>::type;
- // ffs this crashes clang 11.1.0 on bool_algebra operator* -_-
- // template <typename Indices> struct size_of_depths {};
- // template <size_t... I> struct size_of_depths<std::index_sequence<I...>>
- // {
- // using type = std::index_sequence<meta::dimensions<>[I]...>;
- // };
- };
- // template <typename Indices = std::make_index_sequence<meta::depth()>>
- // using shape = typename meta::template size_of_depths<Indices>::type;
- template <typename NewCoord, typename Meta = meta, typename Enabled = void>
- struct map_coordinate;
- template <typename NewCoord, typename Meta>
- struct map_coordinate<NewCoord, Meta, std::enable_if_t<Meta::depth() <= 1>>
- { using type = vector<NewCoord, dimensions, order>; };
- template <typename NewCoord, typename Meta>
- struct map_coordinate<NewCoord, Meta, std::enable_if_t<(Meta::depth() > 1)>>
- { using type =
- vector<typename Coordinate::template map_coordinate<NewCoord>::type, dimensions, order>; };
- template <typename NewCoord>
- using map_coordinate_t = typename map_coordinate<NewCoord>::type;
- [[nodiscard]]
- static constexpr vector zero() { return vector{}; }
- [[nodiscard]]
- static constexpr vector one() { return vector{support::filled_array<Dimensions>(value_type{1})}; }
- [[nodiscard]]
- static constexpr vector one(const Coordinate& scaler) { return vector{support::filled_array<Dimensions>(scaler)}; }
- enum : size_t
- {
- non_index = std::numeric_limits<size_t>::max(),
- new_index = non_index,
- x_index = support::car<Order, 0, non_index>,
- y_index = support::car<Order, 1, non_index>,
- z_index = support::car<Order, 2, non_index>,
- w_index = support::car<Order, 3, non_index>
- };
- template <size_t index>
- [[nodiscard]]
- static constexpr vector unit()
- {
- return unit<index>(Coordinate{1});
- }
- template <size_t index>
- [[nodiscard]]
- static constexpr vector unit(const Coordinate& one)
- {
- return unit<index>(one, Coordinate{});
- }
- template <size_t index>
- [[nodiscard]]
- static constexpr vector unit(const Coordinate& one, const Coordinate& zero)
- {
- static_assert(index < dimensions, "");
- return vector{support::make_array<dimensions>([one, zero](size_t i)
- { return index == i ? one : zero; } )};
- }
- [[nodiscard]]
- static constexpr vector unit(size_t index)
- {
- return unit(index, Coordinate{1});
- }
- [[nodiscard]]
- static constexpr vector unit(size_t index, const Coordinate& one)
- {
- vector ret{};
- ret[index] = one;
- return ret;
- }
- [[nodiscard]]
- static constexpr vector unit(size_t index, const Coordinate& one, const Coordinate& zero)
- {
- vector ret = vector::one(zero);
- ret[index] = one;
- return ret;
- }
- [[nodiscard]] static constexpr vector i() { return unit<x_index>(); }
- [[nodiscard]] static constexpr vector j() { return unit<y_index>(); }
- [[nodiscard]] static constexpr vector k() { return unit<z_index>(); }
- [[nodiscard]] static constexpr vector l() { return unit<w_index>(); }
- [[nodiscard]] static constexpr vector i(const Coordinate& scaler) { return unit<x_index>(scaler); }
- [[nodiscard]] static constexpr vector j(const Coordinate& scaler) { return unit<y_index>(scaler); }
- [[nodiscard]] static constexpr vector k(const Coordinate& scaler) { return unit<z_index>(scaler); }
- [[nodiscard]] static constexpr vector l(const Coordinate& scaler) { return unit<w_index>(scaler); }
- private:
- // Coordinate raw[Dimensions];
- array raw;
- template<typename Vector, size_t n>
- constexpr void set_mixed_index(Vector&) const
- {}
- template<typename Vector, size_t n>
- constexpr void set_mixed_index(Vector&, const Coordinate&) const
- {}
- template<typename Vector, size_t n, size_t index, size_t... Rest>
- constexpr void set_mixed_index(Vector& vec) const
- {
- static_assert(index < dimensions, " Invalid mix index. ");
- vec[n] = raw[index];
- set_mixed_index<Vector, n+1, Rest...>(vec);
- }
- template<typename Vector, size_t n, size_t index, size_t... Rest,
- std::enable_if_t<index < Dimensions>* = nullptr>
- constexpr void set_mixed_index(Vector& vec, const Coordinate& default_value) const
- {
- vec[n] = raw[index];
- set_mixed_index<Vector, n+1, Rest...>(vec, default_value);
- }
- template<typename Vector, size_t n, size_t index, size_t... Rest,
- std::enable_if_t<index >= Dimensions>* = nullptr>
- constexpr void set_mixed_index(Vector& vec, const Coordinate& default_value) const
- {
- vec[n] = default_value;
- set_mixed_index<Vector, n+1, Rest...>(vec, default_value);
- }
- template <typename Another, std::enable_if_t<
- Another::dimensions == Dimensions
- && !std::is_same<Another, vector>::value
- && !std::is_base_of<vector, Another>::value
- && std::is_convertible<typename Another::coordinate_type, Coordinate>::value
- && std::is_constructible<Coordinate, typename Another::coordinate_type>::value
- >* = nullptr>
- struct is_implicitly_convertible_to_me {};
- template <typename Another, std::enable_if_t<
- Another::dimensions == Dimensions
- && !std::is_same<Another, vector>::value
- && !std::is_base_of<vector, Another>::value
- && !std::is_convertible<typename Another::coordinate_type, Coordinate>::value
- // TODO: this does not cover aggregates and enums
- && std::is_constructible<Coordinate, typename Another::coordinate_type>::value
- >* = nullptr>
- struct is_explicitly_convertible_to_me {};
- template <typename Another, size_t dimension = Dimensions - 1>
- constexpr void set_in_order(const Another& another)
- {
- if constexpr (dimension > 0)
- set_in_order<Another, dimension - 1>(another);
- get<dimension>() = another.template get<dimension>();
- }
- public:
- constexpr vector() = default;
- template <typename... Coordinates,
- typename std::enable_if_t<sizeof...(Coordinates) == Dimensions> * = nullptr,
- typename std::enable_if_t<(std::is_convertible_v<Coordinates, Coordinate> && ...)> * = nullptr>
- constexpr vector(Coordinates&&... coordinates)
- : raw {std::forward<Coordinates>(coordinates)...}
- {}
- // TODO: forwarding
- template <typename Another, is_explicitly_convertible_to_me<Another>* = nullptr,
- std::enable_if_t<std::is_same_v<typename Another::order, Order>> *...>
- constexpr explicit vector(const Another& another) : raw{}
- {
- for(size_t i = 0; i < Dimensions; ++i)
- raw[i] = Coordinate(another[i]);
- }
- // TODO: forwarding
- template <typename Another, is_explicitly_convertible_to_me<Another>* = nullptr,
- std::enable_if_t<!std::is_same_v<typename Another::order, Order>> *...>
- constexpr explicit vector(const Another& another) : raw{}
- {
- set_in_order<Another>(another);
- }
- // TODO: forwarding
- template <typename Another, is_implicitly_convertible_to_me<Another>* = nullptr,
- std::enable_if_t<std::is_same_v<typename Another::order, Order>> *...>
- constexpr vector(const Another& another) : raw{}
- {
- for(size_t i = 0; i < Dimensions; ++i)
- raw[i] = another[i];
- }
- // TODO: forwarding
- template <typename Another, is_implicitly_convertible_to_me<Another>* = nullptr,
- std::enable_if_t<!std::is_same_v<typename Another::order, Order>> *...>
- constexpr vector(const Another& another) : raw{}
- {
- set_in_order<Another>(another);
- }
- explicit constexpr vector(const array & coordinates) : raw(coordinates)
- {
- }
- explicit constexpr vector(array && coordinates) : raw(std::move(coordinates))
- {
- }
- [[nodiscard]]
- explicit constexpr operator const array () const noexcept
- {
- return raw;
- }
- template<size_t D = Dimensions, std::enable_if_t<D == 1>* = nullptr>
- [[nodiscard]]
- constexpr operator const Coordinate & () const noexcept
- {
- return raw[0];
- }
- template <typename Function, typename AnotherCoord = std::invoke_result_t<Function, Coordinate>>
- [[nodiscard]]
- constexpr vector<AnotherCoord, Dimensions, Order> transformed(Function&& transform) const&
- {
- vector<AnotherCoord, Dimensions, Order> another{};
- for(size_t i = 0; i < Dimensions; ++i)
- {
- another[i] = support::invoke(
- std::forward<Function>(transform),
- (*this)[i]
- );
- }
- return another;
- }
- // TODO: mix should preserve order if mixed size >= original size
- template<size_t... CoordinateIndices, typename Mixed = vector<Coordinate, sizeof...(CoordinateIndices)> >
- [[nodiscard]]
- constexpr Mixed mix() const
- {
- Mixed result{};
- set_mixed_index<Mixed, 0, CoordinateIndices...>(result);
- return result;
- }
- template<size_t... CoordinateIndices, typename Mixed = vector<Coordinate, sizeof...(CoordinateIndices)> >
- [[nodiscard]]
- constexpr Mixed mix(const Coordinate& default_value) const
- {
- Mixed result{};
- set_mixed_index<Mixed, 0, CoordinateIndices...>(result, default_value);
- return result;
- }
- template <size_t N, typename Mixed = vector<Coordinate, N>>
- [[nodiscard]]
- constexpr Mixed mix(const support::array<size_t,N>& indices) const
- {
- Mixed result{};
- size_t index{};
- for(size_t i = 0; i < N; ++i)
- {
- index = indices[i];
- if(index >= Dimensions)
- throw std::logic_error("simple::geom::vector invalid mix index");
- result[i] = raw[index];
- }
- return result;
- }
- template <size_t N, typename Mixed = vector<Coordinate, N>>
- [[nodiscard]]
- constexpr Mixed mix(const support::array<size_t,N>& indices, const Coordinate& default_value) const
- {
- Mixed result{};
- size_t index{};
- for(size_t i = 0; i < N; ++i)
- {
- index = indices[i];
- result[i] = index < Dimensions ? raw[index] : default_value;
- }
- return result;
- }
- template <size_t N,
- std::enable_if_t<N <= Dimensions>* = nullptr>
- [[nodiscard]]
- constexpr vector<Coordinate,N> last() const
- {
- vector<Coordinate,N> result{};
- for(size_t i = 0; i < N; ++i)
- result[i] = (*this)[Dimensions-N+i];
- return result;
- }
- template <size_t N,
- std::enable_if_t<N <= Dimensions>* = nullptr>
- [[nodiscard]]
- constexpr vector<Coordinate,N> first() const
- {
- vector<Coordinate,N> result{};
- for(size_t i = 0; i < N; ++i)
- result[i] = (*this)[i];
- return result;
- }
- template <size_t N, typename O>
- [[nodiscard]] constexpr
- auto concat(vector<Coordinate, N, O> other) const
- {
- vector<Coordinate,Dimensions + N, support::meta::concat_sequence_t<
- Order, support::meta::offset_sequence_t<O, Dimensions> >>
- result{};
- for(size_t i = 0; i < Dimensions; ++i)
- result[i] = (*this)[i];
- for(size_t i = 0; i < N; ++i)
- result[Dimensions + i] = other[i];
- return result;
- }
- // NOTE: emmm... ok??
- // emmm yes, this is ok, bitwise not on a bool is a warning anyways, so in context of vector we make it elementwise logical not,
- // as that logicaly behaves similar to bitwise not on flags
- // what? you prefer it promoting to int? a freakin signed int?! gtfo!
- template <typename C = Coordinate, std::enable_if_t<std::is_same_v<C,bool>>* = nullptr>
- [[nodiscard]]
- friend
- constexpr vector operator ~(vector one) noexcept
- {
- for(size_t i = 0; i < Dimensions; ++i)
- one[i] = !one[i];
- return one;
- }
- // now this is the one I would say is kind of dubious, because it creates an expectation of proper boolean logic, but it's not.
- // not sure why I have this, other than the consistency with other elementwise operations,
- // maybe it was different at some point, but right now removing this breaks nothing but a couple targeted unit tests.
- // note that comparison ops return a reduction type, not vector<bool,D>,
- // and nothing promotes to bool or anything like that, so this shouldn't bite you unless you ask for it
- template <typename C = typename meta::coordinate_type,
- std::enable_if_t<std::is_same_v<C,bool>>* = nullptr>
- [[nodiscard]] [[deprecated("use operator~ instead")]]
- friend
- constexpr vector operator !(vector one) noexcept
- {
- for(size_t i = 0; i < Dimensions; ++i)
- one[i] = !one[i];
- return one;
- }
- template <size_t dimension>
- [[nodiscard]]
- constexpr const coordinate_type & get() const&
- {
- static_assert(dimension < Dimensions);
- constexpr size_t index = support::car<Order, dimension>;
- return (*this)[index];
- }
- template <size_t dimension>
- [[nodiscard]]
- constexpr coordinate_type & get() &
- {
- static_assert(dimension < Dimensions);
- constexpr size_t index = support::car<Order, dimension>;
- return (*this)[index];
- }
- template <size_t dimension>
- [[nodiscard]]
- constexpr coordinate_type && get() &&
- {
- static_assert(dimension < Dimensions);
- constexpr size_t index = support::car<Order, dimension>;
- return std::move(*this)[index];
- }
- [[nodiscard]] constexpr const coordinate_type &
- operator[](size_t dimension) const&
- {
- assert(dimension < Dimensions);
- return raw[dimension];
- }
- [[nodiscard]] constexpr coordinate_type &
- operator[](size_t dimension) &
- {
- assert(dimension < Dimensions);
- return raw[dimension];
- }
- [[nodiscard]] constexpr coordinate_type &&
- operator[](size_t dimension) &&
- {
- assert(dimension < Dimensions);
- return std::move(raw[dimension]);
- }
- // TODO: not sure if it's ok that this is backwards
- // make sure to change meta::dimensions as well if change this
- // maybe make use of order parameter to decide
- // also definitely not ok that const and non const are so duplicated
- template <typename IndexType, size_t Size, typename O, size_t Depth = Size,
- std::enable_if_t<(Depth > 1)>* = nullptr>
- [[nodiscard]]
- constexpr auto & operator[](vector<IndexType,Size,O> index) &
- {
- return operator[](index[Depth - 1])
- .template operator[]<IndexType, Size, O, Depth-1>(index);
- }
- template <typename IndexType, size_t Size, typename O, size_t Depth,
- std::enable_if_t<Depth == 1>* = nullptr>
- [[nodiscard]]
- constexpr coordinate_type & operator[](vector<IndexType,Size,O> index) &
- {
- return operator[](index[0]);
- }
- template <typename IndexType, size_t Size, typename O, size_t Depth = Size,
- std::enable_if_t<(Depth > 1)>* = nullptr>
- [[nodiscard]]
- constexpr const auto & operator[](vector<IndexType,Size,O> index) const&
- {
- return raw[index[Depth - 1]]
- .template operator[]<IndexType, Size, O, Depth-1>(index);
- }
- template <typename IndexType, size_t Size, typename O, size_t Depth,
- std::enable_if_t<Depth == 1>* = nullptr>
- [[nodiscard]]
- constexpr const coordinate_type & operator[](vector<IndexType,Size,O> index) const&
- {
- return raw[index[0]];
- }
- [[nodiscard]] constexpr auto begin() noexcept { using std::begin; return begin(raw); }
- [[nodiscard]] constexpr auto end() noexcept { using std::end; return end(raw); }
- [[nodiscard]] constexpr auto begin() const noexcept { using std::cbegin; return cbegin(raw); }
- [[nodiscard]] constexpr auto end() const noexcept { using std::cend; return cend(raw); }
- [[nodiscard]] constexpr auto cbegin() const noexcept { using std::cbegin; return cbegin(raw); }
- [[nodiscard]] constexpr auto cend() const noexcept { using std::cend; return cend(raw); }
- [[nodiscard]] constexpr auto rbegin() noexcept { using std::rbegin; return rbegin(raw); }
- [[nodiscard]] constexpr auto rend() noexcept { using std::rend; return rend(raw); }
- [[nodiscard]] constexpr auto rbegin() const noexcept { using std::crbegin; return crbegin(raw); }
- [[nodiscard]] constexpr auto rend() const noexcept { using std::crend; return crend(raw); }
- [[nodiscard]] constexpr auto crbegin() const noexcept { using std::crbegin; return crbegin(raw); }
- [[nodiscard]] constexpr auto crend() const noexcept { using std::crend; return crend(raw); }
- constexpr vector& min(const vector& other)
- {
- using std::min;
- for(size_t i = 0; i < dimensions; ++i)
- raw[i] = min(raw[i], other[i]);
- return *this;
- }
- constexpr vector& max(const vector& other)
- {
- using std::max;
- for(size_t i = 0; i < dimensions; ++i)
- raw[i] = max(raw[i], other[i]);
- return *this;
- }
- constexpr vector& clamp(const vector& lo, const vector& hi)
- {
- using std::clamp;
- for(size_t i = 0; i < dimensions; ++i)
- raw[i] = clamp(raw[i], lo[i], hi[i]);
- return *this;
- }
- constexpr vector& floor()
- {
- using std::floor;
- for(auto&& coord : raw)
- coord = floor(coord);
- return *this;
- }
- constexpr vector& ceil()
- {
- using std::ceil;
- for(auto&& coord : raw)
- coord = ceil(coord);
- return *this;
- }
- constexpr vector& round()
- {
- using std::round;
- for(auto&& coord : raw)
- coord = round(coord);
- return *this;
- }
- constexpr vector& trunc()
- {
- using std::trunc;
- for(auto&& coord : raw)
- coord = trunc(coord);
- return *this;
- }
- constexpr vector& abs()
- {
- using support::abs;
- for(auto&& coord : raw)
- coord = abs(coord);
- return *this;
- }
- constexpr vector& signum()
- {
- using support::abs;
- for(auto&& coord : raw)
- coord = !(coord != Coordinate{}) ? Coordinate{} : coord/abs(coord);
- return *this;
- };
- [[nodiscard]]
- constexpr Coordinate magnitude() const
- {
- Coordinate result = Coordinate{};
- for(auto&& coord : raw)
- result += coord * coord;
- return result;
- }
- [[nodiscard]]
- constexpr Coordinate quadrance() const
- {
- return magnitude();
- }
- [[nodiscard]]
- constexpr Coordinate length() const
- {
- using std::sqrt;
- return sqrt(magnitude());
- }
- // TODO: concider return type deduction for these as well -_-
- constexpr vector & operator++()
- {
- for(auto& coord : raw) ++coord;
- return *this;
- }
- constexpr vector & operator--()
- {
- for(auto& coord : raw) --coord;
- return *this;
- }
- [[nodiscard]]
- constexpr vector operator++(int) &
- {
- vector temp{};
- for(size_t i = 0; i < Dimensions; ++i)
- temp.raw[i] = raw[i]++;
- return temp;
- }
- [[nodiscard]]
- constexpr vector operator--(int) &
- {
- vector temp{};
- for(size_t i = 0; i < Dimensions; ++i)
- temp.raw[i] = raw[i]--;
- return temp;
- }
- template <typename T, typename = std::nullptr_t>
- struct can_apply_s { constexpr static bool value = false; };
- template <typename T>
- struct can_apply_s<T, decltype(void(std::declval<vector>()(std::declval<T>())), nullptr)> { constexpr static bool value = true; };
- template <typename T>
- constexpr static bool can_apply = can_apply_s<T>::value;
- // TODO: common declval code between this and can_apply
- template <typename T, typename = std::nullptr_t>
- struct product_result_s { using type = Coordinate; };
- template <typename T>
- struct product_result_s<T, decltype(void(std::declval<vector>()(std::declval<T>())), nullptr)> { using type = decltype(std::declval<vector>()(std::declval<T>())); };
- template <typename T>
- using product_result = typename product_result_s<T>::type;
- // TODO: gotta deduce return coordinate type now that we're doing it -_-
- // matrix multiplication and matrix-vector multiplication/dot product fusion mutant operator
- template<typename AnotherComponent, size_t AnotherDimesnions, typename AnotherOrder,
- std::enable_if_t<std::is_same_v<Order,AnotherOrder> || can_apply<AnotherComponent>>* = nullptr,
- typename Return = std::conditional_t<can_apply<AnotherComponent>,
- vector<product_result<AnotherComponent>, AnotherDimesnions, AnotherOrder>,
- Coordinate
- >
- >
- [[nodiscard]]
- constexpr Return operator()(const vector<AnotherComponent, AnotherDimesnions, AnotherOrder> & another) const
- {
- Return ret{};
- for(size_t i = 0; i < AnotherDimesnions; ++i)
- if constexpr (can_apply<AnotherComponent>)
- ret[i] = (*this)(another[i]);
- else
- ret += (*this)[i] * another[i];
- return ret;
- }
- [[nodiscard]]
- constexpr Coordinate& x()
- {
- static_assert( Dimensions > x_index );
- return raw[x_index];
- }
- [[nodiscard]]
- constexpr const Coordinate& x() const
- {
- static_assert( Dimensions > x_index );
- return raw[x_index];
- }
- [[nodiscard]]
- constexpr Coordinate& y()
- {
- static_assert( Dimensions > y_index );
- return raw[y_index];
- }
- [[nodiscard]]
- constexpr const Coordinate& y() const
- {
- static_assert( Dimensions > y_index );
- return raw[y_index];
- }
- [[nodiscard]]
- constexpr Coordinate& z()
- {
- static_assert( Dimensions > z_index );
- return raw[z_index];
- }
- [[nodiscard]]
- constexpr const Coordinate& z() const
- {
- static_assert( Dimensions > z_index );
- return raw[z_index];
- }
- [[nodiscard]]
- constexpr Coordinate& w()
- {
- static_assert( Dimensions > w_index );
- return raw[w_index];
- }
- [[nodiscard]]
- constexpr const Coordinate& w() const
- {
- static_assert( Dimensions > w_index );
- return raw[w_index];
- }
- [[nodiscard]]
- constexpr vector<Coordinate,2> xy() const
- {
- return mix<x_index, y_index>();
- }
- [[nodiscard]]
- constexpr vector<Coordinate,3> xyz() const
- {
- return mix<x_index, y_index, z_index>();
- }
- [[nodiscard]]
- constexpr vector<Coordinate,3> xyz(const Coordinate& default_value) const
- {
- return mix<x_index, y_index, z_index>(default_value);
- }
- };
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- vector<C,D,O> min(const vector<C,D,O> & one, const vector<C,D,O> & other)
- {
- auto result = one;
- return result.min(other);
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- vector<C,D,O> max(const vector<C,D,O> & one, const vector<C,D,O> & other)
- {
- auto result = one;
- return result.max(other);
- }
- // TODO: the in place guys dodged the bullet, but these guys below should deduce result element type...
- // TODO: lo and hi can have different element types
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- vector<C,D,O> clamp(vector<C,D,O> v, const vector<C,D,O> & lo, const vector<C,D,O> & hi)
- {
- v.clamp(lo, hi);
- return v;
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- vector<C,D,O> floor(vector<C,D,O> v)
- {
- v.floor();
- return v;
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- vector<C,D,O> ceil(vector<C,D,O> v)
- {
- v.ceil();
- return v;
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- vector<C,D,O> round(vector<C,D,O> v)
- {
- v.round();
- return v;
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- vector<C,D,O> trunc(vector<C,D,O> v)
- {
- v.trunc();
- return v;
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- auto abs(const vector<C,D,O>& v)
- {
- return v.transformed([](auto&& x){ using support::abs; return abs(x); });
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- C magnitude(const vector<C,D,O>& v)
- {
- return v.magnitude();
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- C quadrance(const vector<C,D,O>& v)
- {
- return v.quadrance();
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- C length(const vector<C,D,O>& v)
- {
- return v.length();
- }
- template <typename C, size_t D, typename O>
- [[nodiscard]]
- constexpr
- auto signum(vector<C,D,O> v)
- {
- v.signum();
- return v;
- }
- template <typename It, size_t Size, typename O,
- std::enable_if_t<support::is_iterable<It>{}>* = nullptr>
- auto operator*(vector<It, Size, O> v)
- {
- return support::transform([](auto&& x) -> decltype(auto) {return *x;}, v);
- }
- // for ADL to find these
- using ::operator~;
- using ::operator+;
- using ::operator-;
- using ::operator*;
- using ::operator/;
- using ::operator%;
- using ::operator&;
- using ::operator|;
- using ::operator^;
- using ::operator<<;
- using ::operator>>;
- using ::operator+=;
- using ::operator-=;
- using ::operator*=;
- using ::operator/=;
- using ::operator%=;
- using ::operator&=;
- using ::operator|=;
- using ::operator^=;
- using ::operator<<=;
- using ::operator>>=;
- template<typename Coordinate, size_t Dimensions, typename Order>
- std::ostream & operator<<(std::ostream & out, const vector<Coordinate, Dimensions, Order> & vector)
- {
- out << '(';
- constexpr size_t last = Dimensions - 1;
- for(size_t i = 0; i < last; ++i)
- out << vector[i] << ", ";
- out << vector[last] << ')';
- return out;
- }
- template<typename Coordinate, size_t N, size_t M, typename O1, typename O2>
- std::ostream & operator<<(std::ostream & out, const vector<vector<Coordinate, N, O1>, M, O2> & vector)
- {
- for(size_t i = 0; i < N; ++i)
- out << "^";
- out << "\n";
- for(size_t i = 0; i < M; ++i)
- out << vector[i] << "\n";
- for(size_t i = 0; i < N; ++i)
- out << "v";
- out << "\n";
- return out;
- }
- template <typename F, typename... R> vector(F firts, R... rest)
- -> vector<F, sizeof...(R) + 1>;
- // TODO: ugh, clang
- // https://bugs.llvm.org/show_bug.cgi?id=42757
- // very annoying in many different places :/
- template <typename C, size_t D, typename O, void* SFINAE> vector(vector<C,D,O,SFINAE>)
- -> vector<vector<C,D,O,SFINAE>,1>;
- template <typename>
- struct is_vector_instance : public std::false_type {};
- template <typename C, size_t D, typename O>
- struct is_vector_instance<vector<C,D,O>> : public std::true_type {};
- template <typename V>
- constexpr auto is_vector_instance_v = is_vector_instance<V>::value;
- template <size_t I, typename V,
- std::enable_if_t<is_vector_instance_v<std::decay_t<V>>>* = nullptr>
- constexpr decltype(auto) get(V&& v)
- { return std::forward<V>(v).template get<I>(); }
- // implementing this and similar things as part of vector class crashes clang
- // see meta class above
- template<typename Vector>
- struct vector_traits
- {
- using vector = Vector;
- using meta = typename vector::meta;
- template <typename Indices> struct size_of_depths {};
- template <size_t... I> struct size_of_depths<std::index_sequence<I...>>
- {
- using type = std::index_sequence<meta::template dimensions<>[I]...>;
- };
- using shape = typename size_of_depths<std::make_index_sequence<meta::depth()>>::type;
- };
- } // namespace simple::geom
- namespace simple
- {
- template<typename C, size_t D, typename O>
- struct support::define_array_operators<geom::vector<C,D,O>> :
- public support::trivial_array_accessor<geom::vector<C,D,O>, geom::vector<C,D,O>::dimensions>
- {
- template <typename V>
- constexpr static bool has_scalar_shape = std::is_same_v<
- typename geom::vector_traits<V>::shape,
- std::index_sequence<1>
- >;
- constexpr static auto enabled_operators = array_operator::all;
- constexpr static auto enabled_right_element_operators = array_operator::binary | array_operator::in_place;
- constexpr static auto enabled_left_element_operators = []()
- {
- // workaround for a bug in gcc
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101004
- if constexpr (has_scalar_shape<geom::vector<C,D,O>>)
- return array_operator::binary | array_operator::in_place;
- else
- return array_operator::binary
- ^ array_operator::lshift
- ^ array_operator::rshift;
- }();
- // demote a 1D vector when mingling with scalars
- template <typename T, bool Element>
- using result_shape = std::conditional_t<
- Element && has_scalar_shape<geom::vector<T,D,O>>,
- T, geom::vector<T,D,O>
- >;
- template <typename Deduced, array_operator op, typename Other, bool Element>
- using result = result_shape
- <
- // prevent bool promotion for bitwise ops.
- // cause it dun make no sense, mr std! and we's actually's ave sum
- // use for ese in dis ere multidimensional world of ours...
- std::conditional_t
- <
- std::is_same_v<C,bool> &&
- std::is_same_v<Other,bool> &&
- (op && array_operator::bitwise),
- bool, Deduced
- >,
- Element
- >;
- // initially went with geom::vector_traits<geom::vector<C,D,O>>::shape
- // but then couldn't add a column vector Nx1 to a matrix NxM, so ended up with this,
- // it's a bit arbitrary though, doesn't have a name, feels fragile
- using compatibility_tag = std::tuple<O,
- std::index_sequence<D, geom::vector<C,D,O>::meta::depth()>
- >;
- };
- } // namespace simple
- template<typename T, size_t C, typename O>
- class std::numeric_limits<simple::geom::vector<T,C,O>>
- {
- using vec = simple::geom::vector<T,C,O>;
- using limits = std::numeric_limits<T>;
- public:
- constexpr static bool is_specialized = limits::is_specialized;
- [[nodiscard]]
- constexpr static vec min()
- {
- static_assert(limits::is_specialized);
- vec m{};
- for(auto&& c : m)
- c = limits::min();
- return m;
- }
- [[nodiscard]]
- constexpr static vec lowest()
- {
- static_assert(limits::is_specialized);
- vec m{};
- for(auto&& c : m)
- c = limits::lowest();
- return m;
- }
- [[nodiscard]]
- constexpr static vec max()
- {
- static_assert(limits::is_specialized);
- vec m{};
- for(auto&& c : m)
- c = limits::max();
- return m;
- }
- };
- template <typename C, size_t D, typename O>
- struct std::tuple_size<simple::geom::vector<C,D,O>> :
- std::integral_constant<size_t, D> {};
- template <size_t I, typename C, size_t D, typename O>
- struct std::tuple_element<I, simple::geom::vector<C,D,O>>
- {
- using type = C;
- };
- #endif /* end of include guard */
|