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