Commit 913e7b51 authored by Philip Trettner's avatar Philip Trettner
Browse files

added components algorithm and stats

parent 3499cd38
......@@ -19,8 +19,13 @@
// - intersections
#include "algorithms/operations.hh"
// Mesh statistics
#include "algorithms/components.hh"
// WIP: #include "algorithms/stats.hh"
// Geodesics
#include "algorithms/geodesic_nnf.hh"
// WIP: #include "algorithms/geodesic_fast_marching.hh"
// WIP: #include "algorithms/geodesic_nnf.hh"
// TODO:
// - decimation
......
#pragma once
#include "../Mesh.hh"
#include <queue>
namespace polymesh
{
/// Calculates the number of connected components based on vertex connectivity
/// (reports wiremeshes as connected)
/// Returns a vertex attribute for 0-based per-vertex component
/// Optionally returns the total number of components in `comps`
vertex_attribute<int> vertex_components(Mesh const& m, int* comps = nullptr);
/// Calculates the number of connected components based on face connectivity
/// (only counts face-edge-face as connected)
/// Returns a face attribute for 0-based per-face component
/// Optionally returns the total number of components in `comps`
face_attribute<int> face_components(Mesh const& m, int* comps = nullptr);
/// ======== IMPLEMENTATION ========
inline vertex_attribute<int> vertex_components(Mesh const& m, int* comps)
{
auto comp = m.vertices().make_attribute_with_default(-1);
auto c_cnt = 0;
for (auto seed : m.vertices())
if (comp[seed] == -1)
{
std::queue<vertex_handle> q;
q.push(seed);
seed[comp] = c_cnt;
while (!q.empty())
{
auto v = q.front();
q.pop();
for (auto vv : v.adjacent_vertices())
if (vv[comp] != c_cnt)
{
vv[comp] = c_cnt;
q.push(vv);
}
}
++c_cnt;
}
if (comps)
*comps = c_cnt;
return comp;
}
inline face_attribute<int> face_components(Mesh const& m, int* comps)
{
auto comp = m.faces().make_attribute_with_default(-1);
auto c_cnt = 0;
for (auto seed : m.faces())
if (comp[seed] == -1)
{
std::queue<face_handle> q;
q.push(seed);
seed[comp] = c_cnt;
while (!q.empty())
{
auto f = q.front();
q.pop();
for (auto ff : f.adjacent_faces())
if (ff[comp] != c_cnt)
{
ff[comp] = c_cnt;
q.push(ff);
}
}
++c_cnt;
}
if (comps)
*comps = c_cnt;
return comp;
}
}
#pragma once
#include <iostream>
#include "../Mesh.hh"
#include "../fields.hh"
#include "components.hh"
namespace polymesh
{
/// Prints statistics for the given mesh, including:
/// - number of primitives
/// - components
/// - aabb
template <class Vec3 = void>
void print_stats(std::ostream& out, Mesh const& m, vertex_attribute<Vec3> const* position = nullptr);
/// ======== IMPLEMENTATION ========
template <class Vec3>
void print_stats(std::ostream& out, Mesh const& m, vertex_attribute<Vec3> const* position)
{
auto ln = "\n";
out << "[Mesh]:" << ln;
// # verts, faces, edges, hedges
out << " Vertices: " << m.vertices().size();
if (m.vertices().size() != m.all_vertices().size())
out << " (" << m.all_vertices().size() - m.vertices().size() << " removed)";
out << ln;
out << " Faces: " << m.faces().size();
if (m.faces().size() != m.all_faces().size())
out << " (" << m.all_faces().size() - m.faces().size() << " removed)";
out << ln;
out << " Edges: " << m.edges().size();
if (m.edges().size() != m.all_edges().size())
out << " (" << m.all_edges().size() - m.edges().size() << " removed)";
out << ln;
out << " Half-edges: " << m.halfedges().size();
if (m.halfedges().size() != m.all_halfedges().size())
out << " (" << m.all_halfedges().size() - m.halfedges().size() << " removed)";
out << ln;
if (m.vertices().empty())
return; // no vertices, no further stats
out << ln;
// # isolated, components, boundaries, genus
int face_comps;
int vertex_comps;
vertex_components(m, &vertex_comps);
face_components(m, &face_comps);
out << " Vertex Components: " << vertex_comps << ln;
out << " Face Components: " << face_comps << ln;
// TODO: genus
// TODO: boundaries
// TODO: isolated verts, edges
if (position)
{
out << ln;
auto const& pos = *position;
auto aabb = m.vertices().aabb(pos);
auto min = aabb.min;
auto max = aabb.max;
out << " AABB Min: " << field_3d<Vec3>::to_string(min) << ln;
out << " AABB Max: " << field_3d<Vec3>::to_string(max) << ln;
out << " AABB Size: " << field_3d<Vec3>::to_string(max - min) << ln;
auto avg = m.vertices().avg(pos);
out << " Vertex Centroid: " << field_3d<Vec3>::to_string(avg) << ln;
}
}
}
......@@ -2,6 +2,8 @@
#include <cmath>
#include <cstddef>
#include <sstream>
#include <string>
#include <utility>
namespace polymesh
......@@ -37,5 +39,12 @@ struct field_3d
{
return static_cast<Scalar>(t);
}
static std::string to_string(Vec3 const& v)
{
std::stringstream ss;
ss << "(" << v[0] << ", " << v[1] << ", " << v[2] << ")";
return ss.str();
}
};
}
......@@ -39,12 +39,13 @@ ElementT smart_range<this_t, ElementT>::last() const
template <class this_t, class ElementT>
bool smart_range<this_t, ElementT>::any() const
{
for (auto h : *static_cast<this_t const *>(this))
{
(void)h; // unused
return true;
}
return false;
return static_cast<this_t const *>(this)->begin() != static_cast<this_t const *>(this)->end();
}
template <class this_t, class ElementT>
bool smart_range<this_t, ElementT>::empty() const
{
return static_cast<this_t const *>(this)->begin() == static_cast<this_t const *>(this)->end();
}
template <class this_t, class ElementT>
......
......@@ -34,6 +34,8 @@ struct smart_range
/// TODO: how to make this O(1)
ElementT last() const;
/// returns true if the range is empty
bool empty() const;
/// returns true if the range is non-empty
bool any() const;
/// returns true if any value fulfils p(v)
......
Supports Markdown
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