Subversion Repositories gelsvn

Rev

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

#include <cfloat>
#include "CGLA/Vec3d.h"
#include <queue>
#include "mesh_optimization.h"
#include "HMesh/FaceCirculator.h"
#include "HMesh/VertexCirculator.h"

using namespace std;
using namespace CGLA;
using namespace HMesh;

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

                        LineSeg(const CGLA::Vec3d& _p0, const CGLA::Vec3d& _p1): 
                                p0(_p0), p1(_p1), l(length(p1-p0)), dir((p1-p0)/l) {}
                        
                        bool inseg(const CGLA::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(HalfEdgeIter h)
                {
                        HalfEdgeIter ho = h->opp;
                        Vec3d v[4];
                        v[0]=Vec3d(h->vert->pos);
                        v[1]=Vec3d(ho->vert->pos);
                        v[2]=Vec3d(h->next->vert->pos);
                        v[3]=Vec3d(ho->next->vert->pos);

                        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(HalfEdgeIter he) const
                        {
                                FaceIter f = he->face;
                                
                                VertexIter v1 = he->opp->vert;
                                VertexIter v2 = he->vert;
                                VertexIter vo1 = he->next->vert;
                                VertexIter vo2 = he->opp->next->vert;

                                int val1  = valency(v1);
                                int val2  = valency(v2);
                                int valo1 = valency(vo1);
                                int valo2 = valency(vo2);
                                
                                // The optimal valency is four for a boundary vertex
                                // and six elsewhere.
                                int t1 = is_boundary(v1) ? 4 : 6;
                                int t2 = is_boundary(v2) ? 4 : 6;
                                int to1 = is_boundary(vo1) ? 4 : 6;
                                int to2 = is_boundary(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(HalfEdgeIter he) const
                        {
                                return static_cast<double>(rand()/static_cast<float>(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(HalfEdgeIter he) const
                        {
                                FaceIter f = he->face;
                                FaceIter f2 = he->opp->face;

                                Vec3d v0(he->vert->pos);
                                Vec3d v1(he->next->vert->pos);
                                Vec3d v2(he->opp->vert->pos);
                                Vec3d v3(he->opp->next->vert->pos);

                                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(HalfEdgeIter he) const
                        {
                                Vec3d va(he->vert->pos);
                                Vec3d vb(he->opp->vert->pos);
                                Vec3d vc(he->next->vert->pos);
                                Vec3d vd(he->opp->next->vert->pos);

                                FaceIter fa = he->next->opp->face;
                                FaceIter fb = he->next->next->opp->face;
                                FaceIter fc = he->opp->next->opp->face;
                                FaceIter fd = he->opp->next->next->opp->face;

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

                                Vec3d na = fa==NULL_FACE_ITER? Vec3d(0) : Vec3d(normal(fa));
                                Vec3d nb = fb==NULL_FACE_ITER? Vec3d(0) : Vec3d(normal(fb));
                                Vec3d nc = fc==NULL_FACE_ITER? Vec3d(0) : Vec3d(normal(fc));
                                Vec3d nd = fd==NULL_FACE_ITER? Vec3d(0) : Vec3d(normal(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(HalfEdgeIter he) const
                        {
                                double a = cos_ang(Vec3d(normal(he->face)),
                                                                                                         Vec3d(normal(he->opp->face)));

                                Vec3d va(he->vert->pos);
                                Vec3d vb(he->opp->vert->pos);
                                if(use_alpha)
                                        return edge_alpha_energy(va,vb,a);
                                
                                return edge_c_energy(va,vb,a);
                        }
                                

                        double delta_energy(HalfEdgeIter he) const
                        {
                                compute_angles(he);

                                Vec3d va(he->vert->pos);
                                Vec3d vb(he->opp->vert->pos);
                                Vec3d vc(he->next->vert->pos);
                                Vec3d vd(he->opp->next->vert->pos);

                                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(HalfEdgeIter he) const
                        {
                                compute_angles(he);
                                
                                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(HalfEdgeIter he) const
                        {
                                VertexIter va = he->vert;
                                VertexIter vb = he->opp->vert;
                                VertexIter vc = he->next->vert;
                                VertexIter vd = he->opp->next->vert;

                                Vec3d va_pos(va->pos);
                                Vec3d vb_pos(vb->pos);
                                Vec3d vc_pos(vc->pos);
                                Vec3d vd_pos(vd->pos);

                                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(VertexCirculator c(va); !c.end(); ++c)
                                        {
                                                va_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() != vb)
                                                        va_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                        }
                                for(VertexCirculator c(vb); !c.end(); ++c)
                                        {
                                                vb_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() != va)
                                                        vb_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                        }
                                for(VertexCirculator c(vc); !c.end(); ++c)
                                        {
                                                vc_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                vc_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() == va)
                                                        vc_ring_aft.push_back(Vec3d(vd->pos));
                                        }
                                for(VertexCirculator c(vd); !c.end(); ++c)
                                        {
                                                vd_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                vd_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() == vb)
                                                        vd_ring_aft.push_back(Vec3d(vc->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(HalfEdgeIter he) const
                        {
                                VertexIter va = he->vert;
                                VertexIter vb = he->opp->vert;
                                VertexIter vc = he->next->vert;
                                VertexIter vd = he->opp->next->vert;

                                Vec3d va_pos(va->pos);
                                Vec3d vb_pos(vb->pos);
                                Vec3d vc_pos(vc->pos);
                                Vec3d vd_pos(vd->pos);

                                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(VertexCirculator c(va); !c.end(); ++c)
                                        {
                                                va_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() != vb)
                                                        va_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                        }
                                for(VertexCirculator c(vb); !c.end(); ++c)
                                        {
                                                vb_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() != va)
                                                        vb_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                        }
                                for(VertexCirculator c(vc); !c.end(); ++c)
                                        {
                                                vc_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                vc_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() == va)
                                                        vc_ring_aft.push_back(Vec3d(vd->pos));
                                        }
                                for(VertexCirculator c(vd); !c.end(); ++c)
                                        {
                                                vd_ring_bef.push_back(Vec3d(c.get_vertex()->pos));
                                                vd_ring_aft.push_back(Vec3d(c.get_vertex()->pos));
                                                if(c.get_vertex() == vb)
                                                        vd_ring_aft.push_back(Vec3d(vc->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;
                        HalfEdgeIter he;
                        int time;

                        PQElem() {}
                        PQElem(double _pri, HalfEdgeIter _he, int _time): 
                                pri(_pri), he(_he), time(_time) {}
                };
                
                bool operator<(const PQElem& e0, const PQElem& e1)
                {
                        return e0.pri > e1.pri;
                }
        

                        void add_to_queue(priority_queue<PQElem>& Q,
                                                                                                HalfEdgeIter he,
                                                                                                const EnergyFun& efun)
                        {
                                if(!is_boundary(he))
                                        {
                                                double energy = efun.delta_energy(he);
                                                int t = he->touched + 1;
                                                he->touched = t;
                                                he->opp->touched = t;
                                                if((energy<0) && (t < 10000))
                                                        Q.push(PQElem(energy, he, t));
                                        }
                        }

                void add_one_ring_to_queue(priority_queue<PQElem>& Q,
                                                                                                                         VertexIter vi,
                                                                                                                         const EnergyFun& efun)
                {
                        for(VertexCirculator vc(vi); !vc.end(); ++vc)
                                {
                                        add_to_queue(Q, vc.get_halfedge(), efun);
                                }
                }
        }

        void priority_queue_optimization(Manifold& m, const EnergyFun& efun)
        {
                priority_queue<PQElem> Q;
                {
                        HalfEdgeIter he = m.halfedges_begin();
                        for(;he != m.halfedges_end(); ++he)
                                he->touched = 0;
                        
                        he = m.halfedges_begin();
                        for(;he != m.halfedges_end(); ++he)
                                if(he->touched == 0)
                                        add_to_queue(Q, he, efun);
                }

                while(!Q.empty())
                        {
                                PQElem elem = Q.top();
                                Q.pop();
                                HalfEdgeIter he = elem.he;
                                if(he->touched == elem.time)
                                        {
                                                if(m.flip(he))
                                                        {
                                                                add_one_ring_to_queue(Q, he->vert, efun);
                                                                add_one_ring_to_queue(Q, he->next->vert, efun);
                                                                add_one_ring_to_queue(Q, he->opp->vert, efun);
                                                                add_one_ring_to_queue(Q, he->opp->next->vert, efun);
                                                        }
                                        }
                        }
        }

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

                double tmin=0;
                double tsum=0;
                {
                        HalfEdgeIter he = m.halfedges_begin();
                        for(;he != m.halfedges_end(); ++he)
                                {
                                        if(!is_boundary(he))
                                                {
                                                        double e = efun.delta_energy(he);
                                                        tmin = s_min(e, tmin);
                                                        tsum += efun.energy(he);
                                                }
                                }
                }
                cout << "Initial energy:" << tsum << endl;
                T = -2*tmin;
                        
                if(max_iter>0) do
                        {
                                vector<HalfEdgeIter>  halfedges;
                                HalfEdgeIter he = m.halfedges_begin();
                                for(;he != m.halfedges_end(); ++he)
                                        {
                                                if(!is_boundary(he))
                                                        halfedges.push_back(he);
                                        }
                                random_shuffle(halfedges.begin(), halfedges.end());
                                swaps = 0;
                                for(size_t i=0;i<halfedges.size();++i)
                                        {
                                                he = halfedges[i];

                                                //                                                      if(!would_flip(he))
                                                DihedralEnergy dih_en;
                                                if(dih_en.min_angle(he) > -0.4)
                                                        {
                                                                double delta = efun.delta_energy(he);
                                                                if(delta < -1e-8)
                                                                        {
                                                                                if(m.flip(he))
                                                                                        ++swaps;
                                                                        }
                                                                else
                                                                        {
                                                                                delta = max(1e-8, delta);
                                                                                double prob = min(0.9999, exp(-delta/T));
                                                                                if(rand()/double(RAND_MAX) < prob)
                                                                                        {
                                                                                                if(m.flip(he))
                                                                                                        ++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);
        }




}