From ae581c865a5c82238cb6ce0aea6bbbe9bddd8e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Thu, 2 Jun 2022 02:21:26 +0200 Subject: [PATCH 1/6] Iterative visualization --- .../CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h | 45 ++++++- .../Alpha_wrap_3/Alpha_wrap_3_plugin.cpp | 110 +++++++++++++++++- 2 files changed, 143 insertions(+), 12 deletions(-) diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h index ba170ba02a9..696262f15bc 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h @@ -99,6 +99,17 @@ public: }; }; +struct Wrapping_default_visitor +{ + Wrapping_default_visitor() { } + + template + void before_Steiner_point_insertion(const Wrapper&, const Point&) { } + + template + void after_Steiner_point_insertion(const Wrapper&, VertexHandle) { } +}; + template class Alpha_wrap_3 { @@ -172,6 +183,8 @@ public: public: const Geom_traits& geom_traits() const { return m_dt.geom_traits(); } + Dt& triangulation() { return m_dt; } + const Dt& triangulation() const { return m_dt; } double default_alpha() const { @@ -216,6 +229,15 @@ public: OVPM ovpm = choose_parameter(get_parameter(np, internal_np::vertex_point), get_property_map(vertex_point, output_mesh)); + typedef typename internal_np::Lookup_named_param_def < + internal_np::visitor_t, + NamedParameters, + Wrapping_default_visitor // default + >::reference Visitor; + + Wrapping_default_visitor default_visitor; + Visitor visitor = choose_parameter(get_parameter_reference(np, internal_np::visitor), default_visitor); + std::vector no_seeds; using Seeds = typename internal_np::Lookup_named_param_def< internal_np::seed_points_t, NamedParameters, std::vector >::reference; @@ -237,7 +259,7 @@ public: CGAL::parameters::vertex_point_map(ovpm).stream_precision(17)); #endif - alpha_flood_fill(); + alpha_flood_fill(visitor); #ifdef CGAL_AW3_TIMER t.stop(); @@ -607,11 +629,12 @@ private: return true; } +public: // Manifoldness is tolerated while debugging and extracting at intermediate states // Not the preferred way because it uses 3*nv storage template void extract_possibly_non_manifold_surface(OutputMesh& output_mesh, - OVPM ovpm) + OVPM ovpm) const { namespace PMP = Polygon_mesh_processing; @@ -696,7 +719,7 @@ private: template void extract_manifold_surface(OutputMesh& output_mesh, - OVPM ovpm) + OVPM ovpm) const { namespace PMP = Polygon_mesh_processing; @@ -748,7 +771,12 @@ private: if(faces.empty()) return; - CGAL_assertion(PMP::is_polygon_soup_a_polygon_mesh(faces)); + if(!PMP::is_polygon_soup_a_polygon_mesh(faces)) + { + CGAL_warning_msg(false, "Could NOT extract mesh..."); + return; + } + PMP::polygon_soup_to_polygon_mesh(points, faces, output_mesh, CGAL::parameters::default_values(), CGAL::parameters::vertex_point_map(ovpm)); @@ -763,7 +791,7 @@ private: template void extract_surface(OutputMesh& output_mesh, OVPM ovpm, - const bool tolerate_non_manifoldness = false) + const bool tolerate_non_manifoldness = false) const { if(tolerate_non_manifoldness) extract_possibly_non_manifold_surface(output_mesh, ovpm); @@ -990,7 +1018,8 @@ private: return initialize_with_cavities(seeds); } - void alpha_flood_fill() + template + void alpha_flood_fill(Visitor& visitor) { #ifdef CGAL_AW3_DEBUG std::cout << "> Flood fill..." << std::endl; @@ -1082,10 +1111,14 @@ private: m_queue.erase(Gate(mf)); } + visitor.before_Steiner_point_insertion(*this, steiner_point); + // Actual insertion of the Steiner point Vertex_handle vh = m_dt.insert(steiner_point, lt, conflict_cell, li, lj); vh->info() = DEFAULT; + visitor.after_Steiner_point_insertion(*this, vh); + std::vector new_cells; new_cells.reserve(32); m_dt.incident_cells(vh, std::back_inserter(new_cells)); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp index 49f943f5437..81ee7dc974a 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp @@ -29,6 +29,94 @@ #include "ui_alpha_wrap_3_dialog.h" +template +struct AW3_visu_visitor +{ + AW3_visu_visitor(MainWindow* mw, + Scene* scene, + Scene_surface_mesh_item* wrap_item) + : m_mw(mw), + m_scene(scene), + m_wrap_item(wrap_item) + { } + + void set_fcolors(MainWindow mw, + SMesh* smesh, + std::vector colors) + { + typedef SMesh SMesh; + typedef boost::graph_traits::face_descriptor face_descriptor; + + SMesh::Property_map fcolors = + smesh->property_map("f:color").first; + + bool created; + std::tie(fcolors, created) = smesh->add_property_map("f:color", + CGAL::IO::Color(0,0,0)); + assert(colors.size() == smesh->number_of_faces()); + + int color_id = 0; + for(face_descriptor fd : faces(*smesh)) + fcolors[fd] = colors[color_id++]; + } + +public: + template + void before_Steiner_point_insertion(const Wrapper& wrapper, + const Point& p) + { + if(wrapper.triangulation().number_of_vertices() % 10 != 0) + return; + + if(!m_wrap_item) + return; + + std::cout << "on_Steiner_point_insert (" << wrapper.triangulation().number_of_vertices() << ")" << std::endl; + + SMesh* wrap_ptr = m_wrap_item->polyhedron(); + auto vpm = get(CGAL::vertex_point, *wrap_ptr); + wrapper.extract_surface(*wrap_ptr, vpm, true /*tolerate non manifoldness*/); + + m_wrap_item->invalidateOpenGLBuffers(); + m_wrap_item->redraw(); + m_wrap_item->itemChanged(); + +// SMesh wrap; +// auto vpm = get(CGAL::vertex_point, wrap); +// wrapper.extract_surface(wrap, vpm, true /*tolerate non manifoldness*/); + +// Scene_surface_mesh_item* new_wrap_item = new Scene_surface_mesh_item(wrap); +// new_wrap_item->setName(QString("Wrap")); +// new_wrap_item->setColor(Qt::gray); + +// new_wrap_item->setName(m_wrap_item->name()); +// new_wrap_item->setColor(m_wrap_item->color()); +// new_wrap_item->setRenderingMode(m_wrap_item->renderingMode()); +// new_wrap_item->setVisible(m_wrap_item->visible()); +// Scene_item_with_properties *property_item = dynamic_cast(new_wrap_item); +// m_scene->replaceItem(m_scene->item_id(m_wrap_item), new_wrap_item, true); +// if(property_item) +// property_item->copyProperties(m_wrap_item); +// new_wrap_item->invalidateOpenGLBuffers(); +// m_wrap_item->deleteLater(); + + QApplication::processEvents(); + } + + template + void after_Steiner_point_insertion(const Wrapper& wrapper, + const VertexHandle vh) + { + + } + + +private: + MainWindow* m_mw; + Scene* m_scene; + Scene_surface_mesh_item* m_wrap_item; +}; + class Polyhedron_demo_alpha_wrap_3_plugin : public QObject, public CGAL::Three::Polyhedron_demo_plugin_helper @@ -136,7 +224,7 @@ public Q_SLOTS: if(alpha <= 0. || offset <= 0.) return; - QApplication::setOverrideCursor(Qt::WaitCursor); +// QApplication::setOverrideCursor(Qt::WaitCursor); TS_Oracle ts_oracle; SS_Oracle ss_oracle(ts_oracle); @@ -287,6 +375,11 @@ public Q_SLOTS: } } + std::cout << triangles.size() << " triangles" << std::endl; + std::cout << segments.size() << " edges" << std::endl; + std::cout << points.size() << " points" << std::endl; + std::cout << "wrap s/tr: " << wrap_segments << " " << wrap_triangles << std::endl; + if(wrap_triangles) oracle.add_triangle_soup(triangles); if(wrap_segments) @@ -296,7 +389,7 @@ public Q_SLOTS: if(!oracle.do_call()) { print_message("Warning: empty input - nothing to wrap"); - QApplication::restoreOverrideCursor(); +// QApplication::restoreOverrideCursor(); return; } @@ -315,15 +408,20 @@ public Q_SLOTS: CGAL::Alpha_wraps_3::internal::Alpha_wrap_3 aw3(oracle); SMesh wrap; - aw3(alpha, offset, wrap, - CGAL::parameters::do_enforce_manifoldness(enforce_manifoldness)); Scene_surface_mesh_item* wrap_item = new Scene_surface_mesh_item(wrap); wrap_item->setName(tr("Wrap alpha %2 offset %3").arg(alpha).arg(offset)); - wrap_item->setColor(Qt::cyan); + wrap_item->setColor(Qt::gray); scene->addItem(wrap_item); - QApplication::restoreOverrideCursor(); + AW3_visu_visitormw))>::type, + std::remove_referencescene))>::type> visitor(mw, scene, wrap_item); + + aw3(alpha, offset, wrap, + CGAL::parameters::do_enforce_manifoldness(enforce_manifoldness) + .visitor(visitor)); + +// QApplication::restoreOverrideCursor(); } private: From 965964e8b8d6a66439bf847be089d8f4ac27b9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Thu, 2 Jun 2022 14:25:43 +0200 Subject: [PATCH 2/6] Visualization with a soup, some colors, alpha shading etc. --- .../CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h | 4 + .../Alpha_wrap_3/Alpha_wrap_3_plugin.cpp | 170 +++++++++++++----- 2 files changed, 128 insertions(+), 46 deletions(-) diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h index 696262f15bc..3d09b2a8dff 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h @@ -108,6 +108,8 @@ struct Wrapping_default_visitor template void after_Steiner_point_insertion(const Wrapper&, VertexHandle) { } + + void after_alpha_wrapping() { } }; template @@ -337,6 +339,8 @@ public: dump_triangulation_faces("final_dt3.off", false /*only_boundary_faces*/); #endif #endif + + visitor.after_alpha_wrapping(); } // Convenience overloads diff --git a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp index 81ee7dc974a..eeda371c185 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp @@ -29,53 +29,117 @@ #include "ui_alpha_wrap_3_dialog.h" -template +template struct AW3_visu_visitor { - AW3_visu_visitor(MainWindow* mw, - Scene* scene, - Scene_surface_mesh_item* wrap_item) - : m_mw(mw), - m_scene(scene), + AW3_visu_visitor(Scene* scene, + WrapItem* wrap_item) + : m_scene(scene), m_wrap_item(wrap_item) { } - void set_fcolors(MainWindow mw, - SMesh* smesh, - std::vector colors) - { - typedef SMesh SMesh; - typedef boost::graph_traits::face_descriptor face_descriptor; - - SMesh::Property_map fcolors = - smesh->property_map("f:color").first; - - bool created; - std::tie(fcolors, created) = smesh->add_property_map("f:color", - CGAL::IO::Color(0,0,0)); - assert(colors.size() == smesh->number_of_faces()); - - int color_id = 0; - for(face_descriptor fd : faces(*smesh)) - fcolors[fd] = colors[color_id++]; - } - public: - template - void before_Steiner_point_insertion(const Wrapper& wrapper, + template + void before_Steiner_point_insertion(const AlphaWrapper& wrapper, const Point& p) { - if(wrapper.triangulation().number_of_vertices() % 10 != 0) - return; + // @todo something nicer & smarter + if(wrapper.triangulation().number_of_vertices() < 500) + { + // Do all iterations + } + else if(wrapper.triangulation().number_of_vertices() < 5000) + { + if(wrapper.triangulation().number_of_vertices() % 10 != 0) + return; + } + else if(wrapper.triangulation().number_of_vertices() < 10000) + { + if(wrapper.triangulation().number_of_vertices() % 100 != 0) + return; + } + else if(wrapper.triangulation().number_of_vertices() < 1000000) + { + if(wrapper.triangulation().number_of_vertices() % 1000 != 0) + return; + } if(!m_wrap_item) return; +// for(auto cit=wrapper.triangulation().finite_cells_begin(), cend=wrapper.triangulation().finite_cells_end(); cit!=cend; ++cit) +// std::cout << cit->time_stamp() << std::endl; + std::cout << "on_Steiner_point_insert (" << wrapper.triangulation().number_of_vertices() << ")" << std::endl; - SMesh* wrap_ptr = m_wrap_item->polyhedron(); - auto vpm = get(CGAL::vertex_point, *wrap_ptr); - wrapper.extract_surface(*wrap_ptr, vpm, true /*tolerate non manifoldness*/); + // EXTRACT --- + + using Dt = typename std::decay::type; + using Vertex_handle = typename Dt::Vertex_handle; + using Facet = typename Dt::Facet; + using Cell_handle = typename Dt::Cell_handle; + + std::vector points; + std::vector > faces; + + std::unordered_map vertex_to_id; + std::size_t nv = 0; + + std::size_t min_time_stamp = -1, max_time_stamp = 0; + for(auto cit=wrapper.triangulation().finite_cells_begin(), cend=wrapper.triangulation().finite_cells_end(); cit!=cend; ++cit) + { + if(cit->time_stamp() > max_time_stamp) + max_time_stamp = cit->time_stamp(); + if(cit->time_stamp() < min_time_stamp) + min_time_stamp = cit->time_stamp(); + } + + std::vector vcolors; + std::vector fcolors; + + std::size_t vi = 0, fi = 0; + for(auto fit=wrapper.triangulation().finite_facets_begin(), fend=wrapper.triangulation().finite_facets_end(); fit!=fend; ++fit) + { + Facet f = *fit; + if(!f.first->info().is_outside) + f = wrapper.triangulation().mirror_facet(f); + + const Cell_handle c = f.first; + const int s = f.second; + const Cell_handle nh = c->neighbor(s); + if(c->info().is_outside == nh->info().is_outside) + continue; + + std::array ids; + for(int pos=0; pos<3; ++pos) + { + Vertex_handle vh = c->vertex(Dt::vertex_triple_index(s, pos)); + auto insertion_res = vertex_to_id.emplace(vh, nv); + if(insertion_res.second) // successful insertion, never-seen-before vertex + { + points.push_back(wrapper.triangulation().point(vh)); + vcolors.push_back(CGAL::IO::Color(0, 0, 0)); + ++nv; + } + + ids[pos] = insertion_res.first->second; + } + + faces.emplace_back(std::vector{ids[0], ids[1], ids[2]}); + double color_val = double(c->time_stamp() - min_time_stamp) / double(max_time_stamp - min_time_stamp); + color_val = int(256. * color_val); + // fcolors.push_back(CGAL::IO::Color(color_val, 10, 150)); // young is red, old is blue + // fcolors.push_back(CGAL::IO::Color(256 - color_val, 256 - color_val, 256 - color_val)); // young is light, old is dark + fcolors.push_back(CGAL::IO::Color(100, 100, 100)); // darkish gray + } + +// std::cout << "DT NV " << wrapper.triangulation().number_of_vertices() << " vs " << nv << " vs " << points.size() << std::endl; +// std::cout << "DT NF " << wrapper.triangulation().number_of_finite_facets() << " vs " << fi << " vs " << faces.size() << std::endl; + + // --- EXTRACT + + m_wrap_item->load(points, faces, fcolors, vcolors); + m_wrap_item->setAlpha(255 / 2); m_wrap_item->invalidateOpenGLBuffers(); m_wrap_item->redraw(); @@ -103,18 +167,25 @@ public: QApplication::processEvents(); } - template - void after_Steiner_point_insertion(const Wrapper& wrapper, + template + void after_Steiner_point_insertion(const AlphaWrapper& wrapper, const VertexHandle vh) { } + void after_alpha_wrapping() + { + m_wrap_item->setAlpha(255); + m_wrap_item->invalidateOpenGLBuffers(); + m_wrap_item->redraw(); + m_wrap_item->itemChanged(); + QApplication::processEvents(); + } private: - MainWindow* m_mw; Scene* m_scene; - Scene_surface_mesh_item* m_wrap_item; + WrapItem* m_wrap_item; }; class Polyhedron_demo_alpha_wrap_3_plugin @@ -258,6 +329,8 @@ public Q_SLOTS: get(vpm, source(h, *pMesh))); } + sm_item->setRenderingMode(Flat); + continue; } @@ -280,6 +353,8 @@ public Q_SLOTS: soup_item->points()[p[2]]); } + soup_item->setRenderingMode(Flat); + continue; } @@ -407,20 +482,23 @@ public Q_SLOTS: CGAL::Alpha_wraps_3::internal::Alpha_wrap_3 aw3(oracle); + Scene_polygon_soup_item* iterative_wrap_item = new Scene_polygon_soup_item(); + iterative_wrap_item->setName(tr("Iterative wrap").arg(alpha).arg(offset)); + scene->addItem(iterative_wrap_item); + + AW3_visu_visitorscene))>::type, + Scene_polygon_soup_item> visitor(scene, iterative_wrap_item); + SMesh wrap; - - Scene_surface_mesh_item* wrap_item = new Scene_surface_mesh_item(wrap); - wrap_item->setName(tr("Wrap alpha %2 offset %3").arg(alpha).arg(offset)); - wrap_item->setColor(Qt::gray); - scene->addItem(wrap_item); - - AW3_visu_visitormw))>::type, - std::remove_referencescene))>::type> visitor(mw, scene, wrap_item); - aw3(alpha, offset, wrap, CGAL::parameters::do_enforce_manifoldness(enforce_manifoldness) .visitor(visitor)); +// Scene_surface_mesh_item* wrap_item = new Scene_surface_mesh_item(wrap); +// wrap_item->setName(tr("Wrap alpha %2 offset %3").arg(alpha).arg(offset)); +// wrap_item->setColor(Qt::gray); +// scene->addItem(wrap_item); + // QApplication::restoreOverrideCursor(); } From 37390bb6b8533b07444b2873b9a577678812aaea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 8 Jun 2022 12:03:25 +0200 Subject: [PATCH 3/6] Add dialog-less QGLviewer::saveSnapshot --- GraphicsView/include/CGAL/Qt/qglviewer.h | 15 +++++++++++++ GraphicsView/include/CGAL/Qt/qglviewer_impl.h | 21 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/GraphicsView/include/CGAL/Qt/qglviewer.h b/GraphicsView/include/CGAL/Qt/qglviewer.h index da0dbace517..e353c8de66e 100644 --- a/GraphicsView/include/CGAL/Qt/qglviewer.h +++ b/GraphicsView/include/CGAL/Qt/qglviewer.h @@ -563,6 +563,21 @@ public: */ void saveSnapshot(); + /*! + * Takes a snapshot without any dialog + */ + void saveSnapshot(const QString& fileName, + const qreal finalWidth, + const qreal finalHeight, + const bool expand = false, + const double oversampling = 1., + qglviewer::SnapShotBackground background_color = qglviewer::CURRENT_BACKGROUND); + + void saveSnapshot(const QString& fileName) + { + return saveSnapshot(fileName, this->width(), this->height()); + } + public: Q_SIGNALS: /*! Signal emitted by the default init() method. diff --git a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h index 07eb161909e..1e10b72d0b0 100644 --- a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h +++ b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h @@ -3761,8 +3761,29 @@ void CGAL::QGLViewer::saveSnapshot() } } +CGAL_INLINE_FUNCTION +void CGAL::QGLViewer::saveSnapshot(const QString& fileName, + const qreal finalWidth, const qreal finalHeight, + const bool expand, + const double oversampling, + qglviewer::SnapShotBackground background_color) +{ + if(fileName.isEmpty()) + return; + + QSize finalSize(finalWidth, finalHeight); + + QImage* image = takeSnapshot(qglviewer::SnapShotBackground(background_color), + finalSize, oversampling, expand); + if(image) + { + image->save(fileName); + delete image; + } } +} // namespace CGAL + CGAL_INLINE_FUNCTION bool CGAL::QGLViewer::isSharing() const { From beeae185a78f85de44cab5abdda05f24931b0be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 8 Jun 2022 12:04:02 +0200 Subject: [PATCH 4/6] Add a basic visitor to AW3 --- .../CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h index 3d09b2a8dff..e88e00fafe0 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h @@ -103,13 +103,26 @@ struct Wrapping_default_visitor { Wrapping_default_visitor() { } + template + void on_alpha_wrapping_begin(const AlphaWrapper&) { } + + template + void on_flood_fill_begin(const AlphaWrapper&) { } + + template + void before_facet_treatment(const AlphaWrapper&, const Gate&) { } + template void before_Steiner_point_insertion(const Wrapper&, const Point&) { } template void after_Steiner_point_insertion(const Wrapper&, VertexHandle) { } - void after_alpha_wrapping() { } + template + void on_flood_fill_end(const AlphaWrapper&) { } + + template + void on_alpha_wrapping_end(const AlphaWrapper&) { }; }; template @@ -187,6 +200,7 @@ public: const Geom_traits& geom_traits() const { return m_dt.geom_traits(); } Dt& triangulation() { return m_dt; } const Dt& triangulation() const { return m_dt; } + const Alpha_PQ& queue() const { return m_queue; } double default_alpha() const { @@ -252,6 +266,8 @@ public: t.start(); #endif + visitor.on_alpha_wrapping_begin(*this); + if(!initialize(alpha, offset, seeds)) return; @@ -340,7 +356,7 @@ public: #endif #endif - visitor.after_alpha_wrapping(); + visitor.on_alpha_wrapping_end(*this); } // Convenience overloads @@ -1029,6 +1045,8 @@ private: std::cout << "> Flood fill..." << std::endl; #endif + visitor.on_flood_fill_begin(*this); + // Explore all finite cells that are reachable from one of the initial outside cells. while(!m_queue.empty()) { @@ -1055,6 +1073,8 @@ private: std::cout << "Priority: " << gate.priority() << std::endl; #endif + visitor.before_facet_treatment(*this, gate); + m_queue.pop(); #ifdef CGAL_AW3_DEBUG_DUMP_EVERY_STEP @@ -1170,6 +1190,8 @@ private: } } // while(!queue.empty()) + visitor.on_flood_fill_end(*this); + // Check that no useful facet has been ignored CGAL_postcondition_code(for(auto fit=m_dt.finite_facets_begin(), fend=m_dt.finite_facets_end(); fit!=fend; ++fit) {) CGAL_postcondition_code( if(fit->first->info().is_outside == fit->first->neighbor(fit->second)->info().is_outside) continue;) From 801d9668532677ebc16d7c440ea8c7648e44deeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 8 Jun 2022 12:15:47 +0200 Subject: [PATCH 5/6] Enhance AW3 demo with iteration visualization & snapshot capabilities --- .../Alpha_wrap_3/Alpha_wrap_3_plugin.cpp | 214 ++++++++------ .../Alpha_wrap_3/alpha_wrap_3_dialog.ui | 265 ++++++++++++------ 2 files changed, 302 insertions(+), 177 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp index eeda371c185..49738f0f571 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp @@ -12,13 +12,15 @@ #include #include -#include #include -#include #include -#include #include +#include +#include #include +#include +#include +#include #include #include @@ -29,50 +31,57 @@ #include "ui_alpha_wrap_3_dialog.h" -template -struct AW3_visu_visitor +struct Iterative_AW3_visualization_visitor { - AW3_visu_visitor(Scene* scene, - WrapItem* wrap_item) - : m_scene(scene), - m_wrap_item(wrap_item) - { } +private: + bool m_do_snapshot; + Scene_polygon_soup_item* m_iterative_wrap_item = nullptr; + int sid = 0; public: + template + Iterative_AW3_visualization_visitor(Scene* scene, + const bool visualize_iterations, + const bool do_snapshot) + : m_do_snapshot(do_snapshot) + { + if(!visualize_iterations) + return; + + m_iterative_wrap_item = new Scene_polygon_soup_item(); + m_iterative_wrap_item->setName(QString("Iterative wrap")); + scene->addItem(m_iterative_wrap_item); + } + +public: + template + void on_alpha_wrapping_begin(const AlphaWrapper&) { } + + template + void on_flood_fill_begin(const AlphaWrapper&) { } + + template + void before_facet_treatment(const AlphaWrapper&, + const Facet&) { } + template void before_Steiner_point_insertion(const AlphaWrapper& wrapper, const Point& p) { - // @todo something nicer & smarter - if(wrapper.triangulation().number_of_vertices() < 500) - { - // Do all iterations - } - else if(wrapper.triangulation().number_of_vertices() < 5000) - { - if(wrapper.triangulation().number_of_vertices() % 10 != 0) - return; - } - else if(wrapper.triangulation().number_of_vertices() < 10000) - { - if(wrapper.triangulation().number_of_vertices() % 100 != 0) - return; - } - else if(wrapper.triangulation().number_of_vertices() < 1000000) - { - if(wrapper.triangulation().number_of_vertices() % 1000 != 0) - return; - } - - if(!m_wrap_item) + if(m_iterative_wrap_item == nullptr) return; -// for(auto cit=wrapper.triangulation().finite_cells_begin(), cend=wrapper.triangulation().finite_cells_end(); cit!=cend; ++cit) -// std::cout << cit->time_stamp() << std::endl; + // If the next top of the queue has vertices on the bbox, don't draw (as to avoid producing + // spikes in the visualization) +// const auto& gate = wrapper.queue().top(); +// if(wrapper.triangulation().number_of_vertices() > 500 && gate.is_artificial_facet()) +// return; - std::cout << "on_Steiner_point_insert (" << wrapper.triangulation().number_of_vertices() << ")" << std::endl; + // Skip some... + if(wrapper.triangulation().number_of_vertices() % 50 != 0) + return; - // EXTRACT --- + // Extract the wrap as a triangle soup using Dt = typename std::decay::type; using Vertex_handle = typename Dt::Vertex_handle; @@ -85,6 +94,8 @@ public: std::unordered_map vertex_to_id; std::size_t nv = 0; + // This is used to compute colors depending on what is old and what is new. + // It is not currently used (a uniform gray color is used), but leaving it as it might be useful. std::size_t min_time_stamp = -1, max_time_stamp = 0; for(auto cit=wrapper.triangulation().finite_cells_begin(), cend=wrapper.triangulation().finite_cells_end(); cit!=cend; ++cit) { @@ -128,64 +139,74 @@ public: faces.emplace_back(std::vector{ids[0], ids[1], ids[2]}); double color_val = double(c->time_stamp() - min_time_stamp) / double(max_time_stamp - min_time_stamp); color_val = int(256. * color_val); + // fcolors.push_back(CGAL::IO::Color(color_val, 10, 150)); // young is red, old is blue // fcolors.push_back(CGAL::IO::Color(256 - color_val, 256 - color_val, 256 - color_val)); // young is light, old is dark - fcolors.push_back(CGAL::IO::Color(100, 100, 100)); // darkish gray + fcolors.push_back(CGAL::IO::Color(100, 100, 100)); // uniform darkish gray } -// std::cout << "DT NV " << wrapper.triangulation().number_of_vertices() << " vs " << nv << " vs " << points.size() << std::endl; -// std::cout << "DT NF " << wrapper.triangulation().number_of_finite_facets() << " vs " << fi << " vs " << faces.size() << std::endl; + // Update the wrap item's visualization + m_iterative_wrap_item->load(points, faces, fcolors, vcolors); + m_iterative_wrap_item->setName(QString("Iterative wrap #%1").arg(sid)); + m_iterative_wrap_item->setAlpha(255 / 2); - // --- EXTRACT - - m_wrap_item->load(points, faces, fcolors, vcolors); - m_wrap_item->setAlpha(255 / 2); - - m_wrap_item->invalidateOpenGLBuffers(); - m_wrap_item->redraw(); - m_wrap_item->itemChanged(); - -// SMesh wrap; -// auto vpm = get(CGAL::vertex_point, wrap); -// wrapper.extract_surface(wrap, vpm, true /*tolerate non manifoldness*/); - -// Scene_surface_mesh_item* new_wrap_item = new Scene_surface_mesh_item(wrap); -// new_wrap_item->setName(QString("Wrap")); -// new_wrap_item->setColor(Qt::gray); - -// new_wrap_item->setName(m_wrap_item->name()); -// new_wrap_item->setColor(m_wrap_item->color()); -// new_wrap_item->setRenderingMode(m_wrap_item->renderingMode()); -// new_wrap_item->setVisible(m_wrap_item->visible()); -// Scene_item_with_properties *property_item = dynamic_cast(new_wrap_item); -// m_scene->replaceItem(m_scene->item_id(m_wrap_item), new_wrap_item, true); -// if(property_item) -// property_item->copyProperties(m_wrap_item); -// new_wrap_item->invalidateOpenGLBuffers(); -// m_wrap_item->deleteLater(); + m_iterative_wrap_item->invalidateOpenGLBuffers(); + m_iterative_wrap_item->redraw(); + m_iterative_wrap_item->itemChanged(); + // Refresh the view QApplication::processEvents(); + + if(m_do_snapshot) + { + std::stringstream oss; + oss << "Wrap_iteration-" << sid << ".png" << std::ends; + QString filename = QString::fromStdString(oss.str().c_str()); + + CGAL::Three::Viewer_interface* viewer = CGAL::Three::Three::activeViewer(); + viewer->saveSnapshot(filename, 1920, 1080, true /*expand*/, 2.0 /*oversampling*/); + } + + ++sid; } template - void after_Steiner_point_insertion(const AlphaWrapper& wrapper, - const VertexHandle vh) - { + void after_Steiner_point_insertion(const AlphaWrapper&, + const VertexHandle) { } - } + template + void on_flood_fill_end(const AlphaWrapper&) { } - void after_alpha_wrapping() + template + void on_alpha_wrapping_end(const AlphaWrapper&) { - m_wrap_item->setAlpha(255); - m_wrap_item->invalidateOpenGLBuffers(); - m_wrap_item->redraw(); - m_wrap_item->itemChanged(); + if(m_iterative_wrap_item == nullptr) + return; + + m_iterative_wrap_item->setName(QString("Iterative wrap #%1").arg(sid)); + + m_iterative_wrap_item->setAlpha(255); + m_iterative_wrap_item->invalidateOpenGLBuffers(); + m_iterative_wrap_item->redraw(); + m_iterative_wrap_item->itemChanged(); + + QApplication::processEvents(); + + if(m_do_snapshot) + { + std::stringstream oss; + oss << "Wrap_iteration-" << sid << ".png" << std::ends; + QString filename = QString::fromStdString(oss.str().c_str()); + + CGAL::Three::Viewer_interface* viewer = CGAL::Three::Three::activeViewer(); + viewer->saveSnapshot(filename); + } + + m_iterative_wrap_item->setVisible(false); + + // Refresh the view QApplication::processEvents(); } - -private: - Scene* m_scene; - WrapItem* m_wrap_item; }; class Polyhedron_demo_alpha_wrap_3_plugin @@ -249,6 +270,9 @@ private: connect(ui.wrapEdges, SIGNAL(clicked(bool)), this, SLOT(toggle_wrap_faces())); connect(ui.wrapFaces, SIGNAL(clicked(bool)), this, SLOT(toggle_wrap_edges())); + connect(ui.visualizeIterations, SIGNAL(clicked(bool)), + this, SLOT(update_iteration_snapshot_checkbox())); + connect(ui.buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); connect(ui.buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); @@ -268,6 +292,11 @@ public Q_SLOTS: ui.wrapEdges->setChecked(true); } + void update_iteration_snapshot_checkbox() + { + ui.snapshotIterations->setCheckable(ui.visualizeIterations->isChecked()); + } + void on_actionAlpha_wrap_3_triggered() { using Triangles = std::vector; @@ -291,11 +320,13 @@ public Q_SLOTS: const bool enforce_manifoldness = ui.runManifoldness->isChecked(); double alpha = ui.alphaValue->value(); double offset = ui.offsetValue->value(); + const bool visualize_iterations = ui.visualizeIterations->isChecked(); + const bool do_snapshot_iterations = ui.snapshotIterations->isChecked(); if(alpha <= 0. || offset <= 0.) return; -// QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::WaitCursor); TS_Oracle ts_oracle; SS_Oracle ss_oracle(ts_oracle); @@ -453,7 +484,7 @@ public Q_SLOTS: std::cout << triangles.size() << " triangles" << std::endl; std::cout << segments.size() << " edges" << std::endl; std::cout << points.size() << " points" << std::endl; - std::cout << "wrap s/tr: " << wrap_segments << " " << wrap_triangles << std::endl; + std::cout << "do wrap edges/faces: " << wrap_segments << " " << wrap_triangles << std::endl; if(wrap_triangles) oracle.add_triangle_soup(triangles); @@ -464,7 +495,7 @@ public Q_SLOTS: if(!oracle.do_call()) { print_message("Warning: empty input - nothing to wrap"); -// QApplication::restoreOverrideCursor(); + QApplication::restoreOverrideCursor(); return; } @@ -482,24 +513,21 @@ public Q_SLOTS: CGAL::Alpha_wraps_3::internal::Alpha_wrap_3 aw3(oracle); - Scene_polygon_soup_item* iterative_wrap_item = new Scene_polygon_soup_item(); - iterative_wrap_item->setName(tr("Iterative wrap").arg(alpha).arg(offset)); - scene->addItem(iterative_wrap_item); - - AW3_visu_visitorscene))>::type, - Scene_polygon_soup_item> visitor(scene, iterative_wrap_item); + Iterative_AW3_visualization_visitor visitor(scene, + visualize_iterations, + do_snapshot_iterations); SMesh wrap; aw3(alpha, offset, wrap, CGAL::parameters::do_enforce_manifoldness(enforce_manifoldness) .visitor(visitor)); -// Scene_surface_mesh_item* wrap_item = new Scene_surface_mesh_item(wrap); -// wrap_item->setName(tr("Wrap alpha %2 offset %3").arg(alpha).arg(offset)); -// wrap_item->setColor(Qt::gray); -// scene->addItem(wrap_item); + Scene_surface_mesh_item* wrap_item = new Scene_surface_mesh_item(wrap); + wrap_item->setName(tr("Wrap with alpha %2 offset %3").arg(alpha).arg(offset)); + wrap_item->setColor(Qt::gray); + scene->addItem(wrap_item); -// QApplication::restoreOverrideCursor(); + QApplication::restoreOverrideCursor(); } private: diff --git a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/alpha_wrap_3_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/alpha_wrap_3_dialog.ui index 6877e6a99e5..bbf25c4b345 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/alpha_wrap_3_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/alpha_wrap_3_dialog.ui @@ -10,31 +10,21 @@ 0 0 646 - 432 + 673 3D Alpha Wrapping - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - 3D Alpha Wrapping - - + + @@ -62,71 +52,21 @@ - + true - Enforce 2-manifoldness + <html><head/><body><p>Enforce 2-manifold output</p></body></html> Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - true - - - - - - - - - - true - - - - - - - - - - true - - - - - - - Offset value: - - - - - + + Qt::Vertical @@ -148,27 +88,59 @@ - - + + - Qt::Horizontal + Qt::Vertical - + + + 20 + 40 + + + - - + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - <html><head/><body><p>Wrap edges<br/><span style=" font-size:9pt;">If deactivated, only extremities of the edges are taken into account</span></p></body></html> + Visualize iterations - <html><head/><body><p>Use relative-to-bbox offset<br/><span style=" font-size:9pt;">As ratio of the length of the bbox's diagonal<br/>(value </span><span style=" font-size:9pt; font-style:italic;">x </span><span style=" font-size:9pt;">means </span><span style=" font-size:9pt; font-style:italic;">offset := bbox_diag_l / x)</span></p></body></html> + <html><head/><body><p>Use relative-to-bbox offset<br/><span style=" font-size:9pt;">As ratio of the length of the bbox's diagonal<br/>(i.e., value </span><span style=" font-size:9pt; font-style:italic;">x </span><span style=" font-size:9pt;">means </span><span style=" font-size:9pt; font-style:italic;">offset := bbox_diag_l / x)</span></p></body></html> + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -188,8 +160,29 @@ - - + + + + Qt::Horizontal + + + + + + + <html><head/><body><p>Wrap faces<br/><span style=" font-size:9pt;">If deactivated, only edges of the faces are taken into account</span></p></body></html> + + + + + + + <html><head/><body><p>Wrap edges<br/><span style=" font-size:9pt;">If deactivated, only extremities of the edges are taken into account</span></p></body></html> + + + + + @@ -198,17 +191,47 @@ - - - - <html><head/><body><p>Wrap faces<br/><span style=" font-size:9pt;">If deactivated, only edges of the faces are taken into account</span></p></body></html> + + + + Qt::Vertical - + + + 20 + 40 + + + - <html><head/><body><p>Use relative-to-bbox alpha<br/><span style=" font-size:9pt;">As ratio of the length of the bbox's diagonal<br/>(value </span><span style=" font-size:9pt; font-style:italic;">x </span><span style=" font-size:9pt;">means alpha</span><span style=" font-size:9pt; font-style:italic;"> := bbox_diag_l / x)</span></p></body></html> + <html><head/><body><p>Use relative-to-bbox alpha<br/><span style=" font-size:9pt;">As ratio of the length of the bbox's diagonal<br/>(i.e., value </span><span style=" font-size:9pt; font-style:italic;">x </span><span style=" font-size:9pt;">means alpha</span><span style=" font-size:9pt; font-style:italic;"> := bbox_diag_l / x)</span></p></body></html> + + + + + + + Qt::Vertical + + + + + + + + + + + + + + + + + true @@ -225,16 +248,90 @@ - + + + + + + + true + + + + + + + Qt::Horizontal + + + + + + + Offset value: + + + + Qt::Horizontal + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p>Snapshot iterations<br/><span style=" font-size:9pt;">For each iteration, save a snapshot of the viewer <br/>to a file named </span><span style=" font-size:9pt; font-style:italic;">Wrap-iteration_i.png</span></p></body></html> + + + + + + + + + + false + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + From c46e1d2d243db70c0f7f06056ae55cf2bf7679d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 9 Jun 2022 16:14:22 +0200 Subject: [PATCH 6/6] fix warnings --- .../Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp index 49738f0f571..8a190360c42 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Alpha_wrap_3/Alpha_wrap_3_plugin.cpp @@ -66,7 +66,7 @@ public: template void before_Steiner_point_insertion(const AlphaWrapper& wrapper, - const Point& p) + const Point& /* p */) { if(m_iterative_wrap_item == nullptr) return; @@ -108,7 +108,6 @@ public: std::vector vcolors; std::vector fcolors; - std::size_t vi = 0, fi = 0; for(auto fit=wrapper.triangulation().finite_facets_begin(), fend=wrapper.triangulation().finite_facets_end(); fit!=fend; ++fit) { Facet f = *fit;