KernelCollectionMesh.cpp
Go to the documentation of this file.
1 // (C) Copyright Renaud Detry 2007-2015.
2 // Distributed under the GNU General Public License and under the
3 // BSD 3-Clause License (See accompanying file LICENSE.txt).
4 
5 /** @file */
6 
7 #ifdef NUKLEI_HAS_PARTIAL_VIEW
8 
9 
10 #ifndef CGAL_EIGEN3_ENABLED
11 # define CGAL_EIGEN3_ENABLED 1
12 #endif
13 
14 #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
15 #include <CGAL/pca_estimate_normals.h>
16 #include <CGAL/jet_estimate_normals.h>
17 #include <CGAL/mst_orient_normals.h>
18 #include <CGAL/property_map.h>
19 #include <CGAL/IO/read_off_points.h>
20 #include <CGAL/IO/read_xyz_points.h>
21 #include <CGAL/IO/write_xyz_points.h>
22 
23 #include <CGAL/trace.h>
24 #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
25 #include <CGAL/Polyhedron_3.h>
26 #include <CGAL/IO/Polyhedron_iostream.h>
27 #include <CGAL/Surface_mesh_default_triangulation_3.h>
28 #include <CGAL/make_surface_mesh.h>
29 #include <CGAL/Implicit_surface_3.h>
30 #include <CGAL/IO/output_surface_facets_to_polyhedron.h>
31 #include <CGAL/Poisson_reconstruction_function.h>
32 #include <CGAL/Point_with_normal_3.h>
33 #include <CGAL/property_map.h>
34 #include <CGAL/IO/read_xyz_points.h>
35 #include <CGAL/compute_average_spacing.h>
36 
37 
38 #include <CGAL/Simple_cartesian.h>
39 #include <CGAL/AABB_tree.h>
40 #include <CGAL/AABB_traits.h>
41 #include <CGAL/Polyhedron_3.h>
42 #if CGAL_VERSION_NR >= 1040300000
43 #include <CGAL/AABB_face_graph_triangle_primitive.h>
44 #else
45 #include <CGAL/AABB_polyhedron_triangle_primitive.h>
46 #endif
47 #include <CGAL/property_map.h>
48 #include <CGAL/IO/read_off_points.h>
49 #include <CGAL/IO/read_xyz_points.h>
50 #include <CGAL/IO/write_xyz_points.h>
51 #include <CGAL/IO/Polyhedron_iostream.h>
52 
53 #include <CGAL/squared_distance_3.h>
54 #include <CGAL/Point_with_normal_3.h>
55 
56 #endif
57 
58 #include <vector>
59 #include <fstream>
60 #include <utility> // defines std::pair
61 #include <list>
62 #include <boost/filesystem.hpp>
63 #ifdef NUKLEI_HAS_PARTIAL_VIEW
64 #include <trimesh/TriMesh.h>
65 #endif
66 
68 #include <nuklei/ObservationIO.h>
69 
70 #ifdef NUKLEI_HAS_PARTIAL_VIEW
71 
72 
73 namespace meshing_types {
74  // Types
75  typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
76 
77  typedef Kernel::FT FT;
78  typedef Kernel::Point_3 Point;
79  typedef CGAL::Point_with_normal_3<Kernel> Point_with_normal;
80  typedef Kernel::Sphere_3 Sphere;
81  typedef std::vector<Point_with_normal> PointList;
82  typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
83  typedef CGAL::Poisson_reconstruction_function<Kernel> Poisson_reconstruction_function;
84  typedef CGAL::Surface_mesh_default_triangulation_3 STr;
85  typedef CGAL::Surface_mesh_complex_2_in_triangulation_3<STr> C2t3;
86  typedef CGAL::Implicit_surface_3<Kernel, Poisson_reconstruction_function> Surface_3;
87 
88  // Types
89 
90  typedef Kernel::Vector_3 Vector;
91 
92  // Point with normal vector stored in a std::pair.
93  typedef std::pair<Point, Vector> PointVectorPair;
94 }
95 
96 typedef CGAL::Polyhedron_3< CGAL::Simple_cartesian<double> > SimplePolyhedron;
97 
98 namespace view_types {
99  typedef CGAL::Simple_cartesian<double> K;
100  typedef K::Point_3 Point;
101  typedef K::Plane_3 Plane;
102  typedef K::Vector_3 Vector;
103  typedef K::Segment_3 Segment;
104  typedef K::Line_3 Line;
105  typedef SimplePolyhedron Polyhedron;
106 #if CGAL_VERSION_NR >= 1040300000
107  typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive;
108 #else
109  typedef CGAL::AABB_polyhedron_triangle_primitive<K,Polyhedron> Primitive;
110 #endif
111  typedef CGAL::AABB_traits<K, Primitive> Traits;
112  typedef CGAL::AABB_tree<Traits> Tree;
113  typedef Tree::Object_and_primitive_id Object_and_primitive_id;
114  typedef Tree::Primitive_id Primitive_id;
115 
116 
117  typedef CGAL::Point_with_normal_3<K> Point_with_normal;
118  typedef std::vector<Point_with_normal> PointList;
119 }
120 
121 #endif
122 
123 namespace nuklei {
124 
125 #ifdef NUKLEI_HAS_PARTIAL_VIEW
126  static void buildAABBTree(decoration<int>& deco, const int aabbKey,
127  SimplePolyhedron& poly)
128  {
129  using namespace view_types;
130 
131  boost::shared_ptr<Tree> tree(new Tree);
132 
133  // constructs AABB tree
134 #if CGAL_VERSION_NR >= 1040300000
135  tree.reset(new Tree(faces(poly).first, faces(poly).second, poly));
136 #else
137  tree.reset(new Tree(poly.facets_begin(),poly.facets_end()));
138 #endif
139  tree->accelerate_distance_queries();
140 
141  if (deco.has_key(aabbKey)) deco.erase(aabbKey);
142  deco.insert(aabbKey, tree);
143  }
144 #endif
145 
147  {
148  NUKLEI_TRACE_BEGIN();
149 #ifdef NUKLEI_HAS_PARTIAL_VIEW
150 
151  boost::shared_ptr<SimplePolyhedron> poly(new SimplePolyhedron);
152 
153  {
154  using namespace meshing_types;
155 
156  std::list<PointVectorPair> points;
157 
158  {
159  for (nuklei::KernelCollection::const_iterator i = as_const(*this).begin(); i != as_const(*this).end(); ++i)
160  {
161  nuklei::Vector3 v = i->getLoc();
162  Point p(v.X(),v.Y(),v.Z());
163  points.push_back(std::make_pair(p, Vector()));
164  }
165  }
166 
167  const int nb_neighbors = 16;
168 #if CGAL_VERSION_NR >= 1040800000
169  CGAL::jet_estimate_normals<CGAL::Sequential_tag>
170  (points.begin(), points.end(),
171  CGAL::First_of_pair_property_map<PointVectorPair>(),
172  CGAL::Second_of_pair_property_map<PointVectorPair>(),
173  nb_neighbors);
174 #else
175  CGAL::jet_estimate_normals(points.begin(), points.end(),
176  CGAL::First_of_pair_property_map<PointVectorPair>(),
177  CGAL::Second_of_pair_property_map<PointVectorPair>(),
178  nb_neighbors);
179 #endif
180  std::list<PointVectorPair>::iterator unoriented_points_begin =
181  CGAL::mst_orient_normals(points.begin(), points.end(),
182  CGAL::First_of_pair_property_map<PointVectorPair>(),
183  CGAL::Second_of_pair_property_map<PointVectorPair>(),
184  nb_neighbors);
185 
186  // Optional: delete points with an unoriented normal
187  // if you plan to call a reconstruction algorithm that expects oriented normals.
188  points.erase(unoriented_points_begin, points.end());
189 
190  // Optional: after erase(), use Scott Meyer's "swap trick" to trim excess capacity
191  std::list<PointVectorPair>(points).swap(points);
192 
193  if (0)
194  {
195  std::ofstream stream("/tmp/xyz_points_and_normals.xyz");
196  if (!stream ||
197  !CGAL::write_xyz_points_and_normals(stream,
198  points.begin(), points.end(),
199  CGAL::First_of_pair_property_map<PointVectorPair>(),
200  CGAL::Second_of_pair_property_map<PointVectorPair>()))
201  std::cerr << "Error writing temp file" << std::endl;
202  }
203 
204  // Poisson options
205  FT sm_angle = 20.0; // Min triangle angle in degrees.
206  FT sm_radius = 30; // Max triangle size w.r.t. point set average spacing.
207  FT sm_distance = 0.05; // Surface Approximation error w.r.t. point set average spacing.
208 
209  // Reads the point set file in points[].
210  // Note: read_xyz_points_and_normals() requires an iterator over points
211  // + property maps to access each point's position and normal.
212  // The position property map can be omitted here as we use iterators over Point_3 elements.
213  PointList pl;
214 
215  for (std::list<PointVectorPair>::const_iterator i = points.begin(); i != points.end(); ++i)
216  {
217  pl.push_back(Point_with_normal(i->first, i->second));
218  }
219 
220  // Creates implicit function from the read points using the default solver.
221  // Note: this method requires an iterator over points
222  // + property maps to access each point's position and normal.
223  // The position property map can be omitted here as we use iterators over Point_3 elements.
224  Poisson_reconstruction_function function(
225  pl.begin(), pl.end(),
226 #if CGAL_VERSION_NR < 1040300000
227  CGAL::make_normal_of_point_with_normal_pmap(pl.begin())
228 #elif CGAL_VERSION_NR < 1041200000
229  CGAL::make_normal_of_point_with_normal_pmap(PointList::value_type())
230 #else
231  CGAL::make_normal_of_point_with_normal_map(PointList::value_type())
232 #endif
233  );
234 
235  // Computes the Poisson indicator function f()
236  // at each vertex of the triangulation.
237  if ( ! function.compute_implicit_function() )
238  NUKLEI_THROW("Mesh construction error.");
239 
240  // Computes average spacing
241 #if CGAL_VERSION_NR >= 1040800000
242  FT average_spacing = CGAL::compute_average_spacing<CGAL::Sequential_tag>
243  (pl.begin(), pl.end(),
244  6 /* knn = 1 ring */);
245 #else
246  FT average_spacing = CGAL::compute_average_spacing(pl.begin(), pl.end(),
247  6 /* knn = 1 ring */);
248 #endif
249 
250  // Gets one point inside the implicit surface
251  // and computes implicit function bounding sphere radius.
252  Point inner_point = function.get_inner_point();
253  Sphere bsphere = function.bounding_sphere();
254  FT radius = std::sqrt(bsphere.squared_radius());
255 
256  // Defines the implicit surface: requires defining a
257  // conservative bounding sphere centered at inner point.
258  FT sm_sphere_radius = 5.0 * radius;
259  FT sm_dichotomy_error = sm_distance*average_spacing/1000.0; // Dichotomy error must be << sm_distance
260  Surface_3 surface(function,
261  Sphere(inner_point,sm_sphere_radius*sm_sphere_radius),
262  sm_dichotomy_error/sm_sphere_radius);
263 
264  // Defines surface mesh generation criteria
265  CGAL::Surface_mesh_default_criteria_3<STr> criteria(sm_angle, // Min triangle angle (degrees)
266  sm_radius*average_spacing, // Max triangle size
267  sm_distance*average_spacing); // Approximation error
268 
269  // Generates surface mesh with manifold option
270  STr tr; // 3D Delaunay triangulation for surface mesh generation
271  C2t3 c2t3(tr); // 2D complex in 3D Delaunay triangulation
272  CGAL::make_surface_mesh(c2t3, // reconstructed mesh
273  surface, // implicit surface
274  criteria, // meshing criteria
275  CGAL::Manifold_with_boundary_tag()); // require manifold mesh
276 
277  if(tr.number_of_vertices() == 0)
278  NUKLEI_THROW("Mesh construction error.");
279 
280  Polyhedron output_mesh;
281  CGAL::output_surface_facets_to_polyhedron(c2t3, output_mesh);
282  std::stringstream stream;
283  stream << output_mesh;
284  CGAL::scan_OFF(stream, *poly, true /* verbose */);
285  if(!stream || !poly->is_valid() || poly->empty())
286  {
287  NUKLEI_THROW("Cannot convert mesh.");
288  }
289  }
290 
291  if (deco_.has_key(MESH_KEY)) deco_.erase(MESH_KEY);
292  deco_.insert(MESH_KEY, poly);
293 
294  buildAABBTree(deco_, AABBTREE_KEY, *poly);
295 #else
296  NUKLEI_THROW("This function requires the partial view build of Nuklei. See http://renaud-detry.net/nuklei/group__install.html");
297 #endif
298  NUKLEI_TRACE_END();
299  }
300 
301  void KernelCollection::writeMeshToOffFile(const std::string& filename) const
302  {
303  NUKLEI_TRACE_BEGIN();
304 #ifdef NUKLEI_HAS_PARTIAL_VIEW
305  if (!deco_.has_key(MESH_KEY))
306  NUKLEI_THROW("Undefined mesh. Call buildMesh() first.");
307  std::ofstream out(filename.c_str());
308  out << *deco_.get< boost::shared_ptr<SimplePolyhedron> >(MESH_KEY);
309 #else
310  NUKLEI_THROW("This function requires the partial view build of Nuklei. See http://renaud-detry.net/nuklei/group__install.html");
311 #endif
312  NUKLEI_TRACE_END();
313  }
314 
315  void KernelCollection::writeMeshToPlyFile(const std::string& filename) const
316  {
317  NUKLEI_TRACE_BEGIN();
318 #ifdef NUKLEI_HAS_PARTIAL_VIEW
319  boost::filesystem::path offfile =
320  boost::filesystem::unique_path("/tmp/nuklei-%%%%-%%%%-%%%%-%%%%.off");
321  boost::filesystem::path plyfile =
322  boost::filesystem::unique_path("/tmp/nuklei-%%%%-%%%%-%%%%-%%%%.ply");
323  writeMeshToOffFile(offfile.native());
324  boost::shared_ptr<trimesh::TriMesh> mesh(trimesh::TriMesh::read(offfile.native()));
325  mesh->write(plyfile.native());
326  // copy_file complicates portability between C++03 and C++11
327  //boost::filesystem::copy_file(plyfile, filename);
328  nuklei::copy_file(plyfile.string(), filename);
329 #else
330  NUKLEI_THROW("This function requires the partial view build of Nuklei. See http://renaud-detry.net/nuklei/group__install.html");
331 #endif
332  NUKLEI_TRACE_END();
333  }
334 
335  void KernelCollection::readMeshFromOffFile(const std::string& filename)
336  {
337  NUKLEI_TRACE_BEGIN();
338 #ifdef NUKLEI_HAS_PARTIAL_VIEW
339  boost::shared_ptr<SimplePolyhedron> poly(new SimplePolyhedron);
340  std::ifstream in(filename.c_str());
341  CGAL::scan_OFF(in, *poly, true /* verbose */);
342  if(!in || !poly->is_valid() || poly->empty())
343  {
344  NUKLEI_THROW("Cannot read mesh.");
345  }
346  if (deco_.has_key(MESH_KEY)) deco_.erase(MESH_KEY);
347  deco_.insert(MESH_KEY, poly);
348  buildAABBTree(deco_, AABBTREE_KEY, *poly);
349 #else
350  NUKLEI_THROW("This function requires the partial view build of Nuklei. See http://renaud-detry.net/nuklei/group__install.html");
351 #endif
352  NUKLEI_TRACE_END();
353  }
354 
355  void KernelCollection::readMeshFromPlyFile(const std::string& filename)
356  {
357  NUKLEI_TRACE_BEGIN();
358 #ifdef NUKLEI_HAS_PARTIAL_VIEW
359  boost::filesystem::path offfile =
360  boost::filesystem::unique_path("/tmp/nuklei-%%%%-%%%%-%%%%-%%%%.off");
361  boost::filesystem::path plyfile =
362  boost::filesystem::unique_path("/tmp/nuklei-%%%%-%%%%-%%%%-%%%%.ply");
363  // copy_file complicates portability between C++03 and C++11
364  //boost::filesystem::copy_file(filename, plyfile);
365  nuklei::copy_file(filename, plyfile.string());
366  boost::shared_ptr<trimesh::TriMesh> mesh(trimesh::TriMesh::read(plyfile.native()));
367  mesh->write(offfile.native());
368  readMeshFromOffFile(offfile.native());
369 #else
370  NUKLEI_THROW("This function requires the partial view build of Nuklei. See http://renaud-detry.net/nuklei/group__install.html");
371 #endif
372  NUKLEI_TRACE_END();
373  }
374 
375 }
376 
Public namespace.
Definition: Color.cpp:9
Container::const_iterator const_iterator
KernelCollection iterator.
void buildMesh()
Builds a mesh from kernel positions. See Intermediary Results.
T const & as_const(T const &t)
Provides a const reference to an object.
Definition: Common.h:197
#define NUKLEI_THROW(x)
Throws an Error.
Definition: Common.h:94
© Copyright 2007-2013 Renaud Detry.
Distributed under the terms of the GNU General Public License (GPL).
(See accompanying file LICENSE.txt or copy at http://www.gnu.org/copyleft/gpl.html.)
Revised Sun Sep 13 2020 19:10:06.