// Copyright (c) 2019 CNRS and LIRIS' Establishments (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // // $URL: https://github.com/CGAL/cgal/blob/v5.2/Surface_mesh_topology/include/CGAL/draw_face_graph_with_paths.h $ // $Id: draw_face_graph_with_paths.h 9b5205f 2020-04-22T19:58:34+02:00 Guillaume Damiand // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand // #ifndef CGAL_DRAW_FACE_GRAPH_WITH_PATHS_H #define CGAL_DRAW_FACE_GRAPH_WITH_PATHS_H #include #include #include #include #include #ifdef CGAL_USE_BASIC_VIEWER #include namespace CGAL { // Specialisation for face graph; otherwise use the LCC_geom_utils of LCC. template struct LCC_geom_utils, Local_kernel, 3> { static typename Get_traits::Vector get_face_normal(const CGAL::Face_graph_wrapper& mesh, typename CGAL::Face_graph_wrapper::Dart_const_handle dh) { typename Get_traits::Vector normal(CGAL::NULL_VECTOR); const typename Get_traits::Point* curr=&Get_traits::get_point(mesh.get_fg(), dh); typename CGAL::Face_graph_wrapper::Dart_const_handle adart=dh; unsigned int nb=0; do { const typename Get_traits::Point* next=&Get_traits::get_point(mesh.get_fg(), mesh.other_extremity(adart)); internal::newell_single_step_3_for_lcc(*curr, *next, normal); ++nb; curr=next; adart=mesh.next(adart); } while(adart!=dh); assert(nb>0); return typename Get_traits::Kernel::Construct_scaled_vector_3() (normal, 1.0/nb); } static typename Local_kernel::Vector_3 get_vertex_normal(const CGAL::Face_graph_wrapper& mesh, typename CGAL::Face_graph_wrapper::Dart_const_handle dh) { typename Get_traits::Vector normal(CGAL::NULL_VECTOR); unsigned int nb = 0; for ( typename CGAL::Face_graph_wrapper::template Dart_of_cell_range<0>:: const_iterator it=mesh.template darts_of_cell<0>(dh).begin(), itend=mesh.template darts_of_cell<0>(dh).end(); it!=itend; ++it ) { normal=typename Get_traits::Kernel::Construct_sum_of_vectors_3() (normal, get_face_normal(mesh, it)); ++nb; } if ( nb<2 ) return internal::Geom_utils ::Kernel, Local_kernel>:: get_local_vector(normal); return internal::Geom_utils ::Kernel, Local_kernel>:: get_local_vector(typename Get_traits::Kernel:: Construct_scaled_vector_3()(normal, 1.0/nb)); } }; // Viewer class for Face_graph with paths. template class Face_graph_with_path_viewer : public Basic_viewer_qt { typedef Basic_viewer_qt Base; typedef typename Get_map::type LCC; typedef typename LCC::Dart_const_handle Dart_const_handle; typedef typename LCC::size_type size_type; typedef typename CGAL::Get_traits::Kernel Kernel; typedef typename CGAL::Get_traits::Point Point; typedef typename CGAL::Get_traits::Vector Vector; public: /// Construct the viewer. /// @param alcc the lcc to view /// @param title the title of the window /// @param anofaces if true, do not draw faces (faces are not computed; /// this can be usefull for very big object where this time could be long) Face_graph_with_path_viewer(QWidget* parent, const Mesh& amesh, const std::vector > *paths=nullptr, size_type amark=LCC::INVALID_MARK, const char* title="", bool anofaces=false, const DrawingFunctorLCC& drawing_functor=DrawingFunctorLCC()) : Base(parent, title, true, true, true, false, true), mesh(amesh), lcc(amesh), m_oriented_mark(lcc.get_new_mark()), m_nofaces(anofaces), m_drawing_functor(drawing_functor), m_paths(paths), m_current_path(m_paths->size()), m_current_dart(lcc.darts().end()), m_draw_marked_darts(true), m_amark(amark==(std::numeric_limits::max)()? LCC::INVALID_MARK:amark) { lcc.orient(m_oriented_mark); compute_elements(); } ~Face_graph_with_path_viewer() { lcc.free_mark(m_oriented_mark); } protected: const Point& get_point(Dart_const_handle dh) const { return CGAL::Get_traits::get_point(mesh, dh); } void compute_elements() { clear(); typename LCC::size_type markfaces = lcc.get_new_mark(); typename LCC::size_type markedges = lcc.get_new_mark(); typename LCC::size_type markvertices = lcc.get_new_mark(); if (m_current_dart!=lcc.darts().end()) { // We want to draw only one dart Dart_const_handle selected_dart=m_current_dart; //lcc.dart_handle(m_current_dart); compute_edge(selected_dart, CGAL::Color(255,0,0)); lcc.template mark_cell<1>(selected_dart, markedges); compute_vertex(selected_dart); if ( !m_nofaces ) { compute_face(selected_dart); } for (typename LCC::Dart_range::const_iterator it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it ) { if ( !lcc.is_marked(it, markedges) ) { compute_edge(it); lcc.template mark_cell<1>(it, markedges); } } } else { if (m_current_path==m_paths->size()) { for (std::size_t i=0; isize(); ++i) { compute_path(i, markedges); } } else if (m_current_path!=m_paths->size()+1) { compute_path(m_current_path, markedges); } for (typename LCC::Dart_range::const_iterator it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it ) { if (!m_nofaces && !lcc.is_marked(it, markfaces) && !lcc.is_perforated(it) && lcc.is_marked(it, m_oriented_mark)) { compute_face(it); lcc.template mark_cell<2>(it, markfaces); } if ( !lcc.is_marked(it, markedges) ) { compute_edge(it); lcc.template mark_cell<1>(it, markedges); } if ( !lcc.is_marked(it, markvertices) ) { compute_vertex(it); lcc.template mark_cell<0>(it, markvertices); } } } lcc.free_mark(markfaces); lcc.free_mark(markedges); lcc.free_mark(markvertices); } void compute_face(Dart_const_handle dh) { // We fill only closed faces. Dart_const_handle cur=dh; Dart_const_handle min=dh; do { if (!lcc.is_next_exist(cur)) return; // open face=>not filled if (cur:: get_vertex_normal(lcc, cur)); cur=lcc.next(cur); } while(cur!=dh); face_end(); } void compute_edge(Dart_const_handle dh) { Point p1 = get_point(dh); Dart_const_handle d2 = lcc.other_extremity(dh); if (d2!=LCC::null_handle) { if (m_draw_marked_darts && m_amark!=LCC::INVALID_MARK && (lcc.is_marked(dh, m_amark) || lcc.is_marked(lcc.opposite2(dh), m_amark))) { add_segment(p1, get_point(d2), CGAL::Color(0, 0, 255)); } else { add_segment(p1, get_point(d2)); } } } void compute_edge(Dart_const_handle dh, const CGAL::Color& color) { Point p1 = get_point(dh); Dart_const_handle d2 = lcc.other_extremity(dh); if (d2!=LCC::null_handle) { add_segment(p1, get_point(d2), color); } } void compute_vertex(Dart_const_handle dh) { add_point(get_point(dh)); } virtual void init() { Base::init(); setKeyDescription(::Qt::Key_D, "Increase current dart drawing"); setKeyDescription(::Qt::Key_D+::Qt::ControlModifier, "Decrease current dart drawing"); setKeyDescription(::Qt::Key_D+::Qt::ShiftModifier, "Draw all darts"); setKeyDescription(::Qt::Key_X, "Toggles marked darts display"); setKeyDescription(::Qt::Key_P, "Increase current path drawing"); setKeyDescription(::Qt::Key_P+::Qt::ControlModifier, "Decrease current path drawing"); setKeyDescription(::Qt::Key_P+::Qt::ShiftModifier, "Draw all paths"); } virtual void keyPressEvent(QKeyEvent *e) { const ::Qt::KeyboardModifiers modifiers = e->modifiers(); if ((e->key()==::Qt::Key_D) && (modifiers==::Qt::NoButton)) { if (m_current_dart==lcc.darts().end()) { m_current_dart=lcc.darts().begin(); } else { ++m_current_dart; } if (m_current_dart==lcc.darts().end()) { displayMessage(QString("Draw all darts.")); } else { displayMessage(QString("Draw dart=%1.").arg(lcc.darts().index(m_current_dart))); } compute_elements(); redraw(); } else if ((e->key()==::Qt::Key_D) && (modifiers==::Qt::ControlModifier)) { if (m_current_dart==lcc.darts().begin()) { m_current_dart=lcc.darts().end(); } else { --m_current_dart; } if (m_current_dart==lcc.darts().end()) { displayMessage(QString("Draw all darts.")); } else { displayMessage(QString("Draw dart=%1.").arg(lcc.darts().index(m_current_dart))); } compute_elements(); redraw(); } else if ((e->key()==::Qt::Key_D) && (modifiers==::Qt::ShiftModifier)) { m_current_dart=lcc.darts().end(); displayMessage(QString("Draw all darts.")); compute_elements(); redraw(); } else if ((e->key()==::Qt::Key_P) && (modifiers==::Qt::NoButton)) { m_current_path=(m_current_path+1)%(m_paths->size()+2); if (m_current_path==m_paths->size()) { displayMessage(QString("Draw all paths.")); } else if (m_current_path==m_paths->size()+1) { displayMessage(QString("Do not draw paths.")); } else { displayMessage(QString("Draw path=%1, nb_darts=%2."). arg(m_current_path). arg((*m_paths)[m_current_path].length())); } compute_elements(); redraw(); } else if ((e->key()==::Qt::Key_P) && (modifiers==::Qt::ControlModifier)) { m_current_path=(m_current_path==0?m_paths->size()+1:m_current_path-1); if (m_current_path==m_paths->size()) { displayMessage(QString("Draw all paths.")); } else if (m_current_path==m_paths->size()+1) { displayMessage(QString("Do not draw paths.")); } else { displayMessage(QString("Draw path=%1, nb_darts=%2."). arg(m_current_path). arg((*m_paths)[m_current_path].length())); } compute_elements(); redraw(); } else if ((e->key()==::Qt::Key_P) && (modifiers==::Qt::ShiftModifier)) { m_current_path=m_paths->size(); displayMessage(QString("Draw all paths.")); compute_elements(); redraw(); } else if ((e->key()==::Qt::Key_X) && (modifiers==::Qt::NoButton)) { m_draw_marked_darts=!m_draw_marked_darts; if (m_draw_marked_darts) { displayMessage(QString("Draw marked darts in blue.")); } else { displayMessage(QString("Do not draw marked darts in different color.")); } compute_elements(); redraw(); } else { Base::keyPressEvent(e); } } void compute_path(std::size_t i, typename LCC::size_type amark) { if ((*m_paths)[i].is_empty()) { return; } CGAL::Random random(static_cast(i)); CGAL::Color color=get_random_color(random); add_point(get_point((*m_paths)[i].get_ith_dart(0)), color); for (std::size_t j=0; j<(*m_paths)[i].length(); ++j) { if ( !lcc.is_marked( (*m_paths)[i].get_ith_dart(j), amark) ) { compute_edge((*m_paths)[i].get_ith_dart(j), color); lcc.template mark_cell<1>((*m_paths)[i].get_ith_dart(j), amark); } } } protected: const Mesh& mesh; const typename Get_map::storage_type lcc; typename LCC::size_type m_oriented_mark; bool m_nofaces; const DrawingFunctorLCC& m_drawing_functor; const std::vector >* m_paths; std::size_t m_current_path; typename LCC::Dart_range::const_iterator m_current_dart; bool m_draw_marked_darts; typename LCC::size_type m_amark; // If !=INVALID_MARK, show darts marked with this mark }; template void draw(const Mesh& alcc, const std::vector >& paths, const char* title="Mesh Viewer With Path", typename Get_map::type::size_type amark= (std::numeric_limits::type::size_type>::max)(), bool nofill=false, const DrawingFunctor& drawing_functor=DrawingFunctor()) { #if defined(CGAL_TEST_SUITE) bool cgal_test_suite=true; #else bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif if (!cgal_test_suite) { int argc=1; const char* argv[2]={"lccviewer","\0"}; QApplication app(argc,const_cast(argv)); Face_graph_with_path_viewer mainwindow(app.activeWindow(), alcc, &paths, amark, title, nofill, drawing_functor); mainwindow.show(); app.exec(); } } template void draw(const Mesh& alcc, const std::vector >& paths, const char* title="Mesh Viewer With Path", typename Get_map::type::size_type amark= (std::numeric_limits::type::size_type>::max)(), bool nofill=false) { DefaultDrawingFunctorLCC f; draw(alcc, paths, title, amark, nofill, f); } template void draw(const Mesh& alcc, std::initializer_list> l, const char* title="Mesh Viewer With Path", typename Get_map::type::size_type amark= (std::numeric_limits::type::size_type>::max)(), bool nofill=false) { std::vector > paths=l; draw(alcc, paths, title, amark, nofill); } } // End namespace CGAL #else // CGAL_USE_BASIC_VIEWER namespace CGAL { template void draw(const Mesh&, const std::vector >& , const char* ="", typename Get_map::type::size_type= (std::numeric_limits::type::size_type>::max)(), bool=false) { std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."< void draw(const Mesh&, std::initializer_list>, const char* ="", typename Get_map::type::size_type= (std::numeric_limits::type::size_type>::max)(), bool=false) { std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."<