diff --git a/Polyhedron/demo/Polyhedron/CGAL_demo/Scene_interface.h b/Polyhedron/demo/Polyhedron/CGAL_demo/Scene_interface.h index 425edac66d4..98d44ac510d 100644 --- a/Polyhedron/demo/Polyhedron/CGAL_demo/Scene_interface.h +++ b/Polyhedron/demo/Polyhedron/CGAL_demo/Scene_interface.h @@ -76,6 +76,7 @@ public: // Accessors (getters) virtual int numberOfEntries() const = 0; virtual Scene_item* item(Item_id) const = 0; + virtual Item_id item_id(Scene_item*) const = 0; virtual Item_id mainSelectionIndex() const = 0; virtual QList selectionIndices() const = 0; virtual Item_id selectionAindex() const = 0; diff --git a/Polyhedron/demo/Polyhedron/Hole_filling_widget.ui b/Polyhedron/demo/Polyhedron/Hole_filling_widget.ui index b6a17e8c44f..7050b098b82 100644 --- a/Polyhedron/demo/Polyhedron/Hole_filling_widget.ui +++ b/Polyhedron/demo/Polyhedron/Hole_filling_widget.ui @@ -137,22 +137,22 @@ - + - false + true - Create Polyline Item for each Hole + Visualize Holes - + - false + true - Fill and Update Polyhedron + Fill Selected Holes diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_plugin.cpp index 3eccd1b8a56..87c719f80dc 100644 --- a/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_plugin.cpp @@ -3,6 +3,7 @@ #include "Messages_interface.h" #include "Scene_polyhedron_item.h" #include "Scene_polylines_item.h" +#include "Scene.h" #include "Polyhedron_demo_plugin_interface.h" #include "ui_Hole_filling_widget.h" @@ -17,9 +18,213 @@ #include #include +#include +#include +#include + #include #include -#include + +#include +#include + +// Class for visualizing holes in a polyhedron +// provides mouse selection functionality +class Q_DECL_EXPORT Scene_polylines_collection : public Scene_item +{ + Q_OBJECT +public: + // structs + struct Polyline_data { + Scene_polylines_item* polyline; + Polyhedron::Halfedge_handle halfedge; + qglviewer::Vec position; + }; + struct Mouse_keyboard_state + { + bool ctrl_pressing, left_button_pressing; + Mouse_keyboard_state() : ctrl_pressing(false), left_button_pressing(false) { } + }; +public: typedef std::list Polyline_data_list; +private: + struct List_iterator_comparator { + bool operator()(Polyline_data_list::const_iterator it_1, Polyline_data_list::const_iterator it_2) const + { return (&*it_1) < (&*it_2); } + }; +public: + typedef std::set Selected_holes_set; + + Scene_polylines_collection(Scene_polyhedron_item* poly_item) + : poly_item(poly_item) { + active_hole = polyline_data_list.end(); + + QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin(); + viewer->installEventFilter(this); + } + ~Scene_polylines_collection() { + for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) { + delete it->polyline; + } + } + bool isFinite() const { return true; } + bool isEmpty() const { return polyline_data_list.empty(); } + Bbox bbox() const { + if(polyline_data_list.empty()) { return Bbox(); } + Bbox bbox = polyline_data_list.begin()->polyline->bbox(); + for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) { + bbox = bbox + it->polyline->bbox(); + } + return bbox; + } + Scene_polylines_collection* clone() const { + return 0; + } + QString toolTip() const { + return tr("%1 with %2 holes").arg(name()).arg(polyline_data_list.size()); + } + + bool supportsRenderingMode(RenderingMode m) const { + return (m == Wireframe); + } + void draw() const {} + void draw_edges() const { + ::glLineWidth(3.f); + for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) { + if(selected_holes.find(it) != selected_holes.end()) + { it->polyline->setRbgColor(0, 255, 0); } + else if(it == active_hole) + { it->polyline->setRbgColor(255, 0, 0); } + else + { it->polyline->setRbgColor(0, 0, 255); } + it->polyline->draw_edges(); + } + } + + // find holes in polyhedron and construct a internal polyline for each + void get_holes(Polyhedron& poly) { + typedef Polyhedron::Halfedge_iterator Halfedge_iterator; + typedef Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator; + + clear(); + + for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it) + { it->id() = 0; } + + int counter = 0; + for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it){ + if(it->is_border() && it->id() == 0){ + polyline_data_list.push_back(Polyline_data()); + Scene_polylines_collection::Polyline_data& polyline_data = polyline_data_list.back(); + polyline_data.polyline = new Scene_polylines_item(); + polyline_data.polyline->polylines.push_back(Scene_polylines_item::Polyline()); + polyline_data.halfedge = it; + + qglviewer::Vec center; + int counter = 0; + Halfedge_around_facet_circulator hf_around_facet = it->facet_begin(); + do { + CGAL_assertion(hf_around_facet->id() == 0); + hf_around_facet->id() = 1; + const Polyhedron::Traits::Point_3& p = hf_around_facet->vertex()->point(); + polyline_data.polyline->polylines.front().push_back(p); + center += qglviewer::Vec(p.x(), p.y(), p.z()); + ++counter; + } while(++hf_around_facet != it->facet_begin()); + polyline_data.polyline->polylines.front().push_back(hf_around_facet->vertex()->point()); + polyline_data.position = center / counter; + } + } + } + // filter events for selecting / activating holes with mouse input + bool eventFilter(QObject* /*target*/, QEvent *event) + { + // This filter is both filtering events from 'viewer' and 'main window' + Mouse_keyboard_state old_state = state; + // key events + if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { + QKeyEvent *keyEvent = static_cast(event); + Qt::KeyboardModifiers modifiers = keyEvent->modifiers(); + + state.ctrl_pressing = modifiers.testFlag(Qt::ControlModifier); + } + // mouse events + if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { + QMouseEvent* mouse_event = static_cast(event); + if(mouse_event->button() == Qt::LeftButton) { + state.left_button_pressing = event->type() == QEvent::MouseButtonPress; + } + } + + if(!visible()) { return false; } // if not visible just update event state but don't do any action + + // activate closest hole + if(event->type() == QEvent::HoverMove) + { + QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin(); + const QPoint& p = viewer->mapFromGlobal(QCursor::pos()); + bool need_repaint = activate_closest_hole(p.x(), p.y()); + if(need_repaint) { emit itemChanged(); } + } + + // select closest hole + bool left_clicked_now = state.left_button_pressing && !old_state.left_button_pressing; + if(left_clicked_now && state.ctrl_pressing) { + Selected_holes_set::iterator active_it = selected_holes.find(active_hole); + if(active_it == selected_holes.end()) { + selected_holes.insert(active_hole); + } + else { selected_holes.erase(active_it); } + emit itemChanged(); + } + return false; + } + +private: + // finds closest polyline from polyline_data_list and makes it active_hole + bool activate_closest_hole(int x, int y) { + if(polyline_data_list.empty()) { return false; } + + QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin(); + qglviewer::Camera* camera = viewer->camera(); + + Polyline_data_list::const_iterator min_it = polyline_data_list.begin(); + const qglviewer::Vec& pos_it = camera->projectedCoordinatesOf(min_it->position); + float min_dist = std::pow(pos_it.x - x, 2) + std::pow(pos_it.y - y, 2); + + for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) + { + const qglviewer::Vec& pos_it = camera->projectedCoordinatesOf(it->position); + float dist = std::pow(pos_it.x - x, 2) + std::pow(pos_it.y - y, 2); + if(dist < min_dist) { + min_dist = dist; + min_it = it; + } + } + + if(min_it == active_hole) { + return false; + } + active_hole = min_it; + return true; + } + // clears internal data except poly_item + void clear() { + for(Polyline_data_list::const_iterator it = polyline_data_list.begin(); it != polyline_data_list.end(); ++it) { + delete it->polyline; + } + polyline_data_list.clear(); + selected_holes.clear(); + active_hole = polyline_data_list.end(); + } + + Polyline_data_list::const_iterator active_hole; + Mouse_keyboard_state state; +public: + Selected_holes_set selected_holes; + Scene_polyhedron_item* poly_item; + Polyline_data_list polyline_data_list; +}; // end class Scene_edges_item +/////////////////////////////////////////////////////////////////////////////////////////////////// class Polyhedron_demo_hole_filling_plugin : public QObject, @@ -30,16 +235,17 @@ class Polyhedron_demo_hole_filling_plugin : public: bool applicable() const { return qobject_cast(scene->item(scene->mainSelectionIndex())); } - void print_message(QString message) { messages->information(message);} + void print_message(QString message) { messages->information(message); } QList actions() const { return QList() << actionHoleFilling; } void init(QMainWindow* mainWindow, Scene_interface* scene_interface, Messages_interface* m); public slots: - void hole_filling_action(); - void on_Create_polyline_item_button(); - void on_Fill_and_update_button(); + void hole_filling_action() { dock_widget->show(); } void on_Fill_all_holes_button(); - + void on_Visualize_holes_button(); + void on_Fill_selected_holes_button(); + void item_about_to_be_destroyed(Scene_item*); + void dock_widget_visibility_changed(bool visible); private: QMainWindow* mw; Scene_interface* scene; @@ -49,9 +255,10 @@ private: QDockWidget* dock_widget; Ui::HoleFilling* ui_widget; - typedef std::map > Polyline_data_map; - Polyline_data_map polyline_data_map; + typedef std::map Polyhedron_item_hole_map; + Polyhedron_item_hole_map polyhedron_item_hole_map; + + void fill(Polyhedron& polyhedron, Polyhedron::Halfedge_handle halfedge); }; // end Polyhedron_demo_hole_filling_plugin void Polyhedron_demo_hole_filling_plugin::init(QMainWindow* mainWindow, @@ -72,18 +279,53 @@ void Polyhedron_demo_hole_filling_plugin::init(QMainWindow* mainWindow, ui_widget->setupUi(dock_widget); mw->addDockWidget(Qt::LeftDockWidgetArea, dock_widget); - connect(ui_widget->Create_polyline_item_button, SIGNAL(clicked()), this, SLOT(on_Create_polyline_item_button())); - connect(ui_widget->Fill_and_update_button, SIGNAL(clicked()), this, SLOT(on_Fill_and_update_button())); + connect(dock_widget, SIGNAL(visibilityChanged(bool)), this, SLOT(dock_widget_visibility_changed(bool)) ); + connect(ui_widget->Visualize_holes_button, SIGNAL(clicked()), this, SLOT(on_Visualize_holes_button())); + connect(ui_widget->Fill_selected_holes_button, SIGNAL(clicked()), this, SLOT(on_Fill_selected_holes_button())); connect(ui_widget->Fill_all_holes_button, SIGNAL(clicked()), this, SLOT(on_Fill_all_holes_button())); + + Scene* scene_casted = dynamic_cast(scene_interface); + if(scene_casted) + { connect(scene_casted, SIGNAL(itemAboutToBeDestroyed(Scene_item*)), this, SLOT(item_about_to_be_destroyed(Scene_item*))); } } -void Polyhedron_demo_hole_filling_plugin::hole_filling_action(){ - if(dock_widget != NULL) { - dock_widget->show(); +// removes items from polyhedron_item_hole_map +// if item is Scene_polyhedron_item then takes its Scene_polylines_collection and erases it from scene +// (this will cause another call to this handler) +// else (item is Scene_polylines_collection) removes it from the map +void Polyhedron_demo_hole_filling_plugin::item_about_to_be_destroyed(Scene_item* scene_item) { + Scene_polyhedron_item* poly_item = qobject_cast(scene_item); + if(poly_item) { + Polyhedron_item_hole_map::iterator hole_it = polyhedron_item_hole_map.find(poly_item); + if(hole_it != polyhedron_item_hole_map.end()) { + scene->erase( scene->item_id(hole_it->second) ); + } + } + + Scene_polylines_collection* polyline_item = qobject_cast(scene_item); + if(polyline_item) { + polyhedron_item_hole_map.erase(polyline_item->poly_item); } } +// removes Scene_polylines_collection items on visibility = false +void Polyhedron_demo_hole_filling_plugin::dock_widget_visibility_changed(bool visible) +{ + if(visible) { return; } -void Polyhedron_demo_hole_filling_plugin::on_Create_polyline_item_button() { + for(Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); + i < end; ++i) + { + Scene_polyhedron_item* poly_item = qobject_cast(scene->item(i)); + if(poly_item) { + Polyhedron_item_hole_map::iterator hole_it = polyhedron_item_hole_map.find(poly_item); + if(hole_it != polyhedron_item_hole_map.end()) { + scene->erase( scene->item_id(hole_it->second) ); + } + } + } +} +// creates a Scene_polylines_collection and associate it with active Scene_polyhedron_item +void Polyhedron_demo_hole_filling_plugin::on_Visualize_holes_button() { typedef Polyhedron::Halfedge_iterator Halfedge_iterator; typedef Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator; @@ -93,34 +335,52 @@ void Polyhedron_demo_hole_filling_plugin::on_Create_polyline_item_button() { print_message("Error: there is no selected polyhedron item!"); return; } + if(polyhedron_item_hole_map.find(poly_item) != polyhedron_item_hole_map.end()) { + print_message("Error: selected polyhedron item already has an associated hole item!"); + return; + } + Scene_polylines_collection* polylines_collection = new Scene_polylines_collection(poly_item); + polylines_collection->get_holes(*poly_item->polyhedron()); - Polyhedron& poly = *poly_item->polyhedron(); - - for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it) - { it->id() = 0; } - - int counter = 0; - for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ++it){ - if(it->is_border() && it->id() == 0){ - Scene_polylines_item* new_polylines_item = new Scene_polylines_item(); - polyline_data_map[new_polylines_item] = std::make_pair(poly_item, it); - new_polylines_item->polylines.push_back(Scene_polylines_item::Polyline()); - Halfedge_around_facet_circulator hf_around_facet = it->facet_begin(); - do { - CGAL_assertion(hf_around_facet->id() == 0); - hf_around_facet->id() = 1; - new_polylines_item->polylines.front().push_back(hf_around_facet->vertex()->point()); - } while(++hf_around_facet != it->facet_begin()); - new_polylines_item->polylines.front().push_back(hf_around_facet->vertex()->point()); - - new_polylines_item->setName(QString("Border polyline %1").arg(counter++) ); - new_polylines_item->setColor(Qt::red); - new_polylines_item->setRenderingMode(Wireframe); - scene->addItem(new_polylines_item); - } + if(polylines_collection->polyline_data_list.empty()) { + print_message("There is no holes in selected polyhedron!"); + delete polylines_collection; + return; + } + else { + poly_item->setFlatMode(); // for better visualization + polyhedron_item_hole_map[poly_item] = polylines_collection; + int item_id = scene->addItem(polylines_collection); + scene->setSelectedItem(item_id); + mw->installEventFilter(polylines_collection); + polylines_collection->setName(tr("%1-hole visualizer").arg(poly_item->name())); + // poly_item->setColor(QColor(poly_item->color().red(), poly_item->color().green(), poly_item->color().blue(), 100)); } } +// fills selected holes on active Scene_polylines_collection +void Polyhedron_demo_hole_filling_plugin::on_Fill_selected_holes_button() { + int item_id = scene->mainSelectionIndex(); + Scene_polylines_collection* polyline_item = qobject_cast(scene->item(item_id)); + if(!polyline_item) { + print_message("Error: there is no selected holes item!"); + return; + } + bool create_new = ui_widget->Create_new_polyhedron_check_box->checkState() == Qt::Checked; + + for(Scene_polylines_collection::Selected_holes_set::iterator it = polyline_item->selected_holes.begin(); + it != polyline_item->selected_holes.end(); ++it) { + fill(*(polyline_item->poly_item->polyhedron()), (*it)->halfedge); + } + polyline_item->get_holes(*polyline_item->poly_item->polyhedron()); + + scene->itemChanged(polyline_item->poly_item); + if(polyline_item->polyline_data_list.empty()) { + scene->erase( scene->item_id(polyline_item) ); + } + else { scene->itemChanged(polyline_item); } +}; +// fills all holes and removes associated Scene_polylines_collection if any void Polyhedron_demo_hole_filling_plugin::on_Fill_all_holes_button() { typedef Polyhedron::Halfedge_iterator Halfedge_iterator; typedef Polyhedron::Halfedge_around_facet_circulator Halfedge_around_facet_circulator; @@ -132,12 +392,10 @@ void Polyhedron_demo_hole_filling_plugin::on_Fill_all_holes_button() { return; } - // take parameters + bool create_new = ui_widget->Create_new_polyhedron_check_box->checkState() == Qt::Checked; bool fair = ui_widget->Triangulate_refine_fair_radio_button->isChecked(); bool refine = fair || ui_widget->Triangulate_refine_radio_button->isChecked(); double alpha = ui_widget->Density_control_factor_spin_box->value(); - bool create_new = ui_widget->Create_new_polyhedron_check_box->checkState() == Qt::Checked; - // create new polyhedron item if required Polyhedron* poly_pointer; @@ -158,29 +416,10 @@ void Polyhedron_demo_hole_filling_plugin::on_Fill_all_holes_button() { for(Halfedge_iterator it = poly.halfedges_begin(); it != poly.halfedges_end(); ){ if(it->is_border()){ any_changes = true; - - if(!fair && !refine) { - CGAL::triangulate_hole(poly, it); - } - else if(!fair) { - CGAL::triangulate_and_refine_hole(poly, it, NULL, alpha); - } - else { - if(ui_widget->Scale_dependent_weight_radio_button->isChecked()) - CGAL::triangulate_refine_and_fair_hole(poly, it, NULL, alpha, - CGAL::Fairing_scale_dependent_weight()); - if(ui_widget->Uniform_weight_radio_button->isChecked()) - CGAL::triangulate_refine_and_fair_hole(poly, it, NULL, alpha, - CGAL::Fairing_uniform_weight()); - else - CGAL::triangulate_refine_and_fair_hole(poly, it, NULL, alpha, - CGAL::Fairing_cotangent_weight()); - } - + fill(poly, it); it = poly.halfedges_begin(); - continue; } - ++it; + else { ++it; } } if(!any_changes) { print_message("There is no holes in selected polyhedron!"); @@ -197,51 +436,39 @@ void Polyhedron_demo_hole_filling_plugin::on_Fill_all_holes_button() { else { scene->itemChanged(poly_item); } - -} - -void Polyhedron_demo_hole_filling_plugin::on_Fill_and_update_button() { - int item_id = scene->mainSelectionIndex(); - Scene_polylines_item* polyline_item = qobject_cast(scene->item(item_id)); - if(!polyline_item) { - print_message("Error: there is no selected polyline item!"); - return; + // remove assoc Scene_polylines_collection item if any + if(!create_new) { + Polyhedron_item_hole_map::iterator hole_it = polyhedron_item_hole_map.find(poly_item); + if(hole_it != polyhedron_item_hole_map.end()) { + scene->erase( scene->item_id(hole_it->second) ); + } } +} +// helper function for filling holes +void Polyhedron_demo_hole_filling_plugin::fill + (Polyhedron& poly, Polyhedron::Halfedge_handle it) { - // take parameters bool fair = ui_widget->Triangulate_refine_fair_radio_button->isChecked(); bool refine = fair || ui_widget->Triangulate_refine_radio_button->isChecked(); double alpha = ui_widget->Density_control_factor_spin_box->value(); - //bool create_new = ui_widget->Create_new_polyhedron_check_box->checkState() == Qt::Checked; - Polyline_data_map::iterator it = polyline_data_map.find(polyline_item); - if(it == polyline_data_map.end()) { - print_message("Error: polyline should be associated with a polyhedron by this plugin!"); - return; + if(!fair && !refine) { + CGAL::triangulate_hole(poly, it); + } + else if(!fair) { + CGAL::triangulate_and_refine_hole(poly, it, alpha); + } + else { + if(ui_widget->Scale_dependent_weight_radio_button->isChecked()) + CGAL::triangulate_refine_and_fair_hole(poly, it, alpha, + CGAL::Fairing_scale_dependent_weight()); + if(ui_widget->Uniform_weight_radio_button->isChecked()) + CGAL::triangulate_refine_and_fair_hole(poly, it, alpha, + CGAL::Fairing_uniform_weight()); + else + CGAL::triangulate_refine_and_fair_hole(poly, it, alpha, + CGAL::Fairing_cotangent_weight()); } - - Scene_polyhedron_item* poly_item = it->second.first; - //// create new polyhedron item if required - //Scene_polyhedron_item* new_item = 0; - //if(create_new) { - // new_item = new Scene_polyhedron_item(*poly_item->polyhedron()); - // QString param_exp = fair ? "Refine + Fair" : refine ? "Refine" : "Triangulate"; - // new_item->setName(tr("%1-Filled-%2-(alpha:%3)").arg(poly_item->name()).arg(param_exp).arg(alpha)); - //} - - //CGAL::fill(*poly_item->polyhedron(), it->second.second, refine, alpha, fair); - // scene->itemChanged(poly_item); - //if(create_new) { - // using std::swap; - // swap(*poly_item->polyhedron(), *new_item->polyhedron()); - // Scene_interface::Item_id index = scene->addItem(new_item); - // scene->itemChanged(new_item); - // scene->setSelectedItem(index); - //} - //else { - // scene->itemChanged(poly_item); - //} - // remove polyline } Q_EXPORT_PLUGIN2(Polyhedron_demo_hole_filling_plugin, Polyhedron_demo_hole_filling_plugin) diff --git a/Polyhedron/demo/Polyhedron/Scene.cpp b/Polyhedron/demo/Polyhedron/Scene.cpp index 5bda19c63f5..8f97799332b 100644 --- a/Polyhedron/demo/Polyhedron/Scene.cpp +++ b/Polyhedron/demo/Polyhedron/Scene.cpp @@ -138,6 +138,12 @@ Scene::item(Item_id index) const return m_entries.value(index); // QList::value checks bounds } +Scene::Item_id +Scene::item_id(Scene_item* scene_item) const +{ + return m_entries.indexOf(scene_item); +} + int Scene::numberOfEntries() const { diff --git a/Polyhedron/demo/Polyhedron/Scene.h b/Polyhedron/demo/Polyhedron/Scene.h index 23b411277df..7f68e9a5fa1 100644 --- a/Polyhedron/demo/Polyhedron/Scene.h +++ b/Polyhedron/demo/Polyhedron/Scene.h @@ -60,6 +60,7 @@ public: int numberOfEntries() const; const QList& entries() const { return m_entries; } Q_INVOKABLE Scene_item* item(int) const ; + Item_id item_id(Scene_item*) const; //! \todo Replace Index based selection functionality with those //! functions.