mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-20 03:16:10 +00:00
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
This commit is contained in:
parent
f8fb3783be
commit
f2db6eaa81
@ -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 <vcg/complex/algorithms/create/platonic.h>
|
||||
#include <vcg/complex/algorithms/point_sampling.h>
|
||||
#include <vcg/complex/algorithms/smooth.h>
|
||||
#include <vcg/math/gen_normal.h>
|
||||
|
||||
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<<CR_OCTAHEDRON<<CR_CONE<<CR_TORUS;
|
||||
|
||||
foreach(FilterIDType tt , types())
|
||||
actionList << new QAction(filterName(tt), this);
|
||||
}
|
||||
|
||||
QString FilterCreate::filterName(FilterIDType filterId) const
|
||||
{
|
||||
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("Sphere Cap");
|
||||
case CR_RANDOM_SPHERE: return QString("Points on a Sphere");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Info() must return the longer string describing each filtering action
|
||||
// (this string is used in the About plugin dialog)
|
||||
QString FilterCreate::filterInfo(FilterIDType filterId) const
|
||||
{
|
||||
switch(filterId) {
|
||||
case CR_BOX : return QString("Create a Box");
|
||||
case CR_ANNULUS : return QString("Create an Annulus, e.g. a flat region bounded by two concentric circles");
|
||||
case CR_SPHERE: return QString("Create a Sphere");
|
||||
case CR_SPHERE_CAP: return QString("Create a Sphere Cap subtended by a cone of given angle");
|
||||
case CR_RANDOM_SPHERE: return QString("Create a spherical point cloud, it can be random or regularly distributed.");
|
||||
case CR_ICOSAHEDRON: return QString("Create an Icosahedron");
|
||||
case CR_DODECAHEDRON: return QString("Create an Dodecahedron");
|
||||
case CR_OCTAHEDRON: return QString("Create an Octahedron");
|
||||
case CR_TETRAHEDRON: return QString("Create a Tetrahedron");
|
||||
case CR_CONE: return QString("Create a Cone");
|
||||
case CR_TORUS: return QString("Create a Torus");
|
||||
default : assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// This function define the needed parameters for each filter. Return true if the filter has some parameters
|
||||
// it is called every time, so you can set the default value of parameters according to the mesh
|
||||
// For each parmeter you need to define,
|
||||
// - the name of the parameter,
|
||||
// - the string shown in the dialog
|
||||
// - the default value
|
||||
// - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog)
|
||||
void FilterCreate::initParameterSet(QAction *action, MeshModel & /*m*/, RichParameterSet & parlst)
|
||||
{
|
||||
switch(ID(action)) {
|
||||
|
||||
case CR_SPHERE :
|
||||
parlst.addParam(new RichFloat("radius",1,"Radius","Radius of the sphere"));
|
||||
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).<br>"
|
||||
"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).<br>"
|
||||
"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:"
|
||||
"<b>Montecarlo</b>: The points are randomly generated with an uniform distribution.<br>"
|
||||
"<b>Poisson Disk</b>: The points are to follow a poisson disk distribution.<br>"
|
||||
"<b>Disco Ball</b> Dave Rusin's disco ball algorithm for the regular placement of points on a sphere is used. <br>"
|
||||
"<b>Recursive Octahedron</b> Points are genereate on the vertex of a recursively subdivided octahedron <br>"
|
||||
"<b>Fibonacci</b> . "
|
||||
)));
|
||||
|
||||
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<CMeshO>(m->cm);
|
||||
break;
|
||||
case CR_ICOSAHEDRON:
|
||||
tri::Icosahedron<CMeshO>(m->cm);
|
||||
break;
|
||||
case CR_DODECAHEDRON:
|
||||
tri::Dodecahedron<CMeshO>(m->cm);
|
||||
m->updateDataMask(MeshModel::MM_POLYGONAL);
|
||||
break;
|
||||
case CR_OCTAHEDRON:
|
||||
tri::Octahedron<CMeshO>(m->cm);
|
||||
break;
|
||||
case CR_ANNULUS:
|
||||
tri::Annulus<CMeshO>(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<Point3m> sampleVec;
|
||||
|
||||
|
||||
switch(sphereGenTech)
|
||||
{
|
||||
case 0: // Montecarlo
|
||||
{
|
||||
for(int i=0;i<pointNum;++i)
|
||||
sampleVec.push_back(math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(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<CMeshO>::AddVertices(tt,pointNum*oversamplingFactor);
|
||||
for(CMeshO::VertexIterator vi=tt.vert.begin();vi!=tt.vert.end();++vi)
|
||||
vi->P()=math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(rng);
|
||||
tri::UpdateBounding<CMeshO>::Box(tt);
|
||||
|
||||
const float SphereArea = 4*M_PI;
|
||||
float poissonRadius = 2.0*sqrt((SphereArea / float(pointNum*2))/M_PI);
|
||||
|
||||
std::vector<Point3m> sampleVec;
|
||||
tri::TrivialSampler<CMeshO> pdSampler(sampleVec);
|
||||
tri::SurfaceSampling<CMeshO, tri::TrivialSampler<CMeshO> >::PoissonDiskParam pp;
|
||||
tri::SurfaceSampling<CMeshO,tri::TrivialSampler<CMeshO> >::PoissonDiskPruning(pdSampler, tt, poissonRadius, pp);
|
||||
} break;
|
||||
case 2: // Disco Ball
|
||||
GenNormal<CMeshO::ScalarType>::DiscoBall(pointNum,sampleVec);
|
||||
break;
|
||||
case 3: // Recursive Oct
|
||||
GenNormal<CMeshO::ScalarType>::RecursiveOctahedron(pointNum,sampleVec);
|
||||
break;
|
||||
case 4: // Fibonacci
|
||||
GenNormal<CMeshO::ScalarType>::Fibonacci(pointNum,sampleVec);
|
||||
break;
|
||||
}
|
||||
for(size_t i=0;i<sampleVec.size();++i)
|
||||
tri::Allocator<CMeshO>::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<CMeshO>::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<CMeshO>(m->cm,rec);
|
||||
tri::UpdatePosition<CMeshO>::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<CMeshO>(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<CMeshO>(m->cm,r0,r1,h,subdiv);
|
||||
break;
|
||||
}
|
||||
tri::UpdateBounding<CMeshO>::Box(m->cm);
|
||||
tri::UpdateNormal<CMeshO>::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 <vcg/complex/algorithms/create/platonic.h>
|
||||
#include <vcg/complex/algorithms/point_sampling.h>
|
||||
#include <vcg/complex/algorithms/smooth.h>
|
||||
#include <vcg/math/gen_normal.h>
|
||||
|
||||
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<<CR_OCTAHEDRON<<CR_CONE<<CR_TORUS
|
||||
<< CR_FITPLANE;
|
||||
|
||||
foreach(FilterIDType tt , types())
|
||||
actionList << new QAction(filterName(tt), this);
|
||||
}
|
||||
|
||||
QString FilterCreate::filterName(FilterIDType filterId) const
|
||||
{
|
||||
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("Sphere Cap");
|
||||
case CR_RANDOM_SPHERE: return QString("Points on a Sphere");
|
||||
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("Fit a plane to selection");
|
||||
default : assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Info() must return the longer string describing each filtering action
|
||||
// (this string is used in the About plugin dialog)
|
||||
QString FilterCreate::filterInfo(FilterIDType filterId) const
|
||||
{
|
||||
switch(filterId) {
|
||||
case CR_BOX : return QString("Create a Box");
|
||||
case CR_ANNULUS : return QString("Create an Annulus, e.g. a flat region bounded by two concentric circles");
|
||||
case CR_SPHERE: return QString("Create a Sphere");
|
||||
case CR_SPHERE_CAP: return QString("Create a Sphere Cap subtended by a cone of given angle");
|
||||
case CR_RANDOM_SPHERE: return QString("Create a spherical point cloud, it can be random or regularly distributed.");
|
||||
case CR_ICOSAHEDRON: return QString("Create an Icosahedron");
|
||||
case CR_DODECAHEDRON: return QString("Create an Dodecahedron");
|
||||
case CR_OCTAHEDRON: return QString("Create an Octahedron");
|
||||
case CR_TETRAHEDRON: return QString("Create a Tetrahedron");
|
||||
case CR_CONE: return QString("Create a Cone");
|
||||
case CR_TORUS: return QString("Create a Torus");
|
||||
case CR_FITPLANE: return QString("Create a quad on the plane fitting the selection");
|
||||
default : assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// This function define the needed parameters for each filter. Return true if the filter has some parameters
|
||||
// it is called every time, so you can set the default value of parameters according to the mesh
|
||||
// For each parmeter you need to define,
|
||||
// - the name of the parameter,
|
||||
// - the string shown in the dialog
|
||||
// - the default value
|
||||
// - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog)
|
||||
void FilterCreate::initParameterSet(QAction *action, MeshModel & /*m*/, RichParameterSet & parlst)
|
||||
{
|
||||
switch(ID(action)) {
|
||||
|
||||
case CR_SPHERE :
|
||||
parlst.addParam(new RichFloat("radius",1,"Radius","Radius of the sphere"));
|
||||
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).<br>"
|
||||
"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).<br>"
|
||||
"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:"
|
||||
"<b>Montecarlo</b>: The points are randomly generated with an uniform distribution.<br>"
|
||||
"<b>Poisson Disk</b>: The points are to follow a poisson disk distribution.<br>"
|
||||
"<b>Disco Ball</b> Dave Rusin's disco ball algorithm for the regular placement of points on a sphere is used. <br>"
|
||||
"<b>Recursive Octahedron</b> Points are genereate on the vertex of a recursively subdivided octahedron <br>"
|
||||
"<b>Fibonacci</b> . "
|
||||
)));
|
||||
|
||||
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:"
|
||||
"<b>quasi-Straight Fit</b>: The fitting plane wil be placed (as much as possible) straight with the axis. Works better if the selected area is already almost straight<br>"
|
||||
"<b>Best Fit</b>: The fitting plane wil be placed and sized trying to best fit to the selected area.<br>"
|
||||
)));
|
||||
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<CMeshO>(m->cm);
|
||||
break;
|
||||
case CR_ICOSAHEDRON:
|
||||
m = md.addNewMesh("", this->filterName(ID(filter)));
|
||||
tri::Icosahedron<CMeshO>(m->cm);
|
||||
break;
|
||||
case CR_DODECAHEDRON:
|
||||
m = md.addNewMesh("", this->filterName(ID(filter)));
|
||||
tri::Dodecahedron<CMeshO>(m->cm);
|
||||
m->updateDataMask(MeshModel::MM_POLYGONAL);
|
||||
break;
|
||||
case CR_OCTAHEDRON:
|
||||
m = md.addNewMesh("", this->filterName(ID(filter)));
|
||||
tri::Octahedron<CMeshO>(m->cm);
|
||||
break;
|
||||
case CR_ANNULUS:
|
||||
m = md.addNewMesh("", this->filterName(ID(filter)));
|
||||
tri::Annulus<CMeshO>(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<CMeshO>::VertexClear(currM.cm);
|
||||
tri::UpdateSelection<CMeshO>::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<Point3m> 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<Eigen::Matrix3d> 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<CMeshO>::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<Point3m> sampleVec;
|
||||
|
||||
|
||||
switch(sphereGenTech)
|
||||
{
|
||||
case 0: // Montecarlo
|
||||
{
|
||||
for(int i=0;i<pointNum;++i)
|
||||
sampleVec.push_back(math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(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<CMeshO>::AddVertices(tt,pointNum*oversamplingFactor);
|
||||
for(CMeshO::VertexIterator vi=tt.vert.begin();vi!=tt.vert.end();++vi)
|
||||
vi->P()=math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(rng);
|
||||
tri::UpdateBounding<CMeshO>::Box(tt);
|
||||
|
||||
const float SphereArea = 4*M_PI;
|
||||
float poissonRadius = 2.0*sqrt((SphereArea / float(pointNum*2))/M_PI);
|
||||
|
||||
std::vector<Point3m> sampleVec;
|
||||
tri::TrivialSampler<CMeshO> pdSampler(sampleVec);
|
||||
tri::SurfaceSampling<CMeshO, tri::TrivialSampler<CMeshO> >::PoissonDiskParam pp;
|
||||
tri::SurfaceSampling<CMeshO,tri::TrivialSampler<CMeshO> >::PoissonDiskPruning(pdSampler, tt, poissonRadius, pp);
|
||||
} break;
|
||||
case 2: // Disco Ball
|
||||
GenNormal<CMeshO::ScalarType>::DiscoBall(pointNum,sampleVec);
|
||||
break;
|
||||
case 3: // Recursive Oct
|
||||
GenNormal<CMeshO::ScalarType>::RecursiveOctahedron(pointNum,sampleVec);
|
||||
break;
|
||||
case 4: // Fibonacci
|
||||
GenNormal<CMeshO::ScalarType>::Fibonacci(pointNum,sampleVec);
|
||||
break;
|
||||
}
|
||||
for(size_t i=0;i<sampleVec.size();++i)
|
||||
tri::Allocator<CMeshO>::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<CMeshO>::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<CMeshO>(m->cm,rec);
|
||||
tri::UpdatePosition<CMeshO>::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<CMeshO>(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<CMeshO>(m->cm, r0, r1, h, subdiv);
|
||||
} break;
|
||||
|
||||
}//CASE FILTER
|
||||
|
||||
tri::UpdateBounding<CMeshO>::Box(m->cm);
|
||||
tri::UpdateNormal<CMeshO>::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)
|
||||
|
||||
@ -44,7 +44,7 @@ class FilterCreate : public QObject, public MeshFilterInterface
|
||||
CR_OCTAHEDRON,
|
||||
CR_CONE,
|
||||
CR_TORUS,
|
||||
|
||||
CR_FITPLANE,
|
||||
} ;
|
||||
|
||||
FilterCreate();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user