/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ /**************************************************************************** History $Log: sampling.h,v $ The sampling Class has a set of static functions, that you can call to sample the surface of a mesh. Each function is templated on the mesh and on a Sampler object s. Each function calls many time the sample object with the sampling point as parameter. ****************************************************************************/ #ifndef __VCGLIB_POINT_SAMPLING #define __VCGLIB_POINT_SAMPLING #include #include #include namespace vcg { namespace tri { /// Trivial Sampler, an example sampler object that show the required interface used by the sampling class. /// Most of the sampling classes call the AddFace method with the face containing the sample and its baricentric coord. template class TrivialSampler { public: typedef typename MeshType::CoordType CoordType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::FaceType FaceType; TrivialSampler(){}; std::vector sampleVec; void AddVert(const VertexType &p) { sampleVec.push_back(p.cP()); } void AddFace(const FaceType &f, const CoordType &p) { sampleVec.push_back(f.P(0)*p[0] + f.P(1)*p[1] +f.P(2)*p[2] ); } void AddTextureSample(const FaceType &, const CoordType &, const Point2i &) { // Retrieve the colorof the sample from the face f using the barycentric coord p // and write that color in a texture image at position tp[0],tp[1] } }; // end class TrivialSampler template class SurfaceSampling { typedef typename MetroMesh::CoordType CoordType; typedef typename MetroMesh::ScalarType ScalarType; typedef typename MetroMesh::VertexType VertexType; typedef typename MetroMesh::VertexPointer VertexPointer; typedef typename MetroMesh::VertexIterator VertexIterator; typedef typename MetroMesh::FacePointer FacePointer; typedef typename MetroMesh::FaceIterator FaceIterator; typedef typename MetroMesh::FaceType FaceType; typedef typename MetroMesh::FaceContainer FaceContainer; public: static void AllVertex(MetroMesh & m, VertexSampler &ps) { VertexIterator vi; for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) { ps.AddVert(*vi); } } /// Sample the vertices in a weighted way. Each vertex has a probabiltiy of being chosen /// that is proportional to its quality. /// It assumes that you are asking a number of vertices smaller than nv; /// Algorithm: /// 1) normalize quality so that sum q == 1; /// 2) shuffle vertices. /// 3) for each vertices choose it if rand > thr; static void VertexWeighted(MetroMesh & m, VertexSampler &ps, int sampleNum) { ScalarType qSum = 0; VertexIterator vi; for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) qSum += (*vi).Q(); ScalarType samplePerUnit = sampleNum/qSum; ScalarType floatSampleNum =0; std::vector vertVec; FillAndShuffleVertexPointVector(m,vertVec); std::vector vertUsed(m.vn,false); int i=0; int cnt=0; while(cnt < sampleNum) { if(vertUsed[i]) { floatSampleNum += vertVec[i]->Q() * samplePerUnit; int vertSampleNum = (int) floatSampleNum; floatSampleNum -= (float) vertSampleNum; // for every sample p_i in T... if(vertSampleNum > 1) { ps.AddVert(*vertVec[i]); cnt++; vertUsed[i]=true; } } i = (i+1)%m.vn; } } /// Sample the vertices in a uniform way. Each vertex has a probabiltiy of being chosen /// that is proportional to the area it represent. static void VertexAreaUniform(MetroMesh & m, VertexSampler &ps, int sampleNum) { VertexIterator vi; for(vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) (*vi).Q() = 0; FaceIterator fi; for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) { ScalarType areaThird = DoubleArea(*fi)/6.0; (*fi).V(0).Q()+=areaThird; (*fi).V(1).Q()+=areaThird; (*fi).V(2).Q()+=areaThird; } VertexWeighted(m,ps,sampleNum); } static void FillAndShuffleVertexPointVector(MetroMesh & m, std::vector &vertVec) { VertexIterator vi; for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) vertVec.push_back(&*vi); assert((int)vertVec.size()==m.vn); std::random_shuffle(vertVec.begin(),vertVec.end()); } /// Sample the vertices in a uniform way. Each vertex has the same probabiltiy of being chosen. static void VertexUniform(MetroMesh & m, VertexSampler &ps, int sampleNum) { if(sampleNum>=m.vn) { AllVertex(m,ps); return; } std::vector vertVec; FillAndShuffleVertexPointVector(m,vertVec); for(int i =0; i< sampleNum; ++i) ps.AddVert(*vertVec[i]); } static void AllFace(MetroMesh & m, VertexSampler &ps) { FaceIterator fi; for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) { ps.AddFace(*fi,Barycenter(*fi)); } } static void AllEdge(MetroMesh & m, VertexSampler &ps) { // Edge sampling. typedef typename UpdateTopology::PEdge SimpleEdge; std::vector< SimpleEdge > Edges; UpdateTopology::FillEdgeVector(m,Edges); sort(Edges.begin(), Edges.end()); // Lo ordino per vertici typename std::vector< SimpleEdge>::iterator newEnd = unique(Edges.begin(), Edges.end()); typename std::vector::iterator ei; //qDebug("Edges %i (unique %i) ",(int)Edges.size(), (int)(newEnd-Edges.begin()) ); Edges.resize(newEnd-Edges.begin()); for(ei=Edges.begin(); ei!=Edges.end(); ++ei) { Point3f interp(0,0,0); interp[ (*ei).z ]=.5; interp[((*ei).z+1)%3]=.5; ps.AddFace(*(*ei).f,interp); } } /* // sample edges. typename std::vector::iterator ei; double n_samples_per_length_unit; double n_samples_decimal = 0.0; int cnt=0; if(Flags & SamplingFlags::FACE_SAMPLING) n_samples_per_length_unit = sqrt((double)n_samples_per_area_unit); else n_samples_per_length_unit = n_samples_per_area_unit; for(ei=Edges.begin(); ei!=Edges.end(); ++ei) { n_samples_decimal += Distance((*ei).first->cP(),(*ei).second->cP()) * n_samples_per_length_unit; n_samples = (int) n_samples_decimal; SampleEdge((*ei).first->cP(), (*ei).second->cP(), (int) n_samples); n_samples_decimal -= (double) n_samples; } */ // Generate the baricentric coords of a random point over a single face, with a uniform distribution over the triangle. // It uses the parallelgoram folding trick. static CoordType RandomBaricentric() { CoordType interp; interp[1] = (double)rand() / (double)RAND_MAX; interp[2] = (double)rand() / (double)RAND_MAX; if(interp[1] + interp[2] > 1.0) { interp[1] = 1.0 - interp[1]; interp[2] = 1.0 - interp[2]; } assert(interp[1] + interp[2] <= 1.0); interp[0]=1.0-(interp[1] + interp[2]); return interp; } static void Montecarlo(MetroMesh & m, VertexSampler &ps,int sampleNum) { ScalarType area = Stat::ComputeMeshArea(m); ScalarType samplePerAreaUnit = sampleNum/area; //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); // Montecarlo sampling. double floatSampleNum = 0.0; FaceIterator fi; for(fi=m.face.begin(); fi != m.face.end(); fi++) if(!(*fi).IsD()) { // compute # samples in the current face (taking into account of the remainders) floatSampleNum += 0.5*DoubleArea(*fi) * samplePerAreaUnit; int faceSampleNum = (int) floatSampleNum; // for every sample p_i in T... for(int i=0; i < faceSampleNum; i++) ps.AddFace(*fi,RandomBaricentric()); floatSampleNum -= (double) faceSampleNum; } } static ScalarType WeightedArea(FaceType f) { ScalarType averageQ = ( f.V(0)->Q() + f.V(1)->Q() + f.V(2)->Q() ) /3.0; return DoubleArea(f)*averageQ/2.0; } /// Compute a sampling of the surface that is weighted by the quality /// the area of each face is multiplied by the average of the quality of the vertices. /// So the a face with a zero quality on all its vertices is never sampled and a face with average quality 2 get twice the samples of a face with the same area but with an average quality of 1; static void WeightedMontecarlo(MetroMesh & m, VertexSampler &ps, int sampleNum) { assert(tri::HasPerVertexQuality(m)); ScalarType weightedArea = 0; FaceIterator fi; for(fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) weightedArea += WeightedArea(*fi); ScalarType samplePerAreaUnit = sampleNum/weightedArea; //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); // Montecarlo sampling. double floatSampleNum = 0.0; for(fi=m.face.begin(); fi != m.face.end(); fi++) if(!(*fi).IsD()) { // compute # samples in the current face (taking into account of the remainders) floatSampleNum += WeightedArea(*fi) * samplePerAreaUnit; int faceSampleNum = (int) floatSampleNum; // for every sample p_i in T... for(int i=0; i < faceSampleNum; i++) ps.AddFace(*fi,RandomBaricentric()); floatSampleNum -= (double) faceSampleNum; } } // Subdivision sampling of a single face. // return number of added samples static int SingleFaceSubdivision(const CoordType & v0, const CoordType & v1, const CoordType & v2, int maxdepth, VertexSampler &ps, FacePointer fp) { // recursive face subdivision. if(maxdepth == 0) { // ground case. CoordType SamplePoint((v0+v1+v2)/3.0f); CoordType SampleBary; InterpolationParameters(*fp,SamplePoint,SampleBary[0],SampleBary[1],SampleBary[2]); ps.AddFace(*fp,SampleBary); return 1; } // compute the longest edge. double maxd01 = SquaredDistance(v0,v1); double maxd12 = SquaredDistance(v1,v2); double maxd20 = SquaredDistance(v2,v0); int res; if(maxd01 > maxd12) if(maxd01 > maxd20) res = 0; else res = 2; else if(maxd12 > maxd20) res = 1; else res = 2; int faceSampleNum=0; // break the input triangle along the midpoint of the longest edge. CoordType pp; switch(res) { case 0 : pp = (v0+v1)/2; faceSampleNum+=SingleFaceSubdivision(v0,pp,v2,maxdepth-1,ps,fp); faceSampleNum+=SingleFaceSubdivision(pp,v1,v2,maxdepth-1,ps,fp); break; case 1 : pp = (v1+v2)/2; faceSampleNum+=SingleFaceSubdivision(v0,v1,pp,maxdepth-1,ps,fp); faceSampleNum+=SingleFaceSubdivision(v0,pp,v2,maxdepth-1,ps,fp); break; case 2 : pp = (v2+v0)/2; faceSampleNum+=SingleFaceSubdivision(v0,v1,pp,maxdepth-1,ps,fp); faceSampleNum+=SingleFaceSubdivision(pp,v1,v2,maxdepth-1,ps,fp); break; } return faceSampleNum; } /// Compute a sampling of the surface where the points are regularly scattered over the face surface using a recursive longest-edge subdivision rule. static void FaceSubdivision(MetroMesh & m, VertexSampler &ps,int sampleNum) { ScalarType area = Stat::ComputeMeshArea(m); ScalarType samplePerAreaUnit = sampleNum/area; //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); double floatSampleNum = 0.0; int faceSampleNum; // Subdivision sampling. FaceIterator fi; for(fi=m.face.begin(); fi!=m.face.end(); fi++) { // compute # samples in the current face. floatSampleNum += 0.5*DoubleArea(*fi) * samplePerAreaUnit; faceSampleNum = (int) floatSampleNum; if(faceSampleNum>0) { // face sampling. int maxdepth = ((int)(log((double)faceSampleNum)/log(2.0))); faceSampleNum = SingleFaceSubdivision((*fi).V(0)->cP(), (*fi).V(1)->cP(), (*fi).V(2)->cP(), maxdepth,ps,&*fi); } floatSampleNum -= (double) faceSampleNum; } } // Similar Triangles sampling. // Skip vertex and edges // Sample per edges includes vertexes, so here we should expect n_samples_per_edge >=4 static int SingleFaceSimilar(FacePointer fp, VertexSampler &ps, int n_samples_per_edge) { int n_samples=0; int i, j; float segmentNum=n_samples_per_edge -1 ; float segmentLen = 1.0/segmentNum; // face sampling. for(i=1; i < n_samples_per_edge-1; i++) for(j=1; j < n_samples_per_edge-1-i; j++) { //AddSample( v0 + (V1*(double)i + V2*(double)j) ); CoordType sampleBary(i*segmentLen,j*segmentLen, 1.0 - (i*segmentLen+j*segmentLen) ) ; n_samples++; ps.AddFace(*fp,sampleBary); } return n_samples; } /// Similar sampling. Each triangle is subdivided into similar triangles following a generalization of the classical 1-to-4 splitting rule of triangles. /// According to the level of subdivision you get 1, 4 , 9, 16 , triangles. /// Of these triangles we consider only internal vertices. (to avoid multiple sampling of edges and vertices). /// Therefore the number of internal points is ((k-3)*(k-2))/2. where k is the number of point on an edge (vertex included) // e.g. for a k=4 you get (1*2)/2 == 1 e.g. a single point, etc. /// So if you want N samples in a triangle i have to solve k^2 -5k +6 - 2N = 0 // 5 + sqrt( 1 + 8N ) // k = ------------------- // 2 //template //void Sampling::SimilarFaceSampling() static void FaceSimilar(MetroMesh & m, VertexSampler &ps,int sampleNum) { ScalarType area = Stat::ComputeMeshArea(m); ScalarType samplePerAreaUnit = sampleNum/area; //qDebug("samplePerAreaUnit %f",samplePerAreaUnit); // Similar Triangles sampling. int n_samples_per_edge; double n_samples_decimal = 0.0; FaceIterator fi; printf("Similar Triangles face sampling\n"); for(fi=m.face.begin(); fi != m.face.end(); fi++) { // compute # samples in the current face. n_samples_decimal += 0.5*DoubleArea(*fi) * samplePerAreaUnit; int n_samples = (int) n_samples_decimal; if(n_samples) { // face sampling. n_samples_per_edge = (int)((sqrt(1.0+8.0*(double)n_samples) +5.0)/2.0); //n_samples = 0; //SingleFaceSimilar((*fi).V(0)->cP(), (*fi).V(1)->cP(), (*fi).V(2)->cP(), n_samples_per_edge); n_samples = SingleFaceSimilar(&*fi,ps, n_samples_per_edge); } n_samples_decimal -= (double) n_samples; } } // Rasterization fuction // Take a triangle // T deve essere una classe funzionale che ha l'operatore () // con due parametri x,y di tipo S esempio: // class Foo { public void operator()(int x, int y ) { ??? } }; static void SingleFaceRaster(FaceType &f, VertexSampler &ps, const Point2 & v0, const Point2 & v1, const Point2 & v2) { typedef ScalarType S; // Calcolo bounding box Box2i bbox; if(v0[0]v2[0]) bbox.min[0]=v2[0]; else if(bbox.max[0]v2[1]) bbox.min[1]=v2[1]; else if(bbox.max[1] d10 = v1 - v0; Point2 d21 = v2 - v1; Point2 d02 = v0 - v2; // Preparazione prodotti scalari S b0 = (bbox.min[0]-v0[0])*d10[1] - (bbox.min[1]-v0[1])*d10[0]; S b1 = (bbox.min[0]-v1[0])*d21[1] - (bbox.min[1]-v1[1])*d21[0]; S b2 = (bbox.min[0]-v2[0])*d02[1] - (bbox.min[1]-v2[1])*d02[0]; // Preparazione degli steps S db0 = d10[1]; S db1 = d21[1]; S db2 = d02[1]; // Preparazione segni S dn0 = -d10[0]; S dn1 = -d21[0]; S dn2 = -d02[0]; // Rasterizzazione double de = v0[0]*v1[1]-v0[0]*v2[1]-v1[0]*v0[1]+v1[0]*v2[1]-v2[0]*v1[1]+v2[0]*v0[1]; for(int x=bbox.min[0];x<=bbox.max[0];++x) { bool in = false; S n0 = b0; S n1 = b1; S n2 = b2; for(int y=bbox.min[1];y<=bbox.max[1];++y) { if( (n0>=0 && n1>=0 && n2>=0) || (n0<=0 && n1<=0 && n2<=0) ) { CoordType baryCoord; baryCoord[0] = double(-y*v1[0]+v2[0]*y+v1[1]*x-v2[0]*v1[1]+v1[0]*v2[1]-x*v2[1])/de; baryCoord[1] = -double( x*v0[1]-x*v2[1]-v0[0]*y+v0[0]*v2[1]-v2[0]*v0[1]+v2[0]*y)/de; baryCoord[2] = 1-baryCoord[0]-baryCoord[1]; ps.AddTextureSample(f, baryCoord, Point2i(x,y)); in = true; } else if(in) break; n0 += dn0; n1 += dn1; n2 += dn2; } b0 += db0; b1 += db1; b2 += db2; } } //template //void Sampling::SimilarFaceSampling() static void Texture(MetroMesh & m, VertexSampler &ps, int textureSize) { FaceIterator fi; printf("Similar Triangles face sampling\n"); for(fi=m.face.begin(); fi != m.face.end(); fi++) { Point2f ti[3]; for(int i=0;i<3;++i) ti[i]=Point2f((*fi).WT(i).U() * textureSize, (*fi).WT(i).V() * textureSize); SingleFaceRaster(*fi, ps, ti[0],ti[1],ti[2]); } } }; // end class } // end namespace tri } // end namespace vcg #endif