diff --git a/src/polymesh/algorithms/cache-optimization.hh b/src/polymesh/algorithms/cache-optimization.hh index 9e9f04c8a5d50ed8e9881bf49f023c559acf59bb..fa6e5991a0c1ac44c2b62f851f4e682e9d438b28 100644 --- a/src/polymesh/algorithms/cache-optimization.hh +++ b/src/polymesh/algorithms/cache-optimization.hh @@ -2,7 +2,7 @@ #include <vector> -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/algorithms/components.hh b/src/polymesh/algorithms/components.hh index 03aa1c23deb9e79d033cac18952cc5f3b9c9cd74..57e984ccdb59bb6178c78ccff5cced9a17563771 100644 --- a/src/polymesh/algorithms/components.hh +++ b/src/polymesh/algorithms/components.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> #include "../detail/primitive_set.hh" #include "../detail/topology_iterator.hh" diff --git a/src/polymesh/algorithms/interpolation.hh b/src/polymesh/algorithms/interpolation.hh index 6263237d56b7c74fdce00e1548c975a564cc68d7..e41d69176c8a8d78102c23e79480df9e8b69db2a 100644 --- a/src/polymesh/algorithms/interpolation.hh +++ b/src/polymesh/algorithms/interpolation.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/algorithms/normalize.hh b/src/polymesh/algorithms/normalize.hh index 970d524aa736a9820a7ba26b1baad0c95257b74e..da713a8282483677658bf17e379d0a45001a646f 100644 --- a/src/polymesh/algorithms/normalize.hh +++ b/src/polymesh/algorithms/normalize.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> #include "../fields.hh" namespace polymesh diff --git a/src/polymesh/algorithms/topology.hh b/src/polymesh/algorithms/topology.hh index 9be358bafd6f3b6ea410bdebc38240e92b5373e9..6d3c53b79c46decdf5778e88e55d0abe3c95382c 100644 --- a/src/polymesh/algorithms/topology.hh +++ b/src/polymesh/algorithms/topology.hh @@ -2,7 +2,7 @@ #include <vector> -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/algorithms/tracing.hh b/src/polymesh/algorithms/tracing.hh index d757a17af723e8927a6763e2d8da7a5370080c15..66c74c70377c8ad2ea2769258231faf14eef391b 100644 --- a/src/polymesh/algorithms/tracing.hh +++ b/src/polymesh/algorithms/tracing.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> #include "../fields.hh" namespace polymesh diff --git a/src/polymesh/attributes.hh b/src/polymesh/attributes.hh index cf3635e4ac607415e6beeef947a0d95d6a50be30..d30c5b815477cd39b72f3440aaa121311bfee87a 100644 --- a/src/polymesh/attributes.hh +++ b/src/polymesh/attributes.hh @@ -73,6 +73,8 @@ public: int size() const; int capacity() const; + size_t byte_size() const override { return size() * sizeof(AttrT); } + size_t allocated_byte_size() const override { return capacity() * sizeof(AttrT); } attribute_iterator<primitive_attribute> begin() { return {0, size(), *this}; } attribute_iterator<primitive_attribute const&> begin() const { return {0, size(), *this}; } @@ -146,8 +148,6 @@ protected: protected: void resize_from(int old_size) override; void clear_with_default() override; - size_t byte_size() const override { return size() * sizeof(AttrT); } - size_t allocated_byte_size() const override { return capacity() * sizeof(AttrT); } void apply_remapping(std::vector<int> const& map) override; void apply_transpositions(std::vector<std::pair<int, int>> const& ts) override; @@ -231,6 +231,13 @@ struct halfedge_attribute final : primitive_attribute<halfedge_tag, AttrT> friend struct smart_collection; }; +/** + * creates a new attribute from a primitive collection. + * + * Usage: + * + * auto pos = attribute(m.vertices(), tg::pos3::zero); + */ template <class AttrT, class Collection> auto attribute(Collection const& c, AttrT const& defaultValue = {}) -> decltype(c.make_attribute(defaultValue)) { diff --git a/src/polymesh/attributes/flags.hh b/src/polymesh/attributes/flags.hh index ea87558e307f20d953b9b73471dd4c082415ccf3..8f182ff050ef25b656005380607c5ddb8c622603 100644 --- a/src/polymesh/attributes/flags.hh +++ b/src/polymesh/attributes/flags.hh @@ -1,6 +1,6 @@ #pragma once -#include "../attributes.hh" +#include <polymesh/attributes.hh> namespace polymesh { diff --git a/src/polymesh/attributes/partitioning.hh b/src/polymesh/attributes/partitioning.hh index e38947261f3f6aefbe2fe7693ed7ca99a985abee..0b674482e7b34de0278b8f8cde008e6004f4656d 100644 --- a/src/polymesh/attributes/partitioning.hh +++ b/src/polymesh/attributes/partitioning.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/attributes/raw_attribute.hh b/src/polymesh/attributes/raw_attribute.hh new file mode 100644 index 0000000000000000000000000000000000000000..ca5a1243632126f581b69037b2ea118bd200b8d5 --- /dev/null +++ b/src/polymesh/attributes/raw_attribute.hh @@ -0,0 +1,275 @@ +#pragma once + +#include <cstring> + +#include <polymesh/Mesh.hh> +#include <polymesh/attributes.hh> +#include <polymesh/span.hh> + +namespace polymesh +{ +/** + * Raw attributes are attributes that store a fixed (but runtime determined) number of bytes per primitive + * The number of bytes per primitive is called stride + * + * NOTE: they are always zero-initialized + * + * TODO: iteration + * TODO: smart range + */ +template <class tag> +struct raw_primitive_attribute : primitive_attribute_base<tag> +{ + template <class A> + using attribute = typename primitive<tag>::template attribute<A>; + using index_t = typename primitive<tag>::index; + using handle_t = typename primitive<tag>::handle; + using tag_t = tag; + + // data access +public: + span<std::byte> operator[](handle_t h) + { + POLYMESH_ASSERT(this->mMesh == h.mesh && "Handle belongs to a different mesh"); + POLYMESH_ASSERT(0 <= h.idx.value && h.idx.value < this->size() && "out of bounds"); + return {this->mData.get() + h.idx.value * mStride, mStride}; + } + span<std::byte const> operator[](handle_t h) const + { + POLYMESH_ASSERT(this->mMesh == h.mesh && "Handle belongs to a different mesh"); + POLYMESH_ASSERT(0 <= h.idx.value && h.idx.value < this->size() && "out of bounds"); + return {this->mData.get() + h.idx.value * mStride, mStride}; + } + span<std::byte> operator[](index_t h) + { + POLYMESH_ASSERT(h.is_valid()); + return {this->mData.get() + h.value * mStride, mStride}; + } + span<std::byte const> operator[](index_t h) const + { + POLYMESH_ASSERT(h.is_valid()); + return {this->mData.get() + h.value * mStride, mStride}; + } + + span<std::byte> operator()(handle_t h) + { + POLYMESH_ASSERT(this->mMesh == h.mesh && "Handle belongs to a different mesh"); + POLYMESH_ASSERT(0 <= h.idx.value && h.idx.value < this->size() && "out of bounds"); + return {this->mData.get() + h.idx.value * mStride, mStride}; + } + span<std::byte const> operator()(handle_t h) const + { + POLYMESH_ASSERT(this->mMesh == h.mesh && "Handle belongs to a different mesh"); + POLYMESH_ASSERT(0 <= h.idx.value && h.idx.value < this->size() && "out of bounds"); + return {this->mData.get() + h.idx.value * mStride, mStride}; + } + span<std::byte> operator()(index_t h) + { + POLYMESH_ASSERT(h.is_valid()); + return {this->mData.get() + h.value * mStride, mStride}; + } + + span<std::byte const> operator()(index_t h) const + { + POLYMESH_ASSERT(h.is_valid()); + return {this->mData.get() + h.value * mStride, mStride}; + } + + std::byte* data() { return mData.get(); } + std::byte const* data() const { return mData.get(); } + + int size() const { return primitive<tag>::all_size(*this->mMesh); } + int stride() const { return mStride; } + int capacity() const { return primitive<tag>::capacity(*this->mMesh); } + size_t byte_size() const override { return size() * mStride; } + size_t allocated_byte_size() const override { return capacity() * mStride; } + + /// true iff this attribute is still attached to a mesh + /// do not use the attribute if not valid + bool is_valid() const { return this->mMesh != nullptr; } + + // methods +public: + /// sets all attribute values to zero + void clear() + { + if (byte_size() > 0) + std::memset(this->mData.get(), 0, byte_size()); + } + + /// Saves ALL data into a vector (includes possibly removed ones) + std::vector<std::byte> to_raw_vector() const + { + auto r = std::vector<std::byte>(byte_size()); + if (r.size() > 0) + std::memcpy(r.data(), this->mData.get(), byte_size()); + return r; + } + + /// returns a new attribute where all elements were bitcast to the given type + /// NOTE: requires trivially copyable T with sizeof(T) == stride() + template <class T> + auto to() const -> attribute<T> + { + static_assert(std::is_trivially_copyable_v<T>, "only works for trivially copyable types"); + POLYMESH_ASSERT(sizeof(T) == mStride && "stride must match exactly with type size"); + auto a = attribute<T>(this->mesh()); + if (byte_size() > 0) + std::memcpy(a.data(), this->mData.get(), byte_size()); + return a; + } + + // public ctor +public: + raw_primitive_attribute() = default; + raw_primitive_attribute(Mesh const& mesh, int stride) : primitive_attribute_base<tag>(&mesh), mStride(stride) + { + POLYMESH_ASSERT(mStride > 0 && "only positive stride supported"); + + // register + this->register_attr(); + + // alloc data (zero-init) + this->mData.reset(new std::byte[this->capacity() * mStride]()); + } + + // members +protected: + unique_array<std::byte> mData; + size_t mStride = 0; ///< number of bytes per element + +protected: + void resize_from(int old_size) override + { + // mesh is already resized, thus capacity() and size() return new values + // old_size is size before resize + + auto new_capacity = this->capacity(); + auto shared_size = std::min(this->size(), old_size); + POLYMESH_ASSERT(shared_size <= new_capacity && "size cannot exceed capacity"); + + // alloc new data + auto new_data = new_capacity > 0 ? new std::byte[new_capacity * mStride]() : nullptr; + + // copy shared region to new data + if (shared_size > 0) + std::memcpy(new_data, this->mData.get(), shared_size * mStride); + + // replace old data + this->mData.reset(new_data); + } + void clear_with_default() override { std::memset(this->mData.get(), 0, byte_size()); } + + void apply_remapping(std::vector<int> const& map) override + { + // TODO: could be made faster by special casing a few stride sizes + for (auto i = 0u; i < map.size(); ++i) + std::memcpy(&this->mData[i], &this->mData[map[i]], mStride); + } + void apply_transpositions(std::vector<std::pair<int, int>> const& ts) override + { + for (auto t : ts) + for (auto i = 0u; i < mStride; ++i) + swap(this->mData[t.first * mStride + i], this->mData[t.second * mStride + i]); + } + + template <class MeshT> + friend struct low_level_attribute_api; + + // move & copy +public: + raw_primitive_attribute(raw_primitive_attribute const& rhs) noexcept // copy + : primitive_attribute_base<tag>(rhs.mMesh), mStride(rhs.mStride) + { + // register attr + this->register_attr(); + + // alloc data + this->mData.reset(new std::byte[this->capacity() * mStride]()); + + // copy valid data + std::memcpy(this->mData.get(), rhs.mData.get(), rhs.byte_size()); + } + raw_primitive_attribute(raw_primitive_attribute&& rhs) noexcept // move + : primitive_attribute_base<tag>(rhs.mMesh), mStride(rhs.mStride) + { + // take data from rhs + this->mData = std::move(rhs.mData); + + // deregister rhs + rhs.deregister_attr(); + + // register lhs + this->register_attr(); + } + raw_primitive_attribute& operator=(raw_primitive_attribute const& rhs) noexcept // copy assign + { + if (this == &rhs) // prevent self-copy + return *this; + + // save old capacity for no-realloc path + auto old_capacity_bytes = is_valid() ? this->capacity() * mStride : 0; + + // deregister from old mesh + this->deregister_attr(); + + // register into new mesh + this->mMesh = rhs.mMesh; + this->mStride = rhs.mStride; + this->register_attr(); + + // realloc if new capacity + auto new_capacity_bytes = this->capacity() * mStride; + if (old_capacity_bytes != new_capacity_bytes) + { + if (new_capacity_bytes == 0) + this->mData.reset(); + else + this->mData.reset(new std::byte[new_capacity_bytes]()); + } + + // copy valid AND defaulted data + std::memcpy(this->mData.get(), rhs.mData.get(), new_capacity_bytes); + + return *this; + } + raw_primitive_attribute& operator=(raw_primitive_attribute&& rhs) noexcept // move assign + { + if (this == &rhs) // prevent self-move + return *this; + + // deregister from old mesh + this->deregister_attr(); + + // register into new mesh + this->mMesh = rhs.mMesh; + this->register_attr(); + + // take data of rhs + this->mStride = rhs.mStride; + this->mData = std::move(rhs.mData); + + // deregister rhs + rhs.deregister_attr(); + + return *this; + } +}; + +struct raw_vertex_attribute final : raw_primitive_attribute<vertex_tag> +{ + using raw_primitive_attribute<vertex_tag>::raw_primitive_attribute; +}; +struct raw_face_attribute final : raw_primitive_attribute<face_tag> +{ + using raw_primitive_attribute<face_tag>::raw_primitive_attribute; +}; +struct raw_edge_attribute final : raw_primitive_attribute<edge_tag> +{ + using raw_primitive_attribute<edge_tag>::raw_primitive_attribute; +}; +struct raw_halfedge_attribute final : raw_primitive_attribute<halfedge_tag> +{ + using raw_primitive_attribute<halfedge_tag>::raw_primitive_attribute; +}; +} diff --git a/src/polymesh/detail/topology_iterator.hh b/src/polymesh/detail/topology_iterator.hh index 7dbeb6bb27ce35cb801f4e3673700ec6262fcb6f..90bafd0e0905a4b0c3b6e16b5da0688929a3b371 100644 --- a/src/polymesh/detail/topology_iterator.hh +++ b/src/polymesh/detail/topology_iterator.hh @@ -2,7 +2,7 @@ #include <queue> -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> #include "primitive_set.hh" /// CAUTION: these iterators do NOT work like normal iterators where you can make copies! diff --git a/src/polymesh/detail/unique_array.hh b/src/polymesh/detail/unique_array.hh index ff82441de80384d0d998c74f08b277dffa17caf7..1d316b82dc16df7a8f10a42bfc9d5be28bd71bc5 100644 --- a/src/polymesh/detail/unique_array.hh +++ b/src/polymesh/detail/unique_array.hh @@ -11,7 +11,7 @@ struct unique_array using element_type = T; unique_array() = default; - explicit unique_array(int size) { ptr = new T[size]; } + explicit unique_array(int size) { ptr = new T[size](); } ~unique_array() { delete[] ptr; diff --git a/src/polymesh/formats/obj.hh b/src/polymesh/formats/obj.hh index d61ecbecd08c866fc31fddabaf190b47fe9a5a31..eb56a424e91ddef715847e6a624a6d2d3ab665ea 100644 --- a/src/polymesh/formats/obj.hh +++ b/src/polymesh/formats/obj.hh @@ -3,7 +3,7 @@ #include <iosfwd> #include <string> -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/formats/off.hh b/src/polymesh/formats/off.hh index 7847a862392c8cb88134817936905f6a40b49073..18ea1a42ad03aa031c29d919c3886fe143037161 100644 --- a/src/polymesh/formats/off.hh +++ b/src/polymesh/formats/off.hh @@ -4,7 +4,7 @@ #include <iosfwd> #include <string> -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/formats/ply.hh b/src/polymesh/formats/ply.hh index 9f890e2b26468cd70ec2e768ed8868d4a23bd6be..76b0b1b944a9acef19ba4bdd146f23ebd41ce408 100644 --- a/src/polymesh/formats/ply.hh +++ b/src/polymesh/formats/ply.hh @@ -3,7 +3,7 @@ #include <iosfwd> #include <string> -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/formats/stl.hh b/src/polymesh/formats/stl.hh index 347318acd92d7d758c71637a0fb1f45ffba84f7a..0c5a364697a1f6e2e0a500c3e540253b5f2289bc 100644 --- a/src/polymesh/formats/stl.hh +++ b/src/polymesh/formats/stl.hh @@ -4,7 +4,7 @@ #include <iosfwd> #include <string> -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/impl/impl_attributes.hh b/src/polymesh/impl/impl_attributes.hh index e17c5d5e9a107912bea02a8c970ecc7e3d5cf000..b8aa5a8eb200ee0e8b0f151d19d60b37ed1ec826 100644 --- a/src/polymesh/impl/impl_attributes.hh +++ b/src/polymesh/impl/impl_attributes.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { @@ -189,7 +189,7 @@ primitive_attribute<tag, AttrT>& primitive_attribute<tag, AttrT>::operator=(prim if (new_capacity == 0) this->mData.reset(); else - this->mData.reset(new AttrT[new_capacity]); + this->mData.reset(new AttrT[new_capacity]()); } // copy ALL data (valid and defaulted) @@ -234,7 +234,7 @@ void primitive_attribute<tag, AttrT>::resize_from(int old_size) POLYMESH_ASSERT(shared_size <= new_capacity && "size cannot exceed capacity"); // alloc new data - auto new_data = new_capacity > 0 ? new AttrT[new_capacity] : nullptr; + auto new_data = new_capacity > 0 ? new AttrT[new_capacity]() : nullptr; // copy shared region to new data std::copy_n(this->mData.get(), shared_size, new_data); diff --git a/src/polymesh/impl/impl_cursors.hh b/src/polymesh/impl/impl_cursors.hh index 541efe5bffb51a725d4834f134ad8529c7708137..78ee003203aacce0236f5cc58701156f996a5d9d 100644 --- a/src/polymesh/impl/impl_cursors.hh +++ b/src/polymesh/impl/impl_cursors.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/impl/impl_low_level_api_base.hh b/src/polymesh/impl/impl_low_level_api_base.hh index 7bdd5f93d135eb79c568a15a1eba86f0ac8da142..a9665a0f97fb42fb8d1542aee23691ad3f1b2474 100644 --- a/src/polymesh/impl/impl_low_level_api_base.hh +++ b/src/polymesh/impl/impl_low_level_api_base.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/impl/impl_low_level_api_mutable.hh b/src/polymesh/impl/impl_low_level_api_mutable.hh index 369c92833f1c5cd1ca5aea9fe705fbe4b14308a9..b0987e39701d3e57f86ba5ab1592aa49f2156b13 100644 --- a/src/polymesh/impl/impl_low_level_api_mutable.hh +++ b/src/polymesh/impl/impl_low_level_api_mutable.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/impl/impl_mesh.hh b/src/polymesh/impl/impl_mesh.hh index 892f15ab4dcfa052ba27ae6c6b0d5ee049b93bf3..5af5703acc73fa35bc73673c8222375c413f317c 100644 --- a/src/polymesh/impl/impl_mesh.hh +++ b/src/polymesh/impl/impl_mesh.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> #include "../assert.hh" #include "../detail/split_vector.hh" diff --git a/src/polymesh/impl/impl_primitive.hh b/src/polymesh/impl/impl_primitive.hh index 17d3e0437b3d608c4a31277b2999e5cd7dea9c7c..6aec17e96661f1ee968d2148f056cbdbaffc9a97 100644 --- a/src/polymesh/impl/impl_primitive.hh +++ b/src/polymesh/impl/impl_primitive.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/impl/impl_ranges.hh b/src/polymesh/impl/impl_ranges.hh index e834348c13a56e48558b8128f9577bbc3289b2d2..5725bce6b4591302f47189d0c1f1accf65ce1aae 100644 --- a/src/polymesh/impl/impl_ranges.hh +++ b/src/polymesh/impl/impl_ranges.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/objects/cone.hh b/src/polymesh/objects/cone.hh index 4e17284dc8d70d644be19a851a8af275dbc5d1e0..c2244eedf0063086fade2bf605e6db7f9d3b2efb 100644 --- a/src/polymesh/objects/cone.hh +++ b/src/polymesh/objects/cone.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh::objects { diff --git a/src/polymesh/objects/cube.hh b/src/polymesh/objects/cube.hh index 5389783171a870d99edf581a006d69b37faf360f..5d0c1e026678c6efb86c24b2c2edc97c81a12747 100644 --- a/src/polymesh/objects/cube.hh +++ b/src/polymesh/objects/cube.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> #include "../fields.hh" namespace polymesh diff --git a/src/polymesh/objects/cylinder.hh b/src/polymesh/objects/cylinder.hh index 7e576b6cb9a717c3f0184d84deb1d45fd037e168..86dfa33614209fed0d25890449717b1a893eb76e 100644 --- a/src/polymesh/objects/cylinder.hh +++ b/src/polymesh/objects/cylinder.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh::objects { diff --git a/src/polymesh/objects/quad.hh b/src/polymesh/objects/quad.hh index 229eeab9a0f4cea066c7c9ee5e9caa658e3c01b4..85433cbed95a36df28156fa459e156deebcc9239 100644 --- a/src/polymesh/objects/quad.hh +++ b/src/polymesh/objects/quad.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh { diff --git a/src/polymesh/objects/uv_sphere.hh b/src/polymesh/objects/uv_sphere.hh index a44b2d9e2b3e47ae57d43a40a4392201a5118eb2..fbbea26ab815f9326f6390171ea392a8a0e9e462 100644 --- a/src/polymesh/objects/uv_sphere.hh +++ b/src/polymesh/objects/uv_sphere.hh @@ -1,6 +1,6 @@ #pragma once -#include "../Mesh.hh" +#include <polymesh/Mesh.hh> namespace polymesh::objects { diff --git a/src/polymesh/span.hh b/src/polymesh/span.hh new file mode 100644 index 0000000000000000000000000000000000000000..db18e1a76196c5cd71055be3cf0102b2089fa532 --- /dev/null +++ b/src/polymesh/span.hh @@ -0,0 +1,80 @@ +#pragma once + +#include <cstddef> +#include <type_traits> + +#include <polymesh/assert.hh> +#include <polymesh/tmp.hh> + +namespace polymesh +{ +// a non-owning view of a contiguous array of Ts +// can be read and write (span<const T> vs span<T>) +// is trivially copyable (and cheap) +// NOTE: is range-checked via POLYMESH_ASSERT +template <class T> +struct span +{ + // ctors +public: + constexpr span() = default; + constexpr span(T* data, size_t size) : _data(data), _size(size) {} + constexpr span(T* d_begin, T* d_end) : _data(d_begin), _size(d_end - d_begin) {} + template <size_t N> + constexpr span(T (&data)[N]) : _data(data), _size(N) + { + } + template <class Container, std::enable_if_t<tmp::is_contiguous_range<Container, T>, int> = 0> + constexpr span(Container&& c) : _data(c.data()), _size(c.size()) + { + } + + // container +public: + constexpr T* begin() const { return _data; } + constexpr T* end() const { return _data + _size; } + + constexpr T* data() const { return _data; } + constexpr size_t size() const { return _size; } + constexpr bool empty() const { return _size == 0; } + + constexpr T& operator[](size_t i) const + { + POLYMESH_ASSERT(i < _size); + return _data[i]; + } + + constexpr T& front() const + { + POLYMESH_ASSERT(_size > 0); + return _data[0]; + } + constexpr T& back() const + { + POLYMESH_ASSERT(_size > 0); + return _data[_size - 1]; + } + + // subviews +public: + constexpr span first(size_t n) const + { + POLYMESH_ASSERT(n <= _size); + return {_data, n}; + } + constexpr span last(size_t n) const + { + POLYMESH_ASSERT(n <= _size); + return {_data + (_size - n), n}; + } + constexpr span subspan(size_t offset, size_t count) const + { + POLYMESH_ASSERT(offset + count <= _size); + return {_data + offset, count}; + } + +private: + T* _data = nullptr; + size_t _size = 0; +}; +} diff --git a/src/polymesh/tmp.hh b/src/polymesh/tmp.hh index 527247df3d7ea4b16e1aa031cbf5008e2a39696d..7801d2caf52d4440e8d35f6d3bea9baf80da20a5 100644 --- a/src/polymesh/tmp.hh +++ b/src/polymesh/tmp.hh @@ -1,5 +1,6 @@ #pragma once +#include <cstddef> #include <utility> namespace polymesh @@ -99,5 +100,49 @@ struct constant_rational } }; +namespace detail +{ +template <class Container, class ElementT> +auto contiguous_range_test(Container* c) -> decltype(static_cast<ElementT*>(c->data()), static_cast<size_t>(c->size()), 0); +template <class Container, class ElementT> +char contiguous_range_test(...); + +template <class Container, class ElementT, class = void> +struct is_range_t : std::false_type +{ +}; +template <class ElementT, size_t N> +struct is_range_t<ElementT[N], ElementT> : std::true_type +{ +}; +template <class ElementT, size_t N> +struct is_range_t<ElementT[N], ElementT const> : std::true_type +{ +}; +template <class ElementT, size_t N> +struct is_range_t<ElementT (&)[N], ElementT> : std::true_type +{ +}; +template <class ElementT, size_t N> +struct is_range_t<ElementT (&)[N], ElementT const> : std::true_type +{ +}; +template <class Container, class ElementT> +struct is_range_t<Container, + ElementT, + std::void_t< // + decltype(static_cast<ElementT&>(*std::declval<Container>().begin())), // + decltype(std::declval<Container>().end()) // + >> : std::true_type +{ +}; +} + +template <class Container, class ElementT> +static constexpr bool is_contiguous_range = sizeof(detail::contiguous_range_test<Container, ElementT>(nullptr)) == sizeof(int); + +template <class Container, class ElementT> +static constexpr bool is_range = detail::is_range_t<Container, ElementT>::value; + } // namespace tmp } // namespace polymesh