Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • adfc2e5
  • /
  • nurbs_plugin
  • /
  • ModifiedButterflySubdivision.h
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:7d178ec6dc40841f7ee8727c277667274cc0fbf5
directory badge Iframe embedding
swh:1:dir:1527aafa4f478fbed854d026dc9a61c6f21f769d

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
ModifiedButterflySubdivision.h
#pragma once
// Adapted from OpenMesh
#include "SurfaceMeshModel.h"

#define get_point(x) (points[x])

/** 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.
*/
class ModifiedButterfly{
public:

	typedef double									real_t;
	typedef Surface_mesh							mesh_t;

	typedef std::vector< std::vector<real_t> >      weights_t;
	typedef std::vector<real_t>                     weight_t;

public:

	ModifiedButterfly()
	{ init_weights(); }

public:

	const char *name() const { return "Uniform Spectral"; }


	/// Pre-compute weights
	void init_weights(size_t _max_valence=50)
	{
		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
			real_t   invK  = 1.0/real_t(K);
			real_t sum = 0;
			for(unsigned int j=0; j<K; ++j)
			{
				weights[K][j] = (0.25 + cos(2.0*M_PI*j*invK) + 0.5*cos(4.0*M_PI*j*invK))*invK;
				sum += weights[K][j];
			}
			weights[K][K] = (real_t)1.0 - sum;
		}
	}


protected:


	bool prepare( mesh_t& _m )
	{
		points = _m.vertex_property<Point>("v:point");

		vp_pos_ = _m.vertex_property<Point>("v:point2");
		ep_pos_ = _m.edge_property<Point>("e:point");
		return true;
	}


	bool cleanup( mesh_t& _m )
	{
		_m.remove_vertex_property(vp_pos_);
		_m.remove_edge_property(ep_pos_);

		return true;
	}

public:
	bool subdivide( Surface_mesh& _m, size_t _n )
	{
		prepare(_m);

		///TODO:Implement fixed positions
		mesh_t::Face_iterator   fit, f_end;
		mesh_t::Edge_iterator   eit, e_end;
		mesh_t::Vertex_iterator vit;

		// Do _n subdivisions
		for (size_t i=0; i < _n; ++i)
		{

			// This is an interpolating scheme, old vertices remain the same.
			mesh_t::Vertex_iterator initialVerticesEnd = _m.vertices_end();
			for ( vit  = _m.vertices_begin(); vit != initialVerticesEnd; ++vit)
				vp_pos_[vit] = get_point(vit);

			// Compute position for new vertices and store them in the edge property
			for (eit=_m.edges_begin(); eit != _m.edges_end(); ++eit)
				compute_midpoint( _m, eit );
			
			// 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.
			e_end = _m.edges_end();
			for (eit=_m.edges_begin(); eit != e_end; ++eit)
				split_edge(_m, eit );


			// Commit changes in topology and reconstitute consistency

			// Attention! Creating new faces, hence make sure the loop ends correctly.
			f_end   = _m.faces_end();
			for (fit = _m.faces_begin(); fit != f_end; ++fit)
				split_face(_m, fit );


			// Commit changes in geometry
			for ( vit  = /*initialVerticesEnd;*/_m.vertices_begin();
				vit != _m.vertices_end(); ++vit)
				points[vit] = vp_pos_[vit];
		}

		return cleanup(_m);
	}

private: // topological modifiers

	void split_face(mesh_t& _m, const mesh_t::Face& _fh)
	{
		mesh_t::Halfedge
			heh1(_m.halfedge(_fh)),
			heh2(_m.next_halfedge(_m.next_halfedge(heh1))),
			heh3(_m.next_halfedge(_m.next_halfedge(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 mesh_t::Halfedge& _he)
	{
		// Define Halfedge Handles
		mesh_t::Halfedge
			heh1(_he),
			heh5(heh1),
			heh6(_m.next_halfedge(heh1));

		// Cycle around the polygon to find correct Halfedge
		for (; _m.next_halfedge(_m.next_halfedge(heh5)) != heh1;
			heh5 = _m.next_halfedge(heh5)){}

		mesh_t::Vertex
			vh1 = _m.to_vertex(heh1),
			vh2 = _m.to_vertex(heh5);

		mesh_t::Halfedge
			heh2(_m.next_halfedge(heh5)),
			heh3(_m.new_edge( vh1, vh2)),
			heh4(_m.opposite_halfedge(heh3));

		/* Intermediate result
		*
		*            *
		*         5 /|\
		*          /_  \
		*    vh2> *     *
		*        /|\3   |\
		*       /_  \|4   \
		*      *----\*----\*
		*          1 ^   6
		*            vh1 (adjust_outgoing halfedge!)
		*/

		// Old and new Face
		mesh_t::Face     fh_old(_m.face(heh6));
		mesh_t::Face     fh_new(_m.new_face());


		// Re-Set Handles around old Face
		_m.set_next_halfedge(heh4, heh6);
		_m.set_next_halfedge(heh5, heh4);

		_m.set_face(heh4, fh_old);
		_m.set_face(heh5, fh_old);
		_m.set_face(heh6, fh_old);
		_m.set_halfedge(fh_old, heh4);

		// Re-Set Handles around new Face
		_m.set_next_halfedge(heh1, heh3);
		_m.set_next_halfedge(heh3, heh2);

		_m.set_face(heh1, fh_new);
		_m.set_face(heh2, fh_new);
		_m.set_face(heh3, fh_new);

		_m.set_halfedge(fh_new, heh1);
	}


	void split_edge(mesh_t& _m, const mesh_t::Edge& _eh)
	{
		mesh_t::Halfedge heh     = _m.halfedge(_eh, 0), opp_heh = _m.halfedge(_eh, 1);

		mesh_t::Halfedge new_heh, opp_new_heh, t_heh;
		mesh_t::Vertex   vh;
		mesh_t::Vertex   vh1(_m.to_vertex(heh));
		Point    zero(0,0,0);

		// new vertex
		vh                = _m.add_vertex( zero );

		// memorize position, will be set later
		vp_pos_[vh] = ep_pos_[_eh];

		// Re-link mesh entities
		if (_m.is_boundary(_eh))
		{
			for (t_heh = heh;
				_m.next_halfedge(t_heh) != opp_heh;
				t_heh = _m.opposite_halfedge(_m.next_halfedge(t_heh))){}
		}
		else
		{
			for (t_heh = _m.next_halfedge(opp_heh);
				_m.next_halfedge(t_heh) != opp_heh;
				t_heh = _m.next_halfedge(t_heh) ){}
		}

		new_heh     = _m.new_edge(vh, vh1);
		opp_new_heh = _m.opposite_halfedge(new_heh);
		_m.set_vertex( heh, vh );

		_m.set_next_halfedge(t_heh, opp_new_heh);
		_m.set_next_halfedge(new_heh, _m.next_halfedge(heh));
		_m.set_next_halfedge(heh, new_heh);
		_m.set_next_halfedge(opp_new_heh, opp_heh);

		if (_m.face(opp_heh).is_valid())
		{
			_m.set_face(opp_new_heh, _m.face(opp_heh));
			_m.set_halfedge(_m.face(opp_new_heh), opp_new_heh);
		}

		_m.set_face( new_heh, _m.face(heh) );
		_m.set_halfedge( vh, new_heh);
		_m.set_halfedge( _m.face(heh), heh );
		_m.set_halfedge( vh1, opp_new_heh );

		// Never forget this, when playing with the topology
		_m.adjust_outgoing_halfedge( vh );
		_m.adjust_outgoing_halfedge( vh1 );
	}

private: // geometry helper

	void compute_midpoint(mesh_t& _m, const mesh_t::Edge& _eh)
	{
		mesh_t::Halfedge heh, opp_heh;

		heh      = _m.halfedge( _eh, 0);
		opp_heh  = _m.halfedge( _eh, 1);

		Point pos(0,0,0);

		mesh_t::Vertex a_0(_m.to_vertex(heh));
		mesh_t::Vertex a_1(_m.to_vertex(opp_heh));

		// boundary edge: 4-point scheme
		if (_m.is_boundary(_eh) )
		{
			pos = get_point(a_0);
			pos += get_point(a_1);
			pos *= 9.0/16;
			Point tpos;
			if(_m.is_boundary(heh))
			{
				tpos = get_point(_m.to_vertex(_m.next_halfedge(heh)));
				tpos += get_point(_m.to_vertex(_m.opposite_halfedge(_m.prev_halfedge(heh))));
			}
			else
			{
				assert(_m.is_boundary(opp_heh));
				tpos = get_point(_m.to_vertex(_m.next_halfedge(opp_heh)));
				tpos += get_point(_m.to_vertex(_m.opposite_halfedge(_m.prev_halfedge(opp_heh))));
			}
			tpos *= -1.0/16;
			pos += tpos;
		}
		else
		{
			int valence_a_0 = _m.valence(a_0);
			int valence_a_1 = _m.valence(a_1);
			assert(valence_a_0>2);
			assert(valence_a_1>2);

			if( (valence_a_0==6 && valence_a_1==6) || (_m.is_boundary(a_0) && valence_a_1==6) || (_m.is_boundary(a_1) && valence_a_0==6) || (_m.is_boundary(a_0) && _m.is_boundary(a_1)) )// use 8-point scheme
			{
				real_t alpha    = real_t(1.0/2);
				real_t beta     = real_t(1.0/8);
				real_t gamma    = real_t(-1.0/16);

				//get points
				mesh_t::Vertex b_0, b_1, c_0, c_1, c_2, c_3;
				mesh_t::Halfedge t_he;

				t_he = _m.next_halfedge(_m.opposite_halfedge(heh));
				b_0 = _m.to_vertex(t_he);
				if(!_m.is_boundary(_m.opposite_halfedge(t_he)))
				{
					t_he = _m.next_halfedge(_m.opposite_halfedge(t_he));
					c_0 = _m.to_vertex(t_he);
				}

				t_he = _m.opposite_halfedge(_m.prev_halfedge(heh));
				b_1 = _m.to_vertex(t_he);
				if(!_m.is_boundary(t_he))
				{
					t_he = _m.opposite_halfedge(_m.prev_halfedge(t_he));
					c_1 = _m.to_vertex(t_he);
				}

				t_he = _m.next_halfedge(_m.opposite_halfedge(opp_heh));
				assert(b_1.idx()==_m.to_vertex(t_he).idx());
				if(!_m.is_boundary(_m.opposite_halfedge(t_he)))
				{
					t_he = _m.next_halfedge(_m.opposite_halfedge(t_he));
					c_2 = _m.to_vertex(t_he);
				}

				t_he = _m.opposite_halfedge(_m.prev_halfedge(opp_heh));
				assert(b_0==_m.to_vertex(t_he));
				if(!_m.is_boundary(t_he))
				{
					t_he = _m.opposite_halfedge(_m.prev_halfedge(t_he));
					c_3 = _m.to_vertex(t_he);
				}

				//compute position.
				//a0,a1,b0,b1 must exist.
				assert(a_0.is_valid());
				assert(a_1.is_valid());
				assert(b_0.is_valid());
				assert(b_1.is_valid());
				//The other vertices may be created from symmetry is they are on the other side of the boundary.

				pos = get_point(a_0);
				pos += get_point(a_1);
				pos *= alpha;

				Point tpos ( get_point(b_0) );
				tpos += get_point(b_1);
				tpos *= beta;
				pos += tpos;

				Point pc_0, pc_1, pc_2, pc_3;
				if(c_0.is_valid())
					pc_0 = get_point(c_0);
				else //create the point by symmetry
				{
					pc_0 = get_point(a_1) + get_point(b_0) - get_point(a_0);
				}
				if(c_1.is_valid())
					pc_1 = get_point(c_1);
				else //create the point by symmetry
				{
					pc_1 = get_point(a_1) + get_point(b_1) - get_point(a_0);
				}
				if(c_2.is_valid())
					pc_2 = get_point(c_2);
				else //create the point by symmetry
				{
					pc_2 = get_point(a_0) + get_point(b_1) - get_point(a_1);
				}
				if(c_3.is_valid())
					pc_3 = get_point(c_3);
				else //create the point by symmetry
				{
					pc_3 = get_point(a_0) + get_point(b_0) - get_point(a_1);
				}
				tpos = pc_0;
				tpos += pc_1;
				tpos += pc_2;
				tpos += pc_3;
				tpos *= gamma;
				pos += tpos;
			}
			else //at least one endpoint is [irregular and not in boundary]
			{
				double normFactor = 0.0;

				if(valence_a_0!=6 && !_m.is_boundary(a_0))
				{
					assert((int)weights[valence_a_0].size()==valence_a_0+1);
					mesh_t::Halfedge t_he = opp_heh;
					for(int i = 0; i < valence_a_0 ; t_he=_m.next_halfedge(_m.opposite_halfedge(t_he)), ++i)
					{
						pos += weights[valence_a_0][i] * get_point(_m.to_vertex(t_he));
					}
					assert(t_he==opp_heh);

					//add irregular vertex:
					pos += weights[valence_a_0][valence_a_0] * get_point(a_0);
					++normFactor;
				}

				if(valence_a_1!=6  && !_m.is_boundary(a_1))
				{
					assert((int)weights[valence_a_1].size()==valence_a_1+1);
					mesh_t::Halfedge t_he = heh;
					for(int i = 0; i < valence_a_1 ; t_he=_m.next_halfedge(_m.opposite_halfedge(t_he)), ++i)
					{
						pos += weights[valence_a_1][i] * get_point(_m.to_vertex(t_he));
					}
					assert(t_he==heh);
					//add irregular vertex:
					pos += weights[valence_a_1][valence_a_1] * get_point(a_1);
					++normFactor;
				}

				assert(normFactor>0.1); //normFactor should be 1 or 2

				//if both vertices are irregular, average positions:
				pos /= normFactor;
			}
		}

		ep_pos_ [_eh] = pos;
	}

private: // data

	Surface_mesh::Vertex_property<Point> vp_pos_;
	Surface_mesh::Edge_property<Point> ep_pos_;
	
	Surface_mesh::Vertex_property<Point> points;

	weights_t     weights;

};

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API