From f2db6eaa812641745cf3a810751cf79cd051b409 Mon Sep 17 00:00:00 2001 From: Marco Callieri mcallieri Date: Mon, 11 Apr 2016 11:21:04 +0000 Subject: [PATCH] added new filter: "Fit a plane to selection" creates a subdivided, uv-parametrized plane that fits the current layer selection. FIRST WORKING VERSION - still to be tested --- .../filter_create/filter_create.cpp | 835 +++++++++++------- .../filter_create/filter_create.h | 2 +- 2 files changed, 525 insertions(+), 312 deletions(-) diff --git a/src/meshlabplugins/filter_create/filter_create.cpp b/src/meshlabplugins/filter_create/filter_create.cpp index 002267a9a..32ee0e27c 100644 --- a/src/meshlabplugins/filter_create/filter_create.cpp +++ b/src/meshlabplugins/filter_create/filter_create.cpp @@ -1,311 +1,524 @@ -/**************************************************************************** -* MeshLab o o * -* A versatile mesh processing toolbox o o * -* _ O _ * -* Copyright(C) 2005 \/)\/ * -* 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. * -* * -****************************************************************************/ -#include "filter_create.h" -#include -#include -#include -#include - -using namespace vcg; -using namespace tri; - -// Constructor usually performs only two simple tasks of filling the two lists -// - typeList: with all the possible id of the filtering actions -// - actionList with the corresponding actions. If you want to add icons to your filtering actions you can do here by construction the QActions accordingly - -FilterCreate::FilterCreate() -{ - typeList << CR_BOX<< CR_ANNULUS << CR_SPHERE<< CR_SPHERE_CAP - << CR_RANDOM_SPHERE<< CR_ICOSAHEDRON<< CR_DODECAHEDRON - << CR_TETRAHEDRON<" - "Admitted values are in the range 0 (an icosahedron) to 8 (a 1.3 MegaTris approximation of a sphere)")); - break; - - case CR_SPHERE_CAP : - parlst.addParam(new RichFloat("angle",60,"Angle","Angle of the cone subtending the cap. It must be < 180")); - parlst.addParam(new RichInt("subdiv",3,"Subdiv. Level","Number of the recursive subdivision of the surface. Default is 3 (a sphere approximation composed by 1280 faces).
" - "Admitted values are in the range 0 (an icosahedron) to 8 (a 1.3 MegaTris approximation of a sphere)")); - break; - case CR_ANNULUS : - parlst.addParam(new RichFloat("internalRadius",0.5f,"Internal Radius","Internal Radius of the annulus")); - parlst.addParam(new RichFloat("externalRadius",1.0f,"External Radius","Externale Radius of the annulus")); - parlst.addParam(new RichInt("sides",32,"Sides","Number of the sides of the poligonal approximation of the annulus ")); - break; - case CR_RANDOM_SPHERE : - parlst.addParam(new RichInt("pointNum",100,"Point Num","Number of points (approximate).")); - parlst.addParam(new RichEnum("sphereGenTech", 3, - QStringList() << "Montecarlo" << "Poisson Sampling" << "DiscoBall" << "Octahedron" << "Fibonacci", - tr("Generation Technique:"), - tr("Generation Technique:" - "Montecarlo: The points are randomly generated with an uniform distribution.
" - "Poisson Disk: The points are to follow a poisson disk distribution.
" - "Disco Ball Dave Rusin's disco ball algorithm for the regular placement of points on a sphere is used.
" - "Recursive Octahedron Points are genereate on the vertex of a recursively subdivided octahedron
" - "Fibonacci . " - ))); - - break; - case CR_BOX : - parlst.addParam(new RichFloat("size",1,"Scale factor","Scales the new mesh")); - break; - case CR_CONE: - parlst.addParam(new RichFloat("r0",1,"Radius 1","Radius of the bottom circumference")); - parlst.addParam(new RichFloat("r1",2,"Radius 2","Radius of the top circumference")); - parlst.addParam(new RichFloat("h",3,"Height","Height of the Cone")); - parlst.addParam(new RichInt("subdiv",36,"Side","Number of sides of the polygonal approximation of the cone")); - break; - case CR_TORUS: - parlst.addParam(new RichFloat("hRadius",3,"Horizontal Radius","Radius of the whole horizontal ring of the torus")); - parlst.addParam(new RichFloat("vRadius",1,"Vertical Radius","Radius of the vertical section of the ring")); - parlst.addParam(new RichInt("hSubdiv",24,"Horizontal Subdivision","Subdivision step of the ring")); - parlst.addParam(new RichInt("vSubdiv",12,"Vertical Subdivision","Number of sides of the polygonal approximation of the torus section")); - break; - default : return; - } -} - -// The Real Core Function doing the actual mesh processing. -bool FilterCreate::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, CallBackPos * /*cb*/) -{ - MeshModel* m=md.addNewMesh("",this->filterName(ID(filter))); - switch(ID(filter)) { - case CR_TETRAHEDRON : - tri::Tetrahedron(m->cm); - break; - case CR_ICOSAHEDRON: - tri::Icosahedron(m->cm); - break; - case CR_DODECAHEDRON: - tri::Dodecahedron(m->cm); - m->updateDataMask(MeshModel::MM_POLYGONAL); - break; - case CR_OCTAHEDRON: - tri::Octahedron(m->cm); - break; - case CR_ANNULUS: - tri::Annulus(m->cm,par.getFloat("internalRadius"), - par.getFloat("externalRadius"),par.getInt("sides")); - break; - case CR_TORUS: - { - float hRadius=par.getFloat("hRadius"); - float vRadius=par.getFloat("vRadius"); - int hSubdiv=par.getInt("hSubdiv"); - int vSubdiv=par.getInt("vSubdiv"); - tri::Torus(m->cm,hRadius,vRadius,hSubdiv,vSubdiv); - break; - } - case CR_RANDOM_SPHERE: - { - - int pointNum = par.getInt("pointNum"); - int sphereGenTech = par.getEnum("sphereGenTech"); - math::MarsenneTwisterRNG rng; - m->cm.Clear(); - std::vector sampleVec; - - - switch(sphereGenTech) - { - case 0: // Montecarlo - { - for(int i=0;i(rng)); - } break; - case 1: // Poisson Disk - { - int oversamplingFactor =100; - if(pointNum <= 100) oversamplingFactor = 1000; - if(pointNum >= 10000) oversamplingFactor = 50; - if(pointNum >= 100000) oversamplingFactor = 20; - CMeshO tt; - tri::Allocator::AddVertices(tt,pointNum*oversamplingFactor); - for(CMeshO::VertexIterator vi=tt.vert.begin();vi!=tt.vert.end();++vi) - vi->P()=math::GeneratePointOnUnitSphereUniform(rng); - tri::UpdateBounding::Box(tt); - - const float SphereArea = 4*M_PI; - float poissonRadius = 2.0*sqrt((SphereArea / float(pointNum*2))/M_PI); - - std::vector sampleVec; - tri::TrivialSampler pdSampler(sampleVec); - tri::SurfaceSampling >::PoissonDiskParam pp; - tri::SurfaceSampling >::PoissonDiskPruning(pdSampler, tt, poissonRadius, pp); - } break; - case 2: // Disco Ball - GenNormal::DiscoBall(pointNum,sampleVec); - break; - case 3: // Recursive Oct - GenNormal::RecursiveOctahedron(pointNum,sampleVec); - break; - case 4: // Fibonacci - GenNormal::Fibonacci(pointNum,sampleVec); - break; - } - for(size_t i=0;i::AddVertex(m->cm,sampleVec[i],sampleVec[i]); - - } break; - - case CR_SPHERE_CAP: - { - int rec = par.getInt("subdiv"); - const float angleDeg = par.getFloat("angle"); - m->updateDataMask(MeshModel::MM_FACEFACETOPO); - tri::UpdateTopology::FaceFace(m->cm); - tri::SphericalCap(m->cm,math::ToRad(angleDeg),rec); - } break; - - case CR_SPHERE: - { - int rec = par.getInt("subdiv"); - float radius = par.getFloat("radius"); - m->cm.face.EnableFFAdjacency(); - m->updateDataMask(MeshModel::MM_FACEFACETOPO); - assert(tri::HasPerVertexTexCoord(m->cm) == false); - tri::Sphere(m->cm,rec); - tri::UpdatePosition::Scale(m->cm,radius); - break; - } - case CR_BOX: - { - float sz=par.getFloat("size"); - Box3m b(Point3m(1,1,1)*(-sz/2),Point3m(1,1,1)*(sz/2)); - tri::Box(m->cm,b); - m->updateDataMask(MeshModel::MM_POLYGONAL); - - break; - } - case CR_CONE: - float r0=par.getFloat("r0"); - float r1=par.getFloat("r1"); - float h=par.getFloat("h"); - int subdiv=par.getInt("subdiv"); - tri::Cone(m->cm,r0,r1,h,subdiv); - break; - } - tri::UpdateBounding::Box(m->cm); - tri::UpdateNormal::PerVertexNormalizedPerFaceNormalized(m->cm); - return true; -} - - MeshFilterInterface::FilterClass FilterCreate::getClass(QAction *a) -{ - switch(ID(a)) - { - case CR_BOX: - case CR_TETRAHEDRON: - case CR_ICOSAHEDRON: - case CR_DODECAHEDRON: - case CR_SPHERE: - case CR_SPHERE_CAP: - case CR_ANNULUS: - case CR_RANDOM_SPHERE: - case CR_OCTAHEDRON: - case CR_CONE: - case CR_TORUS: - return MeshFilterInterface::MeshCreation; - break; - default: assert(0); - return MeshFilterInterface::Generic; - } -} - -QString FilterCreate::filterScriptFunctionName( FilterIDType filterID ) - { - switch(filterID) - { - case CR_BOX : return QString("box"); - case CR_ANNULUS : return QString("annulus"); - case CR_SPHERE: return QString("sphere"); - case CR_SPHERE_CAP: return QString("spherecap"); - case CR_RANDOM_SPHERE: return QString("randomsphere"); - case CR_ICOSAHEDRON: return QString("icosahedron"); - case CR_DODECAHEDRON: return QString("dodecahedron"); - case CR_OCTAHEDRON: return QString("octahedron"); - case CR_TETRAHEDRON: return QString("tetrahedron"); - case CR_CONE: return QString("cone"); - case CR_TORUS: return QString("torus"); - default : assert(0); - } - } - - -MESHLAB_PLUGIN_NAME_EXPORTER(FilterCreate) +/**************************************************************************** +* MeshLab o o * +* A versatile mesh processing toolbox o o * +* _ O _ * +* Copyright(C) 2005 \/)\/ * +* 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. * +* * +****************************************************************************/ +#include "filter_create.h" +#include +#include +#include +#include + +using namespace vcg; +using namespace tri; + +// Constructor usually performs only two simple tasks of filling the two lists +// - typeList: with all the possible id of the filtering actions +// - actionList with the corresponding actions. If you want to add icons to your filtering actions you can do here by construction the QActions accordingly + +FilterCreate::FilterCreate() +{ + typeList << CR_BOX<< CR_ANNULUS << CR_SPHERE<< CR_SPHERE_CAP + << CR_RANDOM_SPHERE<< CR_ICOSAHEDRON<< CR_DODECAHEDRON + << CR_TETRAHEDRON<" + "Admitted values are in the range 0 (an icosahedron) to 8 (a 1.3 MegaTris approximation of a sphere)")); + break; + + case CR_SPHERE_CAP : + parlst.addParam(new RichFloat("angle",60,"Angle","Angle of the cone subtending the cap. It must be < 180")); + parlst.addParam(new RichInt("subdiv",3,"Subdiv. Level","Number of the recursive subdivision of the surface. Default is 3 (a sphere approximation composed by 1280 faces).
" + "Admitted values are in the range 0 (an icosahedron) to 8 (a 1.3 MegaTris approximation of a sphere)")); + break; + case CR_ANNULUS : + parlst.addParam(new RichFloat("internalRadius",0.5f,"Internal Radius","Internal Radius of the annulus")); + parlst.addParam(new RichFloat("externalRadius",1.0f,"External Radius","Externale Radius of the annulus")); + parlst.addParam(new RichInt("sides",32,"Sides","Number of the sides of the poligonal approximation of the annulus ")); + break; + case CR_RANDOM_SPHERE : + parlst.addParam(new RichInt("pointNum",100,"Point Num","Number of points (approximate).")); + parlst.addParam(new RichEnum("sphereGenTech", 3, + QStringList() << "Montecarlo" << "Poisson Sampling" << "DiscoBall" << "Octahedron" << "Fibonacci", + tr("Generation Technique:"), + tr("Generation Technique:" + "Montecarlo: The points are randomly generated with an uniform distribution.
" + "Poisson Disk: The points are to follow a poisson disk distribution.
" + "Disco Ball Dave Rusin's disco ball algorithm for the regular placement of points on a sphere is used.
" + "Recursive Octahedron Points are genereate on the vertex of a recursively subdivided octahedron
" + "Fibonacci . " + ))); + + break; + case CR_BOX : + parlst.addParam(new RichFloat("size",1,"Scale factor","Scales the new mesh")); + break; + case CR_CONE: + parlst.addParam(new RichFloat("r0",1,"Radius 1","Radius of the bottom circumference")); + parlst.addParam(new RichFloat("r1",2,"Radius 2","Radius of the top circumference")); + parlst.addParam(new RichFloat("h",3,"Height","Height of the Cone")); + parlst.addParam(new RichInt("subdiv",36,"Side","Number of sides of the polygonal approximation of the cone")); + break; + + case CR_TORUS: + parlst.addParam(new RichFloat("hRadius",3,"Horizontal Radius","Radius of the whole horizontal ring of the torus")); + parlst.addParam(new RichFloat("vRadius",1,"Vertical Radius","Radius of the vertical section of the ring")); + parlst.addParam(new RichInt("hSubdiv",24,"Horizontal Subdivision","Subdivision step of the ring")); + parlst.addParam(new RichInt("vSubdiv",12,"Vertical Subdivision","Number of sides of the polygonal approximation of the torus section")); + break; + + case CR_FITPLANE: + parlst.addParam(new RichFloat("extent", 1.0, "Extent (with respect to selection)", "Howe large is the plane, with respect to the size of the selction: 1.0 means as large as the selection, 1.1 means 10% larger thena the selection")); + parlst.addParam(new RichInt("subdiv", 5, "Plane XY subivisions", "Subdivision steps of plane borders")); + parlst.addParam(new RichBool("hasuv", false, "UV parametrized", "The created plane has an UV parametrization")); + parlst.addParam(new RichEnum("orientation", 1, + QStringList() << "quasi-Straight Fit" << "Best Fit", + tr("Plane orientation"), + tr("Orientation:" + "quasi-Straight Fit: The fitting plane wil be placed (as much as possible) straight with the axis. Works better if the selected area is already almost straight
" + "Best Fit: The fitting plane wil be placed and sized trying to best fit to the selected area.
" + ))); + break; + default : return; + } +} + +// The Real Core Function doing the actual mesh processing. +bool FilterCreate::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, CallBackPos * /*cb*/) +{ + MeshModel & currM = *md.mm(); + MeshModel* m; + + switch(ID(filter)) + { + case CR_TETRAHEDRON : + m = md.addNewMesh("", this->filterName(ID(filter))); + tri::Tetrahedron(m->cm); + break; + case CR_ICOSAHEDRON: + m = md.addNewMesh("", this->filterName(ID(filter))); + tri::Icosahedron(m->cm); + break; + case CR_DODECAHEDRON: + m = md.addNewMesh("", this->filterName(ID(filter))); + tri::Dodecahedron(m->cm); + m->updateDataMask(MeshModel::MM_POLYGONAL); + break; + case CR_OCTAHEDRON: + m = md.addNewMesh("", this->filterName(ID(filter))); + tri::Octahedron(m->cm); + break; + case CR_ANNULUS: + m = md.addNewMesh("", this->filterName(ID(filter))); + tri::Annulus(m->cm,par.getFloat("internalRadius"), par.getFloat("externalRadius"), par.getInt("sides")); + break; + + case CR_TORUS: + { + m = md.addNewMesh("", this->filterName(ID(filter))); + float hRadius=par.getFloat("hRadius"); + float vRadius=par.getFloat("vRadius"); + int hSubdiv=par.getInt("hSubdiv"); + int vSubdiv=par.getInt("vSubdiv"); + tri::Torus(m->cm,hRadius,vRadius,hSubdiv,vSubdiv); + } break; + + case CR_FITPLANE: + { + Box3m selBox; //boundingbox of the selected vertices + std::vector< Point3m > selected_pts; //copy of selected vertices, for plane fitting + + if (&currM == NULL) + { + errorMessage = "No mesh layer selected"; + return false; + } + + if (currM.cm.svn == 0 && currM.cm.sfn == 0) // if no selection, fail + { + errorMessage = "No selection"; + return false; + } + + m = md.addNewMesh("", "Fitted Plane"); + + if (currM.cm.svn == 0 || currM.cm.sfn != 0) + { + tri::UpdateSelection::VertexClear(currM.cm); + tri::UpdateSelection::VertexFromFaceLoose(currM.cm); + } + + Point3m Naccum = Point3m(0.0, 0.0, 0.0); + for (CMeshO::VertexIterator vi = currM.cm.vert.begin(); vi != currM.cm.vert.end(); ++vi) + if (!(*vi).IsD() && (*vi).IsS()) + { + Point3m p = (*vi).P(); + selBox.Add(p); + selected_pts.push_back(p); + Naccum = Naccum + (*vi).N(); + } + Log("Using %i vertexes to build a fitting plane", int(selected_pts.size())); + Plane3m plane; + FitPlaneToPointSet(selected_pts, plane); + plane.Normalize(); + // check if normal of the interpolated plane is coherent with average normal of the used points, otherwise, flip + // i do this because plane fitter does not take in account source noramls, and a fliped fit is terrible to see + Naccum = (Naccum / (CMeshO::ScalarType)selected_pts.size()).Normalize(); + if ((plane.Direction() * Naccum) < 0.0) + plane.Set(-plane.Direction(), -plane.Offset()); + + float errorSum = 0; + for (size_t i = 0; i < selected_pts.size(); ++i) + errorSum += fabs(SignedDistancePlanePoint(plane, selected_pts[i])); + Log("Fitting Plane avg error is %f", errorSum / float(selected_pts.size())); + Log("Fitting Plane normal is [%f, %f, %f]", plane.Direction().X(), plane.Direction().Y(), plane.Direction().Z()); + Log("Fitting Plane offset is %f", plane.Offset()); + + // find center of selection on plane + Point3m centerP; + for (size_t i = 0; i < selected_pts.size(); ++i) + { + centerP += plane.Projection(selected_pts[i]); + } + centerP /= selected_pts.size(); + Log("center [%f, %f, %f]", centerP.X(), centerP.Y(), centerP.Z()); + + // find horizontal and vertical axis + Point3m dirH, dirV; + + int orientation = par.getEnum("orientation"); + + if (orientation == 0) + { + if ((plane.Direction().X() <= plane.Direction().Y()) && (plane.Direction().X() <= plane.Direction().Z())) + dirH = Point3m(1.0, 0.0, 0.0) ^ plane.Direction(); + else if ((plane.Direction().Y() <= plane.Direction().X()) && (plane.Direction().Y() <= plane.Direction().Z())) + dirH = Point3m(0.0, 1.0, 0.0) ^ plane.Direction(); + else + dirH = Point3m(0.0, 0.0, 1.0) ^ plane.Direction(); + + dirH.Normalize(); + dirV = dirH ^ plane.Direction(); + dirV.Normalize(); + } + else + { + Matrix33m cov; + vector PtVec; + for (size_t i = 0; i < selected_pts.size(); ++i) + PtVec.push_back(plane.Projection(selected_pts[i])); + + cov.Covariance(PtVec, centerP); + Matrix33f eigenvecMatrix; + Point3f eigenvecVector; + Eigen::Matrix3d em; + cov.ToEigenMatrix(em); + Eigen::SelfAdjointEigenSolver eig(em); + Eigen::Vector3d c_val = eig.eigenvalues(); + Eigen::Matrix3d c_vec = eig.eigenvectors(); + + eigenvecMatrix.FromEigenMatrix(c_vec); + eigenvecVector.FromEigenVector(c_val); + + // max eigenvector is best horizontal axis, but is not guarantee is orthogonal to plane normal, so + // I use eigenvector ^ plane direction and assign it to vertical plane axis + if ((eigenvecVector[0]<=eigenvecVector[1]) && (eigenvecVector[0]<=eigenvecVector[2])) + dirV = Point3m(eigenvecMatrix[0][0], eigenvecMatrix[0][1], eigenvecMatrix[0][2]) ^ plane.Direction(); + if ((eigenvecVector[1]<=eigenvecVector[0]) && (eigenvecVector[1]<=eigenvecVector[2])) + dirV = Point3m(eigenvecMatrix[1][0], eigenvecMatrix[1][1], eigenvecMatrix[1][2]) ^ plane.Direction(); + else + dirV = Point3m(eigenvecMatrix[2][0], eigenvecMatrix[2][1], eigenvecMatrix[2][2]) ^ plane.Direction(); + + dirV.Normalize(); + dirH = plane.Direction() ^ dirV; + dirH.Normalize(); + } + + Log("H [%f, %f, %f]", dirH.X(), dirH.Y(), dirH.Z()); + Log("V [%f, %f, %f]", dirV.X(), dirV.Y(), dirV.Z()); + + + // find extent + float dimH = -1000000; + float dimV = -1000000; + for (size_t i = 0; i < selected_pts.size(); ++i) + { + Point3m pp = plane.Projection(selected_pts[i]); + float distH = fabs(((pp - centerP) * dirH)); + float distV = fabs(((pp - centerP) * dirV)); + + if (distH > dimH) + dimH = distH; + if (distV > dimV) + dimV = distV; + } + float exScale = par.getFloat("extent"); + dimV = dimV * exScale; + dimH = dimH * exScale; + Log("extent on plane [%f, %f]", dimV, dimH); + + int vertNum = par.getInt("subdiv") + 1; + if (vertNum <= 1) vertNum = 2; + int numV, numH; + numV = numH = vertNum; + + // UV vector, just in case + float *UUs, *VVs; + UUs = new float[numH*numV]; + VVs = new float[numH*numV]; + + int vind = 0; + for (int ir = 0; ir < numV; ir++) + for (int ic = 0; ic < numH; ic++) + { + Point3m newP = (centerP + (dirV * -dimV) + (dirH * -dimH)); + newP = newP + (dirH * ic * (2.0 * dimH / (numH-1))) + (dirV * ir * (2.0 * dimV / (numV-1))); + tri::Allocator::AddVertex(m->cm, newP, plane.Direction()); + UUs[vind] = ic * (1.0 / (numH - 1)); + VVs[vind] = ir * (1.0 / (numV - 1)); + vind++; + } + + FaceGrid(m->cm, numH, numV); + + bool hasUV = par.getBool("hasuv"); + if (hasUV) + { + m->updateDataMask(MeshModel::MM_WEDGTEXCOORD); + + CMeshO::FaceIterator fi; + for (fi = m->cm.face.begin(); fi != m->cm.face.end(); ++fi) + { + for (int i = 0; i<3; ++i) + { + int vind = (*fi).V(i)->Index(); + (*fi).WT(i).U() = UUs[vind]; + (*fi).WT(i).V() = VVs[vind]; + } + } + } + delete[] UUs; // delete temporary UV storage + delete[] VVs; + + } break; + + case CR_RANDOM_SPHERE: + { + int pointNum = par.getInt("pointNum"); + int sphereGenTech = par.getEnum("sphereGenTech"); + math::MarsenneTwisterRNG rng; + m = md.addNewMesh("", this->filterName(ID(filter))); + m->cm.Clear(); + std::vector sampleVec; + + + switch(sphereGenTech) + { + case 0: // Montecarlo + { + for(int i=0;i(rng)); + } break; + case 1: // Poisson Disk + { + int oversamplingFactor =100; + if(pointNum <= 100) oversamplingFactor = 1000; + if(pointNum >= 10000) oversamplingFactor = 50; + if(pointNum >= 100000) oversamplingFactor = 20; + CMeshO tt; + tri::Allocator::AddVertices(tt,pointNum*oversamplingFactor); + for(CMeshO::VertexIterator vi=tt.vert.begin();vi!=tt.vert.end();++vi) + vi->P()=math::GeneratePointOnUnitSphereUniform(rng); + tri::UpdateBounding::Box(tt); + + const float SphereArea = 4*M_PI; + float poissonRadius = 2.0*sqrt((SphereArea / float(pointNum*2))/M_PI); + + std::vector sampleVec; + tri::TrivialSampler pdSampler(sampleVec); + tri::SurfaceSampling >::PoissonDiskParam pp; + tri::SurfaceSampling >::PoissonDiskPruning(pdSampler, tt, poissonRadius, pp); + } break; + case 2: // Disco Ball + GenNormal::DiscoBall(pointNum,sampleVec); + break; + case 3: // Recursive Oct + GenNormal::RecursiveOctahedron(pointNum,sampleVec); + break; + case 4: // Fibonacci + GenNormal::Fibonacci(pointNum,sampleVec); + break; + } + for(size_t i=0;i::AddVertex(m->cm,sampleVec[i],sampleVec[i]); + } break; + + case CR_SPHERE_CAP: + { + int rec = par.getInt("subdiv"); + const float angleDeg = par.getFloat("angle"); + m = md.addNewMesh("", this->filterName(ID(filter))); + m->updateDataMask(MeshModel::MM_FACEFACETOPO); + tri::UpdateTopology::FaceFace(m->cm); + tri::SphericalCap(m->cm,math::ToRad(angleDeg),rec); + } break; + + case CR_SPHERE: + { + int rec = par.getInt("subdiv"); + float radius = par.getFloat("radius"); + m = md.addNewMesh("", this->filterName(ID(filter))); + m->cm.face.EnableFFAdjacency(); + m->updateDataMask(MeshModel::MM_FACEFACETOPO); + assert(tri::HasPerVertexTexCoord(m->cm) == false); + tri::Sphere(m->cm,rec); + tri::UpdatePosition::Scale(m->cm,radius); + } break; + + case CR_BOX: + { + float sz=par.getFloat("size"); + Box3m b(Point3m(1,1,1)*(-sz/2),Point3m(1,1,1)*(sz/2)); + m = md.addNewMesh("", this->filterName(ID(filter))); + tri::Box(m->cm,b); + m->updateDataMask(MeshModel::MM_POLYGONAL); + } break; + + case CR_CONE: + { + float r0 = par.getFloat("r0"); + float r1 = par.getFloat("r1"); + float h = par.getFloat("h"); + int subdiv = par.getInt("subdiv"); + m = md.addNewMesh("", this->filterName(ID(filter))); + tri::Cone(m->cm, r0, r1, h, subdiv); + } break; + + }//CASE FILTER + + tri::UpdateBounding::Box(m->cm); + tri::UpdateNormal::PerVertexNormalizedPerFaceNormalized(m->cm); + return true; +} + + MeshFilterInterface::FilterClass FilterCreate::getClass(QAction *a) +{ + switch(ID(a)) + { + case CR_BOX: + case CR_TETRAHEDRON: + case CR_ICOSAHEDRON: + case CR_DODECAHEDRON: + case CR_SPHERE: + case CR_SPHERE_CAP: + case CR_ANNULUS: + case CR_RANDOM_SPHERE: + case CR_OCTAHEDRON: + case CR_CONE: + case CR_TORUS: + case CR_FITPLANE: + return MeshFilterInterface::MeshCreation; + break; + default: + assert(0); + return MeshFilterInterface::Generic; + } +} + +QString FilterCreate::filterScriptFunctionName( FilterIDType filterID ) + { + switch(filterID) + { + case CR_BOX : return QString("box"); + case CR_ANNULUS : return QString("annulus"); + case CR_SPHERE: return QString("sphere"); + case CR_SPHERE_CAP: return QString("spherecap"); + case CR_RANDOM_SPHERE: return QString("randomsphere"); + case CR_ICOSAHEDRON: return QString("icosahedron"); + case CR_DODECAHEDRON: return QString("dodecahedron"); + case CR_OCTAHEDRON: return QString("octahedron"); + case CR_TETRAHEDRON: return QString("tetrahedron"); + case CR_CONE: return QString("cone"); + case CR_TORUS: return QString("torus"); + case CR_FITPLANE: return QString("fitplane"); + default : assert(0); + } + } + + +MESHLAB_PLUGIN_NAME_EXPORTER(FilterCreate) diff --git a/src/meshlabplugins/filter_create/filter_create.h b/src/meshlabplugins/filter_create/filter_create.h index 28a7b87b1..97982dc79 100644 --- a/src/meshlabplugins/filter_create/filter_create.h +++ b/src/meshlabplugins/filter_create/filter_create.h @@ -44,7 +44,7 @@ class FilterCreate : public QObject, public MeshFilterInterface CR_OCTAHEDRON, CR_CONE, CR_TORUS, - + CR_FITPLANE, } ; FilterCreate();