diff --git a/Polyhedron/demo/Polyhedron/Color_map.h b/Polyhedron/demo/Polyhedron/Color_map.h index a55f0c658d2..150d1424b92 100644 --- a/Polyhedron/demo/Polyhedron/Color_map.h +++ b/Polyhedron/demo/Polyhedron/Color_map.h @@ -3,13 +3,17 @@ #include -inline QColor generate_color(double h, double s_min = 0.35) +#include +#include + +inline QColor generate_color(double h, + double s_min = 0.35) { - std::size_t s_max=255; - if(h >0.8 && h < 0.95) //span of ugly pink, desaturates make it less garish IMO + std::size_t s_max = 255; + if(h > 0.8 && h < 0.95) // span of ugly pink, desaturates make it less garish IMO s_max = 160; std::size_t s = std::rand() % (s_max-static_cast(s_min*255)) + static_cast(s_min*255); - return QColor::fromHsvF(h,s/255.0,1.0); + return QColor::fromHsvF(h, s/255.0, 1.0); } @@ -22,19 +26,23 @@ compute_color_map(QColor base_color, qreal hue = base_color.hueF(); const qreal step = (static_cast(1)) / nb_of_colors; - qreal h = hue==-1 ? 0 - :hue; - for(unsigned i = 0; i < nb_of_colors; ++i) { - if (h!=-1) h += step; - if ( h > 1 ) { h -= 1; } + qreal h = (hue == -1) ? 0 : hue; + for(std::size_t i=0; i 1) + h -= 1; *out++ = generate_color(h); } + return out; } -inline QColor generate_random_color() { +inline QColor generate_random_color() +{ std::size_t h = static_cast(std::rand() % 360); - return generate_color(h/359.0); + return generate_color(h / 359.0); } -#endif +#endif // _COLOR_MAP_H diff --git a/Polyhedron/demo/Polyhedron/MainWindow.cpp b/Polyhedron/demo/Polyhedron/MainWindow.cpp index 39c537dea5b..99c722048ee 100644 --- a/Polyhedron/demo/Polyhedron/MainWindow.cpp +++ b/Polyhedron/demo/Polyhedron/MainWindow.cpp @@ -1440,7 +1440,7 @@ QList MainWindow::getSelectedSceneItemIndices() const void MainWindow::selectionChanged() { scene->setSelectedItemIndex(getSelectedSceneItemIndex()); - scene->setSelectedItemsList(getSelectedSceneItemIndices()); + scene->setSelectedItemIndices(getSelectedSceneItemIndices()); CGAL::Three::Scene_item* item = scene->item(getSelectedSceneItemIndex()); Q_FOREACH(CGAL::QGLViewer* vi, CGAL::QGLViewer::QGLViewerPool()) { @@ -2193,7 +2193,7 @@ void MainWindow::on_actionEraseAll_triggered() QList all_ids; for(int i = 0; i < scene->numberOfEntries(); ++i) all_ids.push_back(i); - scene->setSelectedItemsList(all_ids); + scene->setSelectedItemIndices(all_ids); on_actionErase_triggered(); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Display/CMakeLists.txt index 6e1c9990917..10b1c254b1e 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/CMakeLists.txt @@ -1,11 +1,16 @@ include(polyhedron_demo_macros) + +qt5_wrap_ui(display_propertyUI_FILES Display_property.ui) +polyhedron_demo_plugin(display_property_plugin Display_property_plugin ${display_propertyUI_FILES} KEYWORDS Viewer) +target_link_libraries(display_property_plugin PUBLIC scene_surface_mesh_item + scene_points_with_normal_item + scene_color_ramp) + if(TARGET CGAL::Eigen3_support) - qt5_wrap_ui( display_propertyUI_FILES Display_property.ui ) - polyhedron_demo_plugin(display_property_plugin Display_property_plugin ${display_propertyUI_FILES} KEYWORDS Viewer) - target_link_libraries(display_property_plugin - PUBLIC - scene_surface_mesh_item - scene_points_with_normal_item - scene_color_ramp - CGAL::Eigen3_support) + qt5_wrap_ui(heat_methodUI_FILES Heat_method.ui) + polyhedron_demo_plugin(heat_method_plugin Heat_method_plugin ${heat_methodUI_FILES} KEYWORDS Viewer) + target_link_libraries(heat_method_plugin PUBLIC scene_surface_mesh_item + scene_selection_item + scene_color_ramp + CGAL::Eigen3_support) endif() diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property.ui b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property.ui index 7a5a19621f4..656ec4827c5 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property.ui @@ -2,26 +2,202 @@ DisplayPropertyWidget + + false + 0 0 - 292 - 508 + 514 + 695 + + + 514 + 695 + + - Property Displaying + Property Display - - - - - - - Qt::ScrollBarAlwaysOff + + + + + + Cantarell + true + + + + Color Item + + + + + + + Property + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + QComboBox::AdjustToContents + + + + Smallest Angle Per Face + + + + + Scaled Jacobian + + + + + Heat Intensity + + + + + Heat Intensity (Intrinsic Delaunay) + + + + + + + + + + + + + Qt::Vertical + + + 20 + 40 + + + + + + + + Color Visualization + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Random colors + + + + + + + + + + + + + + + + + First color + + + + + + + Max color + + + + + + + Color Ramp + + + true + + + + + + + + + + + + + + + + + + + + + Min color + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + true @@ -30,12 +206,12 @@ 0 0 - 127 - 430 + 236 + 397 - - + + RAMP DISPLAYING @@ -46,206 +222,70 @@ - - - - - - - - - - - - - - - - - - - - Colorize - - - - - - - 0 - - - - Ramp - - - - - - Ramp Colors - - - - - - Color Min... - - - - - - - Color Max... - - - - - - - - - - - Map - - - - - - Initial Color... - - - - - - - - - - - Select Source Points - - - true - - - - - - - Zoom - - - - - - Zoom to min - - - - - - - Zoom to max - - - - - - - - - - Ramp Extrema - - - - - - 0.00 - - - - - - - 2.00 - - - - - - - - - - false - - - Delete Group - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - QComboBox::AdjustToContents - - - - Smallest Angle Per Face - - - - - Scaled Jacobian - - - - - Heat Intensity - - - - - Heat Intensity (Intrinsic Delaunay) - - - - - - - - - - Qt::Vertical + + + + false - - - 20 - 40 - + + Extreme Values - + + + + + Min Value + + + + + + + Max Value + + + + + + + 2.00 + + + true + + + + + + + Zoom to max value + + + + + + + Zoom to min value + + + + + + + 0.00 + + + true + + + true + + + + + diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp index af1eef9e115..5924212cb39 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp @@ -1,400 +1,148 @@ + +#include "ui_Display_property.h" + +#include "Color_map.h" +#include "Color_ramp.h" +#include "id_printing.h" +#include "Messages_interface.h" + +#include "Scene.h" +#include "Scene_surface_mesh_item.h" +#include "Scene_points_with_normal_item.h" +#include "triangulate_primitive.h" + +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include - -#include "Scene_points_with_normal_item.h" - -#include "Messages_interface.h" -#include "Scene_surface_mesh_item.h" -#include "Color_ramp.h" -#include "Color_map.h" -#include "ui_Display_property.h" -#include "id_printing.h" -#include "Scene.h" -#include "triangulate_primitive.h" -#include -#include #include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + #include #include +#include -#define ARBITRARY_DBL_MIN 1.0E-30 -#define ARBITRARY_DBL_MAX 1.0E+30 +#define ARBITRARY_DBL_MIN 1.0E-17 +#define ARBITRARY_DBL_MAX 1.0E+17 +using namespace CGAL::Three; +Viewer_interface* (&getActiveViewer)() = Three::activeViewer; -//Item for heat values -typedef CGAL::Three::Triangle_container Tri; -typedef CGAL::Three::Viewer_interface VI; - -class Scene_heat_item - : public CGAL::Three::Scene_item_rendering_helper -{ - Q_OBJECT - -public: - Scene_heat_item(Scene_surface_mesh_item* item) - :sm(item->face_graph()), parent(item) - { - setTriangleContainer(0, new Triangle_container(VI::PROGRAM_HEAT_INTENSITY, - true)); - setRenderingMode(Gouraud); - } - Scene_item* clone() const Q_DECL_OVERRIDE {return nullptr;} - QString toolTip() const Q_DECL_OVERRIDE{return QString(); } - void select(double orig_x, - double orig_y, - double orig_z, - double dir_x, - double dir_y, - double dir_z) Q_DECL_OVERRIDE - { - parent->select( orig_x, orig_y, orig_z, - dir_x, dir_y, dir_z); - } - - void initializeBuffers(CGAL::Three::Viewer_interface *viewer) const Q_DECL_OVERRIDE - { - getTriangleContainer(0)->initializeBuffers(viewer); - getTriangleContainer(0)->setIdxSize(nb_idx); - verts.resize(0); - normals .resize(0); - colors.resize(0); - idx.clear(); - idx.shrink_to_fit(); - colors.shrink_to_fit(); - verts.shrink_to_fit(); - normals.shrink_to_fit(); - } - - void draw(CGAL::Three::Viewer_interface *viewer) const Q_DECL_OVERRIDE - { - if(!visible()) - return; - if(!isInit(viewer)) - initGL(viewer); - if ( getBuffersFilled() && - ! getBuffersInit(viewer)) - { - initializeBuffers(viewer); - setBuffersInit(viewer, true); - } - if(!getBuffersFilled()) - { - computeElements(); - initializeBuffers(viewer); - } - - getTriangleContainer(0)->setAlpha(1.0f); - getTriangleContainer(0)->draw(viewer, false); - } - void compute_bbox() const Q_DECL_OVERRIDE - { - SMesh::Property_map pprop = sm->points(); - CGAL::Bbox_3 bbox ; - - for(vertex_descriptor vd :vertices(*sm)) - { - bbox = bbox + pprop[vd].bbox(); - } - _bbox = Bbox(bbox.xmin(),bbox.ymin(),bbox.zmin(), - bbox.xmax(),bbox.ymax(),bbox.zmax()); - is_bbox_computed = true; - } - Scene_item::Bbox bbox() const Q_DECL_OVERRIDE { - if(!is_bbox_computed) - compute_bbox(); - is_bbox_computed = true; - return _bbox; - } - - ~Scene_heat_item(){} - virtual bool supportsRenderingMode(RenderingMode m) const Q_DECL_OVERRIDE { return m==Gouraud; } - virtual void invalidateOpenGLBuffers() Q_DECL_OVERRIDE - { - - setBuffersFilled(false); - compute_bbox(); - getTriangleContainer(0)->reset_vbos(NOT_INSTANCED); - is_bbox_computed = false; - } - void triangulate_convex_facet(face_descriptor fd, - boost::property_map< SMesh, boost::vertex_index_t >::type *im) const - { - const CGAL::qglviewer::Vec v_offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); - EPICK::Vector_3 offset = EPICK::Vector_3(v_offset.x, v_offset.y, v_offset.z); - - EPICK::Point_3 p0,p1,p2; - SMesh::Halfedge_around_face_circulator he(halfedge(fd, *sm), *sm); - SMesh::Halfedge_around_face_circulator he_end = he; - - while(next(*he, *sm) != prev(*he_end, *sm)) - { - ++he; - vertex_descriptor v0(target(*he_end, *sm)), - v1(target(*he, *sm)), - v2(target(next(*he, *sm), *sm)); - p0 = sm->point(v0) + offset; - p1 = sm->point(v1) + offset; - p2 = sm->point(v2) + offset; - idx.push_back((*im)[v0]); - idx.push_back((*im)[v1]); - idx.push_back((*im)[v2]); - } - } - void triangulate_facet(face_descriptor fd, - SMesh::Property_map *fnormals, - boost::property_map< SMesh, boost::vertex_index_t >::type *im) const - { - //Computes the normal of the facet - EPICK::Vector_3 normal = get(*fnormals, fd); - - //check if normal contains NaN values - if (normal.x() != normal.x() || normal.y() != normal.y() || normal.z() != normal.z()) - { - qDebug()<<"Warning : normal is not valid. Facet not displayed"; - return; - } - - typedef FacetTriangulator::vertex_descriptor> FT; - const CGAL::qglviewer::Vec off = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); - EPICK::Vector_3 offset(off.x,off.y,off.z); - FT triangulation(fd,normal,sm, offset); - //iterates on the internal faces - for(FT::CDT::Finite_faces_iterator - ffit = triangulation.cdt->finite_faces_begin(), - end = triangulation.cdt->finite_faces_end(); - ffit != end; ++ffit) - { - if(ffit->info().is_external) - continue; - //add the vertices to the positions - //adds the vertices, normals and colors to the appropriate vectors - //adds the indices to the appropriate vector - idx.push_back((*im)[triangulation.v2v[ffit->vertex(0)]]); - idx.push_back((*im)[triangulation.v2v[ffit->vertex(1)]]); - idx.push_back((*im)[triangulation.v2v[ffit->vertex(2)]]); - } - } - - void computeElements() const Q_DECL_OVERRIDE - { - typedef EPICK::Point_3 Point; - QApplication::setOverrideCursor(Qt::WaitCursor); - const CGAL::qglviewer::Vec o = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); - EPICK::Vector_3 offset(o.x, o.y, o.z); - SMesh::Property_map positions = - sm->points(); - SMesh::Property_map vnormals = - sm->property_map("v:normal").first; - SMesh::Property_map fnormals = - sm->property_map("f:normal").first; - typedef boost::graph_traits::face_descriptor face_descriptor; - typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef boost::graph_traits::vertex_descriptor vertex_descriptor; - SMesh::Property_map vcolors = - sm->property_map("v:color").first; - SMesh::Property_map vdist= - sm->property_map("v:dist").first; - typedef CGAL::Buffer_for_vao CPF; - verts.clear(); - normals.clear(); - idx.clear(); - colors.clear(); - boost::property_map< SMesh, boost::vertex_index_t >::type - im = get(boost::vertex_index, *sm); - - idx.reserve(num_faces(*sm) * 3); - for(face_descriptor fd : faces(*sm)) - { - if(is_triangle(halfedge(fd,*sm),*sm)) - { - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *sm),*sm)) - { - idx.push_back(source(hd, *sm)); - } - } - else - { - std::vector facet_points; - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *sm),*sm)) - { - facet_points.push_back(positions[target(hd, *sm)]); - } - bool is_convex = CPF::is_facet_convex(facet_points, fnormals[fd]); - - if(is_convex && is_quad(halfedge(fd,*sm),*sm) ) - { - halfedge_descriptor hd = halfedge(fd,*sm); - //1st half - idx.push_back(source(hd, *sm)); - idx.push_back(source(next(hd, *sm), *sm)); - idx.push_back(source(next(next(hd, *sm), *sm), *sm)); - - //2nd half - idx.push_back(source(hd, *sm)); - idx.push_back(source(next(next(hd, *sm), *sm), *sm)); - idx.push_back(source(prev(hd, *sm), *sm)); - } - else if(is_convex) - { - triangulate_convex_facet(fd, &im); - } - else - { - triangulate_facet(fd, &fnormals, &im); - } - } - } - for(vertex_descriptor vd : vertices(*sm)) - { - CGAL::IO::Color c = vcolors[vd]; - colors.push_back((float)c.red()/255); - colors.push_back((float)c.green()/255); - colors.push_back((float)c.blue()/255); - - - Point p = positions[vd] + offset; - CPF::add_point_in_buffer(p, verts); - EPICK::Vector_3 n = vnormals[vd]; - CPF::add_normal_in_buffer(n, normals); - heat_values.push_back(vdist[vd]); - } - nb_idx = idx.size(); - getTriangleContainer(0)->allocate(Tri::Vertex_indices, idx.data(), - static_cast(idx.size()*sizeof(unsigned int))); - getTriangleContainer(0)->allocate(Tri::Smooth_vertices, verts.data(), - static_cast(num_vertices(*sm)*3*sizeof(float))); - - getTriangleContainer(0)->allocate(Tri::Smooth_normals, normals.data(), - static_cast(num_vertices(*sm)*3*sizeof(float))); - getTriangleContainer(0)->allocate(Tri::VColors, colors.data(), - static_cast(colors.size()*sizeof(float))); - getTriangleContainer(0)->allocate(Tri::Distances, heat_values.data(), - static_cast(heat_values.size()*sizeof(float))); - compute_bbox(); - setBuffersFilled(true); - QApplication::restoreOverrideCursor(); - } - - bool isEmpty() const Q_DECL_OVERRIDE {return false;} - SMesh *face_graph() { return sm;} - Scene_surface_mesh_item* getParent() { return parent; } - -private: - SMesh* sm; - Scene_surface_mesh_item* parent; - mutable std::vector normals; - mutable std::vector idx; - mutable std::vector verts; - mutable std::vector colors; - mutable std::vector heat_values; - mutable std::size_t nb_idx; -}; // end class Scene_heat_item - -class DockWidget : - public QDockWidget, +class DockWidget + : public QDockWidget, public Ui::DisplayPropertyWidget { public: - DockWidget(QString name, QWidget *parent) - :QDockWidget(name,parent) + DockWidget(const QString& name, QWidget *parent) + : QDockWidget(name, parent) { setupUi(this); } }; -typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; -typedef boost::graph_traits::face_descriptor face_descriptor; -CGAL::Three::Viewer_interface* (&getActiveViewer)() = CGAL::Three::Three::activeViewer; -class DisplayPropertyPlugin : - public QObject, - public CGAL::Three::Polyhedron_demo_plugin_helper +class Display_property_plugin + : public QObject, + public Polyhedron_demo_plugin_helper { Q_OBJECT Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") - typedef SMesh::Property_map::vertex_descriptor, double> Vertex_distance_map; - typedef CGAL::Heat_method_3::Surface_mesh_geodesic_distances_3 Heat_method; - typedef CGAL::Heat_method_3::Surface_mesh_geodesic_distances_3 Heat_method_idt; - typedef CGAL::dynamic_vertex_property_t Vertex_source_tag; - typedef boost::property_map::type Vertex_source_map; + +private: + QAction* actionDisplayProperties; + + DockWidget* dock_widget; + + // coloring choice and legend + double rm = 1.; + double gm = 0.; + double bm = 0.; + double rM = 0.; + double gM = 1.; + double bM = 0.; + double rI = 0.; + double gI = 1.; + double bI = 0.; + + Color_ramp color_ramp; + std::vector color_map; + QPixmap legend; + + // tracks whether property 'i' (aka i-th in propertyBox) applies to vertices or faces + enum class Property_simplex_type { VERTEX, FACE }; + std::vector property_simplex_types; + + enum Extremum + { + MIN_VALUE, + MAX_VALUE + }; public: - - bool applicable(QAction* action) const Q_DECL_OVERRIDE + bool applicable(QAction*) const Q_DECL_OVERRIDE { - CGAL::Three::Scene_item* item = scene->item(scene->mainSelectionIndex()); - if(action == _actions.back()) - return qobject_cast(item); - else - return qobject_cast(item) - || qobject_cast(item); + Scene_item* item = scene->item(scene->mainSelectionIndex()); + if(!item) + return false; + + return qobject_cast(item) || + qobject_cast(item); } QList actions() const Q_DECL_OVERRIDE { - return _actions; + return QList() << actionDisplayProperties; } - QColor textColor(const QColor& color) - { - QColor text_color (255, 255, 255); - if (color.red() * 0.299 + color.green() * 0.587 + color.blue() * 0.114 > 128) - text_color = QColor (0, 0, 0); - return text_color; - } - - void init(QMainWindow* mw, CGAL::Three::Scene_interface* sc, Messages_interface*) Q_DECL_OVERRIDE + void init(QMainWindow* mw, + Scene_interface* sc, + Messages_interface*) Q_DECL_OVERRIDE { this->scene = sc; this->mw = mw; - this->current_item = nullptr; - QAction *actionDisplayProperties= new QAction(QString("Display Properties"), mw); - QAction *actionHeatMethod= new QAction(QString("Heat Method"), mw); - actionHeatMethod->setProperty("submenuName", "Color"); - - rm = 1.0; - rM = 0.0; - gm = 0.0; - gM = 1.0; - bm = 0.0; - bM = 0.0; + // Main action + actionDisplayProperties = new QAction(QString("Display Properties"), mw); actionDisplayProperties->setProperty("submenuName", "Color"); - if(actionDisplayProperties) { - connect(actionDisplayProperties, SIGNAL(triggered()), - this, SLOT(openDialog())); - if(actionHeatMethod) - { - connect(actionHeatMethod, &QAction::triggered, - this, [this](){ - this->dock_widget->propertyBox->setCurrentIndex(2); - this->dock_widget->show(); - }); - } - _actions << actionDisplayProperties; - _actions << actionHeatMethod; + connect(actionDisplayProperties, SIGNAL(triggered()), + this, SLOT(openDialog())); - } - dock_widget = new DockWidget("Property Displaying", mw); - dock_widget->setVisible(false); + Scene* scene_item = static_cast(scene); + connect(scene_item, SIGNAL(itemIndexSelected(int)), + this, SLOT(onItemIndexSelected(int))); + + // Dock Widget + dock_widget = new DockWidget("Property Display", mw); addDockWidget(dock_widget); + + dock_widget->setVisible(false); + dock_widget->setEnabled(false); + + connect(dock_widget->propertyBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(onNewPropertySelected(int))); + QPalette palette(Qt::red); dock_widget->minColorButton->setPalette(palette); dock_widget->minColorButton->setStyle(QStyleFactory::create("Fusion")); @@ -410,1288 +158,1066 @@ public: dock_widget->initColorButton->setStyle(QStyleFactory::create("Fusion")); dock_widget->initColorButton->update(); + displayRampLegend(); + + // lambda to generate the three connect(), for each color button (min, max, map) + auto connect_color_buttons = [this](QPushButton* colorButton, + double& r, double& g, double& b) + { + connect(colorButton, &QPushButton::pressed, + this, [this, colorButton, &r, &g, &b]() + { + QColor color = QColorDialog::getColor(); + if(!color.isValid()) + return; + + r = color.redF(); + g = color.greenF(); + b = color.blueF(); + + QPalette palette(color); + colorButton->setPalette(palette); + colorButton->update(); + + displayLegend(); + }); + }; + + connect_color_buttons(dock_widget->minColorButton, rm, gm, bm); + connect_color_buttons(dock_widget->maxColorButton, rM, gM, bM); + connect_color_buttons(dock_widget->initColorButton, rI, gI, bI); + + displayLegend(); + connect(dock_widget->colorizeButton, SIGNAL(clicked(bool)), this, SLOT(colorize())); - connect(dock_widget->propertyBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_propertyBox_currentIndexChanged(int))); connect(dock_widget->zoomToMinButton, &QPushButton::pressed, - this, &DisplayPropertyPlugin::on_zoomToMinButton_pressed); + this, &Display_property_plugin::on_zoomToMinButton_pressed); connect(dock_widget->zoomToMaxButton, &QPushButton::pressed, - this, &DisplayPropertyPlugin::on_zoomToMaxButton_pressed); - connect(dock_widget->minColorButton, &QPushButton::pressed, - this, [this]() - { - QColor minColor = QColorDialog::getColor(); - if (!minColor.isValid()) - { - return; - } - - rm = minColor.redF(); - gm = minColor.greenF(); - bm = minColor.blueF(); - QPalette palette(minColor); - dock_widget->minColorButton->setPalette(palette); - dock_widget->minColorButton->update(); - replaceRamp(); - }); - connect(dock_widget->maxColorButton, &QPushButton::pressed, - this, [this]() - { - QColor maxColor = QColorDialog::getColor(); - if(!maxColor.isValid()) - return; - QPalette palette(maxColor); - rM = maxColor.redF(); - gM = maxColor.greenF(); - bM = maxColor.blueF(); - - dock_widget->maxColorButton->setPalette(palette); - dock_widget->maxColorButton->update(); - replaceRamp(); - }); - - connect(dock_widget->initColorButton, &QPushButton::pressed, - this, [this]() - { - QColor initColor = QColorDialog::getColor(); - if (!initColor.isValid()) - { - return; - } - - QPalette palette(initColor); - rI = initColor.redF(); - gI = initColor.greenF(); - bI = initColor.blueF(); - dock_widget->initColorButton->setPalette(palette); - dock_widget->initColorButton->update(); - }); - - connect(dock_widget->sourcePointsButton, SIGNAL(toggled(bool)), - this, SLOT(on_sourcePointsButton_toggled(bool))); - connect(dock_widget->deleteButton, &QPushButton::clicked, - this, &DisplayPropertyPlugin::delete_group); - - dock_widget->zoomToMaxButton->setEnabled(false); - dock_widget->zoomToMinButton->setEnabled(false); - Scene* scene_obj =static_cast(scene); - connect(scene_obj, SIGNAL(itemIndexSelected(int)), - this,SLOT(enableButtons(int))); - connect(scene_obj, SIGNAL(itemIndexSelected(int)), - this,SLOT(detectScalarProperties(int))); - - on_propertyBox_currentIndexChanged(0); - - - } -private: - - void detectSMScalarProperties(SMesh* smesh) - { - std::vector vprop = smesh->properties(); - std::vector fprop = smesh->properties(); - for(auto s : vprop) - if(is_property_scalar(s, smesh)) - { - dock_widget->propertyBox->addItem(s.c_str()); - } - for(auto s : fprop) - if(is_property_scalar(s, smesh)) - { - dock_widget->propertyBox->addItem(s.c_str()); - } + this, &Display_property_plugin::on_zoomToMaxButton_pressed); } - void detectPSScalarProperties(Point_set* ps) - { - for(auto s : ps->properties()) - if(is_property_scalar(s, ps)) - { - dock_widget->propertyBox->addItem(s.c_str()); - } - } private Q_SLOTS: - void detectScalarProperties(int i) - { - for(int j = dock_widget->propertyBox->count(); j>=0; --j) - dock_widget->propertyBox->removeItem(j); - - Scene_surface_mesh_item* sm_item = - qobject_cast(scene->item(i)); - Scene_points_with_normal_item* p_item = - qobject_cast(scene->item(i)); - - if(sm_item) - { - dock_widget->propertyBox->addItem("Smallest Angle Per Face"); - dock_widget->propertyBox->addItem("Scaled Jacobian"); - dock_widget->propertyBox->addItem("Heat Intensity"); - dock_widget->propertyBox->addItem("Heat Intensity (Intrinsic Delaunay)"); - detectSMScalarProperties(sm_item->face_graph()); - - } - else if(p_item) - { - detectPSScalarProperties(p_item->point_set()); - } - int width = dock_widget->propertyBox->minimumSizeHint().width(); - dock_widget->propertyBox->view()->setMinimumWidth(width); - } - void openDialog() { - if(dock_widget->isVisible()) { dock_widget->hide(); } - else{ + if(!dock_widget->isVisible()) dock_widget->show(); - dock_widget->raise(); } + dock_widget->raise(); } - void colorizePS(Scene_points_with_normal_item* ps_item) - { - ps_item->point_set()->add_colors(); - if(!treat_point_property(dock_widget->propertyBox->currentText().toStdString(), ps_item->point_set())) - { - QApplication::restoreOverrideCursor(); - return; - } - ps_item->setPointsMode(); - ps_item->invalidateOpenGLBuffers(); - ps_item->itemChanged(); - } - void colorize() - { - Scene_points_with_normal_item* p_item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if(p_item) - { - colorizePS(p_item); - return; - } - Scene_heat_item* h_item = nullptr; - Scene_surface_mesh_item* sm_item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if(!sm_item) - { - h_item = qobject_cast(scene->item(scene->mainSelectionIndex())); - if(!h_item) - return; - sm_item = h_item->getParent(); - } - QApplication::setOverrideCursor(Qt::WaitCursor); - sm_item->face_graph()->collect_garbage(); - - switch(dock_widget->propertyBox->currentIndex()){ - case 0: - displayAngles(sm_item); - break; - case 1: - displayScaledJacobian(sm_item); - break; - case 2: - dock_widget->colorChoiceWidget->setCurrentIndex(0); - if(!displayHeatIntensity(sm_item)){ - QApplication::restoreOverrideCursor(); - return; - } - sm_item->setRenderingMode(Gouraud); - break; - case 3:// Heat Method (Intrinsic Delaunay) - dock_widget->colorChoiceWidget->setCurrentIndex(0); - if(!displayHeatIntensity(sm_item, true)) - return; - sm_item->setRenderingMode(Gouraud); - break; - default: - if(dock_widget->propertyBox->currentText().contains("v:")) - { - if(!treat_sm_property(dock_widget->propertyBox->currentText().toStdString(), sm_item->face_graph())) - { - QApplication::restoreOverrideCursor(); - return; - } - sm_item->setRenderingMode(Gouraud); - } - else if(dock_widget->propertyBox->currentText().contains("f:")) - { - if(!treat_sm_property(dock_widget->propertyBox->currentText().toStdString(), sm_item->face_graph())) - { - QApplication::restoreOverrideCursor(); - return; - } - sm_item->setRenderingMode(Flat); - } - break; - } - - connect(sm_item, &Scene_surface_mesh_item::itemChanged, - this, [sm_item](){ - bool does_exist; - SMesh::Property_map pmap; - std::tie(pmap, does_exist) = - sm_item->face_graph()->property_map("f:jacobian"); - if(does_exist) - sm_item->face_graph()->remove_property_map(pmap); - std::tie(pmap, does_exist) = - sm_item->face_graph()->property_map("f:angle"); - if(does_exist) - sm_item->face_graph()->remove_property_map(pmap); - }); - QApplication::restoreOverrideCursor(); - sm_item->invalidateOpenGLBuffers(); - sm_item->redraw(); - if(dock_widget->propertyBox->currentIndex() != 2){ - dock_widget->zoomToMinButton->setEnabled(true); - dock_widget->zoomToMaxButton->setEnabled(true);} - } - - void enableButtons(int i) - { - Scene_surface_mesh_item* sm_item = - qobject_cast(scene->item(i)); - - Scene_points_with_normal_item* ps_item = - qobject_cast(scene->item(i)); - - if(! sm_item && ! ps_item) - { - dock_widget->zoomToMinButton->setEnabled(false); - dock_widget->zoomToMaxButton->setEnabled(false); - } - else if(ps_item) - { - dock_widget->zoomToMinButton->setEnabled(false); - dock_widget->zoomToMaxButton->setEnabled(false); - } - else if(sm_item){ - switch(dock_widget->propertyBox->currentIndex()) - { - case 0: - dock_widget->zoomToMinButton->setEnabled(angles_max.count(sm_item)>0 ); - dock_widget->zoomToMaxButton->setEnabled(angles_max.count(sm_item)>0 ); - break; - case 1: - dock_widget->zoomToMinButton->setEnabled(jacobian_max.count(sm_item)>0); - dock_widget->zoomToMaxButton->setEnabled(jacobian_max.count(sm_item)>0); - break; - default: - break; - } - } - } - - void resetProperty() - { - Scene_surface_mesh_item* item = - qobject_cast(sender()); - if(!item) - return; - SMesh& smesh = *item->face_graph(); - SMesh::Property_map jacobians; - bool found; - std::tie(jacobians, found) = smesh.property_map("f:jacobian"); - if(found) - { - smesh.remove_property_map(jacobians); - } - SMesh::Property_map angles; - std::tie(angles, found) = smesh.property_map("f:angle"); - if(found) - { - smesh.remove_property_map(angles); - } - } - - void displayScaledJacobian(Scene_surface_mesh_item* item) - { - - SMesh& smesh = *item->face_graph(); - //compute and store the jacobian per face - bool non_init; - SMesh::Property_map fjacobian; - std::tie(fjacobian, non_init) = smesh.add_property_map("f:jacobian", 0); - if(non_init) - { - double res_min = ARBITRARY_DBL_MAX, - res_max = -ARBITRARY_DBL_MAX; - SMesh::Face_index min_index, max_index; - for(boost::graph_traits::face_iterator fit = faces(smesh).begin(); - fit != faces(smesh).end(); - ++fit) - { - fjacobian[*fit] = scaled_jacobian(*fit, smesh); - if(fjacobian[*fit] > res_max) - { - res_max = fjacobian[*fit]; - max_index = *fit; - } - if(fjacobian[*fit] < res_min) - { - res_min = fjacobian[*fit]; - min_index = *fit; - } - } - jacobian_min.erase(item); - jacobian_min.insert(std::make_pair(item, std::make_pair(res_min, min_index))); - jacobian_max.erase(item); - jacobian_max.insert(std::make_pair(item, std::make_pair(res_max, max_index))); - connect(item, &Scene_surface_mesh_item::itemChanged, - this, &DisplayPropertyPlugin::resetProperty); - } - treat_sm_property("f:jacobian", item->face_graph()); - } - - bool resetScaledJacobian(Scene_surface_mesh_item* item) - { - SMesh& smesh = *item->face_graph(); - if(!smesh.property_map("f:jacobian").second) - { - return false; - } - dock_widget->minBox->setValue(jacobian_min[item].first-0.01); - dock_widget->maxBox->setValue(jacobian_max[item].first); - return true; - } - - - void displayAngles(Scene_surface_mesh_item* item) - { - SMesh& smesh = *item->face_graph(); - typedef boost::property_map::type PMap; - PMap pmap = get(boost::vertex_point, smesh); - //compute and store smallest angle per face - bool non_init; - SMesh::Property_map fangle; - std::tie(fangle, non_init) = smesh.add_property_map("f:angle", 0); - if(non_init) - { - double res_min = ARBITRARY_DBL_MAX, - res_max = -ARBITRARY_DBL_MAX; - SMesh::Face_index index_min, index_max; - for(boost::graph_traits::face_iterator fit = faces(smesh).begin(); - fit != faces(smesh).end(); - ++fit) - { - bool is_face_triangle = is_triangle(halfedge(*fit, smesh), smesh); - bool normal_is_ok = true; - EPICK::Vector_3 normal(0,0,0); - - EPICK::Orientation orientation = CGAL::POSITIVE; - if(!is_face_triangle) - { - face_descriptor f = *fit; - CGAL::Halfedge_around_face_circulator - he(halfedge(f, smesh), smesh), - he_end(he); - do{ - normal_is_ok = true; - - //Initializes the facet orientation - - EPICK::Point_3 S,T; - T = get(pmap, source(*he, smesh)); - S = get(pmap, target(*he, smesh)); - EPICK::Vector_3 V1((T-S).x(), (T-S).y(), (T-S).z()); - S = get(pmap,source(next(*he,smesh), smesh)); - T = get(pmap, target(next(*he,smesh), smesh)); - EPICK::Vector_3 V2((T-S).x(), (T-S).y(), (T-S).z()); - - if(normal == EPICK::Vector_3(0,0,0)) - normal_is_ok = false; - { - normal = CGAL::cross_product(V1, V2); - } - if(normal_is_ok) - { - orientation = EPICK::Orientation_3()(V1, V2, normal); - if( orientation == CGAL::COPLANAR ) - normal_is_ok = false; - } - }while( ++he != he_end && !normal_is_ok); - } - - std::vector local_angles; - local_angles.reserve(degree(*fit, smesh)); - for(halfedge_descriptor hd : - halfedges_around_face(halfedge(*fit, smesh),smesh)) - { - halfedge_descriptor hdn = next(hd, smesh); - EPICK::Vector_3 v1(get(pmap, source(hd, smesh)), get(pmap, target(hd, smesh))), - v2(get(pmap, target(hdn, smesh)), get(pmap, source(hdn, smesh))); - float norm1(CGAL::approximate_sqrt(v1.squared_length())), norm2(CGAL::approximate_sqrt(v2.squared_length())); - float dot_prod = v1*v2; - float angle = std::acos(dot_prod/(norm1*norm2)); - if(is_face_triangle || !normal_is_ok) - local_angles.push_back(angle * 180/CGAL_PI); - else - { - bool is_convex = true; - EPICK::Orientation res = EPICK::Orientation_3()(v1, v2, normal) ; - if(res!= orientation && res != CGAL::ZERO) - is_convex = false; - local_angles.push_back(is_convex ? angle * 180/CGAL_PI : 360 - angle * 180/CGAL_PI ); - } - } - std::sort(local_angles.begin(), local_angles.end()); - fangle[*fit]=local_angles.front(); - - if(fangle[*fit] > res_max) - { - res_max = fangle[*fit]; - index_max = *fit; - } - if(fangle[*fit] < res_min) - { - res_min = fangle[*fit]; - index_min = *fit; - } - } - angles_min.erase(item); - angles_min.insert(std::make_pair(item, std::make_pair(res_min, index_min))); - angles_max.erase(item); - angles_max.insert(std::make_pair(item, std::make_pair(res_max, index_max))); - - connect(item, &Scene_surface_mesh_item::itemChanged, - this, &DisplayPropertyPlugin::resetProperty); - } - treat_sm_property("f:angle", item->face_graph()); - } - - bool resetAngles(Scene_surface_mesh_item* item) - { - SMesh& smesh = *item->face_graph(); - if(!smesh.property_map("f:angle").second) - { - return false; - } - dock_widget->minBox->setValue(angles_min[item].first); - dock_widget->maxBox->setValue(angles_max[item].first); - return true; - } - - // AF: This function gets called when we click on the button "Colorize" - bool displayHeatIntensity(Scene_surface_mesh_item* item, bool iDT = false) - { - SMesh& mesh = *item->face_graph(); - bool found = is_source.find(item) != is_source.end(); - if(!found - || ! source_points - || source_points->point_set()->is_empty()) - { - QApplication::restoreOverrideCursor(); - QMessageBox::warning(mw, "Warning","Source vertices are needed for this property."); - return false; - } - if(!is_triangle_mesh(mesh)) - { - QApplication::restoreOverrideCursor(); - QMessageBox::warning(mw,"Error","The mesh must be triangulated."); - return false; - } - Heat_method * hm = nullptr; - Heat_method_idt * hm_idt = nullptr; - SMesh::Property_map heat_intensity = - mesh.add_property_map("v:heat_intensity", 0).first; - if(! iDT){ - if(mesh_heat_method_map.find(item) != mesh_heat_method_map.end()){ - hm = mesh_heat_method_map[item]; - }else { - hm = new Heat_method(mesh); - mesh_heat_method_map[item] = hm; - } - connect(item, &Scene_surface_mesh_item::aboutToBeDestroyed, - [this,item](){ - auto it = mesh_heat_method_map.find(item); - if(it == mesh_heat_method_map.end()) - return; - delete it->second; - mesh_heat_method_map.erase(it); - } - ); - } else { - if(mesh_heat_method_idt_map.find(item) != mesh_heat_method_idt_map.end()){ - hm_idt = mesh_heat_method_idt_map[item]; - }else { - hm_idt = new Heat_method_idt(mesh); - mesh_heat_method_idt_map[item] = hm_idt; - } - connect(item, &Scene_surface_mesh_item::aboutToBeDestroyed, - [this,item](){ - auto it = mesh_heat_method_idt_map.find(item); - if(it == mesh_heat_method_idt_map.end()) - return; - Heat_method_idt *hm_idt = it->second; - delete hm_idt; - mesh_heat_method_idt_map.erase(it); - } - ); - } - - for(vertex_descriptor vd : vertices(mesh)){ - if(get(is_source[item], vd)){ - if(iDT){ - hm_idt->add_source(vd); - } else - hm->add_source(vd); - } - else - { - if(iDT){ - hm_idt->remove_source(vd); - } else - hm->remove_source(vd); - } - } - - if(iDT){ - hm_idt->estimate_geodesic_distances(heat_intensity); - }else{ - hm->estimate_geodesic_distances(heat_intensity); - } - - double max = 0; - double min = (std::numeric_limits::max)(); - - for(vertex_descriptor vd : vertices(mesh)){ - double hi = heat_intensity[vd]; - if(hi < min) - min = hi; - if(hi > max) - max = hi; - } - color_ramp = Color_ramp(rm, rM, gm, gM, bm, bM); - dock_widget->minBox->setValue(min); - dock_widget->maxBox->setValue(max); - displayLegend(); - //} - SMesh::Property_map vcolors = - mesh.add_property_map("v:color", CGAL::IO::Color()).first; - SMesh::Property_map vdist= - mesh.add_property_map("v:dist", 0.0).first; - for(boost::graph_traits::vertex_iterator vit = vertices(mesh).begin(); - vit != vertices(mesh).end(); - ++vit) - { - double h =(heat_intensity[*vit]-min)/(max-min); - CGAL::IO::Color color( - 255*color_ramp.r(h), - 255*color_ramp.g(h), - 255*color_ramp.b(h)); - vcolors[*vit] = color; - vdist[*vit]=h; - } - Scene_group_item* group; - if(mesh_heat_item_map.find(item) != mesh_heat_item_map.end()) - { - group = mesh_heat_item_map[item]->parentGroup(); - group->unlockChild(mesh_heat_item_map[item]); - scene->erase(scene->item_id(mesh_heat_item_map[item])); - } - else - { - group = new Scene_group_item("Heat Visualization"); - group->setProperty("heat_group", true); - scene->addItem(group); - scene->changeGroup(item, group); - scene->changeGroup(source_points, group); - group->lockChild(item); - group->lockChild(source_points); - dock_widget->deleteButton->setEnabled(true); - connect(group, &Scene_group_item::aboutToBeDestroyed, - this, [this](){ - this->dock_widget->deleteButton->setEnabled(false); - }); - } - mesh_heat_item_map[item] = new Scene_heat_item(item); - mesh_heat_item_map[item]->setName(tr("%1 heat").arg(item->name())); - scene->addItem(mesh_heat_item_map[item]); - scene->changeGroup(mesh_heat_item_map[item], group); - group->lockChild(mesh_heat_item_map[item]); - item->setVisible(false); - displayLegend(); - if(dock_widget->sourcePointsButton->isChecked()) - dock_widget->sourcePointsButton->toggle(); - return true; - } - - void replaceRamp() - { - if(dock_widget->colorChoiceWidget->currentIndex() == 0) - { - color_ramp = Color_ramp(rm, rM, gm, gM, bm, bM); - displayLegend(); - minBox = dock_widget->minBox->value(); - maxBox = dock_widget->maxBox->value(); - } - } - - void on_propertyBox_currentIndexChanged(int) - { - dock_widget->sourcePointsButton->setEnabled(false); - Scene_surface_mesh_item* item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if(! item ) - dock_widget->maxBox->setValue(180); - else{ - switch(dock_widget->propertyBox->currentIndex()) - { - case 0: - { - dock_widget->groupBox-> setEnabled(true); - dock_widget->groupBox_3->setEnabled(true); - - dock_widget->minBox->setMinimum(0); - dock_widget->minBox->setMaximum(360); - dock_widget->minBox->setValue(0); - dock_widget->maxBox->setMinimum(0); - dock_widget->maxBox->setMaximum(360); - if(is_triangle_mesh(*item->face_graph())) - dock_widget->maxBox->setValue(60); - else if(is_quad_mesh(*item->face_graph())) - dock_widget->maxBox->setValue(90); - replaceRamp(); - break; - } - case 1: - dock_widget->groupBox-> setEnabled(true); - dock_widget->groupBox_3->setEnabled(true); - - dock_widget->minBox->setMinimum(-1000); - dock_widget->minBox->setMaximum(1000); - dock_widget->minBox->setValue(0); - - dock_widget->maxBox->setMinimum(-1000); - dock_widget->maxBox->setMaximum(1000); - dock_widget->maxBox->setValue(2); - break; - case 2: - case 3: - dock_widget->sourcePointsButton->setEnabled(true); - CGAL_FALLTHROUGH; - default: - dock_widget->maxBox->setMinimum(-99999999); - dock_widget->maxBox->setMaximum(99999999); - dock_widget->minBox->setMinimum(-99999999); - dock_widget->minBox->setMaximum(99999999); - dock_widget->groupBox-> setEnabled(false); - dock_widget->groupBox_3->setEnabled(false); - } - } - } - - void closure()Q_DECL_OVERRIDE + void closure() Q_DECL_OVERRIDE { dock_widget->hide(); } - void on_zoomToMinButton_pressed() +private: + void disableExtremeValues() { - - Scene_surface_mesh_item* item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if(!item) - return; - face_descriptor dummy_fd; - Point_3 dummy_p; - switch(dock_widget->propertyBox->currentIndex()) - { - case 0: - { - ::zoomToId(*item->face_graph(), - QString("f%1").arg(angles_min[item].second), - getActiveViewer(), - dummy_fd, - dummy_p); - } - break; - case 1: - { - ::zoomToId(*item->face_graph(), - QString("f%1").arg(jacobian_min[item].second), - getActiveViewer(), - dummy_fd, - dummy_p); - } - break; - default: - break; - } + dock_widget->extremeValuesGroup->setEnabled(false); + dock_widget->zoomToMinButton->setEnabled(false); + dock_widget->zoomToMaxButton->setEnabled(false); } - void on_zoomToMaxButton_pressed() + void enableExtremeValues() { - Scene_surface_mesh_item* item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if(!item) - return; - face_descriptor dummy_fd; - Point_3 dummy_p; - switch(dock_widget->propertyBox->currentIndex()) - { - case 0: - { - ::zoomToId(*item->face_graph(), - QString("f%1").arg(angles_max[item].second), - getActiveViewer(), - dummy_fd, - dummy_p); - } - break; - case 1: - { - ::zoomToId(*item->face_graph(), - QString("f%1").arg(jacobian_max[item].second), - getActiveViewer(), - dummy_fd, - dummy_p); - } - break; - default: - break; - } + dock_widget->extremeValuesGroup->setEnabled(true); + dock_widget->zoomToMinButton->setEnabled(true); + dock_widget->zoomToMaxButton->setEnabled(true); } - void delete_group() + void resetExtremeValues() { - if(scene->selectionIndices().empty()) - return; - Scene_item* item = scene->item(scene->selectionIndices().first()); - Scene_group_item* group = qobject_cast(item); - if(!group || !group->property("heat_group").toBool()) - return; - for(auto child_id : group->getChildren()) + // setup some dummy values such that the legend can be displayed + const std::string& property_name = dock_widget->propertyBox->currentText().toStdString(); + + if(property_name == "Smallest Angle Per Face" || property_name == "Largest Angle Per Face") { - if(Scene_surface_mesh_item* child = qobject_cast(scene->item(child_id))){ - auto it = mesh_heat_method_map.find(child); - if(it != mesh_heat_method_map.end()) - mesh_heat_method_map.erase(it); - - auto it2 = mesh_heat_item_map.find(child); - if(it2 != mesh_heat_item_map.end()) - mesh_heat_item_map.erase(it2); - - auto it3 = mesh_heat_method_idt_map.find(child); - if(it3 != mesh_heat_method_idt_map.end()) - mesh_heat_method_idt_map.erase(it3); - - group->unlockChild(child); - group->removeChild(child); - scene->addChild(child); - child->setVisible(true); - child->resetColors(); - break; - } + dock_widget->minBox->setRange(0, 360); + dock_widget->minBox->setValue(0); + dock_widget->maxBox->setRange(0, 360); + dock_widget->maxBox->setValue(0); } - scene->erase(scene->item_id(group)); - source_points = nullptr; - - } - - void on_sourcePointsButton_toggled(bool b) - { - if(b) + else if(property_name == "Scaled Jacobian") { - Scene_heat_item* h_item = nullptr; - Scene_surface_mesh_item* item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if(!item) - { - h_item = qobject_cast(scene->item(scene->mainSelectionIndex())); - if(h_item) - item = h_item->getParent(); - } - if(!item) - { - QMessageBox::warning(mw, "Warning", "You must select a Surface_mesh_item to make this work. Aborting."); - dock_widget->sourcePointsButton->setChecked(false); - return; - } - current_item = item; - connect(current_item, &Scene_surface_mesh_item::aboutToBeDestroyed, - this, [this]() - { - dock_widget->sourcePointsButton->setChecked(false); - }); - if(mesh_sources_map.find(item) == mesh_sources_map.end()) - { - source_points = new Scene_points_with_normal_item(); - source_points->setName(QString("Source vertices for %1").arg(current_item->name())); - source_points->setColor(QColor(Qt::red)); - source_points->setPointSize(5); - scene->addItem(source_points); - connect(source_points, &Scene_points_with_normal_item::aboutToBeDestroyed, - [this](){ - std::unordered_map::iterator it; - for(it = mesh_sources_map.begin(); - it != mesh_sources_map.end(); - ++it) - { - if(it->second == source_points) - { - mesh_sources_map.erase(it); - break; - } - } - }); - mesh_sources_map[current_item] = source_points; - } - else - { - source_points=mesh_sources_map[current_item]; - } - connect(item, SIGNAL(selected_vertex(void*)), this, SLOT(on_vertex_selected(void*))); - bool non_init = is_source.find(item) == is_source.end(); - if(non_init) - { - Vertex_source_map map = get(Vertex_source_tag(), *item->face_graph()); - is_source.insert(std::make_pair(item, map)); - connect(item, &Scene_surface_mesh_item::itemChanged, - this, &DisplayPropertyPlugin::resetProperty); - connect(item, &Scene_surface_mesh_item::aboutToBeDestroyed, - [this, item](){ - if(is_source.find(item) != is_source.end()) - { - is_source.erase(item); - } - }); - } + dock_widget->minBox->setRange(-1000, 1000); + dock_widget->minBox->setValue(0); + dock_widget->maxBox->setRange(-1000, 1000); + dock_widget->maxBox->setValue(0); + } + else if(property_name == "Face Area") + { + dock_widget->minBox->setRange(-1000, 1000); + dock_widget->minBox->setValue(0); + dock_widget->maxBox->setRange(-1000, 1000); + dock_widget->maxBox->setValue(0); } else { - if(!current_item) - return; - disconnect(current_item, SIGNAL(selected_vertex(void*)), this, SLOT(on_vertex_selected(void*))); - current_item = nullptr; + dock_widget->minBox->setRange(-99999999, 99999999); + dock_widget->minBox->setValue(0); + dock_widget->maxBox->setRange(-99999999, 99999999); + dock_widget->maxBox->setValue(0); } } - void on_vertex_selected(void* void_ptr) + void displayRampLegend() { - typedef boost::graph_traits::vertices_size_type size_type; - size_type h = static_cast(reinterpret_cast(void_ptr)); - vertex_descriptor vd = static_cast(h) ; - bool found = is_source.find(current_item) != is_source.end(); - if(found) - { - if(!get(is_source[current_item], vd)) - { - put(is_source[current_item], vd, true); - source_points->point_set()->insert(current_item->face_graph()->point(vd)); - } - else - { - put(is_source[current_item], vd, false); - Point_set::iterator it; - for(it = source_points->point_set()->begin(); it != source_points->point_set()->end(); ++it) - if(source_points->point_set()->point(*it) == current_item->face_graph()->point(vd)) - { - source_points->point_set()->remove(it); - source_points->point_set()->collect_garbage(); - break; - } - } - } + color_ramp = Color_ramp(rm, rM, gm, gM, bm, bM); - source_points->invalidateOpenGLBuffers(); - source_points->itemChanged(); - } -private: - template - bool displayPSProperty(Point_set* ps, PM pm); - template - bool displaySMProperty(SMesh& smesh, PM pm, vertex_descriptor); - template - bool displaySMProperty(SMesh& smesh, PM pm, face_descriptor); - template - bool treat_sm_property(std::string name, SMesh* sm); - //cannot be treated as a sm_property because the property_map<>() function takes only 1 template arg. - bool treat_point_property(std::string name, Point_set* sm); - template - bool is_property_scalar(std::string name, const SMesh* sm); - //same problem of number of templates - bool is_property_scalar(std::string name, const Point_set* ps); - template - void displayMapLegend(const std::vector& values) - { - // Create a legend_ and display it - const std::size_t size = (std::min)(color_map.size(), (std::size_t)256); - const int text_height = 20; - const int height = text_height*static_cast(size) + text_height; + const int height = 256; const int width = 140; - const int cell_width = width/3; + const int cell_width = width / 3; + const int top_margin = 5; + const int left_margin = 5; + const int drawing_height = height - 2*top_margin; + const int text_height = 20; + + legend = QPixmap(width, height + text_height); + legend.fill(QColor(200, 200, 200)); + + QPainter painter(&legend); + painter.setPen(Qt::black); + painter.setBrush(QColor(200, 200, 200)); + + const double min_value = dock_widget->minBox->value(); + const double max_value = dock_widget->maxBox->value(); + + // Build legend data + std::vector graduations(100); + for(int i=0; i<100; ++i) + graduations[i] = i / 100.0; + + int i = 0; + for(std::vector::iterator it = graduations.begin(), end = graduations.end(); it != end; ++it, i+=2) + { + QColor color(255 * color_ramp.r(*it), + 255 * color_ramp.g(*it), + 255 * color_ramp.b(*it)); + painter.fillRect(left_margin, drawing_height - top_margin - i, cell_width, 2, color); + } + + // draw right vertical line + painter.setPen(Qt::blue); + painter.drawLine(QPoint(left_margin + cell_width + 10, + drawing_height - top_margin + 2), + QPoint(left_margin + cell_width + 10, + drawing_height - top_margin - static_cast(graduations.size())*2 + 2)); + + // draw min value and max value + painter.setPen(Qt::blue); + QRect min_text_rect(left_margin + cell_width + 10, + drawing_height - top_margin, 100, text_height); + painter.drawText(min_text_rect, Qt::AlignCenter, QObject::tr("%1").arg(min_value, 0, 'f', 3)); + + QRect max_text_rect(left_margin + cell_width + 10, + drawing_height - top_margin - 200, 100, text_height); + painter.drawText(max_text_rect, Qt::AlignCenter, QObject::tr("%1").arg(max_value, 0, 'f', 3)); + + dock_widget->legendLabel->setPixmap(legend); + } + + template + void displayMapLegend(const std::vector& values) + { + const std::size_t size = (std::min)(color_map.size(), std::size_t(1024)); + + const int text_height = 20; + const int height = text_height * static_cast(size) + text_height; + const int width = 140; + const int cell_width = width / 3; const int top_margin = 15; const int left_margin = 5; const int drawing_height = height - text_height + top_margin; - legend_ = QPixmap(width, height ); - legend_.fill(QColor(200, 200, 200)); + legend = QPixmap(width, height); + legend.fill(QColor(200, 200, 200)); - QPainter painter(&legend_); + QPainter painter(&legend); painter.setPen(Qt::black); painter.setBrush(QColor(200, 200, 200)); int j = 0; int tick_height = text_height; - for (std::size_t i = 0; i< size; ++i, j+=tick_height) + for(std::size_t i=0; i size){ - QRect text_rect(left_margin + cell_width+10, 0, - 50, text_height); - painter.drawText(text_rect, Qt::AlignCenter, tr("[...]")); + + if(color_map.size() > size) + { + QRect text_rect(left_margin + cell_width + 10, 0, 50, text_height); + painter.drawText(text_rect, Qt::AlignCenter, QObject::tr("[...]")); } + // draw right vertical line painter.setPen(Qt::blue); + painter.drawLine(QPoint(left_margin + cell_width + 10, + drawing_height - top_margin + tick_height), + QPoint(left_margin + cell_width + 10, + drawing_height - top_margin - static_cast(size)*tick_height + tick_height)); - painter.drawLine(QPoint(left_margin + cell_width+10, drawing_height - top_margin +tick_height), - QPoint(left_margin + cell_width+10, - drawing_height - top_margin - static_cast(size)*tick_height+tick_height)); - dock_widget->legendLabel->setPixmap(legend_); + dock_widget->legendLabel->setPixmap(legend); } + void displayLegend() { - // Create a legend_ and display it - const int height = 256; - const int width = 140; - const int cell_width = width/3; - const int top_margin = 5; - const int left_margin = 5; - const int drawing_height = height - top_margin * 2; - const int text_height = 20; - - legend_ = QPixmap(width, height + text_height); - legend_.fill(QColor(200, 200, 200)); - - QPainter painter(&legend_); - painter.setPen(Qt::black); - painter.setBrush(QColor(200, 200, 200)); - - double min_value(dock_widget->minBox->value()), - max_value(dock_widget->maxBox->value()); - // Build legend_ data - std::vector graduations(100); - for(int i=0; i<100; ++i) - graduations[i] = i/100.0; - - int i=0; - for (std::vector::iterator it = graduations.begin(), end = graduations.end(); - it != end; ++it, i+=2) - { - QColor color(255*color_ramp.r(*it), - 255*color_ramp.g(*it), - 255*color_ramp.b(*it)); - painter.fillRect(left_margin, - drawing_height - top_margin - i, - cell_width, - 2, - color); - } - // draw right vertical line - painter.setPen(Qt::blue); - - painter.drawLine(QPoint(left_margin + cell_width+10, drawing_height - top_margin + 2), - QPoint(left_margin + cell_width+10, - drawing_height - top_margin - static_cast(graduations.size())*2 + 2)); - // draw min value and max value - painter.setPen(Qt::blue); - QRect min_text_rect(left_margin + cell_width+10,drawing_height - top_margin, - 100, text_height); - painter.drawText(min_text_rect, Qt::AlignCenter, tr("%1").arg(min_value, 0, 'f', 1)); - - QRect max_text_rect(left_margin + cell_width+10, drawing_height - top_margin - 200, - 100, text_height); - painter.drawText(max_text_rect, Qt::AlignCenter, tr("%1").arg(max_value, 0, 'f', 1)); - - dock_widget->legendLabel->setPixmap(legend_); + if(dock_widget->colorRampRadioButton->isChecked()) + displayRampLegend(); + else + color_map.clear(); } - double scaled_jacobian(const face_descriptor& f , const SMesh &mesh); - QList _actions; - Color_ramp color_ramp; - std::vector color_map; - DockWidget* dock_widget; - double rm; - double rM; - double rI; - double gm; - double gM; - double gI; - double bm; - double bM; - double bI; - std::unordered_map > jacobian_min; - std::unordered_map > jacobian_max; +private: + template + bool isSMPropertyScalar(const std::string& name, + const SMesh& mesh) const; - std::unordered_map > angles_min; - std::unordered_map > angles_max; - std::unordered_map is_source; + void detectSMScalarProperties(SMesh& mesh) + { + std::vector vprop = mesh.properties(); + for(const std::string& s : vprop) + { + if(isSMPropertyScalar(s, mesh)) + { + dock_widget->propertyBox->addItem(s.c_str()); + property_simplex_types.push_back(Property_simplex_type::VERTEX); + } + } + std::vector fprop = mesh.properties(); + for(const std::string& s : fprop) + { + if(isSMPropertyScalar(s, mesh)) + { + dock_widget->propertyBox->addItem(s.c_str()); + property_simplex_types.push_back(Property_simplex_type::FACE); + } + } + } - double minBox; - double maxBox; - QPixmap legend_; + bool isPSPropertyScalar(const std::string& name, + const Point_set& ps) const; - Scene_surface_mesh_item* current_item; - Scene_points_with_normal_item* source_points; - std::unordered_map mesh_sources_map; - std::unordered_map mesh_heat_item_map; + void detectPSScalarProperties(const Point_set& ps) + { + for(const auto& s : ps.properties()) + if(isPSPropertyScalar(s, ps)) + dock_widget->propertyBox->addItem(s.c_str()); + } - std::unordered_map mesh_heat_method_map; - std::unordered_map mesh_heat_method_idt_map; +private: + // This fills "dock_widget->propertyBox" with the properties that can be displayed for the item at position 'item_index' + void detectScalarProperties(int item_index) + { + dock_widget->propertyBox->clear(); // calls onNewPropertySelected(-1) + property_simplex_types.clear(); - template friend class PropertyDisplayer; + Scene_surface_mesh_item* sm_item = qobject_cast(scene->item(item_index)); + Scene_points_with_normal_item* ps_item = qobject_cast(scene->item(item_index)); - //CRTP used to display properties of surface meshes(vertex and face) and point set. + if(sm_item) + { + dock_widget->propertyBox->addItems({"Smallest Angle Per Face", + "Largest Angle Per Face", + "Scaled Jacobian", + "Face Area"}); + property_simplex_types = { Property_simplex_type::FACE, + Property_simplex_type::FACE, + Property_simplex_type::FACE, + Property_simplex_type::FACE }; + detectSMScalarProperties(*(sm_item->face_graph())); + } + else if(ps_item) + { + detectPSScalarProperties(*(ps_item->point_set())); + } + + int width = dock_widget->propertyBox->minimumSizeHint().width(); + dock_widget->propertyBox->view()->setMinimumWidth(width); + } + +private Q_SLOTS: + // Called when a new geometric object is selected in the scene + void onItemIndexSelected(int item_index) + { + // try to keep the same selected property, if possible + const QString selected_property = dock_widget->propertyBox->currentText(); + + detectScalarProperties(item_index); + + if(dock_widget->propertyBox->count() == 0) + return; + + const int property_index = dock_widget->propertyBox->findText(selected_property); + if(property_index == -1) + dock_widget->propertyBox->setCurrentIndex(0); + else + dock_widget->propertyBox->setCurrentIndex(property_index); + } + + // Called when a new property is selected in the combo box. + // This function is only called if the index actually changed (doesn't trigger + // if you click again the item) + void onNewPropertySelected(int property_index) + { + resetExtremeValues(); // reset extreme value before the legend to get the proper values + displayLegend(); + + if(property_index >= 0 && property_index < dock_widget->propertyBox->count()) // valid property + { + dock_widget->setEnabled(true); + disableExtremeValues(); // only available after coloring + } + else // no or broken property + { + dock_widget->setEnabled(false); + dock_widget->propertyBox->setEnabled(true); + } + } + +private: + void colorizePS(Scene_points_with_normal_item* ps_item) + { + ps_item->point_set()->add_colors(); + if(!displayPSProperty(dock_widget->propertyBox->currentText().toStdString(), + *(ps_item->point_set()))) + { + return; + } + + ps_item->invalidateOpenGLBuffers(); + ps_item->setRenderingMode(Points); + ps_item->redraw(); + } + + void colorizeSM(Scene_surface_mesh_item* sm_item) + { + CGAL_assertion(static_cast(dock_widget->propertyBox->count()) == property_simplex_types.size()); + + // leave it flat if it was, otherwise set to flat+edges + if(sm_item->renderingMode() != Flat && sm_item->renderingMode() != FlatPlusEdges) + sm_item->setRenderingMode(FlatPlusEdges); + + const std::string& property_name = dock_widget->propertyBox->currentText().toStdString(); + if(property_name == "Smallest Angle Per Face") + { + displayExtremumAnglePerFace(sm_item, MIN_VALUE); + } + else if(property_name == "Largest Angle Per Face") + { + displayExtremumAnglePerFace(sm_item, MAX_VALUE); + } + else if(property_name == "Scaled Jacobian") + { + displayScaledJacobian(sm_item); + } + else if(property_name == "Face Area") + { + displayArea(sm_item); + } + else + { + const int property_index = dock_widget->propertyBox->currentIndex(); + if(property_simplex_types.at(property_index) == Property_simplex_type::VERTEX) + { + if(!displaySMProperty(dock_widget->propertyBox->currentText().toStdString(), + *(sm_item->face_graph()))) + { + return; + } + sm_item->setRenderingMode(GouraudPlusEdges); + } + else if(property_simplex_types.at(property_index) == Property_simplex_type::FACE) + { + if(!displaySMProperty(dock_widget->propertyBox->currentText().toStdString(), + *(sm_item->face_graph()))) + { + return; + } + } + } + + sm_item->invalidateOpenGLBuffers(); + sm_item->redraw(); + } + +private Q_SLOTS: + void colorize() + { + const int property_index = dock_widget->propertyBox->currentIndex(); + if(property_index < 0 || property_index >= dock_widget->propertyBox->count()) + return; + + QApplication::setOverrideCursor(Qt::WaitCursor); + + enableExtremeValues(); + + Scene_item* item = scene->item(scene->mainSelectionIndex()); + Scene_points_with_normal_item* ps_item = qobject_cast(item); + Scene_surface_mesh_item* sm_item = qobject_cast(item); + + if(sm_item) + colorizeSM(sm_item); + else if(ps_item) + colorizePS(ps_item); + + // @todo emit a new SIGNAL on successful coloring, something like "colorChanged()" + // itemChanged is too strong and would conflict with the connection below + + QApplication::restoreOverrideCursor(); + + // below is a hackish way to call the connection only once. + // This is required because the itemChanged signal is currently emitted when the colors are reset... + // + // @todo do not emit itemChanged when the colors are reset + // @todo with qt6, single connection can be performed with `static_cast(Qt::SingleShotConnection)` + // see https://www.kdab.com/single-shot-connections/ + auto connection = std::make_shared(); + *connection = connect(item, &Scene_surface_mesh_item::itemChanged, + this, [this, item, ps_item, sm_item, connection]() + { + QObject::disconnect(*connection); + + this->removeDisplayPluginProperties(item); // meaningful only for sm_item + + // @todo Scene_item doesn't have resetColors()... + if(ps_item) + ps_item->resetColors(); + else if(sm_item) + sm_item->resetColors(); + + if(item == scene->item(scene->mainSelectionIndex())) + onItemIndexSelected(scene->item_id(item)); + }); + } + +private: + void removeDisplayPluginProperty(Scene_item* item, + const std::string& property_name) + { + Scene_surface_mesh_item* sm_item = qobject_cast(item); + if(!sm_item) + return; + + SMesh* sm = sm_item->face_graph(); + if(sm == nullptr) + return; + + // Here we only target the property maps added by this plugin, so 'double' is fine + SMesh::Property_map property; + bool found; + std::tie(property, found) = sm->property_map(property_name); + if(found) + sm->remove_property_map(property); + } + + void removeDisplayPluginProperties(Scene_item* item) + { + removeDisplayPluginProperty(item, "f:display_plugin_smallest_angle"); + removeDisplayPluginProperty(item, "f:display_plugin_largest_angle"); + removeDisplayPluginProperty(item, "f:display_plugin_scaled_jacobian"); + removeDisplayPluginProperty(item, "f:display_plugin_area"); + } + + void displayExtremumAnglePerFace(Scene_surface_mesh_item* sm_item, + const Extremum extremum) + { + SMesh* sm = sm_item->face_graph(); + if(sm == nullptr) + return; + + // if the face is already a triangle, do not extract it from the mesh + auto triangular_face_sector_angle = [](halfedge_descriptor h, + const SMesh& mesh) -> double + { + auto vpm = get(boost::vertex_point, mesh); + return CGAL::approximate_angle(get(vpm, source(h, mesh)), + get(vpm, target(h, mesh)), + get(vpm, target(next(h, mesh), mesh))); + }; + + // for non-triangular faces, extract a one-face mesh and triangulate it + auto single_face_sector_angle = [](halfedge_descriptor h, + const SMesh& mesh) -> double + { + CGAL_precondition(!is_border(h, mesh) && is_border(opposite(h, mesh), mesh)); + + auto vpm = get(boost::vertex_point, mesh); + + double sector_angle = 0; + do + { + sector_angle += CGAL::approximate_angle(get(vpm, source(h, mesh)), + get(vpm, target(h, mesh)), + get(vpm, target(next(h, mesh), mesh))); + h = opposite(next(h, mesh), mesh); + } + while(!is_border(h, mesh)); + + return sector_angle; + }; + + bool not_initialized; + SMesh::Property_map fangle; + + if(extremum == MIN_VALUE) + std::tie(fangle, not_initialized) = sm->add_property_map("f:display_plugin_smallest_angle", 0); + else + std::tie(fangle, not_initialized) = sm->add_property_map("f:display_plugin_largest_angle", 0); + + SMesh& mesh = *sm; + auto vpm = get(boost::vertex_point, mesh); + + if(not_initialized) + { + for(face_descriptor f : faces(mesh)) + { + if(CGAL::is_triangle(halfedge(f, mesh), mesh)) + { + if(extremum == MIN_VALUE) + { + fangle[f] = (std::min)({triangular_face_sector_angle(halfedge(f, mesh), mesh), + triangular_face_sector_angle(next(halfedge(f, mesh), mesh), mesh), + triangular_face_sector_angle(prev(halfedge(f, mesh), mesh), mesh)}); + } + else + { + fangle[f] = (std::max)({triangular_face_sector_angle(halfedge(f, mesh), mesh), + triangular_face_sector_angle(next(halfedge(f, mesh), mesh), mesh), + triangular_face_sector_angle(prev(halfedge(f, mesh), mesh), mesh)}); + } + } + else + { + SMesh local_smesh; + auto local_vpm = get(boost::vertex_point, local_smesh); + std::vector local_vertices; + + for(halfedge_descriptor h : halfedges_around_face(halfedge(f, mesh), mesh)) + { + local_vertices.push_back(CGAL::add_vertex(local_smesh)); + put(local_vpm, local_vertices.back(), get(vpm, target(h, mesh))); + } + + face_descriptor local_f = CGAL::Euler::add_face(local_vertices, local_smesh); + + // walk the border of the face to walk the halfedge of the pre-triangulation face + halfedge_descriptor local_border_h = opposite(halfedge(local_f, local_smesh), local_smesh); + CGAL_assertion(is_border(local_border_h, local_smesh)); + + CGAL::Polygon_mesh_processing::triangulate_faces(local_smesh); + + double extremum_angle_in_face = ARBITRARY_DBL_MAX; + halfedge_descriptor local_border_end_h = local_border_h; + do + { + double angle = single_face_sector_angle(opposite(local_border_h, local_smesh), local_smesh); + if(extremum == MIN_VALUE) + extremum_angle_in_face = (std::min)(extremum_angle_in_face, angle); + else + extremum_angle_in_face = (std::max)(extremum_angle_in_face, angle); + + local_border_h = next(local_border_h, local_smesh); + } + while(local_border_h != local_border_end_h); + + fangle[f] = extremum_angle_in_face; + } + } + } + + if(extremum == MIN_VALUE) + displaySMProperty("f:display_plugin_smallest_angle", mesh); + else + displaySMProperty("f:display_plugin_largest_angle", mesh); + } + + double scaled_jacobian(const face_descriptor f, + const SMesh& mesh) const; + + void displayScaledJacobian(Scene_surface_mesh_item* sm_item) + { + SMesh* sm = sm_item->face_graph(); + if(sm == nullptr) + return; + + bool not_initialized; + SMesh::Property_map fjacobian; + std::tie(fjacobian, not_initialized) = sm->add_property_map("f:display_plugin_scaled_jacobian", 0); + + if(not_initialized) + { + for(face_descriptor f : faces(*sm)) + fjacobian[f] = scaled_jacobian(f, *sm); + } + + displaySMProperty("f:display_plugin_scaled_jacobian", *sm); + } + + double area(const face_descriptor f, + const SMesh& mesh) const + { + if(CGAL::is_triangle(halfedge(f, mesh), mesh)) + return CGAL::Polygon_mesh_processing::face_area(f, mesh); + + auto vpm = get(boost::vertex_point, mesh); + + // create a local version of the mesh, triangulate it, sum the triangle areas + SMesh local_smesh; + auto local_vpm = get(boost::vertex_point, local_smesh); + std::vector local_vertices; + + for(halfedge_descriptor h : halfedges_around_face(halfedge(f, mesh), mesh)) + { + local_vertices.push_back(CGAL::add_vertex(local_smesh)); + put(local_vpm, local_vertices.back(), get(vpm, target(h, mesh))); + } + + CGAL::Euler::add_face(local_vertices, local_smesh); + CGAL::Polygon_mesh_processing::triangulate_faces(local_smesh); + return CGAL::Polygon_mesh_processing::area(local_smesh); + } + + void displayArea(Scene_surface_mesh_item* sm_item) + { + SMesh* sm = sm_item->face_graph(); + if(sm == nullptr) + return; + + bool not_initialized; + SMesh::Property_map farea; + std::tie(farea, not_initialized) = sm->add_property_map("f:display_plugin_area", 0); + + if(not_initialized) + { + for(face_descriptor f : faces(*sm)) + farea[f] = area(f, *sm); + } + + displaySMProperty("f:display_plugin_area", *sm); + } + +private: + template + bool call_on_PS_property(const std::string& name, + const Point_set& ps, + const Functor& functor) const; + + template + bool call_on_SM_property(const std::string& name, + const SMesh& sm, + const Functor& functor) const; + +private: + template + bool displayPSProperty(Point_set& ps, + PM pm) + { + PSDisplayer display_property(ps, pm, this); + return display_property(); + } + + bool displayPSProperty(const std::string& name, + Point_set& ps); + + // - + template + bool displaySMProperty(SMesh& mesh, + PM pm, + vertex_descriptor) + { + SMVertexDisplayer display_property(mesh, pm, this); + return display_property(); + } + + template + bool displaySMProperty(SMesh& mesh, + PM pm, + face_descriptor) + { + SMFaceDisplayer display_property(mesh, pm, this); + return display_property(); + } + + template + bool displaySMProperty(const std::string& name, + SMesh& mesh); + +private: + template + auto SimplexWithPropertyExtremum(const SimplexRange& simplex_range, + const SMesh& mesh, + const std::string& property_name, + const Extremum extremum) const + { + using Simplex = typename boost::range_value::type; + + // We don't know what's the type of the property map so we can't simply do + // mesh.property_map(property_name), + // we have to try all acceptable types. + Simplex extremum_s; + call_on_SM_property(property_name, mesh, + [extremum, &extremum_s, &simplex_range](const auto pmap) -> bool + { + double extremum_value = (extremum == MIN_VALUE) ? ARBITRARY_DBL_MAX : - ARBITRARY_DBL_MAX; + + for(Simplex s : simplex_range) + { + if((extremum == MIN_VALUE && get(pmap, s) < extremum_value) || + (extremum == MAX_VALUE && get(pmap, s) > extremum_value)) + { + extremum_value = get(pmap, s); + extremum_s = s; + } + } + + return true; + }); + + CGAL_assertion(extremum_s != Simplex()); + return extremum_s; + } + + template + void zoomToSimplexWithPropertyExtremum(const SimplexRange& simplex_range, + const SMesh& mesh, + const std::string& property_name, + const Extremum extremum) const + { + using Simplex = typename boost::range_value::type; + + const Simplex extremum_s = SimplexWithPropertyExtremum(simplex_range, mesh, property_name, extremum); + + QString sid; + if(std::is_same::value) + sid = QString("v%1").arg(extremum_s); + else + sid = QString("f%1").arg(extremum_s); + + face_descriptor unused_fd; + Point_3 unused_p; + ::zoomToId(mesh, sid, + getActiveViewer(), + unused_fd, unused_p); + }; + + void zoomToPointWithPropertyExtremum(const Point_set& ps, + const std::string& property_name, + const Extremum extremum) const + { + Point_set::Index extremum_i = -1; + call_on_PS_property(property_name, ps, + [extremum, &extremum_i, &ps](const auto pmap) -> bool + { + double extremum_value = (extremum == MIN_VALUE) ? ARBITRARY_DBL_MAX : - ARBITRARY_DBL_MAX; + + for(Point_set::Index i : ps) + { + if((extremum == MIN_VALUE && get(pmap, i) < extremum_value) || + (extremum == MAX_VALUE && get(pmap, i) > extremum_value)) + { + extremum_value = get(pmap, i); + extremum_i = i; + } + } + + return true; + }); + + CGAL_assertion(extremum_i != Point_set::Index(-1)); + Point_3 unused_p; + ::zoomToPoint(ps, extremum_i, + getActiveViewer(), + unused_p); + } + + void on_zoomToButton_pressed(const Extremum extremum) + { + const int property_index = dock_widget->propertyBox->currentIndex(); + if(property_index < 0 || property_index >= dock_widget->propertyBox->count()) + return; + + Scene_item* item = scene->item(scene->mainSelectionIndex()); + Scene_surface_mesh_item* sm_item = qobject_cast(item); + if(sm_item) + { + const SMesh& mesh = *sm_item->face_graph(); + + const std::string& property_name = dock_widget->propertyBox->currentText().toStdString(); + if(property_name == "Smallest Angle Per Face") + zoomToSimplexWithPropertyExtremum(faces(mesh), mesh, "f:display_plugin_smallest_angle", extremum); + else if(property_name == "Largest Angle Per Face") + zoomToSimplexWithPropertyExtremum(faces(mesh), mesh, "f:display_plugin_largest_angle", extremum); + else if(property_name == "Scaled Jacobian") + zoomToSimplexWithPropertyExtremum(faces(mesh), mesh, "f:display_plugin_scaled_jacobian", extremum); + else if(property_name == "Face Area") + zoomToSimplexWithPropertyExtremum(faces(mesh), mesh, "f:display_plugin_area", extremum); + else if(property_simplex_types.at(property_index) == Property_simplex_type::VERTEX) + zoomToSimplexWithPropertyExtremum(vertices(mesh), mesh, property_name, extremum); + else if(property_simplex_types.at(property_index) == Property_simplex_type::FACE) + zoomToSimplexWithPropertyExtremum(faces(mesh), mesh, property_name, extremum); + } + + Scene_points_with_normal_item* ps_item = qobject_cast(item); + if(ps_item) + { + const Point_set& ps = *ps_item->point_set(); + + const std::string& property_name = dock_widget->propertyBox->currentText().toStdString(); + zoomToPointWithPropertyExtremum(ps, property_name, extremum); + } + } + +private Q_SLOTS: + void on_zoomToMinButton_pressed() + { + on_zoomToButton_pressed(MIN_VALUE); + } + + void on_zoomToMaxButton_pressed() + { + on_zoomToButton_pressed(MAX_VALUE); + } + +private: + template + friend class PropertyDisplayer; + + // CRTP used to display properties of surface meshes (vertex and face) and point sets. template struct PropertyDisplayer { - typedef typename PM::value_type Value_type; - PropertyDisplayer(DataSet& ds, PM pm, DisplayPropertyPlugin* parent) - :dataset(ds), property_map(pm), parent(parent) - {} + using value_type = typename PM::value_type; + + DataSet& dataset; + PM property_map; + std::vector values; + Display_property_plugin* parent; + + PropertyDisplayer(DataSet& ds, + PM pm, + Display_property_plugin* parent) + : dataset(ds), property_map(pm), parent(parent) + { } - virtual void fill_values(){} - virtual void set_colors_map(std::unordered_map &){} - virtual void set_colors_ramp(){} bool operator()() { - parent->minBox = ARBITRARY_DBL_MAX; - parent->maxBox = -ARBITRARY_DBL_MAX; static_cast(this)->fill_values(); std::sort(values.begin(), values.end()); auto end = std::unique(values.begin(), values.end()); + parent->dock_widget->minBox->setValue(*values.begin()); + parent->dock_widget->maxBox->setValue(*(std::prev(end))); - parent->minBox = *values.begin(); - parent->maxBox = *(end-1); - parent->dock_widget->minBox->setValue(parent->minBox); - parent->dock_widget->maxBox->setValue(parent->maxBox); - - //fill color pmap - if(parent->dock_widget->colorChoiceWidget->currentIndex() == 1) + // fill color pmap + if(parent->dock_widget->colorRampRadioButton->isChecked()) { - std::unordered_map value_index_map; - //fill map - std::size_t counter = 0; - for(auto it = values.begin(); it != end; ++it) - { - value_index_map[*it] = counter++; - } - parent->color_map.clear(); - compute_color_map(QColor(parent->rI, parent->gI, parent->bI),std::distance(values.begin(), end), - std::back_inserter(parent->color_map)); - static_cast(this)->set_colors_map(value_index_map); - parent->displayMapLegend(values); + // scale a color ramp between min and max + parent->displayRampLegend(); + static_cast(this)->color_with_ramp(); } else { - //scale a color ramp between min and max - parent->replaceRamp(); - static_cast(this)->set_colors_ramp(); + CGAL_assertion(parent->dock_widget->randomColorsRadioButton->isChecked()); + + // generate color map + parent->color_map.clear(); + compute_color_map(QColor(255 * parent->rI, 255 * parent->gI, 255 * parent->bI), + std::distance(values.begin(), end), + std::back_inserter(parent->color_map)); + + // fill map + std::unordered_map value_index_map; + std::size_t counter = 0; + for(auto it=values.begin(); it!=end; ++it) + value_index_map[*it] = counter++; + + static_cast(this)->color_with_map(value_index_map); + parent->displayMapLegend(values); } + return true; } - - - DataSet& dataset; - PM property_map; - std::vector values; - DisplayPropertyPlugin* parent; - }; + }; // struct PropertyDisplayer template struct PSDisplayer : public PropertyDisplayer > { - typedef typename PM::value_type Value_type; - typedef PropertyDisplayer > Base; - PSDisplayer(Point_set& ds, PM pm, DisplayPropertyPlugin* parent) - :Base(ds, pm, parent) + using Base = PropertyDisplayer >; + using value_type = typename PM::value_type; + + PSDisplayer(Point_set& ps, + PM pm, + Display_property_plugin* parent) + : Base(ps, pm, parent) {} + void fill_values() { - for(auto p : this->dataset) - { + for(const auto& p : this->dataset) this->values.push_back(this->property_map[p]); - } } - void set_colors_map(std::unordered_map &value_index_map) + void color_with_map(std::unordered_map& value_index_map) { - for(Point_set::iterator pit = this->dataset.begin(); - pit != this->dataset.end(); - ++pit) + for(const auto& p : this->dataset) { - CGAL::IO::Color color( - this->parent->color_map[value_index_map[this->property_map[*pit]]].red(), - this->parent->color_map[value_index_map[this->property_map[*pit]]].green(), - this->parent->color_map[value_index_map[this->property_map[*pit]]].blue()); - this->dataset.set_color(*pit, color.red(), color.green(), color.blue()); + CGAL::IO::Color color(this->parent->color_map[value_index_map[this->property_map[p]]].red(), + this->parent->color_map[value_index_map[this->property_map[p]]].green(), + this->parent->color_map[value_index_map[this->property_map[p]]].blue()); + this->dataset.set_color(p, color.red(), color.green(), color.blue()); } } - void set_colors_ramp() + void color_with_ramp() const { - float max = this->parent->maxBox; - float min = this->parent->minBox; - for(Point_set::iterator pit = this->dataset.begin(); - pit != this->dataset.end(); - ++pit) + double min = this->parent->dock_widget->minBox->value(); + double max = this->parent->dock_widget->maxBox->value(); + for(const auto& p : this->dataset) { if(min == max) - --min; - float f = (static_cast(this->property_map[*pit])-min)/(max-min); - if(f<0) - f = 0; - if(f>1) - f = 1; - CGAL::IO::Color color( - 255*this->parent->color_ramp.r(f), - 255*this->parent->color_ramp.g(f), - 255*this->parent->color_ramp.b(f)); - this->dataset.set_color(*pit, color.red(), color.green(), color.blue()); + min -= 1.; + + double val = (this->property_map[p] - min) / (max - min); + val = boost::algorithm::clamp(val, 0., 1.); + + CGAL::IO::Color color(255 * this->parent->color_ramp.r(val), + 255 * this->parent->color_ramp.g(val), + 255 * this->parent->color_ramp.b(val)); + this->dataset.set_color(p, color.red(), color.green(), color.blue()); } } - - }; - + }; // PSDisplayer template struct SMVertexDisplayer : public PropertyDisplayer > { - typedef typename PM::value_type Value_type; - typedef PropertyDisplayer > Base; - SMVertexDisplayer(SMesh& ds, PM pm, DisplayPropertyPlugin* parent) - :Base(ds, pm, parent) + using Base = PropertyDisplayer > ; + using value_type = typename PM::value_type; + + SMVertexDisplayer(SMesh& mesh, + PM pm, + Display_property_plugin* parent) + : Base(mesh, pm, parent) {} void fill_values() { - for(auto v : vertices(this->dataset)) - { + for(vertex_descriptor v : vertices(this->dataset)) this->values.push_back(this->property_map[v]); - } } - void set_colors_map(std::unordered_map &value_index_map) + void color_with_map(std::unordered_map& value_index_map) const { SMesh::Property_map vcolors = this->dataset.template add_property_map("v:color", CGAL::IO::Color()).first; - for(boost::graph_traits::vertex_iterator vit = vertices(this->dataset).begin(); - vit != vertices(this->dataset).end(); - ++vit) + for(vertex_descriptor v : vertices(this->dataset)) { - CGAL::IO::Color color( - this->parent->color_map[value_index_map[this->property_map[*vit]]].red(), - this->parent->color_map[value_index_map[this->property_map[*vit]]].green(), - this->parent->color_map[value_index_map[this->property_map[*vit]]].blue()); - vcolors[*vit] = color; + CGAL::IO::Color color(this->parent->color_map[value_index_map[this->property_map[v]]].red(), + this->parent->color_map[value_index_map[this->property_map[v]]].green(), + this->parent->color_map[value_index_map[this->property_map[v]]].blue()); + vcolors[v] = color; } } - void set_colors_ramp() + void color_with_ramp() const { SMesh::Property_map vcolors = this->dataset.template add_property_map("v:color", CGAL::IO::Color()).first; - float max = this->parent->maxBox; - float min = this->parent->minBox; - for(boost::graph_traits::vertex_iterator vit = vertices(this->dataset).begin(); - vit != vertices(this->dataset).end(); - ++vit) + + double min = this->parent->dock_widget->minBox->value(); + double max = this->parent->dock_widget->maxBox->value(); + + if(min == max) + min -= 1.; + + for(vertex_descriptor v : vertices(this->dataset)) { - if(min == max) - --min; - float f = (static_cast(this->property_map[*vit])-min)/(max-min); - if(f<0) - f = 0; - if(f>1) - f = 1; - CGAL::IO::Color color( - 255*this->parent->color_ramp.r(f), - 255*this->parent->color_ramp.g(f), - 255*this->parent->color_ramp.b(f)); - vcolors[*vit] = color; + double val = (this->property_map[v] - min) / (max - min); + val = boost::algorithm::clamp(val, 0., 1.); + + CGAL::IO::Color color(255 * this->parent->color_ramp.r(val), + 255 * this->parent->color_ramp.g(val), + 255 * this->parent->color_ramp.b(val)); + vcolors[v] = color; } } - }; + }; // struct SMVertexDisplayer template struct SMFaceDisplayer : public PropertyDisplayer > { - typedef PropertyDisplayer > Base; - typedef typename PM::value_type Value_type; - SMFaceDisplayer(SMesh& ds, PM pm, DisplayPropertyPlugin* parent) - :Base(ds, pm, parent) - {} + using Base = PropertyDisplayer >; + using value_type = typename PM::value_type; + + SMFaceDisplayer(SMesh& mesh, + PM pm, + Display_property_plugin* parent) + : Base(mesh, pm, parent) + { } + void fill_values() { - for(auto f : faces(this->dataset)) - { + for(face_descriptor f : faces(this->dataset)) this->values.push_back(this->property_map[f]); - } } - void set_colors_map(std::unordered_map &value_index_map) + void color_with_map(std::unordered_map& value_index_map) const { SMesh::Property_map fcolors = this->dataset.template add_property_map("f:color", CGAL::IO::Color()).first; - for(boost::graph_traits::face_iterator fit = faces(this->dataset).begin(); - fit != faces(this->dataset).end(); - ++fit) + for(face_descriptor f : faces(this->dataset)) { - CGAL::IO::Color color( - this->parent->color_map[value_index_map[this->property_map[*fit]]].red(), - this->parent->color_map[value_index_map[this->property_map[*fit]]].green(), - this->parent->color_map[value_index_map[this->property_map[*fit]]].blue()); - fcolors[*fit] = color; + CGAL::IO::Color color(this->parent->color_map[value_index_map[this->property_map[f]]].red(), + this->parent->color_map[value_index_map[this->property_map[f]]].green(), + this->parent->color_map[value_index_map[this->property_map[f]]].blue()); + fcolors[f] = color; } } - void set_colors_ramp() + void color_with_ramp() const { SMesh::Property_map fcolors = this->dataset.template add_property_map("f:color", CGAL::IO::Color()).first; - float max = this->parent->maxBox; - float min = this->parent->minBox; - for(boost::graph_traits::face_iterator fit = faces(this->dataset).begin(); - fit != faces(this->dataset).end(); - ++fit) + + double min = this->parent->dock_widget->minBox->value(); + double max = this->parent->dock_widget->maxBox->value(); + + if(min == max) + min -= 1.; + + for(face_descriptor f : faces(this->dataset)) { - if(min == max) - --min; - float f = (static_cast(this->property_map[*fit])-min)/(max-min); - if(f<0) - f = 0; - if(f>1) - f = 1; - CGAL::IO::Color color( - 255*this->parent->color_ramp.r(f), - 255*this->parent->color_ramp.g(f), - 255*this->parent->color_ramp.b(f)); - fcolors[*fit] = color; + double val = (this->property_map[f] - min) / (max - min); + val = boost::algorithm::clamp(val, 0., 1.); + + CGAL::IO::Color color(255 * this->parent->color_ramp.r(val), + 255 * this->parent->color_ramp.g(val), + 255 * this->parent->color_ramp.b(val)); + fcolors[f] = color; } } - }; - + }; // struct SMFaceDisplayer }; /// Code based on the verdict module of vtk - /*========================================================================= +/*========================================================================= Copyright (c) 2006 Sandia Corporation. All rights reserved. See Copyright.txt or https://www.kitware.com/Copyright.htm for details. @@ -1701,381 +1227,170 @@ private: PURPOSE. See the above copyright notice for more information. =========================================================================*/ - double DisplayPropertyPlugin::scaled_jacobian( const face_descriptor& f , const SMesh& mesh) +double +Display_property_plugin:: +scaled_jacobian(const face_descriptor f, + const SMesh& mesh) const +{ + boost::property_map::type vpm = get(boost::vertex_point, mesh); + std::vector corner_areas(degree(f, mesh)); + + std::vector edges; + for(halfedge_descriptor hd : CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) { - boost::property_map::type - pmap = get(boost::vertex_point, mesh); - std::vector corner_areas(degree(f, mesh)); - std::vector edges; - for(halfedge_descriptor hd : CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) - { - edges.push_back(EPICK::Vector_3(get(pmap, source(hd, mesh)), get(pmap, target(hd, mesh)))); - } - std::vector corner_normals; - for(std::size_t i = 0; i < edges.size(); ++i) - { - corner_normals.push_back(CGAL::cross_product(edges[i], edges[(i+1)%(edges.size())])); - } - - - EPICK::Vector_3 unit_center_normal = CGAL::Polygon_mesh_processing::compute_face_normal(f, mesh); - unit_center_normal *= 1.0/CGAL::approximate_sqrt(unit_center_normal.squared_length()); - - for(std::size_t i = 0; i < corner_areas.size(); ++i) - { - corner_areas[i] = unit_center_normal*corner_normals[i]; - } - std::vector length; - for(std::size_t i=0; i 0 ) - return (double) (std::min)( min_scaled_jac, ARBITRARY_DBL_MAX ); - return (double) (std::max)( min_scaled_jac, -ARBITRARY_DBL_MAX ); - + edges.emplace_back(get(vpm, source(hd, mesh)), + get(vpm, target(hd, mesh))); } - bool DisplayPropertyPlugin::is_property_scalar(std::string name, const Point_set* ps) - { - if(name == "red" - || name == "green" - || name == "blue") - { - return false; - } + std::vector corner_normals; + for(std::size_t i=0; itemplate property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } - if(ps->template property_map(name).second) - { - return true; - } + EPICK::Vector_3 unit_center_normal = CGAL::Polygon_mesh_processing::compute_face_normal(f, mesh); + + for(std::size_t i=0; i length; + for(std::size_t i=0; i 0) + return (std::min)(min_scaled_jac, ARBITRARY_DBL_MAX); + + return (std::max)(min_scaled_jac, -ARBITRARY_DBL_MAX); +} + +bool +Display_property_plugin:: +isPSPropertyScalar(const std::string& name, + const Point_set& ps) const +{ + if(name == "red" || name == "green" || name == "blue") return false; - } - template - bool DisplayPropertyPlugin::is_property_scalar(std::string name, const SMesh* sm) - { + // the dispatch function does the filtering we want: if it founds a property + // with which it can call the functor, then it already has a property we want + return call_on_PS_property(name, ps, [](auto) -> bool { return true; }); +} - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } - if(sm->template property_map(name).second) - { - return true; - } +// Shenanigans to deal with the fact that the value type is not known +// +// find the property map with the correct value, and call the functor +template +bool +Display_property_plugin:: +isSMPropertyScalar(const std::string& name, + const SMesh& mesh) const +{ + // do not detect this plugin's properties + if(name == "f:display_plugin_smallest_angle" || + name == "f:display_plugin_largest_angle" || + name == "f:display_plugin_scaled_jacobian" || + name == "f:display_plugin_area") return false; - } - bool DisplayPropertyPlugin::treat_point_property(std::string name, Point_set* ps) - { - typedef typename Point_set::template Property_map Int8_map; - typedef typename Point_set::template Property_map Uint8_map; - typedef typename Point_set::template Property_map Int16_map; - typedef typename Point_set::template Property_map Uint16_map; - typedef typename Point_set::template Property_map Int32_map; - typedef typename Point_set::template Property_map Uint32_map; - typedef typename Point_set::template Property_map Int64_map; - typedef typename Point_set::template Property_map Uint64_map; - typedef typename Point_set::template Property_map Float_map; - typedef typename Point_set::template Property_map Double_map; + // the dispatch function does the filtering we want: if it founds a property + // with which it can call the functor, then it already has a property we want + return call_on_SM_property(name, mesh, [](auto) -> bool { return true; }); +} - bool okay = false; - { - Int8_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } +bool +Display_property_plugin:: +displayPSProperty(const std::string& name, + Point_set& ps) +{ + return call_on_PS_property(name, ps, + [this, &ps](auto pmap) -> bool + { + return this->displayPSProperty(ps, pmap); + }); +} - { - Uint8_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } +template +bool +Display_property_plugin:: +displaySMProperty(const std::string& name, + SMesh& mesh) +{ + return call_on_SM_property(name, mesh, + [this, &mesh](auto pmap) -> bool + { + return this->displaySMProperty(mesh, pmap, Simplex()); + }); +} - { - Int16_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } +template +bool +Display_property_plugin:: +call_on_PS_property(const std::string& name, + const Point_set& ps, + const Functor& functor) const +{ + if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); - { - Uint16_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } + return false; +} - { - Int32_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } +template +bool +Display_property_plugin:: +call_on_SM_property(const std::string& name, + const SMesh& mesh, + const Functor& functor) const +{ + if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); + else if(mesh.template property_map(name).second) + return functor(mesh.template property_map(name).first); - { - Uint32_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } - - { - Int64_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } - - { - Uint64_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } - - { - Float_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } - - { - Double_map pmap; - std::tie(pmap, okay) = ps->property_map(name); - if(okay) - { - return displayPSProperty(ps, pmap); - } - } - return false; - } - - template - bool DisplayPropertyPlugin::treat_sm_property(std::string name, SMesh* sm) - { - typedef typename SMesh::template Property_map Int8_map; - typedef typename SMesh::template Property_map Uint8_map; - typedef typename SMesh::template Property_map Int16_map; - typedef typename SMesh::template Property_map Uint16_map; - typedef typename SMesh::template Property_map Int32_map; - typedef typename SMesh::template Property_map Uint32_map; - typedef typename SMesh::template Property_map Int64_map; - typedef typename SMesh::template Property_map Uint64_map; - typedef typename SMesh::template Property_map Float_map; - typedef typename SMesh::template Property_map Double_map; - - bool okay = false; - { - Int8_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Uint8_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Int16_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Uint16_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Int32_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Uint32_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Int64_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Uint64_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Float_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - - { - Double_map pmap; - std::tie(pmap, okay) = sm->property_map(name); - if(okay) - { - return displaySMProperty(*sm, pmap, TAG()); - } - } - return false; - } - - template - bool DisplayPropertyPlugin::displayPSProperty(Point_set* ps, PM pm) - { - PSDisplayer display_property(*ps, pm, this); - return display_property(); - } - - template - bool DisplayPropertyPlugin::displaySMProperty(SMesh& smesh, PM pm, vertex_descriptor) - { - SMVertexDisplayer display_property(smesh, pm, this); - return display_property(); - } - - template - bool DisplayPropertyPlugin::displaySMProperty(SMesh& smesh, PM pm, face_descriptor) - { - SMFaceDisplayer display_property(smesh, pm, this); - return display_property(); - } + return false; +} #include "Display_property_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method.ui b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method.ui new file mode 100644 index 00000000000..0c7440b2236 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method.ui @@ -0,0 +1,294 @@ + + + HeatMethodWidget + + + true + + + + 0 + 0 + 365 + 708 + + + + + 365 + 708 + + + + Heat Method + + + + + + + + + true + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + true + + + + + 0 + 0 + 161 + 371 + + + + + + + true + + + RAMP DISPLAYING + + + + + + + + + + + true + + + Color Ramp + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + Min color + + + + + + + true + + + + + + + + + + + + + true + + + Max color + + + + + + + true + + + + + + + + + + + + + true + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + true + + + Method + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + true + + + + + + + + + + true + + + Extreme Value + + + + + + true + + + Zoom to max value + + + + + + + true + + + Max Value + + + + + + + true + + + 0 + + + true + + + + + + + + + + true + + + + Cantarell + true + + + + Estimate Geodesic Distances + + + + + + + true + + + Source Vertices + + + + + + true + + + + + + 1 + + + Create Source Vertices Selection Item + + + + + + + + + + + + DoubleEdit + QLineEdit +
CGAL_double_edit.h
+
+
+ + +
diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp new file mode 100644 index 00000000000..4de8e8fe41f --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp @@ -0,0 +1,822 @@ +#include "ui_Heat_method.h" + +#include "Color_ramp.h" +#include "id_printing.h" +#include "Messages_interface.h" +#include "triangulate_primitive.h" + +#include "Scene.h" +#include "Scene_polyhedron_selection_item.h" +#include "Scene_surface_mesh_item.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// @fixme multiple selection items are broken, so don't create a selection item if there is already one + +using namespace CGAL::Three; + +Viewer_interface* (&getActiveViewer)() = Three::activeViewer; + +class Scene_heat_item + : public Scene_item_rendering_helper +{ + Q_OBJECT + +private: + Scene_surface_mesh_item* parent; + SMesh* sm; + + mutable std::vector verts; + mutable std::vector normals; + mutable std::vector idx; + mutable std::vector colors; + mutable std::vector heat_values; + mutable std::size_t nb_idx; + +public: + Scene_heat_item(Scene_surface_mesh_item* item) + : parent(item), sm(item->face_graph()) + { + CGAL_precondition(is_triangle_mesh(*sm)); + + setTriangleContainer(0, new Triangle_container(Viewer_interface::PROGRAM_HEAT_INTENSITY, true)); + setRenderingMode(Gouraud); + } + + ~Scene_heat_item(){} + + Scene_item* clone() const Q_DECL_OVERRIDE { return nullptr; } + QString toolTip() const Q_DECL_OVERRIDE{ return QString(); } + Scene_surface_mesh_item* getParent() { return parent; } + bool isEmpty() const Q_DECL_OVERRIDE { return false; } + + SMesh* face_graph() { return sm; } + + void initializeBuffers(Viewer_interface *viewer) const Q_DECL_OVERRIDE + { + getTriangleContainer(0)->initializeBuffers(viewer); + getTriangleContainer(0)->setIdxSize(nb_idx); + + verts.resize(0); + verts.shrink_to_fit(); + normals.resize(0); + normals.shrink_to_fit(); + colors.resize(0); + colors.shrink_to_fit(); + idx.clear(); + idx.shrink_to_fit(); + } + + void draw(Viewer_interface *viewer) const Q_DECL_OVERRIDE + { + if(!visible()) + return; + + if(!isInit(viewer)) + initGL(viewer); + + if(getBuffersFilled() && !getBuffersInit(viewer)) + { + initializeBuffers(viewer); + setBuffersInit(viewer, true); + } + + if(!getBuffersFilled()) + { + computeElements(); + initializeBuffers(viewer); + } + + getTriangleContainer(0)->setAlpha(1.0f); + getTriangleContainer(0)->draw(viewer, false); + } + + void compute_bbox() const Q_DECL_OVERRIDE + { + setBbox(parent->bbox()); + } + + virtual bool supportsRenderingMode(RenderingMode m) const Q_DECL_OVERRIDE + { + return (m == Gouraud); + } + + virtual void invalidateOpenGLBuffers() Q_DECL_OVERRIDE + { + setBuffersFilled(false); + compute_bbox(); + getTriangleContainer(0)->reset_vbos(NOT_INSTANCED); + } + + void computeElements() const Q_DECL_OVERRIDE + { + typedef CGAL::Buffer_for_vao CPF; + + QApplication::setOverrideCursor(Qt::WaitCursor); + + auto vpm = CGAL::get(CGAL::vertex_point, *sm); + auto vnormals = sm->property_map("v:normal").first; + auto fnormals = sm->property_map("f:normal").first; + + auto [vcolors, vcolors_found] = sm->property_map("v:color"); + CGAL_assertion(vcolors_found); + auto [vdist, vdist_found] = sm->property_map("v:HM_Plugin_heat_intensity"); + CGAL_assertion(vdist_found); + + verts.clear(); + normals.clear(); + idx.clear(); + colors.clear(); + + idx.reserve(3 * num_faces(*sm)); + for(face_descriptor fd : faces(*sm)) + { + for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *sm),*sm)) + idx.push_back(source(hd, *sm)); + } + + const CGAL::qglviewer::Vec& o = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); + EPICK::Vector_3 offset(o.x, o.y, o.z); + + for(vertex_descriptor vd : vertices(*sm)) + { + const CGAL::IO::Color& c = vcolors[vd]; + colors.push_back(float(c.red()) / 255); + colors.push_back(float(c.green()) / 255); + colors.push_back(float(c.blue()) / 255); + + const EPICK::Point_3& p = get(vpm, vd) + offset; + CPF::add_point_in_buffer(p, verts); + + const EPICK::Vector_3& n = vnormals[vd]; + CPF::add_normal_in_buffer(n, normals); + + heat_values.push_back(vdist[vd]); + } + + nb_idx = idx.size(); + getTriangleContainer(0)->allocate(Triangle_container::Vertex_indices, idx.data(), + static_cast(idx.size()*sizeof(unsigned int))); + getTriangleContainer(0)->allocate(Triangle_container::Smooth_vertices, verts.data(), + static_cast(num_vertices(*sm)*3*sizeof(float))); + getTriangleContainer(0)->allocate(Triangle_container::Smooth_normals, normals.data(), + static_cast(num_vertices(*sm)*3*sizeof(float))); + getTriangleContainer(0)->allocate(Triangle_container::VColors, colors.data(), + static_cast(colors.size()*sizeof(float))); + getTriangleContainer(0)->allocate(Triangle_container::Distances, heat_values.data(), + static_cast(heat_values.size()*sizeof(float))); + + compute_bbox(); + setBuffersFilled(true); + + QApplication::restoreOverrideCursor(); + } +}; // class Scene_heat_item + +class DockWidget + : public QDockWidget, + public Ui::HeatMethodWidget +{ +public: + DockWidget(const QString& name, QWidget *parent) + : QDockWidget(name, parent) + { + setupUi(this); + } +}; + +class Heat_method_plugin + : public QObject, + public Polyhedron_demo_plugin_helper +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + + using Vertex_distance_map = SMesh::Property_map; + using Heat_method = CGAL::Heat_method_3::Surface_mesh_geodesic_distances_3; + using Heat_method_idt = CGAL::Heat_method_3::Surface_mesh_geodesic_distances_3; + +private: + QAction* actionHeatMethod; + + DockWidget* dock_widget; + + // coloring choice and legend + double rm = 1.; + double gm = 0.; + double bm = 0.; + double rM = 0.; + double gM = 1.; + double bM = 0.; + + Color_ramp color_ramp; + QPixmap legend; + + // tracking which scene items have which sources, and which heat method builders + boost::bimap, + boost::bimaps::set_of > item_source_vertices; + + // the point of storing this is that in the Heat Method(s), a number of computations are only + // dependent on the mesh, and not the sources, and such can be performed just once. + std::unordered_map heat_methods; + std::unordered_map idt_heat_methods; + +public: + bool applicable(QAction*) const Q_DECL_OVERRIDE + { + // Single item => it must be a mesh and the selection item will be created through the plugin's button + if(scene->selectionIndices().size() == 1) + { + Scene_item* item = scene->item(scene->mainSelectionIndex()); + return qobject_cast(item); + } + // Two items => it must be a surface mesh and a selection item (in any order) + else if(scene->selectionIndices().size() == 2) + { + Scene_item* item1 = scene->item(scene->selectionIndices().front()); + Scene_item* item2 = scene->item(scene->selectionIndices().back()); + return ((qobject_cast(item1) && + qobject_cast(item2)) || + (qobject_cast(item1) && + qobject_cast(item2))); + } + + return false; + } + + QList actions() const Q_DECL_OVERRIDE + { + return QList() << actionHeatMethod; + } + + void init(QMainWindow* mw, + Scene_interface* sc, + Messages_interface*) Q_DECL_OVERRIDE + { + this->scene = sc; + this->mw = mw; + + actionHeatMethod = new QAction(QString("Heat Method"), mw); + actionHeatMethod->setProperty("submenuName", "Color"); + + connect(actionHeatMethod, SIGNAL(triggered()), + this, SLOT(openDialog())); + + Scene* scene_item = static_cast(scene); + connect(scene_item, SIGNAL(itemIndicesSelected(QList)), + this, SLOT(onItemIndicesSelected(QList))); + + // Dock Widget + dock_widget = new DockWidget("Heat Method", mw); + addDockWidget(dock_widget); + dock_widget->setVisible(false); + + connect(dock_widget->methodBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(onNewMethodSelected(int))); + + dock_widget->methodBox->addItems({"Heat Method", + "Heat Method (Intrinsic Delaunay)"}); + + connect(dock_widget->createSourceVerticesButton, SIGNAL(clicked()), + this, SLOT(createSourceVerticesSelectionItem())); + + QPalette palette(Qt::red); + dock_widget->minColorButton->setPalette(palette); + dock_widget->minColorButton->setStyle(QStyleFactory::create("Fusion")); + dock_widget->minColorButton->update(); + + palette = QPalette(Qt::green); + dock_widget->maxColorButton->setPalette(palette); + dock_widget->maxColorButton->setStyle(QStyleFactory::create("Fusion")); + dock_widget->maxColorButton->update(); + + // lambda to generate the three connect(), for each color button (min, max, map) + auto connect_color_buttons = [this](QPushButton* colorButton, + double& r, double& g, double& b) + { + connect(colorButton, &QPushButton::pressed, + this, [this, colorButton, &r, &g, &b]() + { + QColor color = QColorDialog::getColor(); + if(!color.isValid()) + return; + + r = color.redF(); + g = color.greenF(); + b = color.blueF(); + + QPalette palette(color); + colorButton->setPalette(palette); + colorButton->update(); + + displayRampLegend(); + }); + }; + + connect_color_buttons(dock_widget->minColorButton, rm, gm, bm); + connect_color_buttons(dock_widget->maxColorButton, rM, gM, bM); + + // Main action connection + connect(dock_widget->estimateDistancesButton, SIGNAL(clicked(bool)), + this, SLOT(estimateDistances())); + + // Post coloring connection + connect(dock_widget->zoomToMaxButton, &QPushButton::pressed, + this, &Heat_method_plugin::on_zoomToMaxButton_pressed); + } + +private Q_SLOTS: + void openDialog() + { + if(!dock_widget->isVisible()) + dock_widget->show(); + dock_widget->raise(); + } + + void closure() Q_DECL_OVERRIDE + { + dock_widget->hide(); + } + +private: + void disableExtremeValue() + { + dock_widget->extremeValueGroup->setEnabled(false); + dock_widget->zoomToMaxButton->setEnabled(false); + } + + void enableExtremeValue() + { + dock_widget->extremeValueGroup->setEnabled(true); + dock_widget->zoomToMaxButton->setEnabled(true); + } + + void resetExtremeValue() + { + dock_widget->maxBox->setRange(0, 99999999); + dock_widget->maxBox->setValue(0); + } + + void displayRampLegend() + { + color_ramp = Color_ramp(rm, rM, gm, gM, bm, bM); + + const int height = 256; + const int width = 140; + const int cell_width = width / 3; + const int top_margin = 5; + const int left_margin = 5; + const int drawing_height = height - 2*top_margin; + const int text_height = 20; + + legend = QPixmap(width, height + text_height); + legend.fill(QColor(200, 200, 200)); + + QPainter painter(&legend); + painter.setPen(Qt::black); + painter.setBrush(QColor(200, 200, 200)); + + const double min_value = 0; + const double max_value = dock_widget->maxBox->value(); + + // Build legend data + std::vector graduations(100); + for(int i=0; i<100; ++i) + graduations[i] = i / 100.0; + + int i = 0; + for(std::vector::iterator it = graduations.begin(), end = graduations.end(); it != end; ++it, i+=2) + { + QColor color(255 * color_ramp.r(*it), + 255 * color_ramp.g(*it), + 255 * color_ramp.b(*it)); + painter.fillRect(left_margin, drawing_height - top_margin - i, cell_width, 2, color); + } + + // draw right vertical line + painter.setPen(Qt::blue); + painter.drawLine(QPoint(left_margin + cell_width + 10, + drawing_height - top_margin + 2), + QPoint(left_margin + cell_width + 10, + drawing_height - top_margin - static_cast(graduations.size())*2 + 2)); + + // draw min value and max value + painter.setPen(Qt::blue); + QRect min_text_rect(left_margin + cell_width + 10, + drawing_height - top_margin, 100, text_height); + painter.drawText(min_text_rect, Qt::AlignCenter, QObject::tr("%1").arg(min_value, 0, 'f', 3)); + + QRect max_text_rect(left_margin + cell_width + 10, + drawing_height - top_margin - 200, 100, text_height); + painter.drawText(max_text_rect, Qt::AlignCenter, QObject::tr("%1").arg(max_value, 0, 'f', 3)); + + dock_widget->legendLabel->setPixmap(legend); + } + +private Q_SLOTS: + // Called when new geometric objects are selected in the scene + void onItemIndicesSelected(QList selected_items) + { + resetExtremeValue(); + dock_widget->setEnabled(false); + + Scene_surface_mesh_item* sm_item = nullptr; + Scene_polyhedron_selection_item* source_vertices = nullptr; + + if(selected_items.size() == 1) + { + Scene_item* item = scene->item(scene->mainSelectionIndex()); + source_vertices = qobject_cast(item); + if(source_vertices) + { + // While selecting a selection item, enable coloring if the selection item is linked to a sm_item + if(item_source_vertices.right.count(source_vertices) == 0) + return; + + dock_widget->setEnabled(true); + dock_widget->createSourceVerticesButton->setEnabled(false); + dock_widget->estimateDistancesButton->setEnabled(true); + disableExtremeValue(); + return; + } + else if(qobject_cast(item)) + { + dock_widget->setEnabled(true); + dock_widget->createSourceVerticesButton->setEnabled(false); + dock_widget->estimateDistancesButton->setEnabled(false); + disableExtremeValue(); + return; + } + + sm_item = qobject_cast(item); + } + else if(selected_items.size() == 2) + { + Scene_item* item1 = scene->item(selected_items.front()); + Scene_item* item2 = scene->item(selected_items.back()); + sm_item = qobject_cast(item1); + source_vertices = qobject_cast(item2); + if(!sm_item) + { + sm_item = qobject_cast(item2); + source_vertices = qobject_cast(item1); + } + } + + if(!sm_item) + return; + + dock_widget->setEnabled(true); + const bool has_sources = (source_vertices || item_source_vertices.left.count(sm_item) != 0); + dock_widget->estimateDistancesButton->setEnabled(has_sources); + dock_widget->createSourceVerticesButton->setEnabled(!has_sources); + disableExtremeValue(); + } + + // This function is only called if the index actually changed (doesn't trigger + // if you click again the item) + void onNewMethodSelected(int method_index) + { + resetExtremeValue(); // reset extreme value before the legend to get the proper values + displayRampLegend(); + + if(method_index >= 0 && method_index < dock_widget->methodBox->count()) // valid method + { + dock_widget->setEnabled(true); + disableExtremeValue(); // only available after displaying geodesic distances + } + else // no or broken method? + { + dock_widget->setEnabled(false); + dock_widget->methodBox->setEnabled(true); + } + } + +private: + bool displayHeatIntensity(Scene_surface_mesh_item* sm_item, + Scene_polyhedron_selection_item* source_vertices, + const bool use_iDT = false) + { + SMesh& mesh = *sm_item->face_graph(); + + SMesh::Property_map heat_intensity = + mesh.add_property_map("v:HM_Plugin_heat_intensity", 0).first; + + auto initialize_hm_map = [this, sm_item, &mesh] (auto*& hm_ptr, auto& hm_map) -> void + { + using Method = std::decay_t; + + auto it = hm_map.find(sm_item); + if(it != hm_map.end()) // method already exists + { + hm_ptr = it->second; + + for(vertex_descriptor v : vertices(mesh)) + hm_ptr->remove_source(v); + } + else + { + hm_ptr = new Method(mesh); + hm_map[sm_item] = hm_ptr; + } + + connect(sm_item, &Scene_surface_mesh_item::aboutToBeDestroyed, + this, [this, sm_item, &hm_map]() + { + item_source_vertices.left.erase(sm_item); + + auto it = hm_map.find(sm_item); + if(it == hm_map.end()) + return; + delete it->second; + hm_map.erase(it); + }); + }; + + Heat_method* hm = nullptr; + Heat_method_idt* hm_idt = nullptr; + + if(use_iDT) + initialize_hm_map(hm_idt, idt_heat_methods); + else + initialize_hm_map(hm, heat_methods); + + for(auto v : source_vertices->selected_vertices) + { + if(use_iDT) + hm_idt->add_source(v); + else + hm->add_source(v); + } + + if(use_iDT) + hm_idt->estimate_geodesic_distances(heat_intensity); + else + hm->estimate_geodesic_distances(heat_intensity); + + // Post treatment + double max = 0; + for(vertex_descriptor v : vertices(mesh)) + { + double hi = heat_intensity[v]; + if(hi > max) + max = hi; + } + + displayRampLegend(); + + auto [vcolors, vcolors_added] = mesh.add_property_map("v:color", CGAL::IO::Color()); + for(vertex_descriptor v : vertices(mesh)) + { + double h = heat_intensity[v] / max; + CGAL::IO::Color color(255 * color_ramp.r(h), + 255 * color_ramp.g(h), + 255 * color_ramp.b(h)); + vcolors[v] = color; + } + + // Create the colored item + Scene_heat_item* heat_item = new Scene_heat_item(sm_item); + heat_item->setName(tr("%1 (distance isolines)").arg(sm_item->name())); + heat_item->setVisible(false); + + sm_item->invalidateOpenGLBuffers(); + sm_item->setRenderingMode(GouraudPlusEdges); + sm_item->redraw(); + + scene->addItem(heat_item); + scene->setSelectedItem(scene->item_id(sm_item)); + + // any change of sm_item destroys everything + connect(sm_item, &Scene_surface_mesh_item::itemChanged, + this, [this, sm_item, heat_item]() + { + sm_item->resetColors(); + removePluginProperties(sm_item); + scene->erase(scene->item_id(heat_item)); + onItemIndicesSelected(scene->selectionIndices()); + }); + + connect(sm_item, &Scene_surface_mesh_item::aboutToBeDestroyed, + this, [this, heat_item]() + { + scene->erase(scene->item_id(heat_item)); + onItemIndicesSelected(scene->selectionIndices()); + }); + + // here because if it's put above, the setSelectedItem() might reset the value + dock_widget->maxBox->setValue(max); + + return true; + } + +private Q_SLOTS: + void estimateDistances() + { + // Get the mesh and source vertices items + Scene_surface_mesh_item* sm_item = nullptr; + Scene_polyhedron_selection_item* source_vertices = nullptr; + + if(scene->selectionIndices().size() == 1) + { + Scene_item* item = scene->item(scene->mainSelectionIndex()); + sm_item = qobject_cast(item); + if(sm_item) + { + // a surface mesh item is selected, an existing associated selection item must exist + source_vertices = item_source_vertices.left.at(sm_item); + } + else + { + // a selection item is selected, an existing associated mesh item must exist + source_vertices = qobject_cast(item); + if(source_vertices) + sm_item = item_source_vertices.right.at(source_vertices); + } + } + else if(scene->selectionIndices().size() == 2) + { + // two items, for (possibly unlinked) sm_item and its associated selection + Scene_item* item1 = scene->item(scene->selectionIndices().front()); + Scene_item* item2 = scene->item(scene->selectionIndices().back()); + sm_item = qobject_cast(item1); + source_vertices = qobject_cast(item2); + if(!sm_item) + { + sm_item = qobject_cast(item2); + source_vertices = qobject_cast(item1); + } + + link_mesh_and_selection(sm_item, source_vertices); + } + else + { + QMessageBox::critical(mw, "Error","Unsupported selection of items."); + return; + } + + CGAL_assertion(sm_item && source_vertices); + + if(!is_triangle_mesh(*sm_item->face_graph())) + { + QApplication::restoreOverrideCursor(); + QMessageBox::critical(mw, "Error","The mesh must be triangulated."); + return; + } + + if(source_vertices->selected_vertices.empty()) + { + QApplication::restoreOverrideCursor(); + QMessageBox::critical(mw, "Error","At least one source vertex is required."); + return; + } + + QApplication::setOverrideCursor(Qt::WaitCursor); + + enableExtremeValue(); + + const std::string& method_name = dock_widget->methodBox->currentText().toStdString(); + if(method_name == "Heat Method") + displayHeatIntensity(sm_item, source_vertices); + else if(method_name == "Heat Method (Intrinsic Delaunay)") + displayHeatIntensity(sm_item, source_vertices, true /*use IDT*/); + + // @todo emit a new SIGNAL on successful coloring, something like "colorChanged()" + // itemChanged is too strong and would conflict with the connection below + sm_item->invalidateOpenGLBuffers(); + sm_item->redraw(); + + QApplication::restoreOverrideCursor(); + } + +private: + void removePluginProperty(Scene_item* item, + const std::string& property_name) + { + Scene_surface_mesh_item* sm_item = qobject_cast(item); + if(!sm_item) + return; + + SMesh* sm = sm_item->face_graph(); + if(sm == nullptr) + return; + + // Here we only target the property maps added by this plugin, so 'double' is fine + SMesh::Property_map property; + bool found; + std::tie(property, found) = sm->property_map(property_name); + if(found) + sm->remove_property_map(property); + } + + void removePluginProperties(Scene_item* item) + { + removePluginProperty(item, "v:HM_Plugin_heat_intensity"); + } + +private Q_SLOTS: + // deletion of the selection item removes the pair from the map + void link_mesh_and_selection(Scene_surface_mesh_item* sm_item, + Scene_polyhedron_selection_item* source_vertices) + { + item_source_vertices.left.insert(std::make_pair(sm_item, source_vertices)); + + connect(source_vertices, &Scene_polyhedron_selection_item::aboutToBeDestroyed, + this, [this, sm_item]() + { + item_source_vertices.left.erase(sm_item); + onItemIndicesSelected(scene->selectionIndices()); + }); + } + + void createSourceVerticesSelectionItem() + { + Scene_item* item = scene->item(scene->mainSelectionIndex()); + Scene_surface_mesh_item* sm_item = qobject_cast(item); + if(!sm_item) + { + QMessageBox::warning(mw, "Warning", "Select a surface mesh to add source vertices"); + dock_widget->createSourceVerticesButton->setChecked(false); + return; + } + + CGAL_assertion(item_source_vertices.left.count(sm_item) == 0); + + Scene_polyhedron_selection_item* source_vertices = new Scene_polyhedron_selection_item(sm_item, mw); + source_vertices->setName(tr("%1 (source vertices)").arg(sm_item->name())); + scene->addItem(source_vertices); + + link_mesh_and_selection(sm_item, source_vertices); + + dock_widget->createSourceVerticesButton->setEnabled(false); + dock_widget->estimateDistancesButton->setEnabled(true); + } + +private Q_SLOTS: + void on_zoomToMaxButton_pressed() + { + Scene_surface_mesh_item* sm_item = nullptr; + if(scene->selectionIndices().size() == 1) + { + Scene_item* item = scene->item(scene->mainSelectionIndex()); + sm_item = qobject_cast(item); + } + else if(scene->selectionIndices().size() == 2) + { + Scene_item* item1 = scene->item(scene->selectionIndices().front()); + Scene_item* item2 = scene->item(scene->selectionIndices().back()); + sm_item = qobject_cast(item1); + if(!sm_item) + sm_item = qobject_cast(item2); + } + + const SMesh& mesh = *(sm_item->face_graph()); + + auto [heat_intensity, found] = mesh.property_map("v:HM_Plugin_heat_intensity"); + CGAL_assertion(found); + + double max = 0; + vertex_descriptor max_v = boost::graph_traits::null_vertex(); + for(vertex_descriptor v : vertices(mesh)) + { + if(heat_intensity[v] > max) + { + max = heat_intensity[v]; + max_v = v; + } + } + + CGAL_assertion(max_v != boost::graph_traits::null_vertex()); + + face_descriptor unused_fd; + Point_3 unused_p; + ::zoomToId(mesh, + QString("v%1").arg(max_v), + getActiveViewer(), + unused_fd, unused_p); + } +}; + +#include "Heat_method_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Join_and_split_polyhedra_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Join_and_split_polyhedra_plugin.cpp index 15f65cb1df8..49e21cc6635 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Join_and_split_polyhedra_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Join_and_split_polyhedra_plugin.cpp @@ -217,7 +217,7 @@ void Polyhedron_demo_join_and_split_polyhedra_plugin::on_actionColorConnectedCom item->computeItemColorVectorAutomatically(true); item->invalidateOpenGLBuffers(); item->setProperty("NbPatchIds", nb_patch_ids); - scene->itemChanged(item); + scene->itemChanged(item); // @todo emits } else { diff --git a/Polyhedron/demo/Polyhedron/SMesh_type.h b/Polyhedron/demo/Polyhedron/SMesh_type.h index f79a819cb9a..c6fd5020b8e 100644 --- a/Polyhedron/demo/Polyhedron/SMesh_type.h +++ b/Polyhedron/demo/Polyhedron/SMesh_type.h @@ -7,21 +7,20 @@ #include #include - typedef CGAL::Exact_predicates_inexact_constructions_kernel EPICK; typedef EPICK::Point_3 Point_3; + typedef CGAL::Surface_mesh SMesh; + typedef boost::graph_traits::face_descriptor face_descriptor; typedef boost::graph_traits::vertex_descriptor vertex_descriptor; typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; - namespace boost { template struct property_map, CGAL::vertex_selection_t> { - typedef typename boost::graph_traits >::vertex_descriptor vertex_descriptor; typedef typename CGAL::Surface_mesh

::template Property_map type; @@ -32,7 +31,6 @@ struct property_map, CGAL::vertex_selection_t> template struct property_map, CGAL::face_selection_t> { - typedef typename boost::graph_traits >::face_descriptor face_descriptor; typedef typename CGAL::Surface_mesh

::template Property_map type; @@ -41,11 +39,11 @@ struct property_map, CGAL::face_selection_t> } // namespace boost - namespace CGAL { template -struct Get_pmap_of_surface_mesh_ { +struct Get_pmap_of_surface_mesh_ +{ typedef typename boost::property_map, Property_tag >::type type; }; diff --git a/Polyhedron/demo/Polyhedron/Scene.cpp b/Polyhedron/demo/Polyhedron/Scene.cpp index c70929ab4a5..dc601ccbf75 100644 --- a/Polyhedron/demo/Polyhedron/Scene.cpp +++ b/Polyhedron/demo/Polyhedron/Scene.cpp @@ -172,20 +172,22 @@ Scene::replaceItem(Scene::Item_id index, CGAL::Three::Scene_item* item, bool emi Scene::Item_id Scene::erase(Scene::Item_id index) { - if(index <0 || index >= numberOfEntries()) + if(index < 0 || index >= numberOfEntries()) return -1; CGAL::Three::Scene_item* item = m_entries[index]; + if(qobject_cast(item)) { - setSelectedItemsList(QList()<() << item_id(item)); return erase(selectionIndices()); } + m_groups.removeAll(index); - if(item->parentGroup() - && item->parentGroup()->isChildLocked(item)) + if(item->parentGroup() && item->parentGroup()->isChildLocked(item)) return -1; - //clears the Scene_view + + // clears the Scene_view clear(); index_map.clear(); if(item->parentGroup()) @@ -286,12 +288,12 @@ Scene::erase(QList indices) void Scene::remove_item_from_groups(Scene_item* item) { - CGAL::Three::Scene_group_item* group = item->parentGroup(); - if(group) - { - group->removeChild(item); - children.push_back(item_id(item)); - } + CGAL::Three::Scene_group_item* group = item->parentGroup(); + if(group) + { + group->removeChild(item); + children.push_back(item_id(item)); + } } Scene::~Scene() { @@ -315,19 +317,19 @@ Scene::~Scene() CGAL::Three::Scene_item* Scene::item(Item_id index) const { - return m_entries.value(index); // QList::value checks bounds + return m_entries.value(index); // QList::value checks bounds } Scene::Item_id Scene::item_id(CGAL::Three::Scene_item* scene_item) const { - return m_entries.indexOf(scene_item); + return m_entries.indexOf(scene_item); } int Scene::numberOfEntries() const { - return m_entries.size(); + return m_entries.size(); } // Duplicate a scene item. @@ -335,20 +337,23 @@ Scene::numberOfEntries() const Scene::Item_id Scene::duplicate(Item_id index) { - if(index < 0 || index >= m_entries.size()) - return -1; + if(index < 0 || index >= m_entries.size()) + return -1; - const CGAL::Three::Scene_item* item = m_entries[index]; - CGAL::Three::Scene_item* new_item = item->clone(); - if(new_item) { - new_item->setName(tr("%1 (copy)").arg(item->name())); - new_item->setColor(item->color()); - new_item->setVisible(item->visible()); - addItem(new_item); - return m_entries.size() - 1; - } - else - return -1; + const CGAL::Three::Scene_item* item = m_entries[index]; + CGAL::Three::Scene_item* new_item = item->clone(); + if(new_item) + { + new_item->setName(tr("%1 (copy)").arg(item->name())); + new_item->setColor(item->color()); + new_item->setVisible(item->visible()); + addItem(new_item); + return m_entries.size() - 1; + } + else + { + return -1; + } } void Scene::initializeGL(CGAL::Three::Viewer_interface* viewer) @@ -486,33 +491,33 @@ void Scene::initializeGL(CGAL::Three::Viewer_interface* viewer) void Scene::s_itemAboutToBeDestroyed(CGAL::Three::Scene_item *rmv_itm) { - Q_FOREACH(CGAL::Three::Scene_item* item, m_entries) - { - if(item == rmv_itm) - item->itemAboutToBeDestroyed(item); - } + Q_FOREACH(CGAL::Three::Scene_item* item, m_entries) + { + if(item == rmv_itm) + item->itemAboutToBeDestroyed(item); + } } bool -Scene::keyPressEvent(QKeyEvent* e){ - bool res=false; - for (QList::iterator it=selected_items_list.begin(),endit=selected_items_list.end(); - it!=endit;++it) - { - CGAL::Three::Scene_item* item=m_entries[*it]; - res |= item->keyPressEvent(e); - } - return res; +Scene::keyPressEvent(QKeyEvent* e) +{ + bool res = false; + Q_FOREACH(int i, selected_items_list) + { + CGAL::Three::Scene_item* item = m_entries[i]; + res |= item->keyPressEvent(e); + } + return res; } void Scene::draw(CGAL::Three::Viewer_interface* viewer) { - draw_aux(false, viewer); + draw_aux(false, viewer); } void Scene::drawWithNames(CGAL::Three::Viewer_interface* viewer) { - draw_aux(true, viewer); + draw_aux(true, viewer); } bool item_should_be_skipped_in_draw(Scene_item* item) { @@ -615,12 +620,10 @@ void Scene::renderWireScene(const QList &items, viewer->setGlPointSize(2.f); if(index == selected_item || selected_items_list.contains(index)) { - item.selection_changed(true); } else { - item.selection_changed(false); } item.drawEdges(viewer); @@ -1122,12 +1125,14 @@ bool Scene::dropMimeData(const QMimeData * /*data*/, if(group) { Q_FOREACH(int id, selected_items_list) + { if(group->getChildren().contains(id)) { one_contained = true; break; } + } } //if the drop item is not a group_item or if it already contains the item, then the drop action must be ignored if(!group ||one_contained) diff --git a/Polyhedron/demo/Polyhedron/Scene.h b/Polyhedron/demo/Polyhedron/Scene.h index b661ed7baa8..38114309886 100644 --- a/Polyhedron/demo/Polyhedron/Scene.h +++ b/Polyhedron/demo/Polyhedron/Scene.h @@ -195,8 +195,10 @@ public Q_SLOTS: ++i; } } + //! Sets the target list of indices as the selected indices. - QList setSelectedItemsList(QList l ) + const QList& setSelectedItemIndices(QList l, + const bool do_emit = true) { Q_FOREACH(int i,l) { @@ -206,13 +208,38 @@ public Q_SLOTS: { QList list; Q_FOREACH(Item_id id, group->getChildrenForSelection()) - list<& setSelectedItemList(QList l, + const bool do_emit = true) + { + Q_FOREACH(int i,l) + { + CGAL::Three::Scene_group_item* group = + qobject_cast(item(i)); + if(group) + { + QList list; + Q_FOREACH(Item_id id, group->getChildrenForSelection()) + list << id; + l << setSelectedItemList(list, false /*do not emit*/); + } + } + + selected_items_list = l; + if(do_emit) + Q_EMIT selectionChanged(selected_items_list); + return selected_items_list; } // Accessors (setters) @@ -249,6 +276,8 @@ Q_SIGNALS: void selectionChanged(QList is); //! Used when you don't want to update the selectedItem in the Geometric Objects view. void itemIndexSelected(int i); + //! Used when you don't want to update the selectedItem in the Geometric Objects view. + void itemIndicesSelected(QList is); //! Emit this to reset the collapsed state of all groups after the Geometric Objects view has been redrawn. void restoreCollapsedState(); //! Is emitted when draw() is finished. diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index 21a550c6b70..3d228f88a86 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -1175,11 +1175,11 @@ void* Scene_surface_mesh_item_priv::get_aabb_tree() void Scene_surface_mesh_item::select(double orig_x, - double orig_y, - double orig_z, - double dir_x, - double dir_y, - double dir_z) + double orig_y, + double orig_z, + double dir_x, + double dir_y, + double dir_z) { SMesh *sm = d->smesh_; std::size_t vertex_to_emit = 0; @@ -2005,7 +2005,7 @@ void Scene_surface_mesh_item::resetColors() d->has_feature_edges = false; } invalidate(COLORS); - itemChanged(); + itemChanged(); // @fixme really shouldn't call something that strong } QMenu* Scene_surface_mesh_item::contextMenu() @@ -2387,7 +2387,6 @@ void Scene_surface_mesh_item::computeElements() const { d->compute_elements(ALL); setBuffersFilled(true); - const_cast(this)->itemChanged(); } void diff --git a/Polyhedron/demo/Polyhedron/include/id_printing.h b/Polyhedron/demo/Polyhedron/include/id_printing.h index 46d142a24bb..2418496aa5e 100644 --- a/Polyhedron/demo/Polyhedron/include/id_printing.h +++ b/Polyhedron/demo/Polyhedron/include/id_printing.h @@ -1,79 +1,96 @@ -#ifndef ID_PRINTING_H -#define ID_PRINTING_H +#ifndef CGAL_POLYHEDRON_DEMO_ID_PRINTING_H +#define CGAL_POLYHEDRON_DEMO_ID_PRINTING_H + #include +#include +#include +#include + #include #include #include -#include -#include + #include #define POINT_SIZE 11 template -struct VKRingPMAP{ - typedef typename boost::graph_traits::vertex_descriptor key_type; - typedef bool value_type; - typedef value_type reference; - typedef boost::read_write_property_map_tag category; - typedef typename boost::property_map::type IDmap; +struct VKRingPMAP +{ + using key_type = typename boost::graph_traits::vertex_descriptor; + using value_type = bool; + using reference = value_type; + using category = boost::read_write_property_map_tag; + + using IDmap = typename boost::property_map::type; std::vector* vec; Mesh* poly; IDmap idmap; VKRingPMAP(std::vector* vec, Mesh* poly) - :vec(vec), poly(poly) + : vec(vec), poly(poly) { idmap = get(boost::vertex_index, *poly); } - friend value_type get(const VKRingPMAP& map, const key_type& v){ + friend value_type get(const VKRingPMAP& map, const key_type& v) + { return (*map.vec)[get(map.idmap, v)]; } - friend void put(VKRingPMAP& map, const key_type& v, const value_type i){ + + friend void put(VKRingPMAP& map, const key_type& v, const value_type i) + { (*map.vec)[get(map.idmap, v)] = i; } }; + template -struct EdgeKRingPMAP{ - typedef typename boost::graph_traits::edge_descriptor key_type; - typedef bool value_type; - typedef value_type reference; - typedef boost::read_write_property_map_tag category; - typedef typename boost::property_map::type IDmap; +struct EdgeKRingPMAP +{ + using key_type = typename boost::graph_traits::edge_descriptor; + using value_type = bool; + using reference = value_type; + using category = boost::read_write_property_map_tag; + + using IDmap = typename boost::property_map::type; std::vector* vec; Mesh* poly; IDmap idmap; EdgeKRingPMAP(std::vector* vec, Mesh* poly) - :vec(vec), poly(poly) + : vec(vec), poly(poly) { idmap = get(boost::halfedge_index, *poly); } - friend value_type get(const EdgeKRingPMAP& map, const key_type& e){ + friend value_type get(const EdgeKRingPMAP& map, const key_type& e) + { return (*map.vec)[get(map.idmap, halfedge(e, *map.poly))/2]; } - friend void put(EdgeKRingPMAP& map, const key_type& e, const value_type i){ + + friend void put(EdgeKRingPMAP& map, const key_type& e, const value_type i) + { (*map.vec)[get(map.idmap, halfedge(e, *map.poly))/2] = i; } }; template -struct FKRingPMAP{ - typedef typename boost::graph_traits::face_descriptor key_type; - typedef bool value_type; - typedef value_type reference; - typedef boost::read_write_property_map_tag category; - typedef typename boost::property_map::type IDmap; +struct FKRingPMAP +{ + using key_type = typename boost::graph_traits::face_descriptor; + using value_type = bool; + using reference = value_type; + using category = boost::read_write_property_map_tag; + + using IDmap = typename boost::property_map::type; std::vector* vec; Mesh* poly; IDmap idmap; FKRingPMAP(std::vector* vec, Mesh* poly) - :vec(vec), poly(poly) + : vec(vec), poly(poly) { idmap = get(boost::face_index, *poly); } @@ -81,7 +98,9 @@ struct FKRingPMAP{ friend value_type get(const FKRingPMAP& map, const key_type& f){ return (*map.vec)[get(map.idmap, f)]; } - friend void put(FKRingPMAP& map, const key_type& f, const value_type i){ + + friend void put(FKRingPMAP& map, const key_type& f, const value_type i) + { (*map.vec)[get(map.idmap, f)] = i; } }; @@ -91,26 +110,28 @@ void deleteIds(CGAL::Three::Viewer_interface* viewer, TextListItem* fitems, std::vector* targeted_ids) { - TextRenderer *renderer = viewer->textRenderer(); + TextRenderer* renderer = viewer->textRenderer(); + for(TextItem* it : vitems->textList()) - delete it; + delete it; for(TextItem* it : eitems->textList()) - delete it; + delete it; for(TextItem* it : fitems->textList()) - delete it; + delete it; + vitems->clear(); renderer->removeTextList(vitems); + eitems->clear(); renderer->removeTextList(eitems); + fitems->clear(); renderer->removeTextList(fitems); + targeted_ids->clear(); viewer->update(); } - - - template bool find_primitive_id(const QPoint& point, Tree* aabb_tree, @@ -118,12 +139,13 @@ bool find_primitive_id(const QPoint& point, Handle& selected_fh, Point& pt_under) { - typedef typename CGAL::Kernel_traits::Kernel Traits; + using Traits = typename CGAL::Kernel_traits::Kernel; + bool found = false; CGAL::qglviewer::Vec point_under = viewer->camera()->pointUnderPixel(point,found); const CGAL::qglviewer::Vec offset = static_cast(CGAL::QGLViewer::QGLViewerPool().first())->offset(); - //find clicked facet + // find clicked facet CGAL::qglviewer::Vec dir; Point ray_origin; if(viewer->camera()->type() == CGAL::qglviewer::Camera::PERSPECTIVE) @@ -151,38 +173,39 @@ bool find_primitive_id(const QPoint& point, if(intersections.empty()) return false; + typename Intersections::iterator closest = intersections.begin(); - const Point* closest_point = - boost::get(&closest->first); - for(typename Intersections::iterator - it = boost::next(intersections.begin()), - end = intersections.end(); - it != end; ++it) + const Point* closest_point = boost::get(&closest->first); + for(typename Intersections::iterator it = boost::next(intersections.begin()), + end = intersections.end(); it != end; ++it) { - if(! closest_point) { + if(! closest_point) + { closest = it; } - else { - const Point* it_point = - boost::get(&it->first); - if(it_point && - (ray_dir * (*it_point - *closest_point)) < 0) + else + { + const Point* it_point = boost::get(&it->first); + if(it_point && (ray_dir * (*it_point - *closest_point)) < 0) { closest = it; closest_point = it_point; } } } + if(!closest_point) return false; + pt_under = Point(point_under.x, point_under.y, point_under.z); selected_fh = closest->second; + return true; } -template +template void compute_displayed_ids(Mesh& mesh, - CGAL::Three::Viewer_interface *viewer, + CGAL::Three::Viewer_interface* viewer, const typename boost::graph_traits::face_descriptor& selected_fh, const Point& pt_under, const CGAL::qglviewer::Vec& offset, @@ -191,21 +214,21 @@ void compute_displayed_ids(Mesh& mesh, TextListItem* fitems, std::vector* targeted_ids) { - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + using edge_descriptor = typename boost::graph_traits::edge_descriptor; + using face_descriptor = typename boost::graph_traits::face_descriptor; - typedef typename boost::property_map::type Ppmap; + using Ppmap = typename boost::property_map::type; Ppmap ppmap = get(boost::vertex_point, mesh); - typedef typename boost::property_map::type VIDmap; + using VIDmap = typename boost::property_map::type; VIDmap vidmap = get(boost::vertex_index, mesh); - typedef typename boost::property_map::type HIDmap; + using HIDmap = typename boost::property_map::type; HIDmap hidmap = get(boost::halfedge_index, mesh); - typedef typename boost::property_map::type FIDmap; + using FIDmap = typename boost::property_map::type; FIDmap fidmap = get(boost::face_index, mesh); QFont font; @@ -214,70 +237,77 @@ void compute_displayed_ids(Mesh& mesh, std::vector displayed_vertices; std::vector displayed_edges; std::vector displayed_faces; - //Test spots around facet to find the closest to point + // Test spots around facet to find the closest to point double min_dist = (std::numeric_limits::max)(); // test the vertices of the closest face for(vertex_descriptor vh : vertices_around_face(halfedge(selected_fh, mesh), mesh)) { - Point test=Point(get(ppmap, vh).x()+offset.x, - get(ppmap, vh).y()+offset.y, - get(ppmap, vh).z()+offset.z); + Point test=Point(get(ppmap, vh).x() + offset.x, + get(ppmap, vh).y() + offset.y, + get(ppmap, vh).z() + offset.z); double dist = CGAL::squared_distance(test, pt_under); - if( dist < min_dist){ + if(dist < min_dist) + { min_dist = dist; displayed_vertices.clear(); displayed_vertices.push_back(vh); } } - QVector3D point( - float(get(ppmap, displayed_vertices[0]).x() + offset.x), - float(get(ppmap, displayed_vertices[0]).y() + offset.y), - float(get(ppmap, displayed_vertices[0]).z() + offset.z)); - //test if we want to erase or not + QVector3D point(float(get(ppmap, displayed_vertices[0]).x() + offset.x), + float(get(ppmap, displayed_vertices[0]).y() + offset.y), + float(get(ppmap, displayed_vertices[0]).z() + offset.z)); + + // test if we want to erase or not for(TextItem* text_item : *targeted_ids) { if(text_item->position() == point) { - //hide and stop + // hide and stop deleteIds(viewer, vitems, eitems, fitems, targeted_ids); return; } } + deleteIds(viewer, vitems, eitems, fitems, targeted_ids); + // test the midpoint of edges of the closest face for(halfedge_descriptor e : halfedges_around_face(halfedge(selected_fh, mesh), mesh)) { - Point test=CGAL::midpoint(get(ppmap, source(e, mesh)),get(ppmap, target(e, mesh))); + Point test = CGAL::midpoint(get(ppmap, source(e, mesh)),get(ppmap, target(e, mesh))); test = Point(test.x()+offset.x, test.y()+offset.y, test.z()+offset.z); double dist = CGAL::squared_distance(test, pt_under); - if(dist < min_dist){ + if(dist < min_dist) + { min_dist = dist; displayed_vertices.clear(); displayed_edges.clear(); displayed_edges.push_back(edge(e, mesh)); } } + // test the centroid of the closest face double x(0), y(0), z(0); int total(0); for(vertex_descriptor vh : vertices_around_face(halfedge(selected_fh, mesh), mesh)) { - x+=get(ppmap, vh).x(); - y+=get(ppmap, vh).y(); - z+=get(ppmap, vh).z(); + x += get(ppmap, vh).x(); + y += get(ppmap, vh).y(); + z += get(ppmap, vh).z(); ++total; } - Point test(x/total+offset.x, - y/total+offset.y, - z/total+offset.z); + Point test(x / total+offset.x, + y / total+offset.y, + z / total+offset.z); + double dist = CGAL::squared_distance(test, pt_under); - if(dist < min_dist){ + if(dist < min_dist) + { min_dist = dist; displayed_vertices.clear(); displayed_edges.clear(); @@ -293,23 +323,21 @@ void compute_displayed_ids(Mesh& mesh, if(f != boost::graph_traits::null_face()) displayed_faces.push_back(f); } + for(halfedge_descriptor h : CGAL::halfedges_around_target(halfedge(displayed_vertices[0], mesh), mesh)) - { displayed_edges.push_back(edge(h, mesh)); - } } else if(!displayed_edges.empty()) { displayed_vertices.push_back(target(halfedge(displayed_edges[0], mesh), mesh)); displayed_vertices.push_back(target(opposite(halfedge(displayed_edges[0], mesh), mesh),mesh)); face_descriptor f1(face(halfedge(displayed_edges[0], mesh),mesh)), - f2(face(opposite(halfedge(displayed_edges[0], mesh), mesh),mesh)); + f2(face(opposite(halfedge(displayed_edges[0], mesh), mesh),mesh)); if(f1 != boost::graph_traits::null_face()) displayed_faces.push_back(f1); if(f2 != boost::graph_traits::null_face()) displayed_faces.push_back(f2); } - else if(!displayed_faces.empty()) { for(halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(displayed_faces[0], mesh), mesh)) @@ -318,7 +346,8 @@ void compute_displayed_ids(Mesh& mesh, displayed_vertices.push_back(target(h, mesh)); } } - //fill TextItems + + // fill TextItems std::vector vertex_selection(false); vertex_selection.resize(num_vertices(mesh)); VKRingPMAP vpmap(&vertex_selection, &mesh); @@ -345,9 +374,7 @@ void compute_displayed_ids(Mesh& mesh, face_selection.resize(num_faces(mesh)); FKRingPMAP fpmap(&face_selection, &mesh); for(face_descriptor f_h : displayed_faces) - { - put(fpmap, f_h, true); - } + put(fpmap, f_h, true); CGAL::expand_face_selection(displayed_faces, mesh, 1, @@ -356,9 +383,9 @@ void compute_displayed_ids(Mesh& mesh, for(vertex_descriptor vh : displayed_vertices) { - Point pos=Point(get(ppmap, vh).x()+offset.x, - get(ppmap, vh).y()+offset.y, - get(ppmap, vh).z()+offset.z); + Point pos = Point(get(ppmap, vh).x() + offset.x, + get(ppmap, vh).y() + offset.y, + get(ppmap, vh).z() + offset.z); TextItem* text_item = new TextItem(float(pos.x()), float(pos.y()), float(pos.z()), @@ -366,13 +393,14 @@ void compute_displayed_ids(Mesh& mesh, vitems->append(text_item); targeted_ids->push_back(text_item); } + for(edge_descriptor e : displayed_edges) { - halfedge_descriptor h(halfedge(e, mesh)); - Point pos=CGAL::midpoint(get(ppmap, source(h, mesh)),get(ppmap, target(h, mesh))); - pos = Point(pos.x()+offset.x, - pos.y()+offset.y, - pos.z()+offset.z); + halfedge_descriptor h(halfedge(e, mesh)); + Point pos = CGAL::midpoint(get(ppmap, source(h, mesh)),get(ppmap, target(h, mesh))); + pos = Point(pos.x() + offset.x, + pos.y() + offset.y, + pos.z() + offset.z); TextItem* text_item = new TextItem(float(pos.x()), float(pos.y()), @@ -387,15 +415,15 @@ void compute_displayed_ids(Mesh& mesh, int total(0); for(vertex_descriptor vh :vertices_around_face(halfedge(f, mesh), mesh)) { - x+=get(ppmap, vh).x(); - y+=get(ppmap, vh).y(); - z+=get(ppmap, vh).z(); + x += get(ppmap, vh).x(); + y += get(ppmap, vh).y(); + z += get(ppmap, vh).z(); ++total; } - Point pos(x/total+offset.x, - y/total+offset.y, - z/total+offset.z); + Point pos(x/total + offset.x, + y/total + offset.y, + z/total + offset.z); TextItem* text_item = new TextItem(float(pos.x()), float(pos.y()), float(pos.z()), @@ -408,18 +436,20 @@ template bool printVertexIds(const Mesh& mesh, TextListItem* vitems) { - typedef typename boost::property_map::const_type Ppmap; - typedef typename boost::property_traits::value_type Point; - typedef typename boost::property_map::type IDmap; + using Ppmap = typename boost::property_map::const_type; + using Point = typename boost::property_traits::value_type; + using IDmap = typename boost::property_map::type; Ppmap ppmap = get(boost::vertex_point, mesh); IDmap idmap = get(boost::vertex_index, mesh); + const CGAL::qglviewer::Vec offset = CGAL::Three::Three::mainViewer()->offset(); + QFont font; font.setBold(true); font.setPointSize(POINT_SIZE); - //fills textItems + // fills textItems for(typename boost::graph_traits::vertex_descriptor vh : vertices(mesh)) { const Point& p = get(ppmap, vh); @@ -429,7 +459,8 @@ bool printVertexIds(const Mesh& mesh, QString("%1").arg(get(idmap, vh)), true, font, Qt::red)); } - //add the QList to the render's pool + + // add the QList to the render's pool bool res = true; Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) { @@ -437,10 +468,9 @@ bool printVertexIds(const Mesh& mesh, renderer->addTextList(vitems); v->update(); if(vitems->size() > static_cast(renderer->getMax_textItems())) - { res = false; - } } + return res; } @@ -448,13 +478,15 @@ template bool printEdgeIds(const Mesh& mesh, TextListItem* eitems) { - typedef typename boost::property_map::const_type Ppmap; - typedef typename boost::property_traits::value_type Point; - typedef typename boost::property_map::type IDmap; + using Ppmap = typename boost::property_map::const_type; + using Point = typename boost::property_traits::value_type; + using IDmap = typename boost::property_map::type; Ppmap ppmap = get(boost::vertex_point, mesh); IDmap idmap = get(boost::halfedge_index, mesh); + const CGAL::qglviewer::Vec offset = CGAL::Three::Three::mainViewer()->offset(); + QFont font; font.setBold(true); font.setPointSize(POINT_SIZE); @@ -468,7 +500,8 @@ bool printEdgeIds(const Mesh& mesh, float((p1.z() + p2.z()) / 2 + offset.z), QString("%1").arg(get(idmap, halfedge(e, mesh)) / 2), true, font, Qt::green)); } - //add the QList to the render's pool + + // add the QList to the render's pool bool res = true; Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) { @@ -476,10 +509,9 @@ bool printEdgeIds(const Mesh& mesh, renderer->addTextList(eitems); v->update(); if(eitems->size() > static_cast(renderer->getMax_textItems())) - { res = false; - } } + return res; } @@ -487,15 +519,18 @@ template bool printFaceIds(const Mesh& mesh, TextListItem* fitems) { - typedef typename boost::property_map::const_type Ppmap; - typedef typename boost::property_map::type IDmap; + using Ppmap = typename boost::property_map::const_type; + using IDmap = typename boost::property_map::type; Ppmap ppmap = get(boost::vertex_point, mesh); IDmap idmap = get(boost::face_index, mesh); + const CGAL::qglviewer::Vec offset = CGAL::Three::Three::mainViewer()->offset(); + QFont font; font.setBold(true); font.setPointSize(POINT_SIZE); + for(typename boost::graph_traits::face_descriptor fh : faces(mesh)) { double x(0), y(0), z(0); @@ -513,7 +548,8 @@ bool printFaceIds(const Mesh& mesh, float(z / total + offset.z), QString("%1").arg(get(idmap, fh)), true, font, Qt::blue)); } - //add the QList to the render's pool + + // add the QList to the render's pool bool res = true; Q_FOREACH(CGAL::QGLViewer* v, CGAL::QGLViewer::QGLViewerPool()) { @@ -521,9 +557,7 @@ bool printFaceIds(const Mesh& mesh, renderer->addTextList(fitems); v->update(); if(fitems->size() > static_cast(renderer->getMax_textItems())) - { res = false; - } } return res; } @@ -535,14 +569,15 @@ int zoomToId(const Mesh& mesh, typename boost::graph_traits::face_descriptor& selected_fh, Point& p) { - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::property_map::const_type Ppmap; - typedef typename boost::property_map::type VIDmap; - typedef typename boost::property_map::type EIDmap; - typedef typename boost::property_map::type FIDmap; - typedef typename CGAL::Kernel_traits::Kernel Traits; + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + using face_descriptor = typename boost::graph_traits::face_descriptor; + using Ppmap = typename boost::property_map::const_type; + using VIDmap = typename boost::property_map::type; + using EIDmap = typename boost::property_map::type; + using FIDmap = typename boost::property_map::type; + + using Traits = typename CGAL::Kernel_traits::Kernel; Ppmap ppmap = get(boost::vertex_point, mesh); VIDmap vidmap = get(boost::vertex_index, mesh); @@ -555,10 +590,11 @@ int zoomToId(const Mesh& mesh, if((first != QString("v") && first != QString("e") && first != QString("f")) || - !is_int) + !is_int) { return 1; //("Input must be of the form [v/e/f][int]" } + const CGAL::qglviewer::Vec offset = viewer->offset(); typename Traits::Vector_3 normal; if(first == QString("v")) @@ -567,26 +603,25 @@ int zoomToId(const Mesh& mesh, for(vertex_descriptor vh : vertices(mesh)) { std::size_t cur_id = get(vidmap, vh); - if( cur_id == id) + if(cur_id == id) { p = Point(get(ppmap, vh).x() + offset.x, get(ppmap, vh).y() + offset.y, get(ppmap, vh).z() + offset.z); + typename boost::graph_traits::halfedge_descriptor hf = halfedge(vh, mesh); if(CGAL::is_border(hf, mesh)) - { hf = opposite(hf, mesh); - } + selected_fh = face(hf, mesh); normal = CGAL::Polygon_mesh_processing::compute_vertex_normal(vh, mesh); found = true; break; } } + if(!found) - { - return 2;//"No vertex with id %1").arg(id) - } + return 2; // "No vertex with id %1").arg(id) } else if(first == QString("e")) { @@ -604,10 +639,10 @@ int zoomToId(const Mesh& mesh, typename Traits::Vector_3 normal1(0,0,0); if(!is_border(hf, mesh)) { - normal1= CGAL::Polygon_mesh_processing::compute_face_normal(face(hf,mesh), - mesh); + normal1 = CGAL::Polygon_mesh_processing::compute_face_normal(face(hf,mesh), mesh); selected_fh = face(hf, mesh); } + typename Traits::Vector_3 normal2(0,0,0); if(!is_border(opposite(hf, mesh), mesh)) { @@ -615,15 +650,15 @@ int zoomToId(const Mesh& mesh, mesh); selected_fh = face(hf, mesh); } - normal = 0.5*normal1+0.5*normal2; + + normal = 0.5*normal1 + 0.5*normal2; found = true; break; } } + if(!found) - { - return 3;//"No edge with id %1").arg(id) - } + return 3; // "No edge with id %1").arg(id) } else if(first == QString("f")) { @@ -634,30 +669,30 @@ int zoomToId(const Mesh& mesh, { if(get(fidmap, fh) != id) continue; + for(vertex_descriptor vh : vertices_around_face(halfedge(fh, mesh), mesh)) { - x+=get(ppmap, vh).x(); - y+=get(ppmap, vh).y(); - z+=get(ppmap, vh).z(); + x += get(ppmap, vh).x(); + y += get(ppmap, vh).y(); + z += get(ppmap, vh).z(); ++total; } + p = Point(x/total + offset.x, - y/total + offset.y, - z/total + offset.z); - normal = CGAL::Polygon_mesh_processing::compute_face_normal( - fh, - mesh); + y/total + offset.y, + z/total + offset.z); + normal = CGAL::Polygon_mesh_processing::compute_face_normal(fh, mesh); selected_fh = fh; found = true; break; } + if(!found) - { - return 4; //"No face with id %1").arg(id) - } + return 4; // "No face with id %1").arg(id) } + CGAL::qglviewer::Quaternion new_orientation(CGAL::qglviewer::Vec(0,0,-1), - CGAL::qglviewer::Vec(-normal.x(), -normal.y(), -normal.z())); + CGAL::qglviewer::Vec(-normal.x(), -normal.y(), -normal.z())); Point new_pos = p + 0.25*CGAL::qglviewer::Vec( viewer->camera()->position().x - viewer->camera()->pivotPoint().x, @@ -665,19 +700,69 @@ int zoomToId(const Mesh& mesh, viewer->camera()->position().z - viewer->camera()->pivotPoint().z) .norm() * normal ; - viewer->camera()->setPivotPoint(CGAL::qglviewer::Vec(p.x(), - p.y(), - p.z())); + viewer->camera()->setPivotPoint(CGAL::qglviewer::Vec(p.x(), p.y(), p.z())); viewer->moveCameraToCoordinates(QString("%1 %2 %3 %4 %5 %6 %7").arg(new_pos.x()) - .arg(new_pos.y()) - .arg(new_pos.z()) - .arg(new_orientation[0]) - .arg(new_orientation[1]) - .arg(new_orientation[2]) - .arg(new_orientation[3])); + .arg(new_pos.y()) + .arg(new_pos.z()) + .arg(new_orientation[0]) + .arg(new_orientation[1]) + .arg(new_orientation[2]) + .arg(new_orientation[3])); viewer->update(); - return 0; //all clear; -} -#endif // ID_PRINTING_H + + return 0; // all clear; +} + +template +int zoomToPoint(const PointSet& ps, + const typename PointSet::Index& index, + CGAL::Three::Viewer_interface* viewer, + typename PointSet::Point_3& p) +{ + const CGAL::qglviewer::Vec offset = viewer->offset(); + + using Point_3 = typename PointSet::Point_3; + using Vector_3 = typename PointSet::Vector_3; + + const Point_3& op = ps.point(index); + + p = Point_3(op.x() + offset.x, + op.y() + offset.y, + op.z() + offset.z); + + Vector_3 normal; + if(ps.has_normal_map()) + normal = ps.normal(index); + else + normal = { viewer->camera()->position().x - viewer->camera()->pivotPoint().x, + viewer->camera()->position().y - viewer->camera()->pivotPoint().y, + viewer->camera()->position().z - viewer->camera()->pivotPoint().z }; + + Point_3 new_pos = p + + 0.25 * CGAL::qglviewer::Vec( + viewer->camera()->position().x - viewer->camera()->pivotPoint().x, + viewer->camera()->position().y - viewer->camera()->pivotPoint().y, + viewer->camera()->position().z - viewer->camera()->pivotPoint().z) + .norm() * normal ; + + viewer->camera()->setPivotPoint(CGAL::qglviewer::Vec(p.x(), p.y(), p.z())); + + CGAL::qglviewer::Quaternion new_orientation(CGAL::qglviewer::Vec(0,0,-1), + CGAL::qglviewer::Vec(-normal.x(), -normal.y(), -normal.z())); + + viewer->moveCameraToCoordinates(QString("%1 %2 %3 %4 %5 %6 %7").arg(new_pos.x()) + .arg(new_pos.y()) + .arg(new_pos.z()) + .arg(new_orientation[0]) + .arg(new_orientation[1]) + .arg(new_orientation[2]) + .arg(new_orientation[3])); + + viewer->update(); + + return 0; +} + +#endif // CGAL_POLYHEDRON_DEMO_ID_PRINTING_H diff --git a/Polyhedron/demo/Polyhedron/triangulate_primitive.h b/Polyhedron/demo/Polyhedron/triangulate_primitive.h index c80ceb08aca..cf7aa8a457b 100644 --- a/Polyhedron/demo/Polyhedron/triangulate_primitive.h +++ b/Polyhedron/demo/Polyhedron/triangulate_primitive.h @@ -1,234 +1,246 @@ -#ifndef TRIANGULATE_PRIMITIVE -#define TRIANGULATE_PRIMITIVE +#ifndef CGAL_DEMO_TRIANGULATE_PRIMITIVE_H +#define CGAL_DEMO_TRIANGULATE_PRIMITIVE_H + +#include + #include #include #include + #include -#include -#include #include -//Make sure all the facets are triangles +#include +#include + +#include +#include +#include + +// Ensure that all the facets are triangles +// @todo just use PMP::triangulate_face()...? +// or at least mark_faces_in_domain() + template class FacetTriangulator { - typedef Kernel Traits; +public: + using Traits = Kernel; + using Point = typename Kernel::Point_3; + using Vector = typename Kernel::Vector_3; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + using P_traits = CGAL::Projection_traits_3; - typedef typename Kernel::Vector_3 Vector; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + using face_descriptor = typename boost::graph_traits::face_descriptor; + struct Face_info + { + typename boost::graph_traits::halfedge_descriptor e[3]; + bool is_external; + }; - typedef CGAL::Projection_traits_3 P_traits; + using Vb = CGAL::Triangulation_vertex_base_with_info_2; + using Fbb = CGAL::Triangulation_face_base_with_info_2; + using Fb = CGAL::Constrained_triangulation_face_base_2; + using TDS = CGAL::Triangulation_data_structure_2; + using Itag = CGAL::Exact_predicates_tag; + using CDT = CGAL::Constrained_Delaunay_triangulation_2; - typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; + using Vertex_handle = typename CDT::Vertex_handle; + using Face_handle = typename CDT::Face_handle; - struct Face_info { - typename boost::graph_traits::halfedge_descriptor e[3]; - bool is_external; - }; - - typedef CGAL::Triangulation_face_base_with_info_2 Fb1; - typedef CGAL::Constrained_triangulation_face_base_2 Fb; - typedef CGAL::Triangulation_data_structure_2 TDS; - typedef CGAL::Exact_predicates_tag Itag; + struct PointAndId + { + Point point; + Index_type id; + PointAndId() = default; + PointAndId(const Point& point, const Index_type id) : point(point), id(id) { } + }; public: - struct PointAndId { - typename Kernel::Point_3 point; - Index_type id; - }; + CDT* cdt; + CGAL::Unique_hash_map v2v; - typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; - CDT *cdt; - CGAL::Unique_hash_map v2v; - - //Constructor - FacetTriangulator(typename boost::graph_traits::face_descriptor fd, +public: + // Constructor + FacetTriangulator(face_descriptor fd, const Vector& normal, - Mesh *poly, + Mesh* poly, Vector offset = Vector(0,0,0)) { std::vector idPoints; - for(halfedge_descriptor he_circ : halfedges_around_face( halfedge(fd, *poly), *poly)) - { - PointAndId idPoint; - idPoint.point = get(boost::vertex_point,*poly,source(he_circ, *poly))+offset; - idPoint.id = source(he_circ, *poly); - idPoints.push_back(idPoint); + for(halfedge_descriptor he_circ : halfedges_around_face(halfedge(fd, *poly), *poly)) + idPoints.emplace_back(get(CGAL::vertex_point, *poly, source(he_circ, *poly)) + offset, + source(he_circ, *poly)); - } if(!triangulate(idPoints, normal)) - std::cerr<<"Facet not displayed"<::face_descriptor fd, - const std::vector& more_points, + + FacetTriangulator(face_descriptor fd, + const std::vector& more_points, const Vector& normal, - Mesh *poly, + Mesh* poly, Vector offset = Vector(0,0,0)) { - std::vector idPoints; - for(halfedge_descriptor he_circ : halfedges_around_face( halfedge(fd, *poly), *poly)) - { - PointAndId idPoint; - idPoint.point = get(boost::vertex_point,*poly,source(he_circ, *poly))+offset; - idPoint.id = source(he_circ, *poly); - idPoints.push_back(idPoint); + std::vector idPoints; + for(halfedge_descriptor he_circ : halfedges_around_face(halfedge(fd, *poly), *poly)) + idPoints.emplace_back(get(CGAL::vertex_point, *poly, source(he_circ, *poly)) + offset, + source(he_circ, *poly)); - } - if(!triangulate_with_points(idPoints,more_points, normal)) - std::cerr<<"Facet not displayed"< &idPoints, - const Vector& normal) - { - if(!triangulate(idPoints, normal)) - std::cerr<<"Facet not displayed"< &idPoints, - const std::vector& more_points, + FacetTriangulator(std::vector& idPoints, const Vector& normal) { - if(!triangulate_with_points(idPoints, more_points, normal)) - std::cerr<<"Facet not displayed"<& idPoints, + const std::vector& more_points, + const Vector& normal) + { + if(!triangulate_with_points(idPoints, more_points, normal)) + std::cerr << "Facet not displayed" << std::endl; + } + ~FacetTriangulator() { - if (cdt ) - delete cdt; + if(cdt) + delete cdt; } private: - bool triangulate( std::vector &idPoints, - const Vector& normal ) + bool triangulate(std::vector& idPoints, + const Vector& normal) { P_traits cdt_traits(normal); cdt = new CDT(cdt_traits); - typename CDT::Vertex_handle previous, first, last_inserted; + + Vertex_handle previous, first, last_inserted; // Iterate the points of the facet and decide if they must be inserted in the CDT typename Kernel::FT x(0), y(0), z(0); - for(PointAndId idPoint : idPoints) + for(const PointAndId& idPoint : idPoints) { - x += idPoint.point.x(); - y += idPoint.point.y(); - z += idPoint.point.z(); - typename CDT::Vertex_handle vh; - //Always insert the first point, then only insert - // if the distance with the previous is reasonable. - if(first == typename CDT::Vertex_handle() || idPoint.point != previous->point()) - { - vh = cdt->insert(idPoint.point); - v2v[vh] = idPoint.id; - if(first == typename CDT::Vertex_handle()) { - first = vh; - } - if(previous != nullptr && previous != vh) { - cdt->insert_constraint(previous, vh); - last_inserted = previous; - } - previous = vh; - } - } - if(last_inserted == typename CDT::Vertex_handle()) - return false; - if(previous != first) - cdt->insert_constraint(previous, first); - // sets mark is_external - for(typename CDT::All_faces_iterator - fit2 = cdt->all_faces_begin(), - end = cdt->all_faces_end(); - fit2 != end; ++fit2) - { - fit2->info().is_external = false; - } - //check if the facet is external or internal - std::queue face_queue; - face_queue.push(cdt->infinite_vertex()->face()); - while(! face_queue.empty() ) { - typename CDT::Face_handle fh = face_queue.front(); - face_queue.pop(); - if(fh->info().is_external) continue; - fh->info().is_external = true; - for(int i = 0; i <3; ++i) { - if(!cdt->is_constrained(std::make_pair(fh, i))) - { - face_queue.push(fh->neighbor(i)); - } - } - } - return true; - } + y += idPoint.point.y(); + z += idPoint.point.z(); - bool triangulate_with_points( std::vector &idPoints, - const std::vector& more_points, - const Vector& normal) - { - P_traits cdt_traits(normal); - cdt = new CDT(cdt_traits); - typename CDT::Vertex_handle previous, first, last_inserted; - // Iterate the points of the facet and decide if they must be inserted in the CDT - for(PointAndId idPoint : idPoints) - { - typename CDT::Vertex_handle vh; - //Always insert the first point, then only insert - // if the distance with the previous is reasonable. - if(first == typename CDT::Vertex_handle() || idPoint.point != previous->point()) + Vertex_handle vh; + // Always insert the first point, then only insert if the distance with the previous is reasonable. + if(first == Vertex_handle() || idPoint.point != previous->point()) { vh = cdt->insert(idPoint.point); v2v[vh] = idPoint.id; - if(first == typename CDT::Vertex_handle()) { + if(first == Vertex_handle()) first = vh; - } - if(previous != nullptr && previous != vh) { + + if(previous != nullptr && previous != vh) + { cdt->insert_constraint(previous, vh); last_inserted = previous; } - previous = vh; + previous = vh; } - } - if(last_inserted == typename CDT::Vertex_handle()) - return false; - cdt->insert_constraint(previous, first); - for(typename Kernel::Point_3 point : more_points) - { - cdt->insert(point); - } - // sets mark is_external - for(typename CDT::All_faces_iterator - fit2 = cdt->all_faces_begin(), - end = cdt->all_faces_end(); - fit2 != end; ++fit2) - { - fit2->info().is_external = false; - } - //check if the facet is external or internal - std::queue face_queue; - face_queue.push(cdt->infinite_vertex()->face()); - while(! face_queue.empty() ) { - typename CDT::Face_handle fh = face_queue.front(); - face_queue.pop(); - if(fh->info().is_external) continue; - fh->info().is_external = true; - for(int i = 0; i <3; ++i) { - if(!cdt->is_constrained(std::make_pair(fh, i))) - { - face_queue.push(fh->neighbor(i)); - } - } - } - return true; + } + + if(last_inserted == Vertex_handle()) + return false; + + if(previous != first) + cdt->insert_constraint(previous, first); + + // sets mark is_external + for(Face_handle f2 : cdt->all_face_handles()) + f2->info().is_external = false; + + // check if the facet is external or internal + std::queue face_queue; + face_queue.push(cdt->infinite_vertex()->face()); + while(! face_queue.empty()) + { + typename CDT::Face_handle fh = face_queue.front(); + face_queue.pop(); + if(fh->info().is_external) + continue; + + fh->info().is_external = true; + for(int i = 0; i <3; ++i) + { + if(!cdt->is_constrained(std::make_pair(fh, i))) + face_queue.push(fh->neighbor(i)); + } + } + + return true; + } + + bool triangulate_with_points(std::vector& idPoints, + const std::vector& more_points, + const Vector& normal) + { + P_traits cdt_traits(normal); + cdt = new CDT(cdt_traits); + + // Iterate the points of the facet and decide if they must be inserted in the CDT + Vertex_handle previous, first, last_inserted; + for(const PointAndId& idPoint : idPoints) + { + Vertex_handle vh; + // Always insert the first point, then only insert if the distance with the previous is reasonable. + if(first == Vertex_handle() || idPoint.point != previous->point()) + { + vh = cdt->insert(idPoint.point); + v2v[vh] = idPoint.id; + if(first == Vertex_handle()) + first = vh; + + if(previous != nullptr && previous != vh) + { + cdt->insert_constraint(previous, vh); + last_inserted = previous; + } + previous = vh; + } + } + + if(last_inserted == Vertex_handle()) + return false; + + cdt->insert_constraint(previous, first); + for(const Point& point : more_points) + cdt->insert(point); + + // sets mark is_external + for(Face_handle f2 : cdt->all_face_handles()) + f2->info().is_external = false; + + // check if the facet is external or internal + std::queue face_queue; + face_queue.push(cdt->infinite_vertex()->face()); + while(!face_queue.empty()) + { + typename CDT::Face_handle fh = face_queue.front(); + face_queue.pop(); + if(fh->info().is_external) + continue; + fh->info().is_external = true; + for(int i = 0; i <3; ++i) + { + if(!cdt->is_constrained(std::make_pair(fh, i))) + face_queue.push(fh->neighbor(i)); + } + } + + return true; } }; -#endif // TRIANGULATE_PRIMITIVE +#endif // CGAL_DEMO_TRIANGULATE_PRIMITIVE_H diff --git a/Three/include/CGAL/Three/Scene_interface.h b/Three/include/CGAL/Three/Scene_interface.h index e60049c91a0..64441fe197d 100644 --- a/Three/include/CGAL/Three/Scene_interface.h +++ b/Three/include/CGAL/Three/Scene_interface.h @@ -58,7 +58,6 @@ namespace Three{ * */ class Scene_interface { public: - //!A bounding box is a box with each face corresponding to an extremum of its contents. typedef CGAL::Bbox_3 Bbox; diff --git a/Three/include/CGAL/Three/Scene_item.h b/Three/include/CGAL/Three/Scene_item.h index f22b9be5f52..c7920cbefed 100644 --- a/Three/include/CGAL/Three/Scene_item.h +++ b/Three/include/CGAL/Three/Scene_item.h @@ -81,7 +81,7 @@ public: PROGRAM_OLD_FLAT, /** Used to render flat shading without pre computing normals without geometry shader*/ PROGRAM_SOLID_WIREFRAME, //! Used to render edges with width superior to 1. PROGRAM_NO_INTERPOLATION, //! Used to render faces without interpolating their color. - PROGRAM_HEAT_INTENSITY, //! Used to render special item in Display_property_plugin + PROGRAM_HEAT_INTENSITY, //! Used to render special item in Heat_method_plugin NB_OF_PROGRAMS //! Holds the number of different programs in this enum. }; typedef CGAL::Bbox_3 Bbox; diff --git a/Three/include/CGAL/Three/Viewer_interface.h b/Three/include/CGAL/Three/Viewer_interface.h index c1330a12301..2bdba0aadd3 100644 --- a/Three/include/CGAL/Three/Viewer_interface.h +++ b/Three/include/CGAL/Three/Viewer_interface.h @@ -70,7 +70,7 @@ public: PROGRAM_OLD_FLAT, /** Used to render flat shading without pre computing normals without geometry shader*/ PROGRAM_SOLID_WIREFRAME, //! Used to render edges with width superior to 1. PROGRAM_NO_INTERPOLATION, //! Used to render faces without interpolating their color. - PROGRAM_HEAT_INTENSITY, //! Used to render special item in Display_property_plugin + PROGRAM_HEAT_INTENSITY, //! Used to render special item in Heat_method_plugin PROGRAM_TETRA_FILTERING, //! Used in Scene_tetrahedra_item with Tetrahedra_filtering_plugin NB_OF_PROGRAMS //! Holds the number of different programs in this enum. };