1 /****************************************************************************
2 * MeshLab                                                           o o     *
3 * An extendible mesh processor                                    o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2005, 2006                                          \/)\/    *
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 
24 #include "cleanfilter.h"
25 #include "align_tools.h"
26 
27 #include <vcg/complex/algorithms/clean.h>
28 #include <vcg/complex/algorithms/create/platonic.h>
29 #include <vcg/complex/algorithms/stat.h>
30 #include <vcg/complex/algorithms/create/ball_pivoting.h>
31 #include <vcg/complex/algorithms/update/texture.h>
32 
33 using namespace std;
34 using namespace vcg;
35 
36 int SnapVertexBorder(CMeshO &m, float threshold,vcg::CallBackPos * cb);
37 
CleanFilter()38 CleanFilter::CleanFilter()
39 {
40   typeList
41 	<< FP_BALL_PIVOTING
42 	<< FP_REMOVE_WRT_Q
43 	<< FP_REMOVE_ISOLATED_COMPLEXITY
44 	<< FP_REMOVE_ISOLATED_DIAMETER
45 	<< FP_REMOVE_TVERTEX_FLIP
46 	<< FP_REMOVE_TVERTEX_COLLAPSE
47 	<< FP_SNAP_MISMATCHED_BORDER
48 	<< FP_REMOVE_DUPLICATE_FACE
49 	<< FP_REMOVE_FOLD_FACE
50 	<< FP_REMOVE_NON_MANIF_EDGE
51     << FP_REMOVE_NON_MANIF_EDGE_SPLIT
52 	<< FP_REMOVE_NON_MANIF_VERT
53 	<< FP_REMOVE_UNREFERENCED_VERTEX
54 	<< FP_REMOVE_DUPLICATED_VERTEX
55 	<< FP_REMOVE_FACE_ZERO_AREA
56 	<< FP_MERGE_CLOSE_VERTEX
57     << FP_MERGE_WEDGE_TEX
58 	<< FP_COMPACT_FACE
59 	<< FP_COMPACT_VERT;
60 
61   FilterIDType tt;
62   foreach(tt , types())
63         actionList << new QAction(filterName(tt), this);
64   AC(FP_SNAP_MISMATCHED_BORDER)->setShortcut(QKeySequence("ALT+`"));
65 }
66 
~CleanFilter()67 CleanFilter::~CleanFilter() {
68     for (int i = 0; i < actionList.count() ; i++ )
69         delete actionList.at(i);
70 }
71 
filterName(FilterIDType filter) const72 QString CleanFilter::filterName(FilterIDType filter) const
73 {
74   switch(filter)
75   {
76 	case FP_BALL_PIVOTING:                return QString("Surface Reconstruction: Ball Pivoting");
77 	case FP_REMOVE_WRT_Q:                 return QString("Remove Vertices wrt Quality");
78 	case FP_REMOVE_ISOLATED_DIAMETER:     return QString("Remove Isolated pieces (wrt Diameter)");
79 	case FP_REMOVE_ISOLATED_COMPLEXITY:   return QString("Remove Isolated pieces (wrt Face Num.)");
80 	case FP_REMOVE_TVERTEX_FLIP:          return QString("Remove T-Vertices by Edge Flip");
81 	case FP_REMOVE_TVERTEX_COLLAPSE:      return QString("Remove T-Vertices by Edge Collapse");
82 	case FP_SNAP_MISMATCHED_BORDER:       return QString("Snap Mismatched Borders");
83 	case FP_MERGE_CLOSE_VERTEX:           return QString("Merge Close Vertices");
84     case FP_MERGE_WEDGE_TEX:              return QString("Merge Wedge Texture Coord");
85 	case FP_REMOVE_DUPLICATE_FACE:        return QString("Remove Duplicate Faces");
86 	case FP_REMOVE_FOLD_FACE:             return QString("Remove Isolated Folded Faces by Edge Flip");
87 	case FP_REMOVE_NON_MANIF_EDGE:        return QString("Repair non Manifold Edges by removing faces");
88 	case FP_REMOVE_NON_MANIF_EDGE_SPLIT:  return QString("Repair non Manifold Edges by splitting vertices");
89 	case FP_REMOVE_NON_MANIF_VERT:        return QString("Repair non Manifold Vertices by splitting");
90 	case FP_REMOVE_UNREFERENCED_VERTEX:   return QString("Remove Unreferenced Vertices");
91 	case FP_REMOVE_DUPLICATED_VERTEX:     return QString("Remove Duplicate Vertices");
92 	case FP_REMOVE_FACE_ZERO_AREA:        return QString("Remove Zero Area Faces");
93 	case FP_COMPACT_VERT:                 return QString("Compact vertices");
94 	case FP_COMPACT_FACE:                 return QString("Compact faces");
95 	default: assert(0);
96   }
97   return QString("error!");
98 }
99 
filterInfo(FilterIDType filterId) const100  QString CleanFilter::filterInfo(FilterIDType filterId) const
101 {
102   switch(filterId)
103   {
104 	case FP_BALL_PIVOTING :	return QString("Given a point cloud with normals it reconstructs a surface using the <b>Ball Pivoting Algorithm</b>."
105                                                "Starting with a seed triangle, the BPA algorithm  pivots a ball of the given radius around the already formed edges"
106                                                "until it touches another point, forming another triangle. The process continues until all reachable edges have been tried."
107                                                "This surface reconstruction algorithm uses the existing points without creating new ones. Works better with uniformly sampled point clouds. "
108                                                "If needed first perform a poisson disk subsampling of the point cloud. <br>"
109                                                "Bernardini F., Mittleman J., Rushmeier H., Silva C., Taubin G.<br>"
110                                                "<b>The ball-pivoting algorithm for surface reconstruction.</b><br>"
111                                                "IEEE TVCG 1999");
112     case FP_REMOVE_ISOLATED_COMPLEXITY: return QString("Delete isolated connected components composed by a limited number of triangles");
113     case FP_REMOVE_ISOLATED_DIAMETER:   return QString("Delete isolated connected components whose diameter is smaller than the specified constant");
114     case FP_REMOVE_WRT_Q:               return QString("Delete all the vertices with a quality lower smaller than the specified constant");
115     case FP_REMOVE_TVERTEX_COLLAPSE :   return QString("Delete t-vertices from the mesh by collapsing the shortest of the incident edges");
116     case FP_REMOVE_TVERTEX_FLIP :       return QString("Delete t-vertices by flipping the opposite edge on the degenerate face if the triangulation quality improves");
117     case FP_SNAP_MISMATCHED_BORDER :    return QString("Try to snap together adjacent borders that are slightly mismatched.<br>"
118                                                        "This situation can happen on badly triangulated adjacent patches defined by high order surfaces.<br>"
119                                                        "For each border vertex the filter snap it onto the closest boundary edge only if it is closest of <i>edge_length*threshold</i>. When vertex is snapped the corresponding face is split and a new vertex is created.");
120     case FP_MERGE_CLOSE_VERTEX :        return QString("Merge together all the vertices that are nearer than the specified threshold. Like a unify duplicated vertices but with some tolerance.");
121     case FP_MERGE_WEDGE_TEX :           return QString("Merge together per-wedge texture coords that are very close. Used to correct apparent texture seams that can arise from numerical approximations when saving in ascii formats.");
122     case FP_REMOVE_DUPLICATE_FACE :     return QString("Delete all the duplicate faces. Two faces are considered equal if they are composed by the same set of vertices, regardless of the order of the vertices.");
123     case FP_REMOVE_FOLD_FACE :          return QString("Delete all the single folded faces. A face is considered folded if its normal is opposite to all the adjacent faces. It is removed by flipping it against the face f adjacent along the edge e such that the vertex opposite to e fall inside f");
124     case FP_REMOVE_NON_MANIF_EDGE :     return QString("For each non Manifold edge it iteratively deletes the smallest area face until it becomes 2-Manifold.");
125 	case FP_REMOVE_NON_MANIF_EDGE_SPLIT:return QString("Remove all non manifold edges splitting vertices. Each non manifold edges chain will become a border");
126     case FP_REMOVE_NON_MANIF_VERT :     return QString("Split non Manifold vertices until it becomes 2-Manifold.");
127 	case FP_REMOVE_UNREFERENCED_VERTEX: return QString("Check for every vertex on the mesh: if it is NOT referenced by a face, removes it");
128 	case FP_REMOVE_DUPLICATED_VERTEX:   return QString("Check for every vertex on the mesh: if there are two vertices with same coordinates they are merged into a single one.");
129 	case FP_REMOVE_FACE_ZERO_AREA:      return QString("Remove null faces (the one with area equal to zero)");
130     case FP_COMPACT_VERT:               return QString("Compact all the vertices that have been deleted and put them to the end of the vector");
131     case FP_COMPACT_FACE:               return QString("Compact all the faces that have been deleted and put them to the end of the vector");
132     default: assert(0);
133   }
134   return QString("error!");
135 }
136 
getClass(QAction * a)137  CleanFilter::FilterClass CleanFilter::getClass(QAction *a)
138 {
139 	switch(ID(a))
140 	{
141 		case FP_REMOVE_WRT_Q :
142 		case FP_REMOVE_ISOLATED_DIAMETER :
143 		case FP_REMOVE_ISOLATED_COMPLEXITY :
144 		case FP_REMOVE_TVERTEX_COLLAPSE :
145 		case FP_REMOVE_TVERTEX_FLIP :
146 		case FP_REMOVE_FOLD_FACE :
147 		case FP_MERGE_CLOSE_VERTEX :
148 		case FP_REMOVE_DUPLICATE_FACE:
149 		case FP_SNAP_MISMATCHED_BORDER:
150 		case FP_REMOVE_NON_MANIF_EDGE:
151 		case FP_REMOVE_NON_MANIF_EDGE_SPLIT:
152 		case FP_REMOVE_NON_MANIF_VERT:
153 		case FP_REMOVE_FACE_ZERO_AREA:
154 		case FP_REMOVE_UNREFERENCED_VERTEX:
155 		case FP_REMOVE_DUPLICATED_VERTEX:
156 		case FP_COMPACT_VERT:
157 		case FP_COMPACT_FACE:                 return MeshFilterInterface::Cleaning;
158 		case FP_BALL_PIVOTING: 	              return MeshFilterInterface::Remeshing;
159 		case FP_MERGE_WEDGE_TEX: 	          return MeshFilterInterface::FilterClass(MeshFilterInterface::Cleaning + MeshFilterInterface::Texture);
160 		default : assert(0);
161 	}
162 	return MeshFilterInterface::Generic;
163 }
164 
getRequirements(QAction * action)165 int CleanFilter::getRequirements(QAction *action)
166 {
167 	switch(ID(action))
168 	{
169 		case FP_COMPACT_FACE:
170 		case FP_COMPACT_VERT:
171 		case FP_REMOVE_WRT_Q:
172 		case FP_BALL_PIVOTING:                return MeshModel::MM_VERTMARK;
173 		case FP_REMOVE_ISOLATED_COMPLEXITY:
174 		case FP_REMOVE_ISOLATED_DIAMETER:     return MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEMARK;
175 		case FP_REMOVE_TVERTEX_COLLAPSE:      return MeshModel::MM_VERTMARK;
176 		case FP_REMOVE_TVERTEX_FLIP:          return MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTMARK;
177 		case FP_REMOVE_NON_MANIF_EDGE:        return MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTMARK;
178 		case FP_REMOVE_NON_MANIF_EDGE_SPLIT:  return MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTMARK;
179 		case FP_REMOVE_NON_MANIF_VERT:        return MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTMARK;
180 		case FP_SNAP_MISMATCHED_BORDER:       return MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTMARK| MeshModel::MM_FACEMARK;
181 		case FP_REMOVE_FOLD_FACE:             return MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTMARK;
182 		case FP_MERGE_CLOSE_VERTEX:
183 		case FP_REMOVE_DUPLICATE_FACE:        return MeshModel::MM_NONE;
184 		case FP_MERGE_WEDGE_TEX:              return MeshModel::MM_VERTFACETOPO | MeshModel::MM_WEDGTEXCOORD;
185 		case FP_REMOVE_UNREFERENCED_VERTEX:   return MeshModel::MM_NONE;
186 		case FP_REMOVE_DUPLICATED_VERTEX:     return MeshModel::MM_NONE;
187 		case FP_REMOVE_FACE_ZERO_AREA:        return MeshModel::MM_NONE;
188 		default: assert(0);
189 	}
190 	return 0;
191 }
192 
postCondition(QAction * action) const193 int CleanFilter::postCondition(QAction* action) const
194 {
195 	switch (ID(action))
196 	{
197 		case FP_BALL_PIVOTING:
198 		case FP_REMOVE_WRT_Q:
199 		case FP_REMOVE_ISOLATED_DIAMETER:
200 		case FP_REMOVE_ISOLATED_COMPLEXITY:
201 		case FP_REMOVE_TVERTEX_FLIP:
202 		case FP_REMOVE_TVERTEX_COLLAPSE:
203 		case FP_SNAP_MISMATCHED_BORDER:
204 		case FP_MERGE_CLOSE_VERTEX:
205 		case FP_MERGE_WEDGE_TEX:
206 		case FP_REMOVE_DUPLICATE_FACE:
207 		case FP_REMOVE_FOLD_FACE:
208 		case FP_REMOVE_NON_MANIF_EDGE:
209 		case FP_REMOVE_NON_MANIF_EDGE_SPLIT:
210 		case FP_REMOVE_NON_MANIF_VERT:
211 		case FP_REMOVE_UNREFERENCED_VERTEX:
212 		case FP_REMOVE_DUPLICATED_VERTEX:
213 		case FP_REMOVE_FACE_ZERO_AREA:        return MeshModel::MM_GEOMETRY_AND_TOPOLOGY_CHANGE;
214 		case FP_COMPACT_VERT:
215 		case FP_COMPACT_FACE:                 return MeshModel::MM_NONE; // only internal vector storage should change, nothing more
216 	}
217 	return MeshModel::MM_ALL;
218 }
219 
initParameterSet(QAction * action,MeshDocument & md,RichParameterSet & parlst)220 void CleanFilter::initParameterSet(QAction *action,MeshDocument &md, RichParameterSet & parlst)
221 {
222     pair<float,float> qualityRange;
223   switch(ID(action))
224   {
225     case FP_BALL_PIVOTING :
226           parlst.addParam(new RichAbsPerc("BallRadius",0.0f,0.0f,md.mm()->cm.bbox.Diag(),"Pivoting Ball radius (0 autoguess)","The radius of the ball pivoting (rolling) over the set of points. Gaps that are larger than the ball radius will not be filled; similarly the small pits that are smaller than the ball radius will be filled."));
227           parlst.addParam(new RichFloat("Clustering",20.0f,"Clustering radius (% of ball radius)","To avoid the creation of too small triangles, if a vertex is found too close to a previous one, it is clustered/merged with it."));
228           parlst.addParam(new RichFloat("CreaseThr", 90.0f,"Angle Threshold (degrees)","If we encounter a crease angle that is too large we should stop the ball rolling"));
229           parlst.addParam(new RichBool("DeleteFaces",false,"Delete initial set of faces","if true all the initial faces of the mesh are deleted and the whole surface is rebuilt from scratch. Otherwise the current faces are used as a starting point. Useful if you run the algorithm multiple times with an increasing ball radius."));
230           break;
231     case FP_REMOVE_ISOLATED_DIAMETER:
232           parlst.addParam(new RichAbsPerc("MinComponentDiag",md.mm()->cm.bbox.Diag()/10.0f,0.0f,md.mm()->cm.bbox.Diag(),"Enter max diameter of isolated pieces","Delete all the connected components (floating pieces) with a diameter smaller than the specified one"));
233 		  parlst.addParam(new RichBool("removeUnref", true, "Remove unfreferenced vertices", "if true, the unreferenced vertices remaining after the face deletion are removed."));
234           break;
235     case FP_REMOVE_ISOLATED_COMPLEXITY:
236           parlst.addParam(new RichInt("MinComponentSize",25,"Enter minimum conn. comp size:","Delete all the connected components (floating pieces) composed by a number of triangles smaller than the specified one"));
237 		  parlst.addParam(new RichBool("removeUnref", true, "Remove unfreferenced vertices", "if true, the unreferenced vertices remaining after the face deletion are removed."));
238           break;
239     case FP_REMOVE_WRT_Q:
240           qualityRange=tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(md.mm()->cm);
241           parlst.addParam(new RichAbsPerc("MaxQualityThr",(float)1.0, qualityRange.first, qualityRange.second,"Delete all vertices with quality under:"));
242           break;
243     case  FP_MERGE_CLOSE_VERTEX:
244           parlst.addParam(new RichAbsPerc("Threshold",md.mm()->cm.bbox.Diag()/10000.0f,0.0f,md.mm()->cm.bbox.Diag()/100.0f,"Merging distance","All the vertices that closer than this threshold are merged together. Use very small values, default values is 1/10000 of bounding box diagonal. "));
245           break;
246     case FP_MERGE_WEDGE_TEX :
247           parlst.addParam(new RichFloat("MergeThr",1.0f/10000.0f,"Merging Threshold","All the per-wedge texture coords that are on the same vertex and are distant less then the given threshold are merged together. It can be used to remove the fake texture seams that arise from error. Distance is in texture space (the default, 1e-4, corresponds to one texel on a 10kx10x texture) "));
248           break;
249     case FP_SNAP_MISMATCHED_BORDER:
250     parlst.addParam(new RichFloat("EdgeDistRatio",1/100.0f,"Edge Distance Ratio", "Collapse edge when the edge / distance ratio is greater than this value. E.g. for default value 1000 two straight border edges are collapsed if the central vertex dist from the straight line composed by the two edges less than a 1/1000 of the sum of the edges length. Larger values enforce that only vertices very close to the line are removed."));
251     parlst.addParam(new RichBool("UnifyVertices",true,"UnifyVertices","if true the snap vertices are weld together."));
252       break;
253     case FP_REMOVE_TVERTEX_COLLAPSE :
254     case FP_REMOVE_TVERTEX_FLIP :
255        parlst.addParam(new RichFloat(
256                "Threshold", 40, "Ratio", "Detects faces where the base/height ratio is lower than this value"));
257        parlst.addParam(new RichBool(
258                "Repeat", true, "Iterate until convergence", "Iterates the algorithm until it reaches convergence"));
259        break;
260     case FP_REMOVE_NON_MANIF_VERT :
261        parlst.addParam(new RichFloat("VertDispRatio", 0, "Vertex Displacement Ratio", "When a vertex is split it is moved along the average vector going from its position to the baricyenter of the FF connected faces sharing it"));
262        break;
263   default: break; // do not add any parameter for the other filters
264   }
265 }
266 
applyFilter(QAction * filter,MeshDocument & md,RichParameterSet & par,vcg::CallBackPos * cb)267 bool CleanFilter::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * cb)
268 {
269  MeshModel &m=*(md.mm());
270  switch(ID(filter))
271  {
272 	case FP_BALL_PIVOTING:
273 	{
274 		float Radius = par.getAbsPerc("BallRadius");
275 		float Clustering = par.getFloat("Clustering") / 100.0f;
276 		float CreaseThr = math::ToRad(par.getFloat("CreaseThr"));
277 		bool DeleteFaces = par.getBool("DeleteFaces");
278 		if(DeleteFaces)
279 		{
280 			m.cm.fn=0;
281 			m.cm.face.resize(0);
282 		}
283 		m.updateDataMask(MeshModel::MM_VERTFACETOPO);
284 		int startingFn=m.cm.fn;
285 		tri::BallPivoting<CMeshO> pivot(m.cm, Radius, Clustering, CreaseThr);
286 		// the main processing
287 		pivot.BuildMesh(cb);
288 		m.clearDataMask(MeshModel::MM_FACEFACETOPO);
289 		Log("Reconstructed surface. Added %i faces",m.cm.fn-startingFn);
290 	} break;
291 
292 	case FP_REMOVE_ISOLATED_DIAMETER:
293 	{
294 		float minCC= par.getAbsPerc("MinComponentDiag");
295 		std::pair<int,int> delInfo= tri::Clean<CMeshO>::RemoveSmallConnectedComponentsDiameter(m.cm,minCC);
296 		Log("Removed %i connected components out of %i", delInfo.second, delInfo.first);
297 		if (par.getBool("removeUnref"))
298 		{
299 			int delvert = tri::Clean<CMeshO>::RemoveUnreferencedVertex(m.cm);
300 			Log("Removed %d unreferenced vertices", delvert);
301 		}
302 		m.UpdateBoxAndNormals();
303     }break;
304 	case FP_REMOVE_ISOLATED_COMPLEXITY:
305 	{
306 		float minCC= par.getInt("MinComponentSize");
307 		std::pair<int,int> delInfo=tri::Clean<CMeshO>::RemoveSmallConnectedComponentsSize(m.cm,minCC);
308 		Log("Removed %i connected components out of %i", delInfo.second, delInfo.first);
309 		if (par.getBool("removeUnref"))
310 		{
311 			int delvert = tri::Clean<CMeshO>::RemoveUnreferencedVertex(m.cm);
312 			Log("Removed %d unreferenced vertices", delvert);
313 		}
314 		m.UpdateBoxAndNormals();
315 	} break;
316 
317 	case FP_REMOVE_WRT_Q:
318 	{
319 		int deletedFN=0;
320 		int deletedVN=0;
321 		float val=par.getAbsPerc("MaxQualityThr");
322 		CMeshO::VertexIterator vi;
323 		for(vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi)
324 			if(!(*vi).IsD() && (*vi).Q()<val)
325 			{
326 				tri::Allocator<CMeshO>::DeleteVertex(m.cm, *vi);
327 				deletedVN++;
328 			}
329 
330 		CMeshO::FaceIterator fi;
331 		for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD())
332 			if((*fi).V(0)->IsD() ||(*fi).V(1)->IsD() ||(*fi).V(2)->IsD() )
333 			{
334 				tri::Allocator<CMeshO>::DeleteFace(m.cm, *fi);
335 				deletedFN++;
336 			}
337 
338 		m.clearDataMask(MeshModel::MM_FACEFACETOPO);
339 		Log("Deleted %i vertices and %i faces with a quality lower than %f", deletedVN,deletedFN,val);
340 		m.UpdateBoxAndNormals();
341 	} break;
342 
343     case FP_REMOVE_TVERTEX_COLLAPSE :
344     {
345 		float threshold = par.getFloat("Threshold");
346 		bool repeat = par.getBool("Repeat");
347 		int total = tri::Clean<CMeshO>::RemoveTVertexByCollapse(m.cm, threshold, repeat);
348 		Log("Successfully removed %d t-vertices", total);
349     } break;
350 
351 	case FP_REMOVE_TVERTEX_FLIP :
352     {
353 		float threshold = par.getFloat("Threshold");
354 		bool repeat = par.getBool("Repeat");
355 		int total = tri::Clean<CMeshO>::RemoveTVertexByFlip(m.cm, threshold, repeat);
356 		Log("Successfully removed %d t-vertices", total);
357     } break;
358 
359 	case FP_MERGE_WEDGE_TEX :
360     {
361       float threshold = par.getFloat("MergeThr");
362       tri::UpdateTopology<CMeshO>::VertexFace(m.cm);
363       int total = tri::UpdateTexture<CMeshO>::WedgeTexMergeClose(m.cm, threshold);
364       Log("Successfully merged %d wedge tex coord distant less than %f", total,threshold);
365     } break;
366 
367     case FP_MERGE_CLOSE_VERTEX :
368 	{
369 		float threshold = par.getAbsPerc("Threshold");
370 		int total = tri::Clean<CMeshO>::MergeCloseVertex(m.cm, threshold);
371 		Log("Successfully merged %d vertices", total);
372 	} break;
373 
374 	case FP_REMOVE_DUPLICATE_FACE :
375 	{
376 		int total = tri::Clean<CMeshO>::RemoveDuplicateFace(m.cm);
377 		Log("Successfully deleted %d duplicated faces", total);
378 	} break;
379 
380 	case FP_REMOVE_FOLD_FACE:
381 	{
382 		m.updateDataMask(MeshModel::MM_FACECOLOR);
383 		int total = tri::Clean<CMeshO>::RemoveFaceFoldByFlip(m.cm);
384 		Log("Successfully flipped %d folded faces", total);
385 		m.UpdateBoxAndNormals();
386 	} break;
387 
388 	case FP_REMOVE_NON_MANIF_EDGE :
389 	{
390 		int total = tri::Clean<CMeshO>::RemoveNonManifoldFace(m.cm);
391 		Log("Successfully removed %d non-manifold faces", total);
392 		m.UpdateBoxAndNormals();
393 	} break;
394 
395 	 case FP_REMOVE_NON_MANIF_EDGE_SPLIT :
396 	 {
397 		 int total = tri::Clean<CMeshO>::SplitManifoldComponents(m.cm);
398 		 Log("Successfully split the mesh into %d edge manifold components", total);
399 		 m.UpdateBoxAndNormals();
400 	 } break;
401 
402 	case FP_REMOVE_NON_MANIF_VERT :
403 	{
404 		float threshold = par.getFloat("VertDispRatio");
405 		int total = tri::Clean<CMeshO>::SplitNonManifoldVertex(m.cm,threshold);
406 		Log("Successfully split %d non manifold vertices faces", total);
407 		m.UpdateBoxAndNormals();
408 	} break;
409 
410 	case FP_REMOVE_FACE_ZERO_AREA:
411 	{
412 		int nullFaces = tri::Clean<CMeshO>::RemoveFaceOutOfRangeArea(m.cm, 0);
413 		Log("Removed %d null faces", nullFaces);
414 		m.clearDataMask(MeshModel::MM_FACEFACETOPO);
415 	} break;
416 
417 	case FP_REMOVE_UNREFERENCED_VERTEX:
418 	{
419 		int delvert = tri::Clean<CMeshO>::RemoveUnreferencedVertex(m.cm);
420 		Log("Removed %d unreferenced vertices", delvert);
421 		if (delvert != 0) m.UpdateBoxAndNormals();
422 	} break;
423 
424 	case FP_REMOVE_DUPLICATED_VERTEX:
425 	{
426 		int delvert = tri::Clean<CMeshO>::RemoveDuplicateVertex(m.cm);
427 		Log("Removed %d duplicated vertices", delvert);
428 		if (delvert != 0) m.UpdateBoxAndNormals();
429 		m.clearDataMask(MeshModel::MM_FACEFACETOPO);
430 		m.clearDataMask(MeshModel::MM_VERTFACETOPO);
431 	} break;
432 
433 	case FP_SNAP_MISMATCHED_BORDER :
434 	{
435 		float threshold = par.getFloat("EdgeDistRatio");
436 		int total = SnapVertexBorder(m.cm, threshold,cb);
437 		Log("Successfully Split %d faces to snap", total);
438 		m.clearDataMask(MeshModel::MM_FACEFACETOPO);
439 		m.clearDataMask(MeshModel::MM_VERTFACETOPO);
440 	} break;
441 
442 	case FP_COMPACT_FACE :
443 	{
444 		vcg::tri::Allocator<CMeshO>::CompactFaceVector(m.cm);
445 	} break;
446 
447 	case FP_COMPACT_VERT :
448 	{
449 		vcg::tri::Allocator<CMeshO>::CompactVertexVector(m.cm);
450 	} break;
451 
452 	default : assert(0); // unknown filter;
453  }
454  return true;
455 }
456 
457 
SnapVertexBorder(CMeshO & m,float threshold,vcg::CallBackPos * cb)458 int SnapVertexBorder(CMeshO &m, float threshold, vcg::CallBackPos * cb)
459 {
460   tri::Allocator<CMeshO>::CompactEveryVector(m);
461 
462   tri::UpdateTopology<CMeshO>::FaceFace(m);
463   tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m);
464   tri::UpdateFlags<CMeshO>::VertexBorderFromFaceBorder(m);
465   tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFaceNormalized(m);
466   typedef GridStaticPtr<CMeshO::FaceType, CMeshO::ScalarType > MetroMeshFaceGrid;
467   MetroMeshFaceGrid   unifGridFace;
468   typedef tri::FaceTmark<CMeshO> MarkerFace;
469   MarkerFace markerFunctor(&m);
470   vcg::face::PointDistanceBaseFunctor<CMeshO::ScalarType> PDistFunct;
471   tri::UpdateFlags<CMeshO>::FaceClearV(m);
472   unifGridFace.Set(m.face.begin(),m.face.end());
473 
474   int faceFound;
475   int K = 20;
476   Point3m startPt;
477   float maxDist = m.bbox.Diag()/20;
478   vector<Point3m> splitVertVec;
479   vector<CMeshO::FacePointer> splitFaceVec;
480   vector<int> splitEdgeVec;
481   for(CMeshO::VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
482     if((*vi).IsB())
483       {
484         cb((int(tri::Index(m,*vi)) * 100) / m.vn,"Snapping vertices");
485         vector<CMeshO::FacePointer> faceVec;
486         vector<float> distVec;
487         vector<Point3m> pointVec;
488         Point3m u;
489         startPt = (*vi).P();
490         faceFound = unifGridFace.GetKClosest(PDistFunct,markerFunctor, K, startPt,maxDist, faceVec, distVec, pointVec);
491 
492         CMeshO::FacePointer bestFace = 0;
493         float localThr, bestDist = std::numeric_limits<float>::max();
494         Point3m bestPoint;
495         int bestEdge;
496 //        qDebug("Found %i face for vertex %i",faceFound,vi-m.vert.begin());
497         for(int i=0;i<faceFound;++i)
498         {
499           const float epsilonSmall = float(1e-5);
500 		  const float epsilonBig = float(1e-2);
501           CMeshO::FacePointer fp=faceVec[i];
502           InterpolationParameters(*fp,fp->cN(),pointVec[i],u);
503 //          qDebug(" face %i face for vertex %5.3f %5.3f %5.3f  dist %5.3f  (%c %c %c)",fp-&*m.face.begin(),u[0],u[1],u[2],distVec[i],IsBorder(*fp,0)?'b':' ',IsBorder(*fp,1)?'b':' ',IsBorder(*fp,2)?'b':' ');
504           for(int j=0;j<3;++j)
505           {
506             if(IsBorder(*fp,j) && !fp->IsV())
507             {
508               if( u[(j+0)%3] > epsilonBig &&
509                   u[(j+1)%3] > epsilonBig &&
510                   u[(j+2)%3] < epsilonSmall )
511               {
512                 if(distVec[i] < bestDist)
513                 {
514                   bestDist=distVec[i];
515                   //bestPoint=pointVec[i];
516                   bestPoint=(*vi).cP();
517                   bestFace=fp;
518                   bestEdge=j;
519                 }
520               }
521             }
522           }
523         } // end for each faceFound
524 
525         if(bestFace)
526         {
527           localThr = threshold*Distance(bestFace->P0(bestEdge),bestFace->P1(bestEdge));
528           if(bestDist < localThr && !bestFace->IsV())
529           {
530             bestFace->SetV();
531             (*vi).C()= Color4b::Blue;
532             //bestFace->C()=Color4b::LightBlue;
533             (*vi).SetS();
534             splitVertVec.push_back(bestPoint);
535             splitEdgeVec.push_back(bestEdge);
536             splitFaceVec.push_back(bestFace);
537           }
538         }
539       } // end for all border vertices
540   tri::Allocator<CMeshO>::PointerUpdater<CMeshO::FacePointer> pu;
541   CMeshO::VertexIterator firstVert = tri::Allocator<CMeshO>::AddVertices(m,splitVertVec.size());
542   CMeshO::FaceIterator firstface = tri::Allocator<CMeshO>::AddFaces(m,splitVertVec.size(),pu);
543 //
544 //             ^                           ^
545 //           /   \                       / | \          .
546 //         /       \                   /   |   \        .
547 //       /           \               /     |     \      .
548 //     /       fp      \           /       |       \    .
549 //   /                   \       /    fp   |   ff    \  .
550 //  V0 ------------------V2     V0 -------fv---------V2
551 //       i
552 
553   for(size_t i=0;i<splitVertVec.size();++i)
554     {
555       firstVert->P() = splitVertVec[i];
556       int eInd = splitEdgeVec[i];
557       CMeshO::FacePointer fp = splitFaceVec[i];
558       pu.Update(fp);
559       firstface->V(0) = &*firstVert;
560       firstface->V(1) = fp->V2(eInd);
561       firstface->V(2) = fp->V0(eInd);
562 //      firstface->C()=Color4b::LightBlue;
563 
564       fp->V0(eInd) = &*firstVert;
565 
566       ++firstface;
567       ++firstVert;
568     }
569   tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFaceNormalized(m);
570   return int(splitVertVec.size());
571   }
572 
573 
574 //
575 //             ^-----------------
576 //           / | \             /
577 //         /   |   \     fj   /
578 //       /  i+1|     \ij     /
579 //     /       |       \    /
580 //   /    fi   |   fadj  \ /
581 //  V0 --------V1--------V2
582 //       i
583 //
584 
DeleteCollinearBorder(CMeshO & m,float threshold)585 int  DeleteCollinearBorder(CMeshO &m, float threshold)
586 {
587     int total=0;
588     CMeshO::FaceIterator fi;
589     for(fi=m.face.begin();fi!=m.face.end();++fi)
590     {
591         if(!(*fi).IsD())
592         {
593             for(int i=0;i<3;++i)
594             {
595                 if(face::IsBorder(*fi,i) && !face::IsBorder(*fi,(i+1)%3))
596                 {
597                     CMeshO::VertexPointer V0= (*fi).V0(i);
598                     CMeshO::VertexPointer V1= (*fi).V1(i);
599                     CMeshO::VertexPointer V2=0;
600                     CMeshO::FacePointer fadj = (*fi).FFp((i+1)%3);
601                     int adjBordInd =  (*fi).FFi((i+1)%3);
602                     if(fadj->V1(adjBordInd) == V1)
603                         V2 = fadj->V2(adjBordInd);
604                     else
605                         continue; // non coerent face ordering.
606                     if(face::IsBorder(*fadj,(adjBordInd+1)%3))
607                     {
608                         // the colinearity test;
609                         Point3m pp;
610                         CMeshO::ScalarType dist;
611                         SegmentPointDistance(Segment3m(V0->cP(),V2->cP()),V1->cP(),pp,dist);
612                         if(dist* threshold <  Distance(V0->cP(),V2->cP()) )
613                         {
614                             (*fi).V1(i)=V2;
615                             if(face::IsBorder(*fadj,(adjBordInd+2)%3))
616                             {
617                                 (*fi).FFp((i+1)%3)=&*fi;
618                                 (*fi).FFi((i+1)%3)=(i+1)%3;
619                             }
620                             else
621                             {
622                                 CMeshO::FacePointer fj = fadj->FFp((adjBordInd+2)%3);
623                                 int ij = fadj->FFi((adjBordInd+2)%3);
624                                 (*fi).FFp((i+1)%3)= fj;
625                                 (*fi).FFi((i+1)%3)= ij;
626                                 fj->FFp(ij)=&*fi;
627                                 fj->FFi(ij)=(i+1)%3;
628                             }
629                             tri::Allocator<CMeshO>::DeleteFace(m,*fadj);
630                             total++;
631                         }
632                     }
633                 }
634             }
635         }
636     }
637     return total;
638 }
639 
640 MESHLAB_PLUGIN_NAME_EXPORTER(CleanFilter)
641