Commit 07ddb197 authored by Philip Trettner's avatar Philip Trettner
Browse files

Merge branch 'feature_properties' into 'master'

Attributes as smart_collection + readonly properties.

See merge request !4
parents fda6f9d2 10021693
......@@ -5,6 +5,8 @@
#include "attribute_base.hh"
#include "cursors.hh"
#include "properties.hh"
#include "ranges.hh"
#include "tmp.hh"
/** Attributes
......@@ -21,7 +23,7 @@
* myAttr[v] = 7;
*
* TODO:
* for (auto& a : myAttr) // NOTE: does not include deleted primitives (and is thus a bit slower)
* for (auto& a : myAttr) // NOTE: does include deleted primitives
* a += 1;
* // auto and auto const& also work of course
*/
......@@ -29,7 +31,7 @@
namespace polymesh
{
template <class tag, class AttrT>
struct primitive_attribute : primitive_attribute_base<tag>
struct primitive_attribute : primitive_attribute_base<tag>, smart_range<primitive_attribute<tag, AttrT>, AttrT>
{
template <class A>
using attribute = typename primitive<tag>::template attribute<A>;
......@@ -52,6 +54,11 @@ public:
AttrT const* data() const { return mData.data; }
int size() const;
attribute_iterator<primitive_attribute&> begin() { return {0, *this}; }
attribute_iterator<primitive_attribute const&> begin() const { return {0, *this}; }
attribute_iterator<primitive_attribute&> end() { return {size(), *this}; }
attribute_iterator<primitive_attribute const&> end() const { return {size(), *this}; }
// methods
public:
void clear(AttrT const& value);
......@@ -67,6 +74,16 @@ public:
template <class FuncT>
void compute(FuncT&& f);
template <class FuncT>
auto view(FuncT&& f) const -> readonly_property<primitive_attribute<tag, AttrT> const&, FuncT>;
template <class FuncT>
void view(FuncT&& f) && = delete;
// template <class ReadT, class WriteT>
// auto view(ReadT&& r, WriteT&& w) -> readwrite_property<primitive_attribute<tag, AttrT>, ReadT, WriteT>;
// template <class ReadT, class WriteT>
// void view(ReadT&& r, WriteT&& w) && = delete;
/// copies as much data as possible from the given vector
void copy_from(std::vector<AttrT> const& data);
/// copies as much data as possible from the given array
......@@ -76,6 +93,8 @@ public:
/// Saves ALL data into a vector (includes possibly removed ones)
std::vector<AttrT> to_vector() const;
// TODO: specialized implementation of to_vector(FuncT&&)
using smart_range<primitive_attribute<tag, AttrT>, AttrT>::to_vector;
// public ctor
public:
......
......@@ -123,6 +123,8 @@ struct vertex_handle : primitive_handle<vertex_tag>
halfedge_handle any_incoming_halfedge() const; ///< invalid if isolated
edge_handle any_edge() const; ///< invalid if isolated
// TODO: make all_faces and faces = all_faces.filter(is_valid)
vertex_halfedge_in_ring incoming_halfedges() const;
vertex_halfedge_out_ring outgoing_halfedges() const;
vertex_edge_ring edges() const;
......
......@@ -345,4 +345,12 @@ void primitive_attribute<tag, AttrT>::compute(FuncT &&f)
for (auto h : primitive<tag>::valid_collection_of(*this->mMesh))
d[(int)h] = f(h);
}
template <class tag, class AttrT>
template <class FuncT>
auto primitive_attribute<tag, AttrT>::view(FuncT &&f) const -> readonly_property<primitive_attribute<tag, AttrT> const&, FuncT>
{
return readonly_property<primitive_attribute<tag, AttrT> const&, FuncT>(*this, f);
}
}
......@@ -230,6 +230,8 @@ auto smart_range<this_t, ElementT>::avg(FuncT &&f) const -> tmp::decayed_result_
assert(it_begin != it_end && "requires non-empty range");
auto s = f(*it_begin);
auto cnt = 1;
static_assert(tmp::can_divide_by<decltype(s), decltype(cnt)>::value,
"Cannot divide sum by an integer. (if glm is used, including <glm/ext.hpp> might help)");
++it_begin;
while (it_begin != it_end)
{
......@@ -250,6 +252,8 @@ auto smart_range<this_t, ElementT>::weighted_avg(FuncT &&f, WeightT &&w) const -
auto e = *it_begin;
auto s = f(e);
auto ws = w(e);
static_assert(tmp::can_divide_by<decltype(s), decltype(ws)>::value,
"Cannot divide sum by weight. (if glm is used, including <glm/ext.hpp> might help)");
++it_begin;
while (it_begin != it_end)
{
......@@ -362,8 +366,46 @@ auto smart_range<this_t, ElementT>::to_map(FuncT &&f) const -> std::map<ElementT
return m;
}
template <class this_t, class element_handle>
int primitive_ring<this_t, element_handle>::size() const
template <class this_t, class ElementT>
template <class PredT>
auto smart_range<this_t, ElementT>::where(PredT &&p) const -> filtered_range<ElementT, this_t const &, PredT>
{
auto it_begin = static_cast<this_t const *>(this)->begin();
auto it_end = static_cast<this_t const *>(this)->end();
return {it_begin, it_end, p};
}
template <class this_t, class ElementT>
template <class PredT>
auto smart_range<this_t, ElementT>::where(PredT &&p) -> filtered_range<ElementT, this_t &, PredT>
{
auto it_begin = static_cast<this_t *>(this)->begin();
auto it_end = static_cast<this_t *>(this)->end();
return {it_begin, it_end, p};
}
template <class this_t, class ElementT>
template <class PredT>
auto smart_range<this_t, ElementT>::filter(PredT &&p) const -> filtered_range<ElementT, this_t const &, PredT>
{
auto it_begin = static_cast<this_t const *>(this)->begin();
auto it_end = static_cast<this_t const *>(this)->end();
static_assert(std::is_same<decltype(it_begin), typename filtered_range<ElementT, this_t const &, PredT>::IteratorT>::value, "");
return filtered_range<ElementT, this_t const &, PredT>(it_begin, it_end, p);
}
template <class this_t, class ElementT>
template <class PredT>
auto smart_range<this_t, ElementT>::filter(PredT &&p) -> filtered_range<ElementT, this_t &, PredT>
{
auto it_begin = static_cast<this_t *>(this)->begin();
auto it_end = static_cast<this_t *>(this)->end();
return {it_begin, it_end, p};
}
template <class this_t, class tag>
int primitive_ring<this_t, tag>::size() const
{
auto cnt = 0;
for (auto v : *static_cast<this_t const *>(this))
......@@ -817,4 +859,10 @@ void halfedge_collection<iterator>::rotate_prev(halfedge_handle h) const
{
low_level_api(this->mesh).halfedge_rotate_prev(h.idx);
}
template <class ElementT, class RangeT, class PredT>
filtered_range<ElementT, RangeT, PredT>::filtered_range(filtered_range::IteratorT begin, filtered_range::IteratorT end, PredT predicate)
: obegin(begin), oend(end), pred(predicate)
{
}
}
......@@ -29,7 +29,7 @@ struct valid_primitive_iterator
handle.idx = low_level_api(handle.mesh).next_valid_idx_from(handle.idx);
return *this;
}
valid_primitive_iterator operator++(int) const
valid_primitive_iterator operator++(int)const
{
auto i = *this;
return ++i;
......@@ -68,7 +68,7 @@ struct all_primitive_iterator
++handle.idx.value;
return *this;
}
all_primitive_iterator operator++(int) const
all_primitive_iterator operator++(int)const
{
auto i = *this;
return ++i;
......@@ -90,13 +90,67 @@ private:
handle_t handle;
};
// ================= ATTRIBUTES =================
template <class AttributeT>
struct attribute_iterator
{
int idx;
AttributeT attr;
auto operator*() const -> decltype(attr.data()[idx]) { return attr.data()[idx]; }
attribute_iterator& operator++()
{
++idx;
return *this;
}
attribute_iterator operator++(int)const
{
auto i = *this;
return ++i;
}
bool operator==(attribute_iterator const& rhs) const { return idx == rhs.idx; }
bool operator!=(attribute_iterator const& rhs) const { return idx != rhs.idx; }
};
// ================= FILTER + MAP =================
template <class IteratorT, class PredT>
struct filtering_iterator
{
IteratorT ocurr;
IteratorT oend;
PredT pred;
auto operator*() const -> decltype(*ocurr) { return *ocurr; }
filtering_iterator& operator++()
{
do
++ocurr;
while (ocurr != oend && !pred(*ocurr));
return *this;
}
filtering_iterator operator++(int)const
{
auto i = *this;
return ++i;
}
bool operator==(filtering_iterator const& rhs) const { return ocurr == rhs.ocurr; }
bool operator!=(filtering_iterator const& rhs) const { return ocurr != rhs.ocurr; }
};
// TODO: mapped_iterator
// ================= CIRCULATOR =================
/// Generic half-edge circulator (via CRTP)
/// implement operator* given the current `handle`
/// implement void advance() to change `handle`
/// inherit ctor's from primitive_circulator
template<typename this_t>
template <typename this_t>
struct primitive_circulator
{
primitive_circulator() = default;
......@@ -108,7 +162,7 @@ struct primitive_circulator
not_at_begin = true;
return *this;
}
primitive_circulator operator++(int) const
primitive_circulator operator++(int)const
{
auto i = *this;
return ++i;
......
#pragma once
#include "primitives.hh"
#include "tmp.hh"
namespace polymesh
{
template <class CollectionT, class FuncT>
struct readonly_property
{
readonly_property(CollectionT collection, FuncT func) : mCollection(collection), mFunc(func) {}
using index_t = typename std::decay<CollectionT>::type::index_t;
using handle_t = typename std::decay<CollectionT>::type::handle_t;
using input_t = decltype(std::declval<CollectionT>().operator[](index_t()));
using output_t = typename tmp::decayed_result_type_of<FuncT, input_t>;
output_t operator[](handle_t h) const { return mFunc(mCollection(h)); }
output_t operator[](index_t h) const { return mFunc(mCollection(h)); }
output_t operator()(handle_t h) const { return mFunc(mCollection(h)); }
output_t operator()(index_t h) const { return mFunc(mCollection(h)); }
int size() const { return mCollection.size(); }
template <class Func2T>
auto view(Func2T&& f) const -> readonly_property<readonly_property<CollectionT, FuncT>, Func2T>
{
// TODO: move to impl_properties
return {*this, f};
}
private:
CollectionT mCollection;
FuncT mFunc;
};
}
......@@ -23,9 +23,14 @@ struct weighted_sample
W weight;
};
template <class ElementT, class RangeT, class PredT>
struct filtered_range;
template <class this_t, class ElementT>
struct smart_range
{
// using iterator_t = decltype(std::declval<this_t*>()->begin());
/// returns the first element in this range
/// returns invalid on empty ranges (or default ctor'd one)
ElementT first() const;
......@@ -83,8 +88,8 @@ struct smart_range
/// calculates the avg of f(e) over all elements
/// undefined behavior if range is empty
/// requires operator+ for the elements as well as operator/(ElementT, int)
template <class FuncT>
auto avg(FuncT&& f) const -> tmp::decayed_result_type_of<FuncT, ElementT>;
template <class FuncT = tmp::identity>
auto avg(FuncT&& f = {}) const -> tmp::decayed_result_type_of<FuncT, ElementT>;
/// calculates the weighted avg of f(e) with weight w(e) over all elements
/// undefined behavior if range is empty
......@@ -131,6 +136,17 @@ struct smart_range
template <class FuncT>
auto to_map(FuncT&& f) const -> std::map<ElementT, tmp::decayed_result_type_of<FuncT, ElementT>>;
/// returns a new range that consists of all elements where p(x) is true
template <class PredT>
auto where(PredT&& p) const -> filtered_range<ElementT, this_t const&, PredT>;
template <class PredT>
auto where(PredT&& p) -> filtered_range<ElementT, this_t&, PredT>;
/// same as where
template <class PredT>
auto filter(PredT&& p) const -> filtered_range<ElementT, this_t const&, PredT>;
template <class PredT>
auto filter(PredT&& p) -> filtered_range<ElementT, this_t&, PredT>;
// TODO: (requires new ranges)
// - filter (or where?)
// - map
......@@ -139,6 +155,46 @@ struct smart_range
// - conversions from vector/set/map
};
// ================= FILTER + MAP =================
template <class ElementT, class RangeT, class PredT>
struct filtered_range : smart_range<filtered_range<ElementT, RangeT, PredT>, ElementT>
{
using IteratorT = decltype(std::declval<RangeT>().begin());
filtering_iterator<IteratorT, PredT> begin() const { return {obegin, oend, pred}; }
filtering_iterator<IteratorT, PredT> end() const { return {oend, oend, pred}; }
IteratorT obegin;
IteratorT oend;
PredT pred;
filtered_range() = default;
filtered_range(IteratorT begin, IteratorT end, PredT predicate);
filtered_range(filtered_range const&) = delete;
filtered_range(filtered_range&&) = delete;
filtered_range& operator=(filtered_range const&) = delete;
filtered_range& operator=(filtered_range&&) = delete;
};
/*template <class ElementT, class IteratorT, class PredT>
struct filtered_range : smart_range<filtered_range<ElementT, IteratorT, PredT>, IteratorT, ElementT>
{
filtering_iterator<IteratorT, PredT> begin() const { return {obegin, oend, pred}; }
filtering_iterator<IteratorT, PredT> end() const { return {oend, oend, pred}; }
IteratorT obegin;
IteratorT oend;
PredT pred;
filtered_range() = default;
filtered_range(filtered_range const&) = delete;
filtered_range(filtered_range &&) = delete;
filtered_range& operator=(filtered_range const&) = delete;
filtered_range& operator=(filtered_range &&) = delete;
};*/
// TODO: mapped_range
// ================= COLLECTION =================
template <class mesh_ptr, class tag, class iterator>
......@@ -527,4 +583,5 @@ struct halfedge_ring : halfedge_primitive_ring<halfedge_tag, halfedge_ring_circu
{
using halfedge_primitive_ring<halfedge_tag, halfedge_ring_circulator>::halfedge_primitive_ring;
};
}
......@@ -48,6 +48,23 @@ struct if_then_else<false, TrueType, FalseType>
template <class TargetT, class TestT>
using ref_if_mut = typename if_then_else<std::is_const<TestT>::value, TargetT, typename std::add_lvalue_reference<TargetT>::type>::result;
struct identity
{
template<typename T>
T operator()(T x) const { return x; }
};
template<class T, class DivisorT>
struct can_divide_by
{
template<class C>
static bool test(decltype(std::declval<C>() / std::declval<DivisorT>())*);
template<class C>
static int test(...);
enum { value = sizeof(test<T>(0)) == sizeof(bool) };
};
// std::add_lvalue_reference
// template <class T>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment