The modified butterfly scheme of Denis Zorin, Peter Schröder and Wim Sweldens, ``Interpolating subdivision for meshes with arbitrary topology,'' in Proceedings of SIGGRAPH 1996, ACM SIGGRAPH, 1996, pp. More...
#include <OpenMesh/Tools/Subdivider/Uniform/SubdividerT.hh>#include <OpenMesh/Core/Utils/vector_cast.hh>#include <OpenMesh/Core/Utils/Property.hh>#include <vector>#include <cmath>
Go to the source code of this file.
The modified butterfly scheme of Denis Zorin, Peter Schröder and Wim Sweldens, ``Interpolating subdivision for meshes with arbitrary topology,'' in Proceedings of SIGGRAPH 1996, ACM SIGGRAPH, 1996, pp.
189-192.
Clement Courbet - cleme.nosp@m.nt.c.nosp@m.ourbe.nosp@m.t@ec.nosp@m.p.fr */
CLASS ModifiedButterflyT
-----------------— STL
== NAMESPACE ================================================================
namespace OpenMesh { // BEGIN_NS_OPENMESH namespace Subdivider { // BEGIN_NS_DECIMATER namespace Uniform { // BEGIN_NS_UNIFORM
== CLASS DEFINITION =========================================================
/** Modified Butterfly subdivision algorithm
Implementation of the modified butterfly scheme of Denis Zorin, Peter Schröder and Wim Sweldens, ``Interpolating subdivision for meshes with arbitrary topology,'' in Proceedings of SIGGRAPH 1996, ACM SIGGRAPH, 1996, pp. 189-192.
Clement Courbet - cleme.nosp@m.nt.c.nosp@m.ourbe.nosp@m.t@ec.nosp@m.p.fr */ template <typename MeshType, typename RealType = double> class ModifiedButterflyT : public SubdividerT<MeshType, RealType> { public:
typedef RealType real_t; typedef MeshType mesh_t; typedef SubdividerT< mesh_t, real_t > parent_t;
typedef std::vector< std::vector<real_t> > weights_t; typedef std::vector<real_t> weight_t;
public:
ModifiedButterflyT() : parent_t() { init_weights(); }
explicit ModifiedButterflyT( mesh_t& _m) : parent_t(_m) { init_weights(); }
~ModifiedButterflyT() {}
public:
const char *name() const override { return "Uniform Spectral"; }
/ Pre-compute weights void init_weights(size_t _max_valence=30) { weights.resize(_max_valence);
special case: K==3, K==4 weights[3].resize(4); weights[3][0] = real_t(5.0)/12; weights[3][1] = real_t(-1.0)/12; weights[3][2] = real_t(-1.0)/12; weights[3][3] = real_t(3.0)/4;
weights[4].resize(5); weights[4][0] = real_t(3.0)/8; weights[4][1] = 0; weights[4][2] = real_t(-1.0)/8; weights[4][3] = 0; weights[4][4] = real_t(3.0)/4;
for(unsigned int K = 5; K<_max_valence; ++K) { weights[K].resize(K+1); s(j) = ( 1/4 + cos(2*pi*j/K) + 1/2 * cos(4*pi*j/K) )/K double invK = 1.0/static_cast<double>(K); real_t sum = 0; for(unsigned int j=0; j<K; ++j) { weights[K][j] = static_cast<real_t>((0.25 + cos(2.0*M_PI*static_cast<double>(j)*invK) + 0.5*cos(4.0*M_PI*static_cast<double>(j)*invK))*invK); sum += weights[K][j]; } weights[K][K] = static_cast<real_t>(1.0) - sum; } }
protected:
bool prepare( mesh_t& <em>m ) override { _m.add_property( vp_pos ); m.add_property( ep_pos ); return true; }
bool cleanup( mesh_t& <em>m ) override { _m.remove_property( vp_pos ); m.remove_property( ep_pos ); return true; }
bool subdivide( MeshType& _m, size_t _n , const bool _update_points = true) override {
/TODO:Implement fixed positions
Compute the maximal vertex valence in the mesh unsigned int maxValence = 0; for ( auto vertex : _m.vertices() ) { maxValence = std::max(maxValence,_m.valence(vertex)); }
We pre initialized with 30. If it's larger, we update the weights if (maxValence >= 30) { init_weights( maxValence + 1 ); }
Do n subdivisions for (size_t i=0; i < _n; ++i) {
This is an interpolating scheme, old vertices remain the same. for ( auto vh : _m.vertices()) _m.property( vp_pos, vh ) = _m.point(vh);
Compute position for new vertices and store them in the edge property for (auto eh : _m.edges()) compute_midpoint( _m, eh);
Split each edge at midpoint and store precomputed positions (stored in edge property ep_pos_) in the vertex property vp_pos_;
Attention! Creating new edges, hence make sure the loop ends correctly. for (auto eh : _m.edges()) split_edge(_m, eh );
Commit changes in topology and reconsitute consistency
Attention! Creating new faces, hence make sure the loop ends correctly. for (auto fh : _m.faces()) split_face(_m, fh );
Commit changes in geometry for ( auto vh : m.vertices()) _m.set_point(vh, _m.property( vp_pos, vh ) );
} return true;
}
private: // topological modifiers
void split_face(mesh_t& _m, const typename mesh_t::FaceHandle& _fh) { typename mesh_t::HalfedgeHandle heh1(_m.halfedge_handle(_fh)), heh2(_m.next_halfedge_handle(_m.next_halfedge_handle(heh1))), heh3(_m.next_halfedge_handle(_m.next_halfedge_handle(heh2)));
Cutting off every corner of the 6_gon corner_cutting( _m, heh1 ); corner_cutting( _m, heh2 ); corner_cutting( _m, heh3 ); }
void corner_cutting(mesh_t& _m, const typename mesh_t::HalfedgeHandle& _he) { Define Halfedge Handles typename mesh_t::HalfedgeHandle heh1(_he), heh5(heh1), heh6(_m.next_halfedge_handle(heh1));
Cycle around the polygon to find correct Halfedge for (; _m.next_halfedge_handle(_m.next_halfedge_handle(heh5)) != heh1; heh5 = _m.next_halfedge_handle(heh5)) {}
typename mesh_t::VertexHandle vh1 = _m.to_vertex_handle(heh1), vh2 = _m.to_vertex_handle(heh5);
typename mesh_t::HalfedgeHandle heh2(_m.next_halfedge_handle(heh5)), heh3(_m.new_edge( vh1, vh2)), heh4(_m.opposite_halfedge_handle(heh3));
/* Intermediate result
*
5 /|\
/_ \
vh2> * *
/|\3 |\
/_ \|4 \
*----\*----\*
1 ^ 6
vh1 (adjust_outgoing halfedge!)