1 /****************************************************************************
2 * MeshLab o o *
3 * A versatile mesh processing toolbox o o *
4 * _ O _ *
5 * Copyright(C) 2005 \/)\/ *
6 * Visual Computing Lab /\/| *
7 * ISTI - Italian National Research Council | *
8 * \ *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 * *
22 ****************************************************************************/
23 #include "filter_create.h"
24 #include <vcg/complex/algorithms/create/platonic.h>
25 #include <vcg/complex/algorithms/point_sampling.h>
26 #include <vcg/complex/algorithms/smooth.h>
27 #include <vcg/math/gen_normal.h>
28
29 using namespace vcg;
30 using namespace tri;
31
32 // Constructor usually performs only two simple tasks of filling the two lists
33 // - typeList: with all the possible id of the filtering actions
34 // - actionList with the corresponding actions. If you want to add icons to your filtering actions you can do here by construction the QActions accordingly
35
FilterCreate()36 FilterCreate::FilterCreate()
37 {
38 typeList << CR_BOX<< CR_ANNULUS << CR_SPHERE<< CR_SPHERE_CAP
39 << CR_RANDOM_SPHERE<< CR_ICOSAHEDRON<< CR_DODECAHEDRON
40 << CR_TETRAHEDRON<<CR_OCTAHEDRON<<CR_CONE<<CR_TORUS
41 << CR_FITPLANE;
42
43 foreach(FilterIDType tt , types())
44 actionList << new QAction(filterName(tt), this);
45 }
46
filterName(FilterIDType filterId) const47 QString FilterCreate::filterName(FilterIDType filterId) const
48 {
49 switch(filterId) {
50 case CR_BOX : return QString("Box/Cube");
51 case CR_ANNULUS : return QString("Annulus");
52 case CR_SPHERE: return QString("Sphere");
53 case CR_SPHERE_CAP: return QString("Sphere Cap");
54 case CR_RANDOM_SPHERE: return QString("Points on a Sphere");
55 case CR_ICOSAHEDRON: return QString("Icosahedron");
56 case CR_DODECAHEDRON: return QString("Dodecahedron");
57 case CR_OCTAHEDRON: return QString("Octahedron");
58 case CR_TETRAHEDRON: return QString("Tetrahedron");
59 case CR_CONE: return QString("Cone");
60 case CR_TORUS: return QString("Torus");
61 case CR_FITPLANE: return QString("Fit a plane to selection");
62 default : assert(0);
63 }
64 return NULL;
65 }
66
67 // Info() must return the longer string describing each filtering action
68 // (this string is used in the About plugin dialog)
filterInfo(FilterIDType filterId) const69 QString FilterCreate::filterInfo(FilterIDType filterId) const
70 {
71 switch(filterId) {
72 case CR_BOX : return QString("Create a Box, Cube, Hexahedron. You can specify the side length.");
73 case CR_ANNULUS : return QString("Create an Annulus e.g. a flat region bounded by two concentric circles, or a holed disk.");
74 case CR_SPHERE: return QString("Create a Sphere, whose topology is obtained as regular subdivision of an icosahedron.");
75 case CR_SPHERE_CAP: return QString("Create a Sphere Cap, or spherical dome, subtended by a cone of given angle");
76 case CR_RANDOM_SPHERE: return QString("Create a spherical point cloud, it can be random or regularly distributed.");
77 case CR_ICOSAHEDRON: return QString("Create an Icosahedron");
78 case CR_DODECAHEDRON: return QString("Create an Dodecahedron");
79 case CR_OCTAHEDRON: return QString("Create an Octahedron");
80 case CR_TETRAHEDRON: return QString("Create a Tetrahedron");
81 case CR_CONE: return QString("Create a Cone");
82 case CR_TORUS: return QString("Create a Torus");
83 case CR_FITPLANE: return QString("Create a quad on the plane fitting the selection");
84 default : assert(0);
85 }
86 return NULL;
87 }
88
89 // This function define the needed parameters for each filter. Return true if the filter has some parameters
90 // it is called every time, so you can set the default value of parameters according to the mesh
91 // For each parameter you need to define,
92 // - the name of the parameter,
93 // - the string shown in the dialog
94 // - the default value
95 // - a possibly long string describing the meaning of that parameter (shown as a popup help in the dialog)
initParameterSet(QAction * action,MeshModel &,RichParameterSet & parlst)96 void FilterCreate::initParameterSet(QAction *action, MeshModel & /*m*/, RichParameterSet & parlst)
97 {
98 switch(ID(action)) {
99
100 case CR_SPHERE :
101 parlst.addParam(new RichFloat("radius",1,"Radius","Radius of the sphere"));
102 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>"
103 "Admitted values are in the range 0 (an icosahedron) to 8 (a 1.3 MegaTris approximation of a sphere)"));
104 break;
105
106 case CR_SPHERE_CAP :
107 parlst.addParam(new RichFloat("angle",60,"Angle","Angle of the cone subtending the cap. It must be < 180"));
108 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>"
109 "Admitted values are in the range 0 (an icosahedron) to 8 (a 1.3 MegaTris approximation of a sphere)"));
110 break;
111 case CR_ANNULUS :
112 parlst.addParam(new RichFloat("internalRadius",0.5f,"Internal Radius","Internal Radius of the annulus"));
113 parlst.addParam(new RichFloat("externalRadius",1.0f,"External Radius","Externale Radius of the annulus"));
114 parlst.addParam(new RichInt("sides",32,"Sides","Number of the sides of the poligonal approximation of the annulus "));
115 break;
116 case CR_RANDOM_SPHERE :
117 parlst.addParam(new RichInt("pointNum",100,"Point Num","Number of points (approximate)."));
118 parlst.addParam(new RichEnum("sphereGenTech", 3,
119 QStringList() << "Montecarlo" << "Poisson Sampling" << "DiscoBall" << "Octahedron" << "Fibonacci",
120 tr("Generation Technique:"),
121 tr("Generation Technique:"
122 "<b>Montecarlo</b>: The points are randomly generated with an uniform distribution.<br>"
123 "<b>Poisson Disk</b>: The points are to follow a poisson disk distribution.<br>"
124 "<b>Disco Ball</b> Dave Rusin's disco ball algorithm for the regular placement of points on a sphere is used. <br>"
125 "<b>Recursive Octahedron</b> Points are generated on the vertex of a recursively subdivided octahedron <br>"
126 "<b>Fibonacci</b> . "
127 )));
128
129 break;
130 case CR_BOX :
131 parlst.addParam(new RichFloat("size",1,"Scale factor","Scales the new mesh"));
132 break;
133 case CR_CONE:
134 parlst.addParam(new RichFloat("r0",1,"Radius 1","Radius of the bottom circumference"));
135 parlst.addParam(new RichFloat("r1",2,"Radius 2","Radius of the top circumference"));
136 parlst.addParam(new RichFloat("h",3,"Height","Height of the Cone"));
137 parlst.addParam(new RichInt("subdiv",36,"Side","Number of sides of the polygonal approximation of the cone"));
138 break;
139
140 case CR_TORUS:
141 parlst.addParam(new RichFloat("hRadius",3,"Horizontal Radius","Radius of the whole horizontal ring of the torus"));
142 parlst.addParam(new RichFloat("vRadius",1,"Vertical Radius","Radius of the vertical section of the ring"));
143 parlst.addParam(new RichInt("hSubdiv",24,"Horizontal Subdivision","Subdivision step of the ring"));
144 parlst.addParam(new RichInt("vSubdiv",12,"Vertical Subdivision","Number of sides of the polygonal approximation of the torus section"));
145 break;
146
147 case CR_FITPLANE:
148 parlst.addParam(new RichFloat("extent", 1.0, "Extent (with respect to selection)", "How large is the plane, with respect to the size of the selection: 1.0 means as large as the selection, 1.1 means 10% larger thena the selection"));
149 parlst.addParam(new RichInt("subdiv", 3, "Plane XY subivisions", "Subdivision steps of plane borders"));
150 parlst.addParam(new RichBool("hasuv", false, "UV parametrized", "The created plane has an UV parametrization"));
151 parlst.addParam(new RichEnum("orientation", 0,
152 QStringList() << "quasi-Straight Fit" << "Best Fit" << "XZ Parallel" << "YZ Parallel" << "YX Parallel",
153 tr("Plane orientation"),
154 tr("Orientation:"
155 "<b>quasi-Straight Fit</b>: The fitting plane will be oriented (as much as possible) straight with the axeses.<br>"
156 "<b>Best Fit</b>: The fitting plane will be oriented and sized trying to best fit to the selected area.<br>"
157 "<b>-- Parallel</b>: The fitting plane will be oriented with a side parallel with the chosen plane. WARNING: do not use if the selection is exactly parallel to a plane.<br>"
158 )));
159 break;
160 default : return;
161 }
162 }
163
164 // The Real Core Function doing the actual mesh processing.
applyFilter(QAction * filter,MeshDocument & md,RichParameterSet & par,CallBackPos *)165 bool FilterCreate::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, CallBackPos * /*cb*/)
166 {
167 MeshModel *currM = md.mm();
168 MeshModel *m = nullptr;
169
170 switch(ID(filter))
171 {
172 case CR_TETRAHEDRON :
173 m = md.addNewMesh("", this->filterName(ID(filter)));
174 tri::Tetrahedron<CMeshO>(m->cm);
175 break;
176 case CR_ICOSAHEDRON:
177 m = md.addNewMesh("", this->filterName(ID(filter)));
178 tri::Icosahedron<CMeshO>(m->cm);
179 break;
180 case CR_DODECAHEDRON:
181 m = md.addNewMesh("", this->filterName(ID(filter)));
182 tri::Dodecahedron<CMeshO>(m->cm);
183 m->updateDataMask(MeshModel::MM_POLYGONAL);
184 break;
185 case CR_OCTAHEDRON:
186 m = md.addNewMesh("", this->filterName(ID(filter)));
187 tri::Octahedron<CMeshO>(m->cm);
188 break;
189 case CR_ANNULUS:
190 m = md.addNewMesh("", this->filterName(ID(filter)));
191 tri::Annulus<CMeshO>(m->cm,par.getFloat("internalRadius"), par.getFloat("externalRadius"), par.getInt("sides"));
192 break;
193 case CR_TORUS:
194 {
195 m = md.addNewMesh("", this->filterName(ID(filter)));
196 float hRadius=par.getFloat("hRadius");
197 float vRadius=par.getFloat("vRadius");
198 int hSubdiv=par.getInt("hSubdiv");
199 int vSubdiv=par.getInt("vSubdiv");
200 tri::Torus(m->cm,hRadius,vRadius,hSubdiv,vSubdiv);
201 }
202 break;
203
204 case CR_FITPLANE:
205 {
206 Box3m selBox; //boundingbox of the selected vertices
207 std::vector< Point3m > selected_pts; //copy of selected vertices, for plane fitting
208
209 if (currM == NULL)
210 {
211 errorMessage = "No mesh layer selected";
212 return false;
213 }
214
215 if (currM->cm.svn == 0 && currM->cm.sfn == 0) // if no selection, fail
216 {
217 errorMessage = "No selection";
218 return false;
219 }
220
221 m = md.addNewMesh("", "Fitted Plane");
222
223 if (currM->cm.svn == 0 || currM->cm.sfn != 0)
224 {
225 tri::UpdateSelection<CMeshO>::VertexClear(currM->cm);
226 tri::UpdateSelection<CMeshO>::VertexFromFaceLoose(currM->cm);
227 }
228
229 Point3m Naccum = Point3m(0.0, 0.0, 0.0);
230 for (CMeshO::VertexIterator vi = currM->cm.vert.begin(); vi != currM->cm.vert.end(); ++vi)
231 if (!(*vi).IsD() && (*vi).IsS())
232 {
233 Point3m p = (*vi).P();
234 selBox.Add(p);
235 selected_pts.push_back(p);
236 Naccum = Naccum + (*vi).N();
237 }
238 Log("Using %i vertices to build a fitting plane", int(selected_pts.size()));
239 Plane3m plane;
240 FitPlaneToPointSet(selected_pts, plane);
241 plane.Normalize();
242 // check if normal of the interpolated plane is coherent with average normal of the used points, otherwise, flip
243 // i do this because plane fitter does not take in account source noramls, and a fliped fit is terrible to see
244 Naccum = (Naccum / (CMeshO::ScalarType)selected_pts.size()).Normalize();
245 if ((plane.Direction() * Naccum) < 0.0)
246 plane.Set(-plane.Direction(), -plane.Offset());
247
248 float errorSum = 0;
249 for (size_t i = 0; i < selected_pts.size(); ++i)
250 errorSum += fabs(SignedDistancePlanePoint(plane, selected_pts[i]));
251 Log("Fitting Plane avg error is %f", errorSum / float(selected_pts.size()));
252 Log("Fitting Plane normal is [%f, %f, %f]", plane.Direction().X(), plane.Direction().Y(), plane.Direction().Z());
253 Log("Fitting Plane offset is %f", plane.Offset());
254
255 // find center of selection on plane
256 Point3m centerP;
257 for (size_t i = 0; i < selected_pts.size(); ++i)
258 {
259 centerP += plane.Projection(selected_pts[i]);
260 }
261 centerP /= selected_pts.size();
262 Log("center [%f, %f, %f]", centerP.X(), centerP.Y(), centerP.Z());
263
264 // find horizontal and vertical axis
265 Point3m dirH, dirV;
266
267 int orientation = par.getEnum("orientation");
268
269 if (orientation == 0)
270 {
271 if ((plane.Direction().X() <= plane.Direction().Y()) && (plane.Direction().X() <= plane.Direction().Z()))
272 dirH = Point3m(1.0, 0.0, 0.0) ^ plane.Direction();
273 else if ((plane.Direction().Y() <= plane.Direction().X()) && (plane.Direction().Y() <= plane.Direction().Z()))
274 dirH = Point3m(0.0, 1.0, 0.0) ^ plane.Direction();
275 else
276 dirH = Point3m(0.0, 0.0, 1.0) ^ plane.Direction();
277
278 dirH.Normalize();
279 dirV = dirH ^ plane.Direction();
280 dirV.Normalize();
281 }
282 else if (orientation == 1)
283 {
284 Matrix33m cov;
285 vector<Point3m> PtVec;
286 for (size_t i = 0; i < selected_pts.size(); ++i)
287 PtVec.push_back(plane.Projection(selected_pts[i]));
288
289 cov.Covariance(PtVec, centerP);
290 Matrix33f eigenvecMatrix;
291 Point3f eigenvecVector;
292 Eigen::Matrix3d em;
293 cov.ToEigenMatrix(em);
294 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(em);
295 Eigen::Vector3d c_val = eig.eigenvalues();
296 Eigen::Matrix3d c_vec = eig.eigenvectors();
297
298 eigenvecMatrix.FromEigenMatrix(c_vec);
299 eigenvecVector.FromEigenVector(c_val);
300
301 // max eigenvector is best horizontal axis, but is not guarantee is orthogonal to plane normal, so
302 // I use eigenvector ^ plane direction and assign it to vertical plane axis
303 if ((eigenvecVector[0]<=eigenvecVector[1]) && (eigenvecVector[0]<=eigenvecVector[2]))
304 dirV = Point3m(eigenvecMatrix[0][0], eigenvecMatrix[0][1], eigenvecMatrix[0][2]) ^ plane.Direction();
305 if ((eigenvecVector[1]<=eigenvecVector[0]) && (eigenvecVector[1]<=eigenvecVector[2]))
306 dirV = Point3m(eigenvecMatrix[1][0], eigenvecMatrix[1][1], eigenvecMatrix[1][2]) ^ plane.Direction();
307 else
308 dirV = Point3m(eigenvecMatrix[2][0], eigenvecMatrix[2][1], eigenvecMatrix[2][2]) ^ plane.Direction();
309
310 dirV.Normalize();
311 dirH = plane.Direction() ^ dirV;
312 dirH.Normalize();
313 }
314 else if (orientation == 2)
315 {
316 dirH = Point3m(0.0, 1.0, 0.0) ^ plane.Direction();
317 dirH.Normalize();
318 dirV = dirH ^ plane.Direction();
319 dirV.Normalize();
320
321 }
322 else if (orientation == 3)
323 {
324 dirH = Point3m(1.0, 0.0, 0.0) ^ plane.Direction();
325 dirH.Normalize();
326 dirV = dirH ^ plane.Direction();
327 dirV.Normalize();
328 }
329 else if (orientation == 4)
330 {
331
332 dirH = Point3m(0.0, 0.0, 1.0) ^ plane.Direction();
333 dirH.Normalize();
334 dirV = dirH ^ plane.Direction();
335 dirV.Normalize();
336 }
337
338 // hotfix for unlikely case where the fitting is perfecrly parallel to a plane
339 if (orientation >= 2 )
340 {
341 if (Point3m(0.0, 1.0, 0.0) * plane.Direction() == 1.0)
342 {
343 dirH = Point3m(1.0, 0.0, 0.0);
344 dirV = Point3m(0.0, 0.0, 1.0);
345 }
346 if (Point3m(0.0, 0.0, 1.0) * plane.Direction() == 1.0)
347 {
348 dirH = Point3m(1.0, 0.0, 0.0);
349 dirV = Point3m(0.0, 1.0, 0.0);
350 }
351 if (Point3m(1.0, 0.0, 0.0) * plane.Direction() == 1.0)
352 {
353 dirH = Point3m(0.0, 1.0, 0.0);
354 dirV = Point3m(0.0, 0.0, 1.0);
355 }
356 }
357
358 Log("H [%f, %f, %f]", dirH.X(), dirH.Y(), dirH.Z());
359 Log("V [%f, %f, %f]", dirV.X(), dirV.Y(), dirV.Z());
360
361
362 // find extent
363 float dimH = -1000000;
364 float dimV = -1000000;
365 for (size_t i = 0; i < selected_pts.size(); ++i)
366 {
367 Point3m pp = plane.Projection(selected_pts[i]);
368 float distH = fabs(((pp - centerP) * dirH));
369 float distV = fabs(((pp - centerP) * dirV));
370
371 if (distH > dimH)
372 dimH = distH;
373 if (distV > dimV)
374 dimV = distV;
375 }
376 float exScale = par.getFloat("extent");
377 dimV = dimV * exScale;
378 dimH = dimH * exScale;
379 Log("extent on plane [%f, %f]", dimV, dimH);
380
381 int vertNum = par.getInt("subdiv") + 1;
382 if (vertNum <= 1) vertNum = 2;
383 int numV, numH;
384 numV = numH = vertNum;
385
386 // UV vector, just in case
387 float *UUs, *VVs;
388 UUs = new float[numH*numV];
389 VVs = new float[numH*numV];
390
391 int vind = 0;
392 for (int ir = 0; ir < numV; ir++)
393 for (int ic = 0; ic < numH; ic++)
394 {
395 Point3m newP = (centerP + (dirV * -dimV) + (dirH * -dimH));
396 newP = newP + (dirH * ic * (2.0 * dimH / (numH-1))) + (dirV * ir * (2.0 * dimV / (numV-1)));
397 tri::Allocator<CMeshO>::AddVertex(m->cm, newP, plane.Direction());
398 UUs[vind] = ic * (1.0 / (numH - 1));
399 VVs[vind] = ir * (1.0 / (numV - 1));
400 vind++;
401 }
402
403 FaceGrid(m->cm, numH, numV);
404
405 bool hasUV = par.getBool("hasuv");
406 if (hasUV)
407 {
408 m->updateDataMask(MeshModel::MM_WEDGTEXCOORD);
409
410 CMeshO::FaceIterator fi;
411 for (fi = m->cm.face.begin(); fi != m->cm.face.end(); ++fi)
412 {
413 for (int i = 0; i<3; ++i)
414 {
415 int vind = (*fi).V(i)->Index();
416 (*fi).WT(i).U() = UUs[vind];
417 (*fi).WT(i).V() = VVs[vind];
418 }
419 }
420 }
421 delete[] UUs; // delete temporary UV storage
422 delete[] VVs;
423
424 } break;
425
426 case CR_RANDOM_SPHERE:
427 {
428 int pointNum = par.getInt("pointNum");
429 int sphereGenTech = par.getEnum("sphereGenTech");
430 math::MarsenneTwisterRNG rng;
431 m = md.addNewMesh("", this->filterName(ID(filter)));
432 m->cm.Clear();
433 std::vector<Point3m> sampleVec;
434
435
436 switch(sphereGenTech)
437 {
438 case 0: // Montecarlo
439 {
440 for(int i=0;i<pointNum;++i)
441 sampleVec.push_back(math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(rng));
442 } break;
443 case 1: // Poisson Disk
444 {
445 int oversamplingFactor =100;
446 if(pointNum <= 100) oversamplingFactor = 1000;
447 if(pointNum >= 10000) oversamplingFactor = 50;
448 if(pointNum >= 100000) oversamplingFactor = 20;
449 CMeshO tt;
450 tri::Allocator<CMeshO>::AddVertices(tt,pointNum*oversamplingFactor);
451 for(CMeshO::VertexIterator vi=tt.vert.begin();vi!=tt.vert.end();++vi)
452 vi->P()=math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(rng);
453 tri::UpdateBounding<CMeshO>::Box(tt);
454
455 const float SphereArea = float(4 * M_PI);
456 float poissonRadius = 2.0*sqrt((SphereArea / float(pointNum*2))/M_PI);
457
458 std::vector<Point3m> sampleVec;
459 tri::TrivialSampler<CMeshO> pdSampler(sampleVec);
460 tri::SurfaceSampling<CMeshO, tri::TrivialSampler<CMeshO> >::PoissonDiskParam pp;
461 tri::SurfaceSampling<CMeshO,tri::TrivialSampler<CMeshO> >::PoissonDiskPruning(pdSampler, tt, poissonRadius, pp);
462 } break;
463 case 2: // Disco Ball
464 GenNormal<CMeshO::ScalarType>::DiscoBall(pointNum,sampleVec);
465 break;
466 case 3: // Recursive Oct
467 GenNormal<CMeshO::ScalarType>::RecursiveOctahedron(pointNum,sampleVec);
468 break;
469 case 4: // Fibonacci
470 GenNormal<CMeshO::ScalarType>::Fibonacci(pointNum,sampleVec);
471 break;
472 }
473 for(size_t i=0;i<sampleVec.size();++i)
474 tri::Allocator<CMeshO>::AddVertex(m->cm,sampleVec[i],sampleVec[i]);
475 } break;
476
477 case CR_SPHERE_CAP:
478 {
479 int rec = par.getInt("subdiv");
480 const float angleDeg = par.getFloat("angle");
481 m = md.addNewMesh("", this->filterName(ID(filter)));
482 m->updateDataMask(MeshModel::MM_FACEFACETOPO);
483 tri::UpdateTopology<CMeshO>::FaceFace(m->cm);
484 tri::SphericalCap(m->cm,math::ToRad(angleDeg),rec);
485 } break;
486
487 case CR_SPHERE:
488 {
489 int rec = par.getInt("subdiv");
490 float radius = par.getFloat("radius");
491 m = md.addNewMesh("", this->filterName(ID(filter)));
492 m->cm.face.EnableFFAdjacency();
493 m->updateDataMask(MeshModel::MM_FACEFACETOPO);
494 assert(tri::HasPerVertexTexCoord(m->cm) == false);
495 tri::Sphere<CMeshO>(m->cm,rec);
496 tri::UpdatePosition<CMeshO>::Scale(m->cm,radius);
497 } break;
498
499 case CR_BOX:
500 {
501 float sz=par.getFloat("size");
502 Box3m b(Point3m(1,1,1)*(-sz/2),Point3m(1,1,1)*(sz/2));
503 m = md.addNewMesh("", this->filterName(ID(filter)));
504 tri::Box<CMeshO>(m->cm,b);
505 m->updateDataMask(MeshModel::MM_POLYGONAL);
506 } break;
507
508 case CR_CONE:
509 {
510 float r0 = par.getFloat("r0");
511 float r1 = par.getFloat("r1");
512 float h = par.getFloat("h");
513 int subdiv = par.getInt("subdiv");
514 m = md.addNewMesh("", this->filterName(ID(filter)));
515 tri::Cone<CMeshO>(m->cm, r0, r1, h, subdiv);
516 } break;
517
518 }//CASE FILTER
519
520 tri::UpdateBounding<CMeshO>::Box(m->cm);
521 tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFaceNormalized(m->cm);
522 return true;
523 }
524
getClass(QAction * a)525 MeshFilterInterface::FilterClass FilterCreate::getClass(QAction *a)
526 {
527 switch(ID(a))
528 {
529 case CR_BOX:
530 case CR_TETRAHEDRON:
531 case CR_ICOSAHEDRON:
532 case CR_DODECAHEDRON:
533 case CR_SPHERE:
534 case CR_SPHERE_CAP:
535 case CR_ANNULUS:
536 case CR_RANDOM_SPHERE:
537 case CR_OCTAHEDRON:
538 case CR_CONE:
539 case CR_TORUS:
540 case CR_FITPLANE:
541 return MeshFilterInterface::MeshCreation;
542 break;
543 default:
544 assert(0);
545 return MeshFilterInterface::Generic;
546 }
547 }
548
filterScriptFunctionName(FilterIDType filterID)549 QString FilterCreate::filterScriptFunctionName( FilterIDType filterID )
550 {
551 switch(filterID)
552 {
553 case CR_BOX : return QString("box");
554 case CR_ANNULUS : return QString("annulus");
555 case CR_SPHERE: return QString("sphere");
556 case CR_SPHERE_CAP: return QString("spherecap");
557 case CR_RANDOM_SPHERE: return QString("randomsphere");
558 case CR_ICOSAHEDRON: return QString("icosahedron");
559 case CR_DODECAHEDRON: return QString("dodecahedron");
560 case CR_OCTAHEDRON: return QString("octahedron");
561 case CR_TETRAHEDRON: return QString("tetrahedron");
562 case CR_CONE: return QString("cone");
563 case CR_TORUS: return QString("torus");
564 case CR_FITPLANE: return QString("fitplane");
565 default : assert(0);
566 }
567 return NULL;
568 }
569
570
571 MESHLAB_PLUGIN_NAME_EXPORTER(FilterCreate)
572