Commit 7fb57662 authored by Philip Trettner's avatar Philip Trettner
Browse files

flags and partitioning datatype, std hash for indices, improved optimization code a bit

parent 42932d88
#pragma once
#include <unordered_map>
#include "../Mesh.hh"
#include "../detail/permutation.hh"
#include "../detail/random.hh"
#include "../detail/union_find.hh"
#include "../data/partitioning.hh"
namespace polymesh
{
/// Optimizes mesh layout for face traversals
......@@ -67,15 +71,15 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
return {};
assert(m.faces().size() == m.all_faces().size() && "non-compact currently not supported");
polymesh::detail::disjoint_set clusters(m.all_faces().size());
auto clusters = make_partitioning(m.faces());
std::vector<std::pair<float, std::pair<int, int>>> edges;
std::vector<std::pair<float, std::pair<face_index, face_index>>> edges;
for (auto e : m.edges())
edges.push_back({-1, {(int)e.faceA(), (int)e.faceB()}});
edges.push_back({-1, {e.faceA(), e.faceB()}});
struct node
{
int rep;
face_index rep;
std::vector<node*> children;
bool is_leaf() const { return children.empty(); }
......@@ -84,7 +88,7 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
{
if (is_leaf())
{
indices[rep] = next_idx++;
indices[(int)rep] = next_idx++;
}
else
{
......@@ -100,11 +104,12 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
}
};
std::map<int, node*> cluster_centers;
std::unordered_map<face_index, node*> cluster_centers;
for (auto f : m.faces())
cluster_centers[(int)f] = new node{(int)f};
cluster_centers[f] = new node{f};
std::map<std::pair<int, int>, float> cluster_neighbors;
int64_t fcnt = m.all_faces().size();
std::unordered_map<int64_t, float> cluster_neighbors;
// bottom-up clustering
auto cluster_limit = 1;
......@@ -118,11 +123,11 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
auto f0 = e.second.first;
auto f1 = e.second.second;
auto s0 = clusters.size_of(f0);
auto s1 = clusters.size_of(f1);
auto s0 = clusters[f0].size();
auto s1 = clusters[f1].size();
if (s0 + s1 <= cluster_limit)
clusters.do_union(f0, f1);
clusters.merge(f0, f1);
}
// collect new neighbors
......@@ -130,8 +135,8 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
for (auto e : edges)
{
auto w = e.first;
auto f0 = clusters.find(e.second.first);
auto f1 = clusters.find(e.second.second);
auto f0 = clusters[e.second.first].root();
auto f1 = clusters[e.second.second].root();
if (f0 == f1)
continue;
......@@ -139,24 +144,28 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
if (f0 > f1)
std::swap(f0, f1);
cluster_neighbors[{f0, f1}] += w;
cluster_neighbors[(int)f0 * fcnt + (int)f1] += w;
}
// create new edges
edges.clear();
for (auto const& kvp : cluster_neighbors)
edges.push_back({kvp.second, kvp.first});
{
auto f0 = face_index(kvp.first / fcnt);
auto f1 = face_index(kvp.first % fcnt);
edges.push_back({kvp.second, {f0, f1}});
}
sort(edges.begin(), edges.end());
// new cluster centers
std::map<int, node*> new_centers;
std::unordered_map<face_index, node*> new_centers;
// .. create nodes
for (auto const& kvp : cluster_centers)
if (clusters.is_representative(kvp.first))
if (clusters[kvp.first].is_root())
new_centers[kvp.first] = new node{kvp.first};
// .. add children
for (auto const& kvp : cluster_centers)
new_centers[clusters.find(kvp.first)]->children.push_back(kvp.second);
new_centers[clusters[kvp.first].root()]->children.push_back(kvp.second);
// .. replace old
cluster_centers = new_centers;
}
......@@ -177,8 +186,121 @@ inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
inline std::vector<int> cache_coherent_vertex_layout(Mesh const& m)
{
assert(0 && "TODO");
return {};
if (m.vertices().empty())
return {};
assert(m.vertices().size() == m.all_vertices().size() && "non-compact currently not supported");
auto clusters = make_partitioning(m.vertices());
std::vector<std::pair<float, std::pair<vertex_index, vertex_index>>> edges;
for (auto e : m.edges())
edges.push_back({-1, {e.vertexA(), e.vertexB()}});
struct node
{
vertex_index rep;
std::vector<node*> children;
bool is_leaf() const { return children.empty(); }
void assign_idx(int& next_idx, std::vector<int>& indices) const
{
if (is_leaf())
{
indices[(int)rep] = next_idx++;
}
else
{
for (auto n : children)
n->assign_idx(next_idx, indices);
}
}
~node()
{
for (auto n : children)
delete n;
}
};
std::unordered_map<vertex_index, node*> cluster_centers;
for (auto f : m.vertices())
cluster_centers[f] = new node{f};
int64_t vcnt = m.all_vertices().size();
std::unordered_map<int64_t, float> cluster_neighbors;
// bottom-up clustering
auto cluster_limit = 1;
while (!edges.empty())
{
cluster_limit *= 2;
// merge edges where appropriate
for (auto e : edges)
{
auto f0 = e.second.first;
auto f1 = e.second.second;
auto s0 = clusters[f0].size();
auto s1 = clusters[f1].size();
if (s0 + s1 <= cluster_limit)
clusters.merge(f0, f1);
}
// collect new neighbors
cluster_neighbors.clear();
for (auto e : edges)
{
auto w = e.first;
auto f0 = clusters[e.second.first].root();
auto f1 = clusters[e.second.second].root();
if (f0 == f1)
continue;
if (f0 > f1)
std::swap(f0, f1);
cluster_neighbors[(int)f0 * vcnt + (int)f1] += w;
}
// create new edges
edges.clear();
for (auto const& kvp : cluster_neighbors)
{
auto f0 = vertex_index(kvp.first / vcnt);
auto f1 = vertex_index(kvp.first % vcnt);
edges.push_back({kvp.second, {f0, f1}});
}
sort(edges.begin(), edges.end());
// new cluster centers
std::unordered_map<vertex_index, node*> new_centers;
// .. create nodes
for (auto const& kvp : cluster_centers)
if (clusters[kvp.first].is_root())
new_centers[kvp.first] = new node{kvp.first};
// .. add children
for (auto const& kvp : cluster_centers)
new_centers[clusters[kvp.first].root()]->children.push_back(kvp.second);
// .. replace old
cluster_centers = new_centers;
}
// distribute indices
std::vector<int> new_indices(m.all_vertices().size());
int next_idx = 0;
for (auto const& kvp : cluster_centers)
kvp.second->assign_idx(next_idx, new_indices);
assert(next_idx == m.vertices().size());
// cleanup
for (auto const& kvp : cluster_centers)
delete kvp.second;
return new_indices;
}
inline void optimize_edges_for_faces(Mesh& m)
......
......@@ -128,5 +128,7 @@ protected:
public:
virtual ~primitive_attribute_base() { deregister_attr(); }
Mesh const& mesh() { return *mMesh; }
};
}
......@@ -52,7 +52,7 @@ public:
AttrT* data() { return mData.data; }
AttrT const* data() const { return mData.data; }
int size() const;
int size() const; ///< INCLUDES flagged-for-removal entries!
attribute_iterator<primitive_attribute&> begin() { return {0, *this}; }
attribute_iterator<primitive_attribute const&> begin() const { return {0, *this}; }
......
......@@ -190,3 +190,34 @@ struct halfedge_handle : primitive_handle<halfedge_tag>
halfedge_ring ring() const; ///< all half-edges along the same ring
};
}
// ======================== STD Extensions ========================
namespace std
{
template <class tag>
struct hash<polymesh::primitive_index<tag>>
{
size_t operator()(polymesh::primitive_index<tag> const& i) const { return std::hash<int>()(i.value); }
};
template <>
struct hash<polymesh::face_index>
{
size_t operator()(polymesh::face_index const& i) const { return std::hash<int>()(i.value); }
};
template <>
struct hash<polymesh::vertex_index>
{
size_t operator()(polymesh::vertex_index const& i) const { return std::hash<int>()(i.value); }
};
template <>
struct hash<polymesh::edge_index>
{
size_t operator()(polymesh::edge_index const& i) const { return std::hash<int>()(i.value); }
};
template <>
struct hash<polymesh::halfedge_index>
{
size_t operator()(polymesh::halfedge_index const& i) const { return std::hash<int>()(i.value); }
};
}
#pragma once
#include "../attributes.hh"
namespace polymesh
{
/**
* Datatype for disjoint subsets (union-find datastructure)
*
* Usage:
*
* TODO!
*/
/* template<class index_t>
struct disjoint_set
{
public:
int size() { return }
private:
index_t mParent;
int mSize = 0;
};
*/
}
#pragma once
#include "../attributes.hh"
namespace polymesh
{
template <class enum_t, class tag>
struct flags;
template <class enum_t = bool, class mesh_ptr, class tag, class iterator>
flags<enum_t, tag> make_flags(smart_collection<mesh_ptr, tag, iterator> const& c, enum_t initial_value = (enum_t)0);
/**
* Datatype for flags
*
* Usage (boolean):
* auto p = make_flags(m.vertices());
* p[v0].set();
* p[v0].unset();
* p[v0].is_set();
* p.clear();
*
* Usage (enum):
* enum my_flags {
* None = 0x00,
* BitA = 0x01,
* BitB = 0x02,
* ...
* };
*
* auto p = make_flags(m.vertices(), None);
* p[v0].set(BitA);
* p[v0].unset(BitB);
* p[v0].is_set(BitB);
* p.clear();
*/
template <class enum_t, class tag>
struct flags
{
using index_t = typename primitive<tag>::index;
template <class AttrT>
using attribute = typename primitive<tag>::template attribute<AttrT>;
enum values
{
NONE = (enum_t)0,
ALL = (enum_t)~0
};
// methods
public:
void clear(enum_t value = (enum_t)0);
// partition
public:
struct flag
{
// methods
public:
bool is_set(enum_t value = NONE) { return (p.entries[i] & value) != 0; }
void set(enum_t value = ALL) { p.entries[i] |= value; }
void unset(enum_t value = NONE) { p.entries[i] &= ~value; }
bool operator==(flag const& f) { return (enum_t)f == *this; }
bool operator!=(flag const& f) { return (enum_t)f != *this; }
/// conversion
operator enum_t() const { return p.entries[i]; }
private:
flags& p;
index_t i;
public:
flag(flags& p, index_t i) : p(p), i(i) {}
};
flag operator[](index_t i) { return {*this, i}; }
// ctor
public:
flags(Mesh const& m, enum_t initial_value) : entries(m) { clear(initial_value); }
private:
attribute<enum_t> entries;
};
/// ======== IMPLEMENTATION ========
template <class enum_t, class mesh_ptr, class tag, class iterator>
flags<enum_t, tag> make_flags(smart_collection<mesh_ptr, tag, iterator> const& c, enum_t initial_value)
{
return {c.mesh()};
}
template <class enum_t, class tag>
void flags<enum_t, tag>::clear(enum_t value)
{
entries.clear(value);
}
}
#pragma once
#include "../Mesh.hh"
namespace polymesh
{
template <class tag>
struct partitioning;
template <class mesh_ptr, class tag, class iterator>
partitioning<tag> make_partitioning(smart_collection<mesh_ptr, tag, iterator> const& c);
/**
* Datatype for disjoint subsets (union-find datastructure)
*
* Usage:
* auto p = make_partitioning(m.vertices());
* p.merge(v0, v1);
* p[v0].size();
* p.clear();
*/
template <class tag>
struct partitioning
{
using index_t = typename primitive<tag>::index;
template <class AttrT>
using attribute = typename primitive<tag>::template attribute<AttrT>;
// methods
public:
/// merges two partitions
/// returns true iff i and j were in different partitions before
bool merge(index_t i, index_t j);
/// returns the size of the partition of i
int size_of(index_t i);
/// returns the root element of the partition of i
index_t root_of(index_t i);
/// resets all partitions to singletons
void clear();
/// returns the number of partitions
int size();
// partition
public:
struct partition
{
// methods
public:
/// returns the size of this partition
int size() { return p.size_of(i); }
/// returns the root (representative) element of this partition
index_t root() { return p.root_of(i); }
/// returns true iff this index is the representative
bool is_root() { return i == root(); }
/// merges this partition with another one
bool merge_with(index_t j) { return p.merge(i, j); }
/// returns true iff j belongs to the same partition
bool operator==(index_t j) { return root() == p.root_of(j); }
/// returns true iff j belongs to a different partition
bool operator!=(index_t j) { return root() != p.root_of(j); }
private:
partitioning& p;
index_t i;
public:
partition(partitioning& p, index_t i) : p(p), i(i) {}
};
partition operator[](index_t i) { return {*this, i}; }
// ctor
public:
partitioning(Mesh const& m) : parents(m), sizes(m) { clear(); }
private:
attribute<index_t> parents;
attribute<int> sizes;
int partitions;
};
/// ======== IMPLEMENTATION ========
template <class mesh_ptr, class tag, class iterator>
partitioning<tag> make_partitioning(smart_collection<mesh_ptr, tag, iterator> const& c)
{
return {c.mesh()};
}
template <class tag>
bool partitioning<tag>::merge(index_t i, index_t j)
{
auto ri = root_of(i);
auto rj = root_of(j);
if (ri == rj)
return false;
// ensure |I| >= |J|
if (size_of(ri) < size_of(rj))
std::swap(ri, rj);
parents[rj] = ri;
sizes[ri] += sizes[rj];
--partitions;
return true;
}
template <class tag>
int partitioning<tag>::size_of(index_t i)
{
return sizes[root_of(i)];
}
template <class tag>
int partitioning<tag>::size()
{
return partitions;
}
template <class tag>
typename partitioning<tag>::index_t partitioning<tag>::root_of(index_t i)
{
auto p = parents[i];
if (p != i)
{
p = root_of(p);
parents[i] = p;
}
return p;
}
template <class tag>
void partitioning<tag>::clear()
{
auto const& m = parents.mesh();
auto s = parents.size();
auto ll = low_level_api(m);
for (auto i = 0; i < s; ++i)
{
auto idx = index_t(i);
parents[idx] = idx;
sizes[idx] = ll.is_removed(idx) ? 0 : 1;
}
partitions = primitive<tag>::valid_size(m);
}
}
This diff is collapsed.
......@@ -222,9 +222,13 @@ struct smart_collection : smart_range<smart_collection<mesh_ptr, tag, iterator>,
protected:
/// Backreference to mesh
mesh_ptr mesh;
mesh_ptr m;
friend class Mesh;
public:
/// returns reference to mesh
decltype(*m) mesh() const { return *m; }
};
/// Collection of all vertices of a mesh
......
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