Subversion Repositories gelsvn

Rev

Rev 508 | Rev 512 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* Written by Christian Thode Larsen 2009-2010
* Contact: thode2d@gmail.com
* Based on original work by J. Andreas Baerentzen
* Inspired by OpenMesh (www.openmesh.org)
*/

#include "mesh_optimization.h"

#include <cfloat>
#include <queue>
#include <vector>

#include <CGLA/Vec3d.h>

//#include "Manifold.h"
#include "AttributeVector.h"

namespace HMesh
{
        using namespace std;
        using namespace CGLA;

    // Small utility functions
    namespace 
    {
        class LineSeg
        {
            const Vec3d p0;
            const Vec3d p1;
            const float l;
            const CGLA::Vec3d dir;
        public:

            LineSeg(const Vec3d& _p0, const Vec3d& _p1): 
              p0(_p0), p1(_p1), l(length(p1-p0)), dir((p1-p0)/l) {}

              bool inseg(const Vec3d& p) const
              {
                  double t = dot(dir, p-p0);
                  if(t<0)
                      return false;
                  if(t>l)
                      return false;
                  return true;
              }
        };

        Vec3d compute_normal(Vec3d* v)
        {
            Vec3d norm;
            for(int i = 0; i < 4; ++i)
            {
                norm[0] += (v[i][1]-v[(i+1)%4][1])*(v[i][2]+v[(i+1)%4][2]);
                norm[1] += (v[i][2]-v[(i+1)%4][2])*(v[i][0]+v[(i+1)%4][0]);
                norm[2] += (v[i][0]-v[(i+1)%4][0])*(v[i][1]+v[(i+1)%4][1]);
            }
            float l = norm.length();
            if(l>0.0f)
                norm /= l;
            return norm;
        }

        bool would_flip(const Manifold& m, HalfEdgeID h)
        {
                        HalfEdgeWalker w = m.halfedgewalker(h);

                        VertexID hv = w.vertex();
                        VertexID hov = w.opp().vertex();
                        VertexID hnv = w.next().vertex();
                        VertexID honv = w.opp().next().vertex();

            Vec3d v[4];
            v[0] = Vec3d(m.pos(hv));
            v[1] = Vec3d(m.pos(hov));
            v[2] = Vec3d(m.pos(hnv));
            v[3] = Vec3d(m.pos(honv));

            Vec3d dir(0, 0, -1);//compute_normal(v);

            Vec3d n1a = cross(v[3]-v[0], v[2]-v[0]);
            Vec3d n2a = cross(v[2]-v[1], v[3]-v[1]);

            if(dot(normalize(n1a), dir) < 0)
                return true;
            if(dot(normalize(n2a), dir) < 0)
                return true;
            return false;
        }
    }

    // Energy functionals
    namespace
    {


        class ValencyEnergy: public EnergyFun
        {
        public:
            double delta_energy(const Manifold& m, HalfEdgeID h) const
            {
                                HalfEdgeWalker w = m.halfedgewalker(h);

                VertexID v1 = w.opp().vertex();
                VertexID v2 = w.vertex();
                VertexID vo1 = w.next().vertex();
                VertexID vo2 = w.opp().next().vertex();

                int val1  = valency(m, v1);
                int val2  = valency(m, v2);
                int valo1 = valency(m, vo1);
                int valo2 = valency(m, vo2);

                // The optimal valency is four for a boundary vertex
                // and six elsewhere.
                int t1 = boundary(m, v1) ? 4 : 6;
                int t2 = boundary(m, v2) ? 4 : 6;
                int to1 = boundary(m, vo1) ? 4 : 6;
                int to2 = boundary(m, vo2) ? 4 : 6;

                int before = 
                    sqr(val1-t1)+sqr(val2-t2)+
                    sqr(valo1-to1)+sqr(valo2-to2);
                int after = 
                    sqr(valo1+1-to1)+sqr(val1-1-t1)+
                    sqr(val2-1-t2)+sqr(valo2+1-to2);

                return static_cast<double>(after-before);
            }
        };

        class RandomEnergy: public EnergyFun
        {
        public:
            double delta_energy(const Manifold& m, HalfEdgeID he) const
            {
                return static_cast<double>(gel_rand()/static_cast<float>(GEL_RAND_MAX));
            }
        };

        class MinAngleEnergy: public EnergyFun
        {
            double min_angle(const Vec3d& v0, const Vec3d& v1, const Vec3d& v2) const
            {
                Vec3d a = normalize(v1-v0);
                Vec3d b = normalize(v2-v1);
                Vec3d c = normalize(v0-v2);

                return min(dot(a,-c), min(dot(b,-a), dot(c,-b)));
            }

            double thresh;

        public:

            MinAngleEnergy(double _thresh): thresh(_thresh) {}

            double delta_energy(const Manifold& m, HalfEdgeID h) const
            {
                                HalfEdgeWalker w = m.halfedgewalker(h);

                                VertexID hv = w.vertex();
                                VertexID hnv = w.next().vertex();
                                VertexID hov= w.opp().vertex();
                                VertexID honv = w.opp().next().vertex();

                Vec3d v0(m.pos(hv));
                Vec3d v1(m.pos(hnv));
                Vec3d v2(m.pos(hov));
                Vec3d v3(m.pos(honv));

                Vec3d n1a = normalize(cross(v1-v0,v3-v0));
                Vec3d n2a = normalize(cross(v3-v2,v1-v2));

                if(dot(n1a, n2a) > thresh){
                    double before = min(min_angle(v0,v1,v2), min_angle(v0,v2,v3));
                    double after = min(min_angle(v0,v1,v3), min_angle(v1,v2,v3));
                    return -(after-before);
                }
                return DBL_MAX;
            }
        };

        class DihedralEnergy: public EnergyFun
        {
            const double gamma;
            const bool use_alpha;

            double cos_ang(const Vec3d& n1, const Vec3d& n2) const
            {
                return s_max(-1.0, s_min(1.0, dot(n1, n2)));
            }

            double edge_alpha_energy(Vec3d v1, Vec3d v2, double ca) const
            {
                return pow(length(v1-v2)*(acos(ca)), 1.0f/gamma); 
            }

            double edge_c_energy(Vec3d v1, Vec3d v2, double ca) const
            {
                return pow(length(v1-v2)*(1-ca), 1.0f/gamma); 
            }

            mutable double ab_12;
            mutable double ab_a1;
            mutable double ab_b1;
            mutable double ab_2c;
            mutable double ab_2d;

            mutable double aa_12;
            mutable double aa_b1;
            mutable double aa_c1;
            mutable double aa_2a;
            mutable double aa_2d;

        public:

            DihedralEnergy(double _gamma = 4.0, bool _use_alpha=false): 
              gamma(_gamma), use_alpha(_use_alpha) {}

              void compute_angles(const Manifold & m, HalfEdgeID h) const
              {
                                  HalfEdgeWalker w = m.halfedgewalker(h);

                                  VertexID hv = w.vertex();
                                  VertexID hov = w.opp().vertex();
                                  VertexID hnv = w.next().vertex();
                                  VertexID honv = w.opp().next().vertex();

                  Vec3d va(m.pos(hv));
                  Vec3d vb(m.pos(hov));
                  Vec3d vc(m.pos(hnv));
                  Vec3d vd(m.pos(honv));

                  FaceID fa = w.next().opp().face();
                  FaceID fb = w.next().next().opp().face();
                  FaceID fc = w.opp().next().opp().face();
                  FaceID fd = w.opp().next().next().opp().face();

                  Vec3d n1 = normalize(cross(vc-va, vb-va));
                  Vec3d n2 = normalize(cross(vb-va, vd-va));

                  Vec3d na = !fa.valid() ? Vec3d(0) : Vec3d(normal(m, fa));
                  Vec3d nb = !fb.valid() ? Vec3d(0) : Vec3d(normal(m, fb));
                  Vec3d nc = !fc.valid() ? Vec3d(0) : Vec3d(normal(m, fc));
                  Vec3d nd = !fd.valid() ? Vec3d(0) : Vec3d(normal(m, fd));


                  Vec3d fn1 = normalize(cross(vb-vc, vd-vc));
                  Vec3d fn2 = normalize(cross(vd-vc, va-vc));

                  ab_12 =       cos_ang(n1,n2);
                  ab_a1 =       cos_ang(na,n1);
                  ab_b1 =       cos_ang(nb,n1);
                  ab_2c =       cos_ang(n2,nc);
                  ab_2d =       cos_ang(n2,nd);

                  aa_12 = cos_ang(fn1,fn2);
                  aa_b1 = cos_ang(nb,fn1);
                  aa_c1 = cos_ang(nc, fn1);
                  aa_2a = cos_ang(fn2, na);
                  aa_2d = cos_ang(fn2,nd);
              }

              double energy(const Manifold& m, HalfEdgeID h) const
              {
                                  HalfEdgeWalker w = m.halfedgewalker(h);

                                  FaceID hf = w.face();
                                  FaceID hof = w.opp().face();

                  double a = cos_ang(Vec3d(normal(m, hf)), Vec3d(normal(m, hof)));

                                  VertexID hv = w.vertex();
                                  VertexID hov = w.opp().vertex();

                  Vec3d va(m.pos(hv));
                  Vec3d vb(m.pos(hov));

                  if(use_alpha)
                      return edge_alpha_energy(va,vb,a);
                        
                  return edge_c_energy(va,vb,a);
              }


              double delta_energy(const Manifold& m, HalfEdgeID h) const
              {
                  compute_angles(m, h);

                                  HalfEdgeWalker w = m.halfedgewalker(h);

                                  VertexID hv = w.vertex();
                                  VertexID hov = w.opp().vertex();
                                  VertexID hnv = w.next().vertex();
                                  VertexID honv = w.opp().next().vertex();

                  Vec3d va(m.pos(hv));
                  Vec3d vb(m.pos(hov));
                  Vec3d vc(m.pos(hnv));
                  Vec3d vd(m.pos(honv));

                  if(use_alpha){
                      double before = 
                          edge_alpha_energy(va,vb,ab_12)
                          +edge_alpha_energy(va,vc,ab_a1)
                          +edge_alpha_energy(vc,vb,ab_b1)
                          +edge_alpha_energy(vd,vb,ab_2c)
                          +edge_alpha_energy(vd,va,ab_2d);

                      double after = 
                          edge_alpha_energy(vd,vc,aa_12)
                          +edge_alpha_energy(vb,vc,aa_b1)
                          +edge_alpha_energy(vd,vb,aa_c1)
                          +edge_alpha_energy(va,vc,aa_2a)
                          +edge_alpha_energy(vd,va,aa_2d);

                      return after-before;
                  }
                  double before = 
                      edge_c_energy(va,vb,ab_12)
                      +edge_c_energy(va,vc,ab_a1)
                      +edge_c_energy(vc,vb,ab_b1)
                      +edge_c_energy(vd,vb,ab_2c)
                      +edge_c_energy(vd,va,ab_2d);

                  double after = 
                      edge_c_energy(vd,vc,aa_12)
                      +edge_c_energy(vb,vc,aa_b1)
                      +edge_c_energy(vd,vb,aa_c1)
                      +edge_c_energy(va,vc,aa_2a)
                      +edge_c_energy(vd,va,aa_2d);

                  return after-before;
              }

              double min_angle(const Manifold& m, HalfEdgeID h) const
              {
                  compute_angles(m, h);

                  return s_min(s_min(s_min(s_min(aa_12, aa_b1), aa_c1), aa_2a), aa_2d);
              }
        };

        class CurvatureEnergy: public EnergyFun
        {

            //                  double abs_mean_curv(const Vec3d& v, const vector<Vec3d>& ring) const
            //                  {
            //                          const int N = ring.size();
            //                          vector<Vec3d> norms(N,Vec3d(0));
            //                          for(int i=0;i<N; ++i)
            //                                  {
            //                                          const Vec3d& vn = ring[i];
            //                                          const Vec3d& vnn = ring[(i+1)%N];
            //                                          Vec3d n = cross(vnn-v,vn-v);
            //                                          double ln = length(vn);
            //                                          if(ln> 1e-20)
            //                                                  norms[i] = n/ln;
            //                                  }
            //                          double H = 0.0f;
            //                          for(int i=0;i<N; ++i)
            //                                  {
            //                                          const Vec3d& vn = ring[i];
            //                                          double e_len = length(v-vn);
            //                                          double beta = 
            //                                                  acos(max(-1.0, min(1.0, dot(norms[i], norms[(i+N-1)%N]))));
            //                                          H += e_len * beta;
            //                                  }
            //                          return H/4.0f;
            //                  }

            double abs_mean_curv(const Vec3d& v, const vector<Vec3d>& ring) const
            {
                const int N = ring.size();

                double H = 0;
                for(int i = 0; i < N; ++i){
                    Vec3d vnim1 = ring[(i+N-1)%N] - v;
                    Vec3d vni   = ring[i] - v;
                    Vec3d vnip1 = ring[(i+1)%N] - v;

                    Vec3d Nm = normalize(cross(vni, vnim1));
                    Vec3d Np = normalize(cross(vnip1, vni));

                    double beta = acos(max(-1.0, min(1.0, dot(Nm, Np))));
                    H += vni.length() * beta;
                }
                H /= 4;

                return H;
            }

        public:
            double delta_energy(const Manifold& m, HalfEdgeID h) const
            {
                                HalfEdgeWalker w = m.halfedgewalker(h);

                                VertexID va = w.vertex();
                                VertexID vb = w.opp().vertex();
                                VertexID vc = w.next().vertex();
                                VertexID vd = w.opp().next().vertex();

                Vec3d va_pos(m.pos(va));
                Vec3d vb_pos(m.pos(vb));
                Vec3d vc_pos(m.pos(vc));
                Vec3d vd_pos(m.pos(vd));

                vector<Vec3d> va_ring_bef;
                vector<Vec3d> va_ring_aft;
                vector<Vec3d> vb_ring_bef;
                vector<Vec3d> vb_ring_aft;
                vector<Vec3d> vc_ring_bef;
                vector<Vec3d> vc_ring_aft;
                vector<Vec3d> vd_ring_bef;
                vector<Vec3d> vd_ring_aft;

                                for(HalfEdgeWalker wv = m.halfedgewalker(va); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    va_ring_bef.push_back(pos);
                    if(v != vb)
                        va_ring_aft.push_back(pos);
                }
                                for(HalfEdgeWalker wv = m.halfedgewalker(vb); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    vb_ring_bef.push_back(pos);
                    if(v != va)
                        vb_ring_aft.push_back(pos);
                }
                                for(HalfEdgeWalker wv = m.halfedgewalker(vc); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    vc_ring_bef.push_back(pos);
                    vc_ring_aft.push_back(pos);
                    if(v == va)
                        vc_ring_aft.push_back(pos);
                }
                                for(HalfEdgeWalker wv = m.halfedgewalker(vd); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    vd_ring_bef.push_back(pos);
                    vd_ring_aft.push_back(pos);
                    if(v == vb)
                        vd_ring_aft.push_back(pos);
                }
                double before =
                    abs_mean_curv(va_pos, va_ring_bef) +
                    abs_mean_curv(vb_pos, vb_ring_bef) +
                    abs_mean_curv(vc_pos, vc_ring_bef) +
                    abs_mean_curv(vd_pos, vd_ring_bef);

                double after =
                    abs_mean_curv(va_pos, va_ring_aft) +
                    abs_mean_curv(vb_pos, vb_ring_aft) +
                    abs_mean_curv(vc_pos, vc_ring_aft) +
                    abs_mean_curv(vd_pos, vd_ring_aft);

                return after-before;
            }
        };

        class GaussCurvatureEnergy: public EnergyFun
        {

            double gauss_curv(const Vec3d& v, const vector<Vec3d>& ring) const
            {
                const int N = ring.size();
                double asum=0.0f;
                double area_sum=0;
                for(int i = 0; i < N; ++i){
                    const Vec3d& v1 = ring[i];
                    const Vec3d& v2 = ring[(i+1)%N];
                    Vec3d a = v1-v;
                    Vec3d b = v2-v;
                    asum += acos(max(-1.0, min(1.0, dot(a,b)/(length(a)*length(b)))));
                    area_sum += 0.5 * length(cross(a,b));
                }
                return 3*abs(2 * M_PI - asum)/area_sum;
            }

        public:
            double delta_energy(const Manifold& m, HalfEdgeID h) const
            {
                                HalfEdgeWalker w = m.halfedgewalker(h);

                VertexID va = w.vertex();
                VertexID vb = w.opp().vertex();
                VertexID vc = w.next().vertex();
                VertexID vd = w.opp().next().vertex();

                Vec3d va_pos(m.pos(va));
                Vec3d vb_pos(m.pos(vb));
                Vec3d vc_pos(m.pos(vc));
                Vec3d vd_pos(m.pos(vd));

                vector<Vec3d> va_ring_bef;
                vector<Vec3d> va_ring_aft;
                vector<Vec3d> vb_ring_bef;
                vector<Vec3d> vb_ring_aft;
                vector<Vec3d> vc_ring_bef;
                vector<Vec3d> vc_ring_aft;
                vector<Vec3d> vd_ring_bef;
                vector<Vec3d> vd_ring_aft;

                                for(HalfEdgeWalker wv = m.halfedgewalker(va); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    va_ring_bef.push_back(pos);
                    if(v != vb)
                        va_ring_aft.push_back(pos);
                }
                                for(HalfEdgeWalker wv = m.halfedgewalker(vb); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    vb_ring_bef.push_back(pos);
                    if(v != va)
                        vb_ring_aft.push_back(pos);
                }
                                for(HalfEdgeWalker wv = m.halfedgewalker(vc); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    vc_ring_bef.push_back(pos);
                    vc_ring_aft.push_back(pos);
                    if(v == va)
                        vc_ring_aft.push_back(pos);
                }
                                for(HalfEdgeWalker wv = m.halfedgewalker(vd); !wv.full_circle(); wv = wv.circulate_vertex_cw()){
                                        VertexID v = wv.vertex();
                                        Vec3d pos(m.pos(v));

                    vd_ring_bef.push_back(pos);
                    vd_ring_aft.push_back(pos);
                    if(v == vb)
                        vd_ring_aft.push_back(pos);
                }
                double before =
                    gauss_curv(va_pos,va_ring_bef) +
                    gauss_curv(vb_pos,vb_ring_bef) +
                    gauss_curv(vc_pos,vc_ring_bef) +
                    gauss_curv(vd_pos,vd_ring_bef);

                double after =
                    gauss_curv(va_pos,va_ring_aft) +
                    gauss_curv(vb_pos,vb_ring_aft) +
                    gauss_curv(vc_pos,vc_ring_aft) +
                    gauss_curv(vd_pos,vd_ring_aft);

                return after-before;
            }
        };

        struct PQElem
        {
            double pri;
            HalfEdgeID h;
            int time;

            //PQElem() {}
            PQElem(double _pri, HalfEdgeID _h, int _time): 
            pri(_pri), h(_h), time(_time) {}
        };

        bool operator<(const PQElem& e0, const PQElem& e1)
        {
            return e0.pri > e1.pri;
        }


        void add_to_queue(const Manifold& m, HalfEdgeAttributeVector<int>& touched, priority_queue<PQElem>& Q, HalfEdgeID h, const EnergyFun& efun)
        {
            if(!boundary(m, h)){
                                HalfEdgeWalker w = m.halfedgewalker(h);
                                HalfEdgeID ho = w.next().halfedge();

                double energy = efun.delta_energy(m, h);
                int t = touched[h] + 1;
                                touched[h] = t;
                touched[ho] = t;
                if((energy<0) && (t < 10000)){
                    Q.push(PQElem(energy, h, t));
                }
            }
        }

        void add_one_ring_to_queue(const Manifold& m, HalfEdgeAttributeVector<int>& touched, priority_queue<PQElem>& Q, VertexID v, const EnergyFun& efun)
        {
                        
                        for(HalfEdgeWalker w = m.halfedgewalker(v); !w.full_circle(); w = w.circulate_vertex_cw()){
                add_to_queue(m, touched, Q, w.halfedge(), efun);
            }
        }
    }

    void priority_queue_optimization(Manifold& m, const EnergyFun& efun)
    {
                HalfEdgeAttributeVector<int> touched(m);

        priority_queue<PQElem> Q;
        {
            for(HalfEdgeIDIterator h = m.halfedges_begin(); h != m.halfedges_end(); ++h){
                                touched[*h] = 0;
            }

            for(HalfEdgeIDIterator h = m.halfedges_begin(); h != m.halfedges_end(); ++h){
                if(touched[*h] == 0)
                    add_to_queue(m, touched, Q, *h, efun);
            }
        }

        while(!Q.empty())
        {
            PQElem elem = Q.top();
            Q.pop();
            HalfEdgeID h = elem.h;
            if(touched[h] == elem.time){
                if(!precond_flip_edge(m, h))
                    continue;
                m.flip_edge(h);

                                HalfEdgeWalker w = m.halfedgewalker(h);
                add_one_ring_to_queue(m, touched, Q, w.vertex(), efun);
                add_one_ring_to_queue(m, touched, Q, w.next().vertex(), efun);
                add_one_ring_to_queue(m, touched, Q, w.opp().vertex(), efun);
                add_one_ring_to_queue(m, touched, Q, w.opp().next().vertex(), efun);
            }
        }
    }

    void simulated_annealing_optimization(Manifold& m, const EnergyFun& efun, int max_iter)
    {
        gel_srand(0);
        int swaps;
        int iter = 0;
        double T = 1;

        double tmin=0;
        double tsum=0;
        {
            for(HalfEdgeIDIterator h = m.halfedges_begin(); h != m.halfedges_end(); ++h){
                if(boundary(m, *h)) 
                    continue;  
                double e = efun.delta_energy(m, *h);
                tmin = s_min(e, tmin);
                tsum += efun.energy(m, *h);
            }
        }
        cout << "Initial energy:" << tsum << endl;
        T = -2*tmin;

        if(max_iter>0){ 
            do{
                vector<HalfEdgeID>  halfedges;
                for(HalfEdgeIDIterator h = m.halfedges_begin(); h != m.halfedges_end(); ++h){
                    if(boundary(m, *h))
                        continue;
                    halfedges.push_back(*h);
                }
                random_shuffle(halfedges.begin(), halfedges.end());
                swaps = 0;
                for(IndexType i = 0; i < halfedges.size(); ++i){
                                        HalfEdgeID h = halfedges[i];
                    DihedralEnergy dih_en;
                    if(dih_en.min_angle(m, h) > -0.4){
                        double delta = efun.delta_energy(m, h);
                        if(delta < -1e-8){
                            if(precond_flip_edge(m, h)){
                                m.flip_edge(h);
                                ++swaps;
                            }
                        }
                        else{
                            delta = max(1e-8, delta);
                            double prob = min(0.9999, exp(-delta/T));
                            if(gel_rand()/double(GEL_RAND_MAX) < prob){
                                if(precond_flip_edge(m, h)){
                                    m.flip_edge(h);
                                    ++swaps;
                                }
                            }
                        }
                    }
                }
                cout << "Swaps = " << swaps << " T = " << T << endl;
                if(iter % 5 == 0 && iter > 0)
                    T *= 0.9;
            }
            while(++iter < max_iter && swaps);
        }
        cout << "Iterations "  << iter << endl; 

    }


    void minimize_dihedral_angle(Manifold& m, 
        int iter,
        bool anneal, 
        bool alpha,
        double gamma)
    {
        DihedralEnergy energy_fun(gamma, alpha);
        if(anneal)
            simulated_annealing_optimization(m, energy_fun, iter);
        else
            priority_queue_optimization(m, energy_fun);
    }

    void randomize_mesh(Manifold& m, int max_iter)
    {
        RandomEnergy energy_fun;
        simulated_annealing_optimization(m, energy_fun, max_iter);
    }

    void minimize_curvature(Manifold& m, bool anneal)
    {
        CurvatureEnergy energy_fun;
        if(anneal)
            simulated_annealing_optimization(m, energy_fun);
        else
            priority_queue_optimization(m, energy_fun);
    }

    void minimize_gauss_curvature(Manifold& m, bool anneal)
    {
        GaussCurvatureEnergy energy_fun;
        if(anneal)
            simulated_annealing_optimization(m, energy_fun);
        else
            priority_queue_optimization(m, energy_fun);
    }

    void maximize_min_angle(Manifold& m, float thresh, bool anneal)
    {
        MinAngleEnergy energy_fun(thresh);
        if(anneal)
            simulated_annealing_optimization(m, energy_fun);
        else
            priority_queue_optimization(m, energy_fun);
    }

    void optimize_valency(Manifold& m, bool anneal)
    {
        ValencyEnergy energy_fun;
        if(anneal)
            simulated_annealing_optimization(m, energy_fun);
        else
            priority_queue_optimization(m, energy_fun);
    }
}