Goosefoot Mesher - CGAL
PolyhedronUtils.imp.h
1 // Copyright (C) 2012 Benjamin Kehlet
2 //
3 // This file is part of DOLFIN.
4 //
5 // DOLFIN is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // DOLFIN is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // First added: 2012-10-31
19 // Last changed: 2012-11-14
20 
21 #include <fstream>
22 #include <iostream>
23 #include <sstream>
24 #include <string>
25 #include <algorithm>
26 #include <cstdio>
27 
28 #include <CGAL/Polyhedron_incremental_builder_3.h>
29 #include <CGAL/Min_sphere_of_spheres_d.h>
30 #include <CGAL/Min_sphere_of_spheres_d_traits_3.h>
31 
32 #include <boost/tuple/tuple.hpp>
33 #include <boost/tuple/tuple_comparison.hpp>
34 #include <boost/tokenizer.hpp>
35 #include <boost/algorithm/string.hpp>
36 
37 #include <vtkSmartPointer.h>
38 #include <vtkXMLPolyDataReader.h>
39 #include <vtkPolyDataReader.h>
40 #include <vtkPolyData.h>
41 #include <vtkSTLWriter.h>
42 
43 #define MAX(a,b) (a < b ? b : a)
44 
45 static inline double strToDouble(const std::string& s, bool print=false)
46 {
47  std::istringstream is(s);
48  double val;
49  is >> val;
50 
51  if (print)
52  std::cout << "to_double " << s << " : " << val << std::endl;
53 
54  return val;
55 }
56 
57 template <class HDS>
58 class BuildFromSTL : public CGAL::Modifier_base<HDS>
59 {
60  bool _error;
61 public:
62  BuildFromSTL(std::string filename) : _filename(filename){}
63  bool error() { return _error; }
64  void operator()(HDS& hds)
65  {
66  //std::cout << "Reading surface from " << _filename << std::endl;
67 
68  CGAL::Polyhedron_incremental_builder_3<HDS> builder( hds, true);
69  builder.begin_surface(100000, 100000);
70 
71  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
72 
73  std::ifstream file(_filename.c_str());
74  if (!file.is_open())
75  {
76  std::cerr << ("PolyhedronUtils.cpp"
77  "open .stl file to read 3D surface"
78  "Failed to open file") << std::endl;
79  }
80 
81  std::size_t num_vertices = 0;
82  std::map<boost::tuple<double, double, double>, std::size_t> vertex_map;
83  std::vector<std::vector<std::size_t> > facets;
84  std::string line;
85  const boost::char_separator<char> sep(" ");
86 
87  // Read the first line and trim away whitespaces
88  std::getline(file, line);
89  boost::algorithm::trim(line);
90 
91  if (line.substr(0, 5) != "solid")
92  {
93  std::cerr << ("PolyhedronUtils.cpp"
94  "open .stl file to read 3D surface"
95  "File does not start with \"solid\"") << std::endl;
96  }
97 
98  // TODO: Read name of solid
99 
100  std::getline(file, line);
101  boost::algorithm::trim(line);
102 
103  while (file.good())
104  {
105  //bool has_normal = false;
106  //Point normal;
107 
108  // Read the line "facet normal n1 n2 n3"
109  {
110  tokenizer tokens(line, sep);
111  tokenizer::iterator tok_iter = tokens.begin();
112 
113  if (*tok_iter != "facet")
114  std::cerr << ("PolyhedronUtils.cpp"
115  "open .stl file to read 3D surface"
116  "Expected keyword \"facet\"") << std::endl;
117  ++tok_iter;
118 
119  // Check if a normal different from zero is given
120  if (tok_iter != tokens.end())
121  {
122  //cout << "Expecting normal" << std::endl;
123 
124  if (*tok_iter != "normal")
125  std::cerr << ("PolyhedronUtils.cpp"
126  "open .stl file to read 3D surface"
127  "Expected keyword \"normal\"") << std::endl;
128  ++tok_iter;
129 
130  //cout << "Read line: " << line << std::endl;
131 
132  // for (std::size_t i = 0; i < 3; ++i)
133  // {
134  // normal[i] = strToDouble(*tok_iter);
135  // ++tok_iter;
136  // }
137 
138 
139  //cout << "Normal: " << normal << std::endl;
140  // if (normal.norm() > DOLFIN_EPS)
141  // has_normal = true;
142 
143  // if (tok_iter != tokens.end())
144  // std::cerr << ("PolyhedronUtils.cpp",
145  // "open .stl file to read 3D surface",
146  // "Expected end of line") << std::endl;
147  }
148  }
149 
150  // Read "outer loop" line
151  std::getline(file, line);
152  boost::algorithm::trim(line);
153 
154  if (line != "outer loop")
155  std::cerr << ("PolyhedronUtils.cpp"
156  "open .stl file to read 3D surface"
157  "Expected key word outer loop") << std::endl;
158 
159  std::vector<std::size_t> v_indices(3);
160 
161  // Read lines with vertices
162  for (std::size_t i = 0; i < 3; ++i)
163  {
164  std::getline(file, line);
165  boost::algorithm::trim(line);
166 
167  //cout << "read line: " << line << std::endl;
168 
169  tokenizer tokens(line, sep);
170  tokenizer::iterator tok_iter = tokens.begin();
171 
172  if (*tok_iter != "vertex")
173  {
174  std::cerr << ("PolyhedronUtils.cpp"
175  "open .stl file to read 3D surface"
176  "Expected key word vertex") << std::endl;
177  }
178  ++tok_iter;
179 
180  const double x = strToDouble(*tok_iter); ++tok_iter;
181  const double y = strToDouble(*tok_iter); ++tok_iter;
182  const double z = strToDouble(*tok_iter); ++tok_iter;
183 
184  boost::tuple<double, double, double> v(x, y, z);
185 
186  if (vertex_map.count(v) > 0)
187  v_indices[i] = vertex_map[v];
188  else
189  {
190  vertex_map[v] = num_vertices;
191  v_indices[i] = num_vertices;
192  //cout << "Adding vertex " << num_vertices << " : " << x << " " << y << " " << z << std::endl;
193  builder.add_vertex(Exact_Point(x, y, z));
194  //cout << "Done adding vertex" << std::endl;
195  num_vertices++;
196  }
197  }
198 
199  // TODO
200  // if (has_normal)
201  // {
202  // std::cout << "Has normal" << std::endl;
203  // }
204 
205  //cout << "Adding facet : " << v_indices[0] << " " << v_indices[1] << " " << v_indices[2] << std::endl;
206  builder.add_facet(v_indices.begin(), v_indices.end());
207  //facets.push_back(v_indices);
208 
209  // Read 'endloop' line
210  std::getline(file, line);
211  boost::algorithm::trim(line);
212  if (line != "endloop")
213  {
214  std::cerr << ("PolyhedronUtils.cpp"
215  "open .stl file to read 3D surface"
216  "Expected key word std::endloop") << std::endl;
217  }
218 
219  std::getline(file, line);
220  boost::algorithm::trim(line);
221  if (line != "endfacet")
222  {
223  std::cerr << ("PolyhedronUtils.cpp"
224  "open .stl file to read 3D surface"
225  "Expected key word endfacet") << std::endl;
226  }
227 
228  std::getline(file, line);
229  boost::algorithm::trim(line);
230 
231  if (line.substr(0, 5) != "facet")
232  break;
233  }
234 
235  // Read the 'endsolid' line
236  tokenizer tokens(line, sep);
237  tokenizer::iterator tok_iter = tokens.begin();
238 
239  if (*tok_iter != "endsolid")
240  {
241  std::cerr << ("PolyhedronUtils.cpp"
242  "open .stl file to read 3D surface"
243  "Expected key word endsolid") << std::endl;
244  }
245  ++tok_iter;
246 
247  // Add all the facets
248  //cout << "Inputting facets" << std::endl;
249  // for (std::vector<std::vector<std::size_t> >::iterator it = facets.begin();
250  // it != facets.end(); it++)
251  // {
252  // builder.add_facet(it->begin(), it->end());
253  // }
254 
255  builder.end_surface();
256 
257  _error = builder.error();
258 
259  // TODO: Check name of solid
260 
261  //std::cout << "Done reading surface" << std::endl;
262  }
263  const std::string _filename;
264 };
265 bool PolyhedronUtils::_compare_ending(std::string filename, const char* ending)
266 {
267  std::string suffix(ending);
268  if (filename.length() > suffix.length()) {
269  if (filename.compare(filename.length() - suffix.length(), suffix.length(), suffix) == 0)
270  return true;
271  }
272  return false;
273 }
274 
275 //-----------------------------------------------------------------------------
276 bool PolyhedronUtils::readSurfaceFile(std::string filename, Exact_polyhedron& p)
277 {
278  bool result = false;
279  if (_compare_ending(filename, ".stl"))
280  {
281  result = readSTLFile(filename, p);
282  }
283  else if(_compare_ending(filename, ".vtk"))
284  {
285  result = PolyhedronUtils::readVTKFile(filename, p);
286  }
287  else if(_compare_ending(filename, ".vtp"))
288  {
289  result = PolyhedronUtils::readVTKXMLFile(filename, p);
290  }
291  else if(_compare_ending(filename, ".off"))
292  {
293  std::ifstream fileinput(filename.c_str());
294  fileinput >> p;
295  return true;
296  }
297  else
298  {
299  std::cerr << ("PolyhedronUtils.cpp"
300  "open file to read 3D surface"
301  "Unknown file type") << std::endl;
302  }
303 
304  return result;
305 }
306 bool PolyhedronUtils::readVTKXMLFile(std::string filename, Exact_polyhedron& p)
307 {
308  vtkSmartPointer<vtkXMLPolyDataReader> reader =
309  vtkSmartPointer<vtkXMLPolyDataReader>::New();
310 
311  reader->SetFileName(filename.c_str());
312  reader->Update();
313 
314  char tmpnamebuffer[MAX(L_tmpnam, 128)] = "/tmp/.gssfmesher-XXXXXX.stl";
315 
316  vtkSmartPointer<vtkSTLWriter> writer =
317  vtkSmartPointer<vtkSTLWriter>::New();
318 
319 #if VTK_MAJOR_VERSION >= 6
320  FILE* f;
321  int fd;
322 
323  writer->WriteToOutputStringOn();
324  writer->SetInputData(reader->GetOutput());
325  writer->Write();
326 
327  if ((fd = mkstemp(tmpnamebuffer)) == -1 || !(f = fdopen(fd, "w"))) {
328  std::cerr << ("Could not create temporary file!") << std::endl;
329  exit(-9);
330  }
331 
332  fwrite(writer->GetOutputString(), sizeof(char), writer->GetOutputStringLength(), f);
333  fclose(f);
334 #else
335  writer->SetInput(reader->GetOutput());
336  if (!tmpnam(tmpnamebuffer)) {
337  std::cerr << ("Could not create temporary file!") << std::endl;
338  exit(-9);
339  }
340 
341  writer->SetFileName(tmpnamebuffer);
342  writer->Write();
343 #endif
344 
345  bool result = PolyhedronUtils::readSTLFile(tmpnamebuffer, p);
346  remove(tmpnamebuffer);
347 
348  return result;
349 }
350 
351 bool PolyhedronUtils::readVTKFile(std::string filename, Exact_polyhedron& p)
352 {
353  vtkSmartPointer<vtkPolyDataReader> reader =
354  vtkSmartPointer<vtkPolyDataReader>::New();
355 
356  reader->SetFileName(filename.c_str());
357  reader->Update();
358  std::string filenamestl = filename + ".stl";
359  const char *tmpnamebuffer = filenamestl.c_str();
360 
361  std::string tmpname(tmpnamebuffer);
362  vtkSmartPointer<vtkSTLWriter> writer =
363  vtkSmartPointer<vtkSTLWriter>::New();
364 
365  writer->SetFileName(tmpname.c_str());
366 #if VTK_MAJOR_VERSION >= 6
367  writer->SetInputData(reader->GetOutput());
368 #else
369  writer->SetInput(reader->GetOutput());
370 #endif
371  writer->Write();
372 
373  bool result = PolyhedronUtils::readSTLFile(tmpname, p);
374  remove(tmpnamebuffer);
375  return result;
376 }
377 //-----------------------------------------------------------------------------
378 bool PolyhedronUtils::readSTLFile(std::string filename, Exact_polyhedron& p)
379 {
380  BuildFromSTL<Exact_HalfedgeDS> stl_builder(filename);
381  p.delegate(stl_builder);
382  return !stl_builder.error();
383 }
384 //-----------------------------------------------------------------------------
385 CGAL::Bbox_3 PolyhedronUtils::getBoundingBox(Polyhedron& polyhedron)
386 {
387  Polyhedron::Vertex_iterator it = polyhedron.vertices_begin();
388 
389  // Initialize bounding box with the first point
390  Polyhedron::Point_3 p0 = it->point();
391  CGAL::Bbox_3 b(p0[0], p0[1], p0[2], p0[0], p0[1], p0[2]);
392  ++it;
393 
394  for (; it != polyhedron.vertices_end(); ++it)
395  {
396  Polyhedron::Point_3 p1 = it->point();
397  b = b + CGAL::Bbox_3(p1[0], p1[1], p1[2], p1[0], p1[1], p1[2]);
398  }
399 
400  return b;
401 }
402 //-----------------------------------------------------------------------------
403 double PolyhedronUtils::getBoundingSphereRadius(Polyhedron& polyhedron)
404 {
405  typedef CGAL::Min_sphere_of_spheres_d_traits_3<Polyhedron::Traits, double> Traits;
406  typedef Traits::Sphere Sphere;
407  typedef CGAL::Min_sphere_of_spheres_d<Traits> Min_sphere;
408 
409  std::vector<Sphere> s(polyhedron.size_of_vertices());
410 
411  for (Polyhedron::Vertex_iterator it=polyhedron.vertices_begin();
412  it != polyhedron.vertices_end(); ++it)
413  {
414  const Polyhedron::Point_3 p = it->point();
415  s.push_back(Sphere(p, 0.0));
416  }
417 
418  Min_sphere ms(s.begin(),s.end());
419 
420  assert(ms.is_valid());
421 
422  return CGAL::to_double(ms.radius());
423 }
424 //-----------------------------------------------------------------------------
425 template<typename Polyhedron>
426 static inline double get_edge_length(typename Polyhedron::Halfedge::Halfedge_handle halfedge)
427 {
428  return CGAL::to_double((halfedge->vertex()->point() -
429  halfedge->opposite()->vertex()->point()).squared_length());
430 }
431 //-----------------------------------------------------------------------------
432 template <typename Polyhedron>
433 static inline double get_triangle_area(typename Polyhedron::Facet_handle facet)
434 {
435  const typename Polyhedron::Halfedge_handle edge = facet->halfedge();
436  const typename Polyhedron::Point_3 a = edge->vertex()->point();
437  const typename Polyhedron::Point_3 b = edge->next()->vertex()->point();
438  const typename Polyhedron::Point_3 c = edge->next()->next()->vertex()->point();
439  return CGAL::to_double(CGAL::cross_product(b-a, c-a).squared_length());
440 }
441 //-----------------------------------------------------------------------------
442 template<typename Polyhedron>
443 static inline double get_min_edge_length(typename Polyhedron::Facet_handle facet)
444 {
445  typename Polyhedron::Facet::Halfedge_around_facet_circulator half_edge = facet->facet_begin();
446  double min_length = CGAL::to_double((half_edge->vertex()->point()
447  - half_edge->opposite()->vertex()->point()).squared_length());
448 
449  half_edge++;
450  min_length = std::min(min_length, get_edge_length<Polyhedron>(half_edge));
451 
452  half_edge++;
453  min_length = std::min(min_length, get_edge_length<Polyhedron>(half_edge));
454 
455  return min_length;
456 }
457 //-----------------------------------------------------------------------------
458 template<typename Polyhedron>
459 bool facet_is_degenerate(typename Polyhedron::Facet_handle facet, const double threshold)
460 {
461  // print_facet(facet);
462  // if ( get_min_edge_length(facet) < threshold || get_triangle_area(facet) < threshold )
463  // std::cout << "Is degenerate" << std::endl;
464  // else
465  // std::cout << "Not degenerate" << std::endl;
466 
467  return get_min_edge_length<Polyhedron>(facet) < threshold
468  || get_triangle_area<Polyhedron>(facet) < threshold;
469  //return get_min_edge_length(facet) < threshold;
470 }
471 //-----------------------------------------------------------------------------
472 template<typename Polyhedron>
473 static int number_of_degenerate_facets(Polyhedron& p, const double threshold)
474 {
475  int count = 0;
476  for (typename Polyhedron::Facet_iterator facet = p.facets_begin();
477  facet != p.facets_end(); facet++)
478  {
479  assert(facet->is_triangle());
480  if ( facet_is_degenerate<Polyhedron>(facet, threshold) )
481  count++;
482  }
483  return count;
484 }
485 //-----------------------------------------------------------------------------
486 template <typename Polyhedron>
487 static typename Polyhedron::Halfedge_handle get_longest_edge(typename Polyhedron::Facet_handle facet)
488 {
489  typename Polyhedron::Halfedge_handle edge = facet->halfedge();
490  double length = get_edge_length<Polyhedron>(edge);
491 
492  {
493  typename Polyhedron::Halfedge_handle e_tmp = edge->next();
494  if (get_edge_length<Polyhedron>(e_tmp) > length)
495  {
496  length = get_edge_length<Polyhedron>(e_tmp);
497  edge = e_tmp;
498  }
499  }
500 
501  {
502  typename Polyhedron::Halfedge_handle e_tmp = edge->next()->next();
503  if ( get_edge_length<Polyhedron>(e_tmp) > length )
504  {
505  length = get_edge_length<Polyhedron>(e_tmp);
506  edge = e_tmp;
507  }
508  }
509 
510  return edge;
511 }
512 //-----------------------------------------------------------------------------
513 template <typename Polyhedron>
514 double shortest_edge(Polyhedron& p)
515 {
516  double shortest = std::numeric_limits<double>::max();
517  for (typename Polyhedron::Halfedge_iterator halfedge = p.halfedges_begin();
518  halfedge != p.halfedges_end(); halfedge++)
519  {
520  const double length = get_edge_length<Polyhedron>(halfedge);
521  shortest = std::min(shortest, length);
522  }
523 
524  return shortest;
525 }
526 //-----------------------------------------------------------------------------
527 template <typename Polyhedron>
528 static void remove_edge(Polyhedron& p, typename Polyhedron::Halfedge_handle& edge)
529 {
530 
531  // // FIXME: Is it possible to do this in a smarter way than a linear scan
532  // for (Polyhedron::Facet_iterator facet = p.facets_begin();
533  // facet != p.facets_end(); facet++)
534  // {
535  // if ( facet_is_degenerate<Polyhedron>(facet, threshold) )
536  // {
537  // //print_facet(facet);
538 
539  // // Find a short edge
540  // Polyhedron::Halfedge::Halfedge_handle shortest_edge = facet->facet_begin();
541  // Polyhedron::Facet::Halfedge_around_facet_circulator current_edge = facet->facet_begin();
542  // double min_length = get_edge_length(current_edge);
543 
544  // for (int i = 0; i < 2; i++)
545  // {
546  // current_edge++;
547  // if (get_edge_length(current_edge) < min_length)
548  // {
549  // shortest_edge = current_edge;
550  // min_length = get_edge_length(current_edge);
551  // }
552  // }
553 
554  // Join small triangles with neighbor facets
555  edge = p.join_facet(edge->next());
556  p.join_facet(edge->opposite()->prev());
557 
558  // The joined facets are now quads
559  // Join the two close vertices
560  p.join_vertex(edge);
561 }
562 //-----------------------------------------------------------------------------
563 template <typename Polyhedron>
564 static void remove_short_edges(Polyhedron& p, const double threshold)
565 {
566  while (true)
567  {
568  bool removed = false;
569  for (typename Polyhedron::Halfedge_iterator halfedge = p.halfedges_begin();
570  halfedge != p.halfedges_end(); halfedge++)
571  {
572  if (get_edge_length<Polyhedron>(halfedge) < threshold)
573  {
574  remove_edge<Polyhedron>(p, halfedge);
575  removed = true;
576  break;
577  }
578  }
579 
580  if (!removed)
581  break;
582  }
583 }
584 //-----------------------------------------------------------------------------
585 template <typename Polyhedron>
586 static typename Polyhedron::Point_3 facet_midpoint(typename Polyhedron::Facet_handle facet)
587 {
588  typename Polyhedron::Point_3 p(CGAL::ORIGIN);
589 
590  typename Polyhedron::Facet::Halfedge_around_facet_circulator half_edge = facet->facet_begin();
591 
592  for (std::size_t i = 0; i < facet->facet_degree(); i++)
593  {
594  p = p + (half_edge->vertex()->point() - CGAL::ORIGIN);
595  half_edge++;
596  }
597 
598  p = CGAL::ORIGIN + (p - CGAL::ORIGIN)/static_cast<double>(facet->facet_degree());
599 
600  // std::cout << "Center coordinates computed: " << p << std::endl;
601 
602  // half_edge = facet->facet_begin();
603  // for (std::size_t i = 0; i < facet->facet_degree(); i++)
604  // {
605  // std::cout << "Distance to point << " << half_edge->vertex()->point() << " = " << (half_edge->vertex()->point() - p).squared_length() << std::endl;
606  // half_edge++;
607  // }
608 
609  return p;
610 }
611 //-----------------------------------------------------------------------------
612 template <typename Polyhedron>
613 static void remove_triangle(Polyhedron& p, typename Polyhedron::Facet_handle facet)
614 {
615  assert(facet->is_triangle());
616 
617  // std::cout << "Removing triangle" << std::endl;
618  // print_facet<Polyhedron>(facet);
619 
620  // Find the longest edge
621  typename Polyhedron::Halfedge_handle edge = get_longest_edge<Polyhedron>(facet);
622 
623  // std::cout << "Longest edge" << std::endl;
624  // print_halfedge<Polyhedron>(edge);
625 
626  // std::cout << "Opposite triangle" << std::endl;
627  // print_facet<Polyhedron>(edge->opposite()->facet());
628 
629  edge = p.join_facet(edge);
630  // std::cout << "Edge after join: " << std::endl;
631  // print_halfedge<Polyhedron>(edge);
632 
633  // std::cout << "Facet after join" << std::endl;
634  // print_facet<Polyhedron>(edge->facet());
635 
636  typename Polyhedron::Point_3 new_center = facet_midpoint<Polyhedron>(edge->facet());
637 
638  edge = p.create_center_vertex(edge);
639 
640  edge->vertex()->point() = new_center;
641 
642  // std::cout << "Center vertex: " << edge->vertex()->point() << std::endl;
643 
644  // for (std::size_t i=0; i < 4; i++)
645  // {
646  // print_facet<Polyhedron>(edge->facet());
647  // edge = edge->next()->opposite();
648  // }
649 }
650 //-----------------------------------------------------------------------------
651 template<typename Polyhedron>
652 static void remove_small_triangles(Polyhedron& p, const double threshold)
653 {
654  int n = number_of_degenerate_facets(p, threshold);
655 
656  while (n > 0)
657  {
658  for (typename Polyhedron::Facet_iterator facet = p.facets_begin();
659  facet != p.facets_end(); facet++)
660  {
661  assert(facet->is_triangle());
662 
663  if (get_triangle_area<Polyhedron>(facet) < threshold)
664  {
665  // std::cout << "Small triangle detected" << std::endl;
666  // print_facet<Polyhedron>(facet);
667  remove_triangle<Polyhedron>(p, facet);
668  n = number_of_degenerate_facets<Polyhedron>(p, threshold);
669  break;
670  }
671  }
672  }
673 }
674 //-----------------------------------------------------------------------------
675 void PolyhedronUtils::remove_degenerate_facets(Exact_polyhedron& p, const double threshold)
676 {
677  int degenerate_facets = number_of_degenerate_facets(p, threshold);
678 
679  std::cout << "Number of degenerate facets: " << degenerate_facets << std::endl;
680  // FIXME: Use has_degenerate_facets() when debugging is done
681  if (degenerate_facets > 0)
682  {
683  assert(p.is_pure_triangle());
684 
685  shortest_edge(p);
686 
687  std::cout << "Removing triangles with short edges" << std::endl;
688  remove_short_edges(p, threshold);
689 
690  std::cout << "Number of degenerate facets: " << number_of_degenerate_facets(p, threshold) << std::endl;
691 
692  std::cout << "Removing small triangles" << std::endl;
693  remove_small_triangles(p, threshold);
694 
695  std::cout << "Number of degenerate facets: " << number_of_degenerate_facets(p, threshold) << std::endl;
696 
697  // Removal of triangles should preserve the triangular structure of the polyhedron
698  assert(p.is_pure_triangle());
699  }
700 }
701 //-----------------------------------------------------------------------------
702 bool PolyhedronUtils::has_degenerate_facets(Exact_polyhedron& p, double threshold)
703 {
704  for (Exact_polyhedron::Facet_iterator facet = p.facets_begin();
705  facet != p.facets_end(); facet++)
706  {
707  assert(facet->is_triangle());
708 
709  if ( facet_is_degenerate<Exact_polyhedron>(facet, threshold) )
710  return true;
711  }
712  return false;
713 }
Definition: PolyhedronUtils.imp.h:58