1 #include <QElapsedTimer>
2 #include <QApplication>
3 #include <QAction>
4 #include <QList>
5 #include <QMainWindow>
6 #include <QObject>
7 
8 #include <fstream>
9 
10 #include "Scene_polygon_soup_item.h"
11 #include "Scene_surface_mesh_item.h"
12 #include "Scene_points_with_normal_item.h"
13 #include "SMesh_type.h"
14 #include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
15 #include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
16 
17 #include <CGAL/Real_timer.h>
18 
19 #include "ui_Surface_reconstruction_plugin.h"
20 #include "CGAL/Kernel_traits.h"
21 
22 typedef Kernel::Point_3 Point;
23 typedef Kernel::Vector_3 Vector;
24 
25 SMesh* advancing_front (const Point_set& points,
26                         double longest_edge,
27                         double radius_ratio_bound,
28                         double beta,
29                         bool structuring,
30                         double sampling);
31 
32 SMesh* poisson_reconstruct (Point_set& points,
33                             bool marching_tets,
34                             Kernel::FT sm_angle, // Min triangle angle (degrees).
35                             Kernel::FT sm_radius, // Max triangle size w.r.t. point set average spacing.
36                             Kernel::FT sm_distance, // Approximation error w.r.t. point set average spacing.
37                             bool conjugate_gradient,
38                             bool use_two_passes,
39                             bool do_not_fill_holes);
40 
41 void scale_space (const Point_set& points,
42                   std::vector<Scene_polygon_soup_item*>& items,
43                   bool jet_smoother,
44                   unsigned int iterations,
45                   unsigned int neighbors, unsigned int fitting, unsigned int monge,
46                   unsigned int neighborhood_size, unsigned int samples,
47                   bool advancing_front_mesher,
48                   bool generate_smooth,
49                   double longest_edge, double radius_ratio_bound, double beta_angle,
50                   bool separate_shells, bool force_manifold);
51 
52 SMesh* polygonal_reconstruct (const Point_set& points,
53                               double data_fitting,
54                               double data_coverage,
55                               double model_complexity,
56                               const QString& solver_name);
57 
58 class Polyhedron_demo_surface_reconstruction_plugin_dialog : public QDialog, private Ui::SurfaceReconstructionDialog
59 {
60   Q_OBJECT
61 public:
Polyhedron_demo_surface_reconstruction_plugin_dialog(QWidget * =nullptr)62   Polyhedron_demo_surface_reconstruction_plugin_dialog(QWidget* /*parent*/ = nullptr)
63   {
64     setupUi(this);
65 #if !defined(CGAL_USE_GLPK) && !defined(CGAL_USE_SCIP)
66     tabWidget->removeTab(3); // If no MIP solver is available, Polygonal method is disabled
67 #endif
68 
69 #if defined(CGAL_USE_SCIP)
70     m_solver->addItem("SCIP");
71 #endif
72 
73 #if defined(CGAL_USE_GLPK)
74     m_solver->addItem("GLPK");
75 #endif
76 
77   }
78 
method() const79   unsigned int method () const
80   {
81     return tabWidget->currentIndex();
82   }
83 
disable_poisson()84   void disable_poisson()
85   {
86     tabWidget->setTabEnabled(1, false);
87     tabWidget->setTabToolTip(1, QString("Poisson requires oriented normals, please estimate normals first"));
88   }
89 
disable_polygonal()90   void disable_polygonal()
91   {
92     tabWidget->setTabEnabled(3, false);
93     tabWidget->setTabToolTip(3, QString("Polygonal requires normals, please estimate normals first"));
94   }
95 
96 
disable_structuring()97   void disable_structuring()
98   {
99     m_use_structuring->setEnabled(false);
100     m_use_structuring->setToolTip(QString("Point Set Structuring requires detected planes, please detect shapes first"));
101   }
102 
103   // Advancing front
longest_edge() const104   double longest_edge () const { return m_longestEdge->value (); }
radius_ratio_bound() const105   double radius_ratio_bound () const { return m_radiusRatioBound->value (); }
beta_angle() const106   double beta_angle () const { return m_betaAngle->value (); }
structuring() const107   bool structuring() const { return m_use_structuring->isChecked(); }
sampling() const108   double sampling () const { return m_sampling->value (); }
109 
110   // Scale Space
scalespace_js() const111   bool scalespace_js() const { return m_scalespace_jet->isChecked(); }
iterations() const112   unsigned int iterations () const { return m_iterations->value (); }
neighbors() const113   unsigned int neighbors () const { return m_neighbors->value(); }
fitting() const114   unsigned int fitting () const { return m_fitting->value(); }
monge() const115   unsigned int monge () const { return m_monge->value(); }
neighborhood_size() const116   unsigned int neighborhood_size () const { return m_neighborhood_size->value (); }
samples() const117   unsigned int samples () const { return m_samples->value (); }
scalespace_af() const118   bool scalespace_af() const { return m_scalespace_af->isChecked(); }
generate_smoothed() const119   bool generate_smoothed () const { return m_genSmooth->isChecked (); }
longest_edge_2() const120   double longest_edge_2 () const { return m_longestEdge_2->value (); }
radius_ratio_bound_2() const121   double radius_ratio_bound_2 () const { return m_radiusRatioBound_2->value (); }
beta_angle_2() const122   double beta_angle_2 () const { return m_betaAngle_2->value (); }
separate_shells() const123   bool separate_shells () const { return m_genShells->isChecked (); }
force_manifold() const124   bool force_manifold () const { return m_forceManifold->isChecked (); }
125 
126   // Poisson
marching_tets() const127   bool marching_tets() const { return m_marching_tets->isChecked(); }
angle() const128   double angle () const { return m_inputAngle->value (); }
radius() const129   double radius () const { return m_inputRadius->value (); }
distance() const130   double distance () const { return m_inputDistance->value (); }
conjugate_gradient() const131   bool conjugate_gradient() const { return m_conjugate_gradient->isChecked(); }
two_passes() const132   bool two_passes () const { return m_inputTwoPasses->isChecked (); }
do_not_fill_holes() const133   bool do_not_fill_holes () const { return m_doNotFillHoles->isChecked (); }
134 
135   // Polygonal
data_fitting() const136   double data_fitting() const { return m_data_fitting->value(); }
data_coverage() const137   double data_coverage() const { return m_data_coverage->value(); }
model_complexity() const138   double model_complexity() const { return m_model_complexity->value(); }
solver_name() const139   QString solver_name() const { return m_solver->currentText(); }
140 
141 };
142 
143 class Polyhedron_demo_surface_reconstruction_plugin :
144   public QObject,
145   public CGAL::Three::Polyhedron_demo_plugin_helper
146 {
147   Q_OBJECT
148   Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
149 
150   Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
151   QAction* actionSurfaceReconstruction;
152 
153 public:
init(QMainWindow * mainWindow,CGAL::Three::Scene_interface * scene_interface,Messages_interface *)154   void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) {
155     scene = scene_interface;
156     mw = mainWindow;
157     actionSurfaceReconstruction = new QAction(tr("Surface Reconstruction"), mainWindow);
158     actionSurfaceReconstruction->setObjectName("actionSurfaceReconstruction");
159     autoConnectActions();
160 
161   }
162 
163   void advancing_front_reconstruction (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog);
164   void scale_space_reconstruction (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog);
165   void poisson_reconstruction (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog);
166   void polygonal_reconstruction (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog);
167 
168   //! Applicate for Point_sets with normals.
applicable(QAction *) const169   bool applicable(QAction*) const {
170     return qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()));
171   }
172 
actions() const173   QList<QAction*> actions() const {
174     return QList<QAction*>() << actionSurfaceReconstruction;
175   }
176 
177 private:
178 
179 public Q_SLOTS:
180   void on_actionSurfaceReconstruction_triggered();
181 }; // end class Polyhedron_surface_reconstruction_plugin
182 
183 
on_actionSurfaceReconstruction_triggered()184 void Polyhedron_demo_surface_reconstruction_plugin::on_actionSurfaceReconstruction_triggered()
185 {
186   const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
187 
188   Scene_points_with_normal_item* pts_item =
189     qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
190 
191   if(pts_item)
192   {
193     //generate the dialog box to set the options
194     Polyhedron_demo_surface_reconstruction_plugin_dialog dialog;
195     dialog.setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint|Qt::WindowCloseButtonHint);
196 
197     if (!pts_item->point_set()->has_normal_map())
198     {
199       dialog.disable_poisson();
200       dialog.disable_polygonal();
201     }
202     if (!pts_item->point_set()->has_property_map<int> ("shape"))
203     {
204       dialog.disable_structuring();
205       dialog.disable_polygonal();
206     }
207 
208     if(!dialog.exec())
209       return;
210 
211     CGAL::Real_timer t;
212     t.start();
213     unsigned int method = dialog.method ();
214     switch (method)
215     {
216       case 0:
217         advancing_front_reconstruction (dialog);
218         break;
219       case 1:
220         poisson_reconstruction (dialog);
221         break;
222       case 2:
223         scale_space_reconstruction (dialog);
224         break;
225       case 3:
226         polygonal_reconstruction (dialog);
227         break;
228       default:
229         std::cerr << "Error: unkown method." << std::endl;
230         return;
231     }
232     std::cerr << "Reconstruction achieved in " << t.time() << "s" << std::endl;
233 
234   }
235 }
236 
advancing_front_reconstruction(const Polyhedron_demo_surface_reconstruction_plugin_dialog & dialog)237 void Polyhedron_demo_surface_reconstruction_plugin::advancing_front_reconstruction
238 (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog)
239 {
240   const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
241 
242   Scene_points_with_normal_item* pts_item =
243     qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
244 
245   if(pts_item)
246   {
247     // Gets point set
248     Point_set* points = pts_item->point_set();
249 
250     // wait cursor
251     QApplication::setOverrideCursor(Qt::WaitCursor);
252 
253     std::cerr << "Advancing front reconstruction... ";
254 
255     // Reconstruct point set as a polyhedron
256     SMesh* mesh = advancing_front (*points,
257                                    dialog.longest_edge(),
258                                    dialog.radius_ratio_bound(),
259                                    CGAL_PI * dialog.beta_angle() / 180.,
260                                    dialog.structuring(),
261                                    dialog.sampling());
262     if (mesh)
263     {
264       // Add polyhedron to scene
265       Scene_surface_mesh_item* new_item = new Scene_surface_mesh_item(mesh);
266       new_item->setName(tr("%1 (advancing front)").arg(pts_item->name()));
267       new_item->setColor(Qt::darkGray);
268       new_item->invalidateOpenGLBuffers();
269       scene->addItem(new_item);
270 
271       // Hide point set
272       pts_item->setVisible(false);
273       scene->itemChanged(index);
274     }
275 
276     QApplication::restoreOverrideCursor();
277   }
278 }
279 
280 
scale_space_reconstruction(const Polyhedron_demo_surface_reconstruction_plugin_dialog & dialog)281 void Polyhedron_demo_surface_reconstruction_plugin::scale_space_reconstruction
282 (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog)
283 {
284   const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
285 
286   Scene_points_with_normal_item* pts_item =
287     qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
288 
289   if(pts_item)
290   {
291     // Gets point set
292     Point_set* points = pts_item->point_set();
293 
294     // wait cursor
295     QApplication::setOverrideCursor(Qt::WaitCursor);
296 
297     std::cout << "Scale scape surface reconstruction...";
298 
299     std::vector<Scene_polygon_soup_item*> reco_items;
300 
301     scale_space (*points, reco_items,
302                  dialog.scalespace_js(),
303                  dialog.iterations(),
304                  dialog.neighbors(), dialog.fitting(), dialog.monge(),
305                  dialog.neighborhood_size (), dialog.samples(),
306                  dialog.scalespace_af(),
307                  dialog.generate_smoothed (),
308                  dialog.longest_edge_2(), dialog.radius_ratio_bound_2(),
309                  CGAL_PI * dialog.beta_angle_2 () / 180.,
310                  dialog.separate_shells (), dialog.force_manifold ());
311 
312     for (std::size_t i = 0; i < reco_items.size (); ++ i)
313     {
314       if (!(dialog.scalespace_af()))
315       {
316         if (dialog.force_manifold () && i > reco_items.size () - 3)
317         {
318           if (dialog.generate_smoothed () && i % 2)
319             reco_items[i]->setName(tr("%1 (scale space smooth garbage)").arg(scene->item(index)->name()));
320           else
321             reco_items[i]->setName(tr("%1 (scale space garbage)").arg(scene->item(index)->name()));
322         }
323         else
324         {
325           if (dialog.generate_smoothed ())
326           {
327             if (i % 2)
328               reco_items[i]->setName(tr("%1 (scale space smooth shell %2)").arg(scene->item(index)->name()).arg((i+1)/2));
329             else
330               reco_items[i]->setName(tr("%1 (scale space shell %2)").arg(scene->item(index)->name()).arg(((i+1)/2)+1));
331           }
332           else
333             reco_items[i]->setName(tr("%1 (scale space shell %2)").arg(scene->item(index)->name()).arg(i+1));
334         }
335       }
336       else
337       {
338         if (dialog.generate_smoothed ())
339         {
340           if (i % 2)
341             reco_items[i]->setName(tr("%1 (scale space smooth)").arg(scene->item(index)->name()));
342           else
343             reco_items[i]->setName(tr("%1 (scale space)").arg(scene->item(index)->name()));
344         }
345         else
346           reco_items[i]->setName(tr("%1 (scale space)").arg(scene->item(index)->name()));
347       }
348       scene->addItem (reco_items[i]);
349     }
350 
351     QApplication::restoreOverrideCursor();
352   }
353 }
354 
355 
poisson_reconstruction(const Polyhedron_demo_surface_reconstruction_plugin_dialog & dialog)356 void Polyhedron_demo_surface_reconstruction_plugin::poisson_reconstruction
357 (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog)
358 {
359   const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
360 
361   Scene_points_with_normal_item* point_set_item =
362     qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
363 
364   if(point_set_item)
365   {
366     // Gets point set
367     Point_set* points = point_set_item->point_set();
368     if(!points) return;
369 
370     bool marching_tets = dialog.marching_tets();
371     const double sm_angle     = dialog.angle ();
372     const double sm_radius    = dialog.radius ();
373     const double sm_distance  = dialog.distance ();
374     bool conjugate_gradient = dialog.conjugate_gradient();
375     bool use_two_passes = dialog.two_passes();
376     bool do_not_fill_holes = dialog.do_not_fill_holes();
377 
378     QApplication::setOverrideCursor(Qt::WaitCursor);
379 
380     // Reconstruct point set as a polyhedron
381     SMesh* mesh = poisson_reconstruct (*points, marching_tets,
382                                        sm_angle, sm_radius, sm_distance,
383                                        conjugate_gradient, use_two_passes,
384                                        do_not_fill_holes);
385     if (mesh)
386     {
387       // Add polyhedron to scene
388       Scene_surface_mesh_item* new_item = new Scene_surface_mesh_item(mesh);
389       new_item->setName(tr("%1 (poisson)").arg(point_set_item->name()));
390       new_item->setColor(Qt::darkGray);
391       scene->addItem(new_item);
392 
393       // Hide point set
394       point_set_item->setVisible(false);
395       scene->itemChanged(index);
396     }
397 
398     QApplication::restoreOverrideCursor();
399   }
400 }
401 
polygonal_reconstruction(const Polyhedron_demo_surface_reconstruction_plugin_dialog & dialog)402 void Polyhedron_demo_surface_reconstruction_plugin::polygonal_reconstruction
403 (const Polyhedron_demo_surface_reconstruction_plugin_dialog& dialog)
404 {
405   const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
406 
407   Scene_points_with_normal_item* point_set_item =
408     qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
409 
410   if(point_set_item)
411   {
412     // Gets point set
413     Point_set* points = point_set_item->point_set();
414     if(!points) return;
415 
416     double data_fitting = dialog.data_fitting();
417     double data_coverage = dialog.data_coverage();
418     double model_complexity = dialog.model_complexity();
419     QString solver_name = dialog.solver_name();
420 
421     double sum = data_fitting + data_coverage + model_complexity;
422     data_fitting /= sum;
423     data_coverage /= sum;
424     model_complexity /= sum;
425 
426     std::cerr << "Polygonal reconstruction using:" << std::endl
427               << " * data fitting term = " << data_fitting << std::endl
428               << " * data coverage term = " << data_coverage << std::endl
429               << " * model complexity term = " << model_complexity << std::endl
430               << " * solver = " << solver_name.toStdString() << std::endl;
431 
432     QApplication::setOverrideCursor(Qt::WaitCursor);
433 
434     // Reconstruct point set as a polyhedron
435     SMesh* mesh = polygonal_reconstruct (*points,
436                                          data_fitting,
437                                          data_coverage,
438                                          model_complexity,
439                                          solver_name);
440     if (mesh)
441     {
442       // Add polyhedron to scene
443       Scene_surface_mesh_item* new_item = new Scene_surface_mesh_item(mesh);
444       new_item->setName(tr("%1 (polygonal)").arg(point_set_item->name()));
445       new_item->setColor(Qt::darkGray);
446       scene->addItem(new_item);
447 
448       // Hide point set
449       point_set_item->setVisible(false);
450       scene->itemChanged(index);
451     }
452 
453     QApplication::restoreOverrideCursor();
454   }
455 }
456 
457 #include "Surface_reconstruction_plugin.moc"
458