File: Mesh_2_plugin.cpp

package info (click to toggle)
cgal 6.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 144,952 kB
  • sloc: cpp: 811,597; ansic: 208,576; sh: 493; python: 411; makefile: 286; javascript: 174
file content (445 lines) | stat: -rw-r--r-- 15,018 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445

// Needed for lloyd_optimize_mesh_2 which does it too late
// (and we don't want to spend the time on finding out who
// includes the header file that sets it too a value too low

#include <stdexcept>

#define CGAL_CT2_WANTS_TO_HAVE_EXTRA_ACTION_FOR_INTERSECTING_CONSTRAINTS
#define CGAL_CDT2_EXTRA_ACTION_FOR_INTERSECTING_CONSTRAINTS \
  throw std::runtime_error("This plugin does not deal with intersecting edges");

#include <QtCore/qglobal.h>

#include <CGAL/Three/CGAL_Lab_plugin_helper.h>
#include <CGAL/Three/CGAL_Lab_plugin_interface.h>


#include "Scene_surface_mesh_item.h"
#include "Scene_polylines_item.h"
#include "Scene_points_with_normal_item.h"

#include <CGAL/iterator.h>

#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Projection_traits_yz_3.h>
#include <CGAL/Projection_traits_xz_3.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Delaunay_mesh_vertex_base_2.h>
#include <CGAL/Delaunay_mesh_face_base_2.h>
#include <CGAL/Delaunay_mesher_2.h>
#include <CGAL/Delaunay_mesh_size_criteria_2.h>
#include <CGAL/Triangulation_face_base_with_info_2.h>
#include <CGAL/lloyd_optimize_mesh_2.h>

#include <CGAL/boost/graph/Euler_operations.h>

#include <QElapsedTimer>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QString>
#include <QDialog>
#include <QMessageBox>
#include <QtPlugin>

#include <vector>
#include <algorithm>
#include <queue>
#include <sstream>

#include "ui_mesh_2_dialog.h"

struct FaceInfo2
{
  FaceInfo2(){}
  int nesting_level;
  bool in_domain(){
    return nesting_level%2 == 1;
  }
};

template <class CDT>
void
mark_domains(CDT& ct,
             typename CDT::Face_handle start,
             int index,
             std::list<typename CDT::Edge>& border )
{
  if(start->info().nesting_level != -1){
    return;
  }
  std::list<typename CDT::Face_handle> queue;
  queue.push_back(start);
  while(! queue.empty()){
    typename CDT::Face_handle fh = queue.front();
    queue.pop_front();
    if(fh->info().nesting_level == -1){
      fh->info().nesting_level = index;
      for(int i = 0; i < 3; i++){
        typename CDT::Edge e(fh,i);
        typename CDT::Face_handle n = fh->neighbor(i);
        if(n->info().nesting_level == -1){
          if(ct.is_constrained(e)) border.push_back(e);
          else queue.push_back(n);
        }
      }
    }
  }
}

template <class CDT>
void
mark_nested_domains(CDT& cdt)
{
  for(typename CDT::All_faces_iterator it = cdt.all_faces_begin(); it != cdt.all_faces_end(); ++it){
    it->info().nesting_level = -1;
  }
  std::list<typename CDT::Edge> border;
  mark_domains(cdt, cdt.infinite_face(), 0, border);
  while(! border.empty()){
    typename CDT::Edge e = border.front();
    border.pop_front();
    typename CDT::Face_handle n = e.first->neighbor(e.second);
    if(n->info().nesting_level == -1){
      mark_domains(cdt, n, e.first->info().nesting_level+1, border);
    }
  }
}

template <class CDT, class TriangleMesh>
void cdt2_to_face_graph(const CDT& cdt, TriangleMesh& tm, int constant_coordinate_index, double constant_coordinate)
{
  typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;

  typedef std::map<typename CDT::Vertex_handle, vertex_descriptor> Map;
  Map descriptors;
  for (typename CDT::Finite_faces_iterator fit=cdt.finite_faces_begin(),
                                           fit_end=cdt.finite_faces_end();
                                           fit!=fit_end; ++fit)
  {
    if (!fit->is_in_domain()) continue;
    std::array<vertex_descriptor,3> vds;
    for(int i=0; i<3; ++i)
    {
      typename Map::iterator it;
      bool insert_ok;
      std::tie(it,insert_ok) =
        descriptors.insert(std::make_pair(fit->vertex(i),vertex_descriptor()));
      if (insert_ok){
        const Kernel::Point_3& pt=fit->vertex(i)->point();
        double coords[3] = {pt[0], pt[1], pt[2]};
        coords[constant_coordinate_index]=constant_coordinate;
        it->second = add_vertex(Kernel::Point_3(coords[0],coords[1],coords[2]), tm);
      }
      vds[i]=it->second;
    }

    CGAL::Euler::add_face(vds, tm);
  }
}

class CGAL_Lab_mesh_2_plugin :
  public QObject,
  public CGAL::Three::CGAL_Lab_plugin_helper
{
  Q_OBJECT
  Q_INTERFACES(CGAL::Three::CGAL_Lab_plugin_interface)
  Q_PLUGIN_METADATA(IID "com.geometryfactory.CGALLab.PluginInterface/1.0")

public:
  void init(QMainWindow* mainWindow,
            CGAL::Three::Scene_interface* scene_interface,
            Messages_interface*)
  {
    this->scene = scene_interface;
    this->mw = mainWindow;

    actionMesh_2_ = new QAction("Mesh_2", mw);
    // actionMesh_2_->setProperty("subMenuName", "Polygon Mesh Processing");
    if (actionMesh_2_) {
      connect(actionMesh_2_, SIGNAL(triggered()),
        this, SLOT(run()));
    }
  }

  QList<QAction*> actions() const {
    return QList<QAction*>() << actionMesh_2_;
  }

  bool applicable(QAction*) const
  {
    for(int index : scene->selectionIndices())
    {
      //if one polyhedron is found in the selection, it's fine
      if (qobject_cast<Scene_polylines_item*>(scene->item(index)))
        return true;
    }
    return false;
  }

private:
  Ui::mesh_2_dialog
  create_dialog(QDialog* dialog, double diag_length, bool no_seeds)
  {
    Ui::mesh_2_dialog ui;
    ui.setupUi(dialog);
    connect(ui.buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
    connect(ui.buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));

    //connect checkbox to spinbox
    connect(ui.runLloyd_checkbox, SIGNAL(toggled(bool)),
            ui.nbIterations_spinbox, SLOT(setEnabled(bool)));
    connect(ui.runMesh2_checkbox, SIGNAL(toggled(bool)),
            ui.edgeLength_dspinbox, SLOT(setEnabled(bool)));

    //Set default parameter edge length
    ui.edgeLength_dspinbox->setRange(1e-6 * diag_length, //min
                                     2.   * diag_length);//max
    ui.edgeLength_dspinbox->setValue(0.05 * diag_length);
    ui.edgeLength_dspinbox->setToolTip(
      "Diagonal length of the Bbox of the selection to mesh is "+
      QString::number(diag_length)+" - default is 5% of it");

    //Set default for nb iterations
    ui.nbIterations_spinbox->setSingleStep(1);
    ui.nbIterations_spinbox->setRange(1/*min*/, 1000/*max*/);
    ui.nbIterations_spinbox->setValue(1);

    // Run Mesh_2 by default, not Lloyd
    ui.runLloyd_checkbox->setChecked(false);
    ui.runMesh2_checkbox->setChecked(true);

    // Domain definition and disabling all options if no Mesh_2 run
    if (no_seeds){
      ui.radioSeedsIn->setDisabled(true);
      ui.radioSeedsOut->setDisabled(true);
      ui.radioNesting->setChecked(true);
    }
    else
      ui.radioSeedsOut->setChecked(true);
    return ui;
  }

  template <class ProjectionTraits>
  void mesh(const std::vector<Scene_polylines_item*>& polylines_items,
            const std::vector<Scene_points_with_normal_item*>& points_items,
            double diagonal_length,
            int constant_coordinate_index)
  {
    // extract seeds
    std::vector<Kernel::Point_3> seeds;
    for(Scene_points_with_normal_item* points_item : points_items)
      for(Point_set_3<Kernel>::Index it : *points_item->point_set())
        seeds.push_back(points_item->point_set()->point(it));

    // Create dialog box
    QDialog dialog(mw);
    Ui::mesh_2_dialog ui =
      create_dialog(&dialog, diagonal_length, seeds.empty());
    dialog.setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint|Qt::WindowCloseButtonHint);

    // Get values
    int i = dialog.exec();
    if (i == QDialog::Rejected)
    {
      std::cout << "2D Meshing aborted" << std::endl;
      return;
    }
    bool runMesh2 = ui.runMesh2_checkbox->isChecked();
    double target_length = ui.edgeLength_dspinbox->value();
    unsigned int nb_iter = ui.nbIterations_spinbox->value();
    bool runLloyd = runMesh2?ui.runLloyd_checkbox->isChecked():false;

    // wait cursor
    QApplication::setOverrideCursor(Qt::WaitCursor);
    typedef ProjectionTraits                                              Gt;
    typedef CGAL::Delaunay_mesh_vertex_base_2<Gt>                         Vb;
    typedef CGAL::Delaunay_mesh_face_base_2<Gt>                           Fm;
    typedef CGAL::Triangulation_face_base_with_info_2<FaceInfo2,Gt,Fm>    Fb;
    typedef CGAL::Triangulation_data_structure_2<Vb, Fb>                  TDS;
    typedef CGAL::No_constraint_intersection_requiring_constructions_tag  Tag;
    typedef CGAL::Constrained_Delaunay_triangulation_2<Gt, TDS, Tag>      CDT;
    typedef CGAL::Delaunay_mesh_size_criteria_2<CDT>                      Criteria;
    typedef CGAL::Delaunay_mesher_2<CDT, Criteria>                        Mesher;

    QElapsedTimer time; // global timer
    time.start();

    std::cout << " Building Constrained_Delaunay_triangulation_2..."
              << std::flush;
    CDT cdt;

    QElapsedTimer ltime; //local timer
    ltime.start();
    double constant_coordinate =
      polylines_items.back()->polylines.back().back()[constant_coordinate_index];
    try{
      for(Scene_polylines_item* polylines_item : polylines_items)
        for(const std::vector<Kernel::Point_3>& points :
                    polylines_item->polylines)
          cdt.insert_constraint(points.begin(),points.end());
    }catch(std::runtime_error&)
    {
      QApplication::restoreOverrideCursor();
      throw;
    }
    std::cout << " done (" << ltime.elapsed() << " ms)" << std::endl;

    if (cdt.dimension()!=2){
      QApplication::restoreOverrideCursor();
      std::cout << "Triangulation is not of dimension 2" << std::endl;
      return;
    }

    // start by marking the domain to mesh
    Criteria criteria(0.125, target_length);
    Mesher mesher(cdt, criteria);
    bool use_seeds=ui.radioSeedsOut->isChecked() ||
                   ui.radioSeedsIn->isChecked();
    bool use_nesting = ui.radioNesting->isChecked();

    if (!use_seeds){
      if (use_nesting){
        mark_nested_domains(cdt);
        for(typename CDT::All_faces_iterator fit=cdt.all_faces_begin(),
                                             fit_end=cdt.all_faces_end();
                                             fit!=fit_end;++fit)
        {
          fit->set_in_domain(fit->info().in_domain());
        }
      }
      mesher.init(use_nesting);
    }
    else
      mesher.set_seeds(seeds.begin(), seeds.end(), ui.radioSeedsIn->isChecked(), true);

    if (runMesh2){
      time.restart();
      std::cout << " Running refine_Delaunay_mesh_2 ..." << std::flush;
      mesher.refine_mesh();
      std::cout << " done (" << ltime.elapsed() << " ms)" << std::endl;
    }

    if (runLloyd){
      ltime.restart();
      std::cout << " Running lloyd_optimize_mesh_2..." << std::flush;
      CGAL::lloyd_optimize_mesh_2(cdt,
        CGAL::parameters::number_of_iterations(nb_iter));
      std::cout << " done (" << ltime.elapsed() << " ms)" << std::endl;
    }

    // export result as a surface_mesh item
    QString iname = runMesh2 ? (( polylines_items.size()==1?
                                  polylines_items.front()->name()+QString("_meshed_"):
                                  QString("2dmesh_") )+QString::number(target_length) )
                             : ( polylines_items.size()==1?
                                 polylines_items.front()->name()+QString("_triangulated"):
                                 QString("polylines_triangulation") );
    if (runLloyd) iname+=QString("_Lloyd_")+QString::number(nb_iter);
    Scene_surface_mesh_item* poly_item = new Scene_surface_mesh_item();
    poly_item->setName(iname);
    cdt2_to_face_graph(cdt,
                       *poly_item->polyhedron(),
                       constant_coordinate_index,
                       constant_coordinate);
    scene->addItem(poly_item);
    poly_item->invalidateOpenGLBuffers();

    std::cout << "ok (" << time.elapsed() << " ms)" << std::endl;
    // default cursor
    QApplication::restoreOverrideCursor();
  }

  int detect_constant_coordinate(
    const std::vector<Scene_polylines_item*>& polylines_items,
    const std::vector<Scene_points_with_normal_item*>& points_items)
  {
    int res=-1;
    Kernel::Point_3 ref = polylines_items.front()->polylines.front().front();
    for(Scene_polylines_item* polylines_item : polylines_items)
      for(const std::vector<Kernel::Point_3>& points :
                  polylines_item->polylines)
        for(const Kernel::Point_3& pt : points)
        {
          int nbe=0, candidate=-1;
          for (int i=0; i<3; ++i)
            if (ref[i]==pt[i]){
              ++nbe;
              candidate=i;
            }
          if (nbe==0) return -1;
          if (nbe==1){
            if (res==-1)
              res=candidate;
            else
              if(res!=candidate) return -1;
          }
        }
    if (res==-1) return res;
    for(Scene_points_with_normal_item* points_item : points_items)
      for(Point_set_3<Kernel>::Index pt : *points_item->point_set())
      if (points_item->point_set()->point(pt)[res]!=ref[res])
          return -1;
    return res;
  }

public Q_SLOTS:

  void run()
  {
    QApplication::setOverrideCursor(Qt::WaitCursor);
    //collect input polylines
    std::vector<Scene_polylines_item*> polylines_items;
    std::vector<Scene_points_with_normal_item*> points_items;
    double inf = std::numeric_limits<double>::infinity();
    CGAL::Three::Scene_interface::Bbox bbox(inf,inf,inf,-inf,-inf,-inf);
    for(int index : scene->selectionIndices())
    {
      Scene_polylines_item* polylines_item =
        qobject_cast<Scene_polylines_item*>(scene->item(index));
      if (polylines_item){
        polylines_items.push_back(polylines_item);
        bbox=bbox+polylines_item->bbox();
      }
      else{
        Scene_points_with_normal_item* points_item =
          qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
        if (points_item)
          points_items.push_back(points_item);
      }
    }

    double diag = CGAL::sqrt(
          (bbox.xmax()-bbox.xmin())*(bbox.xmax()-bbox.xmin())
          +(bbox.ymax()-bbox.ymin())*(bbox.ymax()-bbox.ymin())
          +(bbox.zmax()-bbox.zmax()) *(bbox.zmax()-bbox.zmax())
          );
    QApplication::restoreOverrideCursor();
    switch( detect_constant_coordinate(polylines_items, points_items) )
    {
      using namespace CGAL;
      typedef Kernel K;
      case 0:
        mesh<Projection_traits_yz_3<K> >(polylines_items, points_items, diag, 0);
      break;
      case 1:
        mesh<Projection_traits_xz_3<K> >(polylines_items, points_items, diag, 1);
      break;
      case 2:
        mesh<Projection_traits_xy_3<K> >(polylines_items, points_items, diag, 2);
      break;
      default:
        QMessageBox::critical(mw,
                              "Invalid Input Data",
                              "Polylines and seed points must all be in "
                              "the same xy, yz or xz plane");
    }
  }

private:
  QAction* actionMesh_2_;

}; // end Mesh_2_plugin

#include "Mesh_2_plugin.moc"