// 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 #define BOOST_PARAMETER_MAX_ARITY 8 #include #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 #include #include #include "Scene_polyhedron_item.h" #include "Scene_surface_mesh_item.h" #include "Scene_polylines_item.h" #include "Scene_points_with_normal_item.h" #include "Polyhedron_type.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_mesh_2_dialog.h" struct FaceInfo2 { FaceInfo2(){} int nesting_level; bool in_domain(){ return nesting_level%2 == 1; } }; template void mark_domains(CDT& ct, typename CDT::Face_handle start, int index, std::list& border ) { if(start->info().nesting_level != -1){ return; } std::list 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 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 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 void cdt2_to_face_graph(const CDT& cdt, TriangleMesh& tm, int constant_coordinate_index, double constant_coordinate) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef std::map 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; CGAL::cpp11::array vds; for(int i=0; i<3; ++i) { typename Map::iterator it; bool insert_ok; boost::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 Polyhedron_demo_mesh_2_plugin : public QObject, public CGAL::Three::Polyhedron_demo_plugin_helper { Q_OBJECT Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.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 actions() const { return QList() << actionMesh_2_; } bool applicable(QAction*) const { Q_FOREACH(int index, scene->selectionIndices()) { //if one polyhedron is found in the selection, it's fine if (qobject_cast(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->setDecimals(3); ui.edgeLength_dspinbox->setSingleStep(0.001); 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 void mesh(const std::vector& polylines_items, const std::vector& points_items, double diagonal_length, int constant_coordinate_index) { // extract seeds std::vector seeds; Q_FOREACH(Scene_points_with_normal_item* points_item, points_items) Q_FOREACH(Point_set_3::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 = ui.runLloyd_checkbox->isChecked(); // wait cursor QApplication::setOverrideCursor(Qt::WaitCursor); typedef ProjectionTraits Gt; typedef CGAL::Delaunay_mesh_vertex_base_2 Vb; typedef CGAL::Delaunay_mesh_face_base_2 Fm; typedef CGAL::Triangulation_face_base_with_info_2 Fb; typedef CGAL::Triangulation_data_structure_2 TDS; typedef CGAL::No_intersection_tag Tag; typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; typedef CGAL::Delaunay_mesh_size_criteria_2 Criteria; typedef CGAL::Delaunay_mesher_2 Mesher; QTime time; // global timer time.start(); std::cout << " Building Constrained_Delaunay_triangulation_2..." << std::flush; CDT cdt; QTime ltime; //local timer ltime.start(); double constant_coordinate = polylines_items.back()->polylines.back().back()[constant_coordinate_index]; try{ Q_FOREACH(Scene_polylines_item* polylines_item, polylines_items) Q_FOREACH(const std::vector& 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::max_iteration_number = nb_iter); std::cout << " done (" << ltime.elapsed() << " ms)" << std::endl; } // export result as a polyhedron item QString iname = polylines_items.size()==1? polylines_items.front()->name()+QString("_meshed_"): QString("2dmesh_"); iname+=QString::number(target_length); if (runLloyd) iname+=QString("_Lloyd_")+QString::number(nb_iter); if(mw->property("is_polyhedron_mode").toBool()){ Scene_polyhedron_item* poly_item = new Scene_polyhedron_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(); }else{ 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& polylines_items, const std::vector& points_items) { int res=-1; Kernel::Point_3 ref = polylines_items.front()->polylines.front().front(); Q_FOREACH(Scene_polylines_item* polylines_item, polylines_items) Q_FOREACH(const std::vector& points, polylines_item->polylines) Q_FOREACH(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; Q_FOREACH(Scene_points_with_normal_item* points_item, points_items) Q_FOREACH(Point_set_3::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 polylines_items; std::vector points_items; double inf = std::numeric_limits::infinity(); CGAL::Three::Scene_interface::Bbox bbox(inf,inf,inf,-inf,-inf,-inf); Q_FOREACH(int index, scene->selectionIndices()) { Scene_polylines_item* polylines_item = qobject_cast(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->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 >(polylines_items, points_items, diag, 0); break; case 1: mesh >(polylines_items, points_items, diag, 1); break; case 2: mesh >(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"