void ClampVertexQuality(uintptr_t meshptr, float qMin, float qMax, float perc, bool zerosym) { MyMesh &m = *((MyMesh*) meshptr); bool usePerc = (perc > 0); Distribution<float> H; tri::Stat<MyMesh>::ComputePerVertexQualityDistribution(m, H); float percLo = H.Percentile(perc/100.0f); float percHi = H.Percentile(1.0f - (perc/100.0f)); if (qMin == qMax) { std::pair<float, float> minmax = tri::Stat<MyMesh>::ComputePerVertexQualityMinMax(m); qMin = minmax.first; qMax = minmax.second; } if (zerosym) { qMin = std::min(qMin, -math::Abs(qMax)); qMax = -qMin; percLo = std::min(percLo, -math::Abs(percHi)); percHi = -percLo; } if (usePerc) { tri::UpdateQuality<MyMesh>::VertexClamp(m, percLo, percHi); printf("Quality Range: %f %f; Used (%f %f) percentile (%f %f)\n", H.Min(), H.Max(), percLo, percHi, perc, (100.0f-perc)); } else { tri::UpdateQuality<MyMesh>::VertexClamp(m, qMin, qMax); printf("Quality Range: %f %f; Used (%f %f)\n", H.Min(), H.Max(), qMin, qMax); } }
void ColorizeByQuality(uintptr_t meshptr, bool vertexQuality, float qMin, float qMax, float perc, bool zerosym, int colorMap) { MyMesh &m = *((MyMesh*) meshptr); bool usePerc = (perc > 0); Distribution<float> H; if(vertexQuality) tri::Stat<MyMesh>::ComputePerVertexQualityDistribution(m, H); else tri::Stat<MyMesh>::ComputePerFaceQualityDistribution(m, H); float percLo = H.Percentile(perc/100.0f); float percHi = H.Percentile(1.0f - (perc/100.0f)); if (qMin == qMax) { std::pair<float, float> minmax; if(vertexQuality) minmax = tri::Stat<MyMesh>::ComputePerVertexQualityMinMax(m); else minmax = tri::Stat<MyMesh>::ComputePerFaceQualityMinMax(m); qMin = minmax.first; qMax = minmax.second; } if (zerosym) { qMin = std::min(qMin, -math::Abs(qMax)); qMax = -qMin; percLo = std::min(percLo, -math::Abs(percHi)); percHi = -percLo; } if (usePerc) { qMin = percLo; qMax = percHi; printf("Used (%f %f) percentile (%f %f)\n", percLo, percHi, perc, (100.0f-perc)); } printf("Quality Range: %f %f; Used (%f %f)\n", H.Min(), H.Max(), qMin, qMax); switch (colorMap) { case 0: if(vertexQuality) tri::UpdateColor<MyMesh>::PerVertexQualityRamp(m, qMin, qMax); else tri::UpdateColor<MyMesh>::PerFaceQualityRamp(m, qMin, qMax); break; case 1: if(vertexQuality) tri::UpdateColor<MyMesh>::PerVertexQualityGray(m, qMin, qMax); else tri::UpdateColor<MyMesh>::PerFaceQualityGray(m, qMin, qMax); break; case 2: if(vertexQuality) tri::UpdateColor<MyMesh>::PerVertexQualityRamp(m, qMin, qMax); else tri::UpdateColor<MyMesh>::PerFaceQualityRamp(m, qMin, qMax); break; default: assert(0); } }
// Core Function doing the actual mesh processing. bool FilterMeasurePlugin::applyFilter( const QString& filterName,MeshDocument& md,EnvWrap& env, vcg::CallBackPos * /*cb*/ ) { if (filterName == "Compute Topological Measures") { CMeshO &m=md.mm()->cm; tri::Allocator<CMeshO>::CompactFaceVector(m); tri::Allocator<CMeshO>::CompactVertexVector(m); md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO); md.mm()->updateDataMask(MeshModel::MM_VERTFACETOPO); int edgeManifNum = tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m,true); int faceEdgeManif = tri::UpdateSelection<CMeshO>::FaceCount(m); tri::UpdateSelection<CMeshO>::VertexClear(m); tri::UpdateSelection<CMeshO>::FaceClear(m); int vertManifNum = tri::Clean<CMeshO>::CountNonManifoldVertexFF(m,true); tri::UpdateSelection<CMeshO>::FaceFromVertexLoose(m); int faceVertManif = tri::UpdateSelection<CMeshO>::FaceCount(m); int edgeNum=0,borderNum=0; tri::Clean<CMeshO>::CountEdges(m, edgeNum, borderNum); int holeNum; Log("V: %6i E: %6i F:%6i",m.vn,edgeNum,m.fn); int unrefVertNum = tri::Clean<CMeshO>::CountUnreferencedVertex(m); Log("Unreferenced Vertices %i",unrefVertNum); Log("Boundary Edges %i",borderNum); int connectedComponentsNum = tri::Clean<CMeshO>::CountConnectedComponents(m); Log("Mesh is composed by %i connected component(s)\n",connectedComponentsNum); if(edgeManifNum==0 && vertManifNum==0) { Log("Mesh is two-manifold "); } if(edgeManifNum!=0) Log("Mesh has %i non two manifold edges and %i faces are incident on these edges\n",edgeManifNum,faceEdgeManif); if(vertManifNum!=0) Log("Mesh has %i non two manifold vertexes and %i faces are incident on these vertices\n",vertManifNum,faceVertManif); // For Manifold meshes compute some other stuff if(vertManifNum==0 && edgeManifNum==0) { holeNum = tri::Clean<CMeshO>::CountHoles(m); Log("Mesh has %i holes",holeNum); int genus = tri::Clean<CMeshO>::MeshGenus(m.vn-unrefVertNum, edgeNum, m.fn, holeNum, connectedComponentsNum); Log("Genus is %i",genus); } else { Log("Mesh has a undefined number of holes (non 2-manifold mesh)"); Log("Genus is undefined (non 2-manifold mesh)"); } return true; } /************************************************************/ if (filterName == "Compute Topological Measures for Quad Meshes") { CMeshO &m=md.mm()->cm; md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO); md.mm()->updateDataMask(MeshModel::MM_FACEQUALITY); if (! tri::Clean<CMeshO>::IsFFAdjacencyConsistent(m)) { this->errorMessage = "Error: mesh has a not consistent FF adjacency"; return false; } if (! tri::Clean<CMeshO>::HasConsistentPerFaceFauxFlag(m)) { this->errorMessage = "QuadMesh problem: mesh has a not consistent FauxEdge tagging"; return false; } int nQuads = tri::Clean<CMeshO>::CountBitQuads(m); int nTris = tri::Clean<CMeshO>::CountBitTris(m); int nPolys = tri::Clean<CMeshO>::CountBitPolygons(m); int nLargePolys = tri::Clean<CMeshO>::CountBitLargePolygons(m); if(nLargePolys>0) nQuads=0; Log("Mesh has %8i triangles \n",nTris); Log(" %8i quads \n",nQuads); Log(" %8i polygons \n",nPolys); Log(" %8i large polygons (with internal faux vertexes)",nLargePolys); if (! tri::Clean<CMeshO>::IsBitTriQuadOnly(m)) { this->errorMessage = "QuadMesh problem: the mesh is not TriQuadOnly"; return false; } // // i // // // i+1 i+2 tri::UpdateFlags<CMeshO>::FaceClearV(m); Distribution<float> AngleD; // angle distribution Distribution<float> RatioD; // ratio distribution tri::UpdateFlags<CMeshO>::FaceClearV(m); for(CMeshO::FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi) if(!fi->IsV()) { fi->SetV(); // Collect the vertices Point3f qv[4]; bool quadFound=false; for(int i=0; i<3; ++i) { if((*fi).IsF(i) && !(*fi).IsF((i+1)%3) && !(*fi).IsF((i+2)%3) ) { qv[0] = fi->V0(i)->P(), qv[1] = fi->FFp(i)->V2( fi->FFi(i) )->P(), qv[2] = fi->V1(i)->P(), qv[3] = fi->V2(i)->P(); quadFound=true; } } assert(quadFound); for(int i=0; i<4; ++i) AngleD.Add(fabs(90-math::ToDeg(Angle(qv[(i+0)%4] - qv[(i+1)%4], qv[(i+2)%4] - qv[(i+1)%4])))); float edgeLen[4]; for(int i=0; i<4; ++i) edgeLen[i]=Distance(qv[(i+0)%4],qv[(i+1)%4]); std::sort(edgeLen,edgeLen+4); RatioD.Add(edgeLen[0]/edgeLen[3]); } Log("Right Angle Discrepancy Avg %4.3f Min %4.3f Max %4.3f StdDev %4.3f Percentile 0.05 %4.3f percentile 95 %4.3f", AngleD.Avg(), AngleD.Min(), AngleD.Max(),AngleD.StandardDeviation(),AngleD.Percentile(0.05),AngleD.Percentile(0.95)); Log("Quad Ratio Avg %4.3f Min %4.3f Max %4.3f", RatioD.Avg(), RatioD.Min(), RatioD.Max()); return true; } /************************************************************/ if(filterName == "Compute Geometric Measures") { CMeshO &m=md.mm()->cm; tri::Inertia<CMeshO> I(m); float Area = tri::Stat<CMeshO>::ComputeMeshArea(m); float Volume = I.Mass(); Log("Mesh Bounding Box Size %f %f %f", m.bbox.DimX(), m.bbox.DimY(), m.bbox.DimZ()); Log("Mesh Bounding Box Diag %f ", m.bbox.Diag()); Log("Mesh Volume is %f", Volume); Log("Mesh Surface is %f", Area); Point3f bc=tri::Stat<CMeshO>::ComputeShellBarycenter(m); Log("Thin shell barycenter %9.6f %9.6f %9.6f",bc[0],bc[1],bc[2]); if(Volume<=0) Log("Mesh is not 'solid', no information on barycenter and inertia tensor."); else { Log("Center of Mass is %f %f %f", I.CenterOfMass()[0], I.CenterOfMass()[1], I.CenterOfMass()[2]); Matrix33f IT; I.InertiaTensor(IT); Log("Inertia Tensor is :"); Log(" | %9.6f %9.6f %9.6f |",IT[0][0],IT[0][1],IT[0][2]); Log(" | %9.6f %9.6f %9.6f |",IT[1][0],IT[1][1],IT[1][2]); Log(" | %9.6f %9.6f %9.6f |",IT[2][0],IT[2][1],IT[2][2]); Matrix33f PCA; Point3f pcav; I.InertiaTensorEigen(PCA,pcav); Log("Principal axes are :"); Log(" | %9.6f %9.6f %9.6f |",PCA[0][0],PCA[0][1],PCA[0][2]); Log(" | %9.6f %9.6f %9.6f |",PCA[1][0],PCA[1][1],PCA[1][2]); Log(" | %9.6f %9.6f %9.6f |",PCA[2][0],PCA[2][1],PCA[2][2]); Log("axis momenta are :"); Log(" | %9.6f %9.6f %9.6f |",pcav[0],pcav[1],pcav[2]); } return true; } /************************************************************/ if((filterName == "Per Vertex Quality Stat") || (filterName == "Per Face Quality Stat") ) { CMeshO &m=md.mm()->cm; Distribution<float> DD; if(filterName == "Per Vertex Quality Stat") tri::Stat<CMeshO>::ComputePerVertexQualityDistribution(m, DD, false); else tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m, DD, false); Log(" Min %f Max %f",DD.Min(),DD.Max()); Log(" Avg %f Med %f",DD.Avg(),DD.Percentile(0.5f)); Log(" StdDev %f",DD.StandardDeviation()); Log(" Variance %f",DD.Variance()); return true; } if((filterName == "Per Vertex Quality Histogram") || (filterName == "Per Face Quality Histogram") ) { CMeshO &m=md.mm()->cm; float RangeMin = env.evalFloat("HistMin"); float RangeMax = env.evalFloat("HistMax"); int binNum = env.evalInt("binNum"); Histogramf H; H.SetRange(RangeMin,RangeMax,binNum); if(filterName == "Per Vertex Quality Histogram") { for(CMeshO::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) { assert(!math::IsNAN((*vi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); H.Add((*vi).Q()); } } else { for(CMeshO::FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) { assert(!math::IsNAN((*fi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); H.Add((*fi).Q()); } } Log("( -inf..%15.7f) : %4.0f",RangeMin,H.BinCountInd(0)); for(int i=1; i<=binNum; ++i) Log("[%15.7f..%15.7f) : %4.0f",H.BinLowerBound(i),H.BinUpperBound(i),H.BinCountInd(i)); Log("[%15.7f.. +inf) : %4.0f",RangeMax,H.BinCountInd(binNum+1)); return true; } return false; }
bool ExtraMeshColorizePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb){ MeshModel &m=*(md.mm()); switch(ID(filter)) { case CP_SATURATE_QUALITY:{ m.updateDataMask(MeshModel::MM_VERTFACETOPO); tri::UpdateQuality<CMeshO>::VertexSaturate(m.cm, par.getFloat("gradientThr")); if(par.getBool("updateColor")) { Histogramf H; tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H); m.updateDataMask(MeshModel::MM_VERTCOLOR); tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,H.Percentile(0.1f),H.Percentile(0.9f)); } Log("Saturated "); } break; case CP_MAP_VQUALITY_INTO_COLOR: m.updateDataMask(MeshModel::MM_VERTCOLOR); case CP_CLAMP_QUALITY: { float RangeMin = par.getFloat("minVal"); float RangeMax = par.getFloat("maxVal"); bool usePerc = par.getDynamicFloat("perc")>0; Histogramf H; tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H); float PercLo = H.Percentile(par.getDynamicFloat("perc")/100.f); float PercHi = H.Percentile(1.0-par.getDynamicFloat("perc")/100.f); if(par.getBool("zeroSym")) { RangeMin = min(RangeMin, -math::Abs(RangeMax)); RangeMax = max(math::Abs(RangeMin), RangeMax); PercLo = min(PercLo, -math::Abs(PercHi)); PercHi = max(math::Abs(PercLo), PercHi); } if(usePerc) { if(ID(filter)==CP_CLAMP_QUALITY) tri::UpdateQuality<CMeshO>::VertexClamp(m.cm,PercLo,PercHi); else tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,PercLo,PercHi); Log("Quality Range: %f %f; Used (%f %f) percentile (%f %f) ",H.MinV(),H.MaxV(),PercLo,PercHi,par.getDynamicFloat("perc"),100-par.getDynamicFloat("perc")); } else { if(ID(filter)==CP_CLAMP_QUALITY) tri::UpdateQuality<CMeshO>::VertexClamp(m.cm,RangeMin,RangeMax); else tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,RangeMin,RangeMax); Log("Quality Range: %f %f; Used (%f %f)",H.MinV(),H.MaxV(),RangeMin,RangeMax); } break; } case CP_MAP_FQUALITY_INTO_COLOR: { m.updateDataMask(MeshModel::MM_FACECOLOR); float RangeMin = par.getFloat("minVal"); float RangeMax = par.getFloat("maxVal"); float perc = par.getDynamicFloat("perc"); Histogramf H; tri::Stat<CMeshO>::ComputePerFaceQualityHistogram(m.cm,H); float PercLo = H.Percentile(perc/100.f); float PercHi = H.Percentile(1.0-perc/100.f); // Make the range and percentile symmetric w.r.t. zero, so that // the value zero is always colored in yellow if(par.getBool("zeroSym")){ RangeMin = min(RangeMin, -math::Abs(RangeMax)); RangeMax = max(math::Abs(RangeMin), RangeMax); PercLo = min(PercLo, -math::Abs(PercHi)); PercHi = max(math::Abs(PercLo), PercHi); } tri::UpdateColor<CMeshO>::FaceQualityRamp(m.cm,PercLo,PercHi); Log("Quality Range: %f %f; Used (%f %f) percentile (%f %f) ", H.MinV(), H.MaxV(), PercLo, PercHi, perc, 100-perc); break; } case CP_DISCRETE_CURVATURE: { m.updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTCURV); m.updateDataMask(MeshModel::MM_VERTCOLOR | MeshModel::MM_VERTQUALITY); tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm); if ( tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m.cm) > 0) { errorMessage = "Mesh has some not 2-manifold faces, Curvature computation requires manifoldness"; // text return false; // can't continue, mesh can't be processed } int delvert=tri::Clean<CMeshO>::RemoveUnreferencedVertex(m.cm); if(delvert) Log("Pre-Curvature Cleaning: Removed %d unreferenced vertices",delvert); tri::Allocator<CMeshO>::CompactVertexVector(m.cm); tri::UpdateCurvature<CMeshO>::MeanAndGaussian(m.cm); int curvType = par.getEnum("CurvatureType"); switch(curvType){ case 0: tri::UpdateQuality<CMeshO>::VertexFromMeanCurvature(m.cm); Log( "Computed Mean Curvature"); break; case 1: tri::UpdateQuality<CMeshO>::VertexFromGaussianCurvature(m.cm); Log( "Computed Gaussian Curvature"); break; case 2: tri::UpdateQuality<CMeshO>::VertexFromRMSCurvature(m.cm); Log( "Computed RMS Curvature"); break; case 3: tri::UpdateQuality<CMeshO>::VertexFromAbsoluteCurvature(m.cm); Log( "Computed ABS Curvature"); break; default : assert(0); } Histogramf H; tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H); tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,H.Percentile(0.1f),H.Percentile(0.9f)); Log( "Curvature Range: %f %f (Used 90 percentile %f %f) ",H.MinV(),H.MaxV(),H.Percentile(0.1f),H.Percentile(0.9f)); break; } case CP_TRIANGLE_QUALITY: { m.updateDataMask(MeshModel::MM_FACECOLOR | MeshModel::MM_FACEQUALITY); CMeshO::FaceIterator fi; Distribution<float> distrib; float minV = 0; float maxV = 1.0; int metric = par.getEnum("Metric"); if(metric ==4 || metric ==5 ) { if(!m.hasDataMask(MeshModel::MM_VERTTEXCOORD) && !m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)) { this->errorMessage = "This metric need Texture Coordinate"; return false; } } switch(metric){ case 0: { //area / max edge minV = 0; maxV = sqrt(3.0f)/2.0f; for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::Quality((*fi).P(0), (*fi).P(1),(*fi).P(2)); } break; case 1: { //inradius / circumradius for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::QualityRadii((*fi).P(0), (*fi).P(1), (*fi).P(2)); } break; case 2: { //mean ratio for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::QualityMeanRatio((*fi).P(0), (*fi).P(1), (*fi).P(2)); } break; case 3: { // AREA for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::DoubleArea((*fi))*0.5f; tri::Stat<CMeshO>::ComputePerFaceQualityMinMax(m.cm,minV,maxV); } break; case 4: { //TEXTURE Angle Distortion if(m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)) { for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,true>::AngleDistortion(&*fi); } else { for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,false>::AngleDistortion(&*fi); } tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m.cm,distrib); minV = distrib.Percentile(0.05); maxV = distrib.Percentile(0.95); } break; case 5: { //TEXTURE Area Distortion float areaScaleVal, edgeScaleVal; if(m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)) { Distortion<CMeshO,true>::MeshScalingFactor(m.cm, areaScaleVal,edgeScaleVal); for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,true>::AreaDistortion(&*fi,areaScaleVal); } else { Distortion<CMeshO,false>::MeshScalingFactor(m.cm, areaScaleVal,edgeScaleVal); for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,false>::AreaDistortion(&*fi,areaScaleVal); } tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m.cm,distrib); minV = distrib.Percentile(0.05); maxV = distrib.Percentile(0.95); } break; default: assert(0); } tri::UpdateColor<CMeshO>::FaceQualityRamp(m.cm,minV,maxV,false); break; } case CP_RANDOM_CONNECTED_COMPONENT: m.updateDataMask(MeshModel::MM_FACEFACETOPO); m.updateDataMask(MeshModel::MM_FACEMARK | MeshModel::MM_FACECOLOR); vcg::tri::UpdateColor<CMeshO>::FaceRandomConnectedComponent(m.cm); break; case CP_RANDOM_FACE: m.updateDataMask(MeshModel::MM_FACEFACETOPO); m.updateDataMask(MeshModel::MM_FACEMARK | MeshModel::MM_FACECOLOR); vcg::tri::UpdateColor<CMeshO>::MultiFaceRandom(m.cm); break; case CP_VERTEX_SMOOTH: { int iteration = par.getInt("iteration"); tri::Smooth<CMeshO>::VertexColorLaplacian(m.cm,iteration,false,cb); } break; case CP_FACE_SMOOTH: { m.updateDataMask(MeshModel::MM_FACEFACETOPO); int iteration = par.getInt("iteration"); tri::Smooth<CMeshO>::FaceColorLaplacian(m.cm,iteration,false,cb); } break; case CP_FACE_TO_VERTEX: m.updateDataMask(MeshModel::MM_VERTCOLOR); tri::UpdateColor<CMeshO>::VertexFromFace(m.cm); break; case CP_VERTEX_TO_FACE: m.updateDataMask(MeshModel::MM_FACECOLOR); tri::UpdateColor<CMeshO>::FaceFromVertex(m.cm); break; case CP_TEXTURE_TO_VERTEX: { m.updateDataMask(MeshModel::MM_VERTCOLOR); if(!HasPerWedgeTexCoord(m.cm)) break; CMeshO::FaceIterator fi; QImage tex(m.cm.textures[0].c_str()); for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) { for (int i=0; i<3; i++) { // note the trick for getting only the fractional part of the uv with the correct wrapping (e.g. 1.5 -> 0.5 and -0.3 -> 0.7) vcg::Point2f newcoord((*fi).WT(i).P().X()-floor((*fi).WT(i).P().X()),(*fi).WT(i).P().Y()-floor((*fi).WT(i).P().Y())); QRgb val = tex.pixel(newcoord[0]*tex.width(),(1-newcoord[1])*tex.height()-1); (*fi).V(i)->C().SetRGB(qRed(val),qGreen(val),qBlue(val)); } } } break; } return true; }
// Core Function doing the actual mesh processing. bool FilterMeasurePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos */*cb*/) { CMeshO::FaceIterator fi; switch(ID(filter)) { case FP_MEASURE_TOPO : { CMeshO &m=md.mm()->cm; md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO); md.mm()->updateDataMask(MeshModel::MM_VERTFACETOPO); tri::Allocator<CMeshO>::CompactFaceVector(m); tri::Allocator<CMeshO>::CompactVertexVector(m); tri::UpdateTopology<CMeshO>::FaceFace(m); tri::UpdateTopology<CMeshO>::VertexFace(m); int edgeManif = tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m,true); int faceEdgeManif = tri::UpdateSelection<CMeshO>::CountFace(m); tri::UpdateSelection<CMeshO>::ClearVertex(m); tri::UpdateSelection<CMeshO>::ClearFace(m); int vertManif = tri::Clean<CMeshO>::CountNonManifoldVertexFF(m,true); tri::UpdateSelection<CMeshO>::FaceFromVertexLoose(m); int faceVertManif = tri::UpdateSelection<CMeshO>::CountFace(m); int edgeNum=0,borderNum=0; tri::Clean<CMeshO>::CountEdges(m, edgeNum, borderNum); int holeNum; Log("V: %6i E: %6i F:%6i",m.vn,edgeNum,m.fn); Log("Boundary Edges %i",borderNum); int connectedComponentsNum = tri::Clean<CMeshO>::CountConnectedComponents(m); Log("Mesh is composed by %i connected component(s)",connectedComponentsNum); if(edgeManif==0 && vertManif==0) Log("Mesh has is two-manifold "); if(edgeManif!=0) Log("Mesh has %i non two manifold edges and %i faces are incident on these edges\n",edgeManif,faceEdgeManif); if(vertManif!=0) Log("Mesh has %i non two manifold vertexes and %i faces are incident on these vertices\n",vertManif,faceVertManif); // For Manifold meshes compute some other stuff if(vertManif && edgeManif) { holeNum = tri::Clean<CMeshO>::CountHoles(m); Log("Mesh has %i holes",holeNum); int genus = tri::Clean<CMeshO>::MeshGenus(m, holeNum, connectedComponentsNum, edgeNum); Log("Genus is %i",genus); } } break; /************************************************************/ case FP_MEASURE_TOPO_QUAD : { CMeshO &m=md.mm()->cm; md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO); md.mm()->updateDataMask(MeshModel::MM_FACEQUALITY); if (! tri::Clean<CMeshO>::IsFFAdjacencyConsistent(m)){ Log("Error: mesh has a not consistent FF adjacency"); return false; } if (! tri::Clean<CMeshO>::HasConsistentPerFaceFauxFlag(m)) { Log("Warning: mesh has a not consistent FauxEdge tagging"); return false; } if (! tri::Clean<CMeshO>::IsBitTriQuadOnly(m)) { Log("Warning: IsBitTriQuadOnly"); //return false; } // if (! tri::Clean<CMeshO>::HasConsistentEdges(m)) lastErrorDetected |= NOT_EDGES_CONST; int nsinglets= tri::BitQuadOptimization< tri::BitQuad<CMeshO> >::MarkSinglets(m); if ( nsinglets ) { Log("Warning: MarkSinglets"); //return false; } if (! tri::BitQuad<CMeshO>::HasConsistentValencyFlag(m)) { Log("Warning: HasConsistentValencyFlag"); //return false; } int nQuads = tri::Clean<CMeshO>::CountBitQuads(m); int nTris = tri::Clean<CMeshO>::CountBitTris(m); int nPolys = tri::Clean<CMeshO>::CountBitPolygons(m); Log("Mesh has %i tri %i quad and %i polig",nTris,nQuads,nPolys); } break; /************************************************************/ case FP_MEASURE_GEOM : { CMeshO &m=md.mm()->cm; tri::Inertia<CMeshO> I; I.Compute(m); tri::UpdateBounding<CMeshO>::Box(m); float Area = tri::Stat<CMeshO>::ComputeMeshArea(m); float Volume = I.Mass(); Log("Mesh Bounding Box Size %f %f %f", m.bbox.DimX(), m.bbox.DimY(), m.bbox.DimZ()); Log("Mesh Bounding Box Diag %f ", m.bbox.Diag()); Log("Mesh Volume is %f", Volume); Log("Center of Mass is %f %f %f", I.CenterOfMass()[0], I.CenterOfMass()[1], I.CenterOfMass()[2]); Matrix33f IT; I.InertiaTensor(IT); Log("Inertia Tensor is :"); Log(" | %9.6f %9.6f %9.6f |",IT[0][0],IT[0][1],IT[0][2]); Log(" | %9.6f %9.6f %9.6f |",IT[1][0],IT[1][1],IT[1][2]); Log(" | %9.6f %9.6f %9.6f |",IT[2][0],IT[2][1],IT[2][2]); Log("Mesh Surface is %f", Area); Matrix44f PCA; Point4f pcav; I.InertiaTensorEigen(PCA,pcav); Log("Principal axes are :"); Log(" | %9.6f %9.6f %9.6f |",PCA[0][0],PCA[0][1],PCA[0][2]); Log(" | %9.6f %9.6f %9.6f |",PCA[1][0],PCA[1][1],PCA[1][2]); Log(" | %9.6f %9.6f %9.6f |",PCA[2][0],PCA[2][1],PCA[2][2]); // Point3f ax0(PCA[0][0],PCA[0][1],PCA[0][2]); // Point3f ax1(PCA[1][0],PCA[1][1],PCA[1][2]); // Point3f ax2(PCA[2][0],PCA[2][1],PCA[2][2]); // Log("ax0*ax1 %f (len ax0 %f) ",ax0*ax1, Norm(ax0)); // Log("ax1*ax2 %f (len ax1 %f) ",ax1*ax2, Norm(ax1)); // Log("ax0*ax2 %f (len ax2 %f) ",ax0*ax2, Norm(ax2)); Log("axis momenta are :"); Log(" | %9.6f %9.6f %9.6f |",pcav[0],pcav[1],pcav[2]); } break; /************************************************************/ case FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION : case FP_MEASURE_FACE_QUALITY_DISTRIBUTION : { CMeshO &m=md.mm()->cm; Distribution<float> DD; if(ID(filter)==FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION) tri::Stat<CMeshO>::ComputePerVertexQualityDistribution(m, DD, false); else tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m, DD, false); Log(" Min %f Max %f",DD.Min(),DD.Max()); Log(" Avg %f Med %f",DD.Avg(),DD.Percentile(0.5f)); Log(" StdDev %f",DD.StandardDeviation()); Log(" Variance %f",DD.Variance()); } break; case FP_MEASURE_GAUSSCURV : { CMeshO &m=md.mm()->cm; SimpleTempData<CMeshO::VertContainer, float> TDArea(m.vert,0.0f); SimpleTempData<CMeshO::VertContainer, float> TDAngleSum(m.vert,0); tri::UpdateQuality<CMeshO>::VertexConstant(m,0); float angle[3]; CMeshO::FaceIterator fi; for(fi=m.face.begin(); fi!= m.face.end(); ++fi) { angle[0] = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) )); angle[1] = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) )); angle[2] = M_PI-(angle[0]+angle[1]); float area= DoubleArea(*fi)/6.0f; for(int i=0;i<3;++i) { TDArea[(*fi).V(i)]+=area; TDAngleSum[(*fi).V(i)]+=angle[i]; } } CMeshO::VertexIterator vi; float totalSum=0; for(vi=m.vert.begin(); vi!= m.vert.end(); ++vi) { (*vi).Q() = (2.0*M_PI-TDAngleSum[vi]);//*TDArea[vi]; //if((*vi).IsS()) totalSum += (*vi).Q(); } Log("integrated is %f (%f*pi)", totalSum,totalSum/M_PI); } break; case FP_MEASURE_VERTEX_QUALITY_HISTOGRAM: case FP_MEASURE_FACE_QUALITY_HISTOGRAM: { CMeshO &m=md.mm()->cm; float RangeMin = par.getFloat("minVal"); float RangeMax = par.getFloat("maxVal"); int binNum = par.getInt("binNum"); Histogramf H; H.SetRange(RangeMin,RangeMax,binNum); if(ID(filter)==FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION) { for(CMeshO::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) { assert(!math::IsNAN((*vi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); H.Add((*vi).Q()); } }else{ for(CMeshO::FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) { assert(!math::IsNAN((*fi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); H.Add((*fi).Q()); } } Log("( -inf..%15.7f) : %i",RangeMin,H.BinCountInd(0)); for(int i=1;i<=binNum;++i) Log("[%15.7f..%15.7f) : %i",H.BinLowerBound(i),H.BinUpperBound(i),H.BinCountInd(i)); Log("[%15.7f.. +inf) : %i",RangeMax,H.BinCountInd(binNum+1)); } break; default: assert(0); } return true; }