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