Skip to content
Snippets Groups Projects
Commit 913e7b51 authored by Philip Trettner's avatar Philip Trettner
Browse files

added components algorithm and stats

parent 3499cd38
No related branches found
No related tags found
No related merge requests found
......@@ -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 static_cast<this_t const *>(this)->begin() != static_cast<this_t const *>(this)->end();
}
return false;
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)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment