diff --git a/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h b/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h index f11ae76a1c6..3a5a31421be 100644 --- a/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h +++ b/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h @@ -17,20 +17,23 @@ #ifndef CGAL_DRAW_POLYGON_SET_2_H #define CGAL_DRAW_POLYGON_SET_2_H -#include +#include #ifdef DOXYGEN_RUNNING namespace CGAL { /*! -\ingroup PkgDrawPolygonSet2 - -opens a new window and draws `aps`, an instance of the `CGAL::Polygon_set_2` class. A call to this function is blocking, that is the program continues as soon as the user closes the window. This function requires `CGAL_Qt5`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` is defined. -Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition `CGAL_USE_BASIC_VIEWER`. -\tparam PS an instance of the `CGAL::Polygon_set_2` class. -\param aps the polygon set to draw. - -*/ + * \ingroup PkgDrawPolygonSet2 + * + * opens a new window and draws `aps`, an instance of the `CGAL::Polygon_set_2` + * class. A call to this function is blocking, that is the program continues as + * soon as the user closes the window. This function requires `CGAL_Qt5`, and is + * only available if the macro `CGAL_USE_BASIC_VIEWER` is defined. Linking with + * the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add + * the definition `CGAL_USE_BASIC_VIEWER`. + * \tparam PS an instance of the `CGAL::Polygon_set_2` class. + * \param aps the polygon set to draw. + */ template void draw(const PS& aps); @@ -38,50 +41,174 @@ void draw(const PS& aps); #endif #ifdef CGAL_USE_BASIC_VIEWER + +#include #include -namespace CGAL -{ +namespace CGAL { -template -class SimplePolygonSet2ViewerQt : - public SimplePolygonWithHoles2ViewerQt -{ - typedef SimplePolygonWithHoles2ViewerQt Base; +template +class Polygon_set_2_basic_viewer_qt : public Basic_viewer_qt { + using Base = Basic_viewer_qt; + using Ps = PolygonSet_2; + using Pwh = typename Ps::Polygon_with_holes_2; + using Pgn = typename Ps::Polygon_2; + using Pnt = typename Pgn::Point_2; public: - SimplePolygonSet2ViewerQt(QWidget* parent, const PS2& aps2, - const char* title="Basic Polygon_set_2 Viewer") : - Base(parent, title) + Polygon_set_2_basic_viewer_qt(QWidget* parent, const Ps& ps, + const char* title = "Basic Polygon_set_2 Viewer", + bool draw_unbounded = false, + bool draw_vertices = false) : + Base(parent, title, draw_vertices), + m_ps(ps), + m_draw_unbounded(draw_unbounded) { - std::vector polygons; - aps2.polygons_with_holes(std::back_inserter(polygons)); + if (ps.is_empty()) return; - for (typename PS2::Polygon_with_holes_2& P: polygons) { - Base::compute_elements(P); + // mimic the computation of Camera::pixelGLRatio() + auto bbox = bounding_box(); + CGAL::qglviewer::Vec minv(bbox.xmin(), bbox.ymin(), 0); + CGAL::qglviewer::Vec maxv(bbox.xmax(), bbox.ymax(), 0); + auto diameter = (maxv - minv).norm(); + m_pixel_ratio = diameter / m_height; + } + + /*! Intercept the resizing of the window. + */ + virtual void resizeGL(int width, int height) { + CGAL::QGLViewer::resizeGL(width, height); + m_width = width; + m_height = height; + CGAL::qglviewer::Vec p; + auto ratio = camera()->pixelGLRatio(p); + if (ratio != m_pixel_ratio) { + m_pixel_ratio = ratio; + add_elements(); } } + + /*! Compute the elements of a polygon set. + */ + virtual void add_elements() { + clear(); + + std::vector pwhs; + m_ps.polygons_with_holes(std::back_inserter(pwhs)); + for (const auto& pwh : pwhs) add_elements(pwh); + } + + /*! Obtain the pixel ratio. + */ + double pixel_ratio() const { return m_pixel_ratio; } + + /*! Compute the bounding box. + */ + CGAL::Bbox_2 bounding_box() { + Bbox_2 bbox; + std::vector pwhs; + m_ps.polygons_with_holes(std::back_inserter(pwhs)); + for (const auto& pwh : pwhs) { + if (! pwh.is_unbounded()) { + bbox += pwh.outer_boundary().bbox(); + continue; + } + for (auto it = pwh.holes_begin(); it != pwh.holes_end(); ++it) + bbox += it->bbox(); + } + return bbox; + } + +protected: + /*! Compute the elements of a polygon with holes. + */ + void add_elements(const Pwh& pwh) { + if (! m_draw_unbounded && pwh.outer_boundary().is_empty()) return; + + CGAL::IO::Color c(75,160,255); + face_begin(c); + + const Pnt* point_in_face; + if (pwh.outer_boundary().is_empty()) { + Pgn pgn; + pgn.push_back(Pnt(-m_width, -m_height)); + pgn.push_back(Pnt(m_width, -m_height)); + pgn.push_back(Pnt(m_width, m_height)); + pgn.push_back(Pnt(-m_width, m_height)); + compute_loop(pgn, false); + point_in_face = &(pgn.vertex(pgn.size()-1)); + } + else { + const auto& outer_boundary = pwh.outer_boundary(); + compute_loop(outer_boundary, false); + point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); + } + + for (auto it = pwh.holes_begin(); it != pwh.holes_end(); ++it) { + compute_loop(*it, true); + add_point_in_face(*point_in_face); + } + + face_end(); + } + + void compute_loop(const Pgn& p, bool hole) { + if (hole) add_point_in_face(p.vertex(p.size()-1)); + + auto prev = p.vertices_begin(); + auto it = prev; + add_point(*it); + add_point_in_face(*it); + for (++it; it != p.vertices_end(); ++it) { + add_point(*it); // add vertex + add_segment(*prev, *it); // add segment with previous point + add_point_in_face(*it); // add point in face + prev = it; + } + + // Add the last segment between the last point and the first one + add_segment(*prev, *(p.vertices_begin())); + } + +private: + //! The window width in pixels. + int m_width = CGAL_BASIC_VIEWER_INIT_SIZE_X; + + //! The window height in pixels. + int m_height = CGAL_BASIC_VIEWER_INIT_SIZE_Y; + + //! The ratio between pixel and opengl units (in world coordinate system). + double m_pixel_ratio = 1; + + //! The polygon set to draw. + const Ps& m_ps; + + //! Indicates whether to draw unbounded polygons with holes. + bool m_draw_unbounded = false; }; // Specialization of draw function. -template -void draw(const CGAL::Polygon_set_2& aps2, - const char* title="Polygon_set_2 Basic Viewer") +template +void draw(const CGAL::Polygon_set_2& ps, + const char* title = "Polygon_set_2 Basic Viewer", + bool draw_vertices = false, + bool draw_unbounded = false) { #if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; + bool cgal_test_suite = true; #else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); + bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif - if (!cgal_test_suite) - { + if (! cgal_test_suite) { + using Ps = CGAL::Polygon_set_2; + using Viewer = Polygon_set_2_basic_viewer_qt; CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"t2_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimplePolygonSet2ViewerQt > - mainwindow(app.activeWindow(), aps2, title); + int argc = 1; + const char* argv[2] = {"t2_viewer", nullptr}; + QApplication app(argc, const_cast(argv)); + Viewer mainwindow(app.activeWindow(), ps, title, draw_unbounded, draw_vertices); + mainwindow.add_elements(); mainwindow.show(); app.exec(); } diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index a28ce66da54..17a1e1bac1c 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -137,6 +137,12 @@ extracted from labeled images. straight skeletons of polygons with holes. The output is a closed, combinatorially 2-manifold surface triangle mesh. +### [2D Regularized Boolean Set Operations](https://doc.cgal.org/5.6/Manual/packages.html#PkgBooleanSetOperations2) +- Exposed all required member functions of the `GeneralPolygonWithHoles_2` concept (e.g., `clear_outer_boundary()`, `clear_holes()`, and `clear()`). + +### [Polygon](https://doc.cgal.org/5.6/Manual/packages.html#PkgPolygon2) +- Fixed the function `draw(const CGAL::Polygon_with_holes_2& pwh, const char* title)` to enable the correct drawing of unbounded polygons with holes. + [Release 5.5](https://github.com/CGAL/cgal/releases/tag/v5.5) ----------- diff --git a/Polygon/doc/Polygon/Concepts/GeneralPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/GeneralPolygonWithHoles_2.h index e133b35e03d..97517ca4a3d 100644 --- a/Polygon/doc/Polygon/Concepts/GeneralPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/GeneralPolygonWithHoles_2.h @@ -18,19 +18,15 @@ public: /// \name Types /// @{ -/*! the polygon type used to represent the outer boundary and each hole. - */ +//! the polygon type used to represent the outer boundary and each hole. typedef unspecified_type Polygon_2; -/*! a bidirectional iterator - * over the polygonal holes. Its value type is `Polygon_2`. +/*! a bidirectional iterator over the polygonal holes. + * Its value type is `Polygon_2`. */ typedef unspecified_type Hole_const_iterator; - -/*! -range type for iterating over holes. -*/ +//! range type for iterating over holes. typedef unspecified_type Holes_container; /// @} @@ -54,10 +50,18 @@ GeneralPolygonWithHoles_2(Polygon_2 & outer, /// \name Predicates /// @{ -/*! returns `true` if the outer boundary is empty, and `false` otherwise. +/*! returns `true` if the outer boundary is empty and `false` otherwise. */ bool is_unbounded(); +/*! returns `true` if the polygon with holes has holes and `false` otherwise. + */ +bool has_holes(); + +/*! returns the number of holes. + */ +Size number_of_holes(); + /// @} /// \name Access Functions @@ -77,12 +81,36 @@ Hole_const_iterator holes_begin() const; */ Hole_const_iterator holes_end() const; - -/*! -returns the range of holes. -*/ +/*! returns the range of holes. + */ const Holes_container& holes() const; /// @} +/// \name Modifiers +/// @{ + +/*! adds a given polygon as a hole. + * \pre the hole must be clockwise oriented. + */ +void add_hole(const Polygon_2& hole); + +/*! erases the specified hole. + */ +void erase_hole(Hole_iterator hit); + +/*! clears the output boundary. + */ +void clear_outer_boundary(); + +/*! removes all the holes. + */ +void clear_holes(); + +/*! removes the outer boundary and all holes. + */ +void clear(); + +/// @} + }; /* end GeneralPolygonWithHoles_2 */ diff --git a/Polygon/include/CGAL/General_polygon_with_holes_2.h b/Polygon/include/CGAL/General_polygon_with_holes_2.h index cf3d666421b..ae37e05eba9 100644 --- a/Polygon/include/CGAL/General_polygon_with_holes_2.h +++ b/Polygon/include/CGAL/General_polygon_with_holes_2.h @@ -12,7 +12,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // // -// Author(s) : Baruch Zukerman +// Author(s): Baruch Zukerman +// Efi Fogel #ifndef CGAL_GENERAL_POLYGON_WITH_HOLES_2_H #define CGAL_GENERAL_POLYGON_WITH_HOLES_2_H @@ -107,6 +108,10 @@ public: void erase_hole(Hole_iterator hit) { m_holes.erase(hit); } + void clear_outer_boundary() { m_pgn.clear(); } + + void clear_holes() { m_holes.clear(); } + bool has_holes() const { return (!m_holes.empty()); } Size number_of_holes() const { return static_cast(m_holes.size()); } diff --git a/Polygon/include/CGAL/draw_polygon_with_holes_2.h b/Polygon/include/CGAL/draw_polygon_with_holes_2.h index be58464ebf8..d8b72c9bdd9 100644 --- a/Polygon/include/CGAL/draw_polygon_with_holes_2.h +++ b/Polygon/include/CGAL/draw_polygon_with_holes_2.h @@ -23,103 +23,149 @@ namespace CGAL { /*! -\ingroup PkgDrawPolygonWithHoles2 + * \ingroup PkgDrawPolygonWithHoles2 + * + * opens a new window and draws `aph`, an instance of the + * `CGAL::Polygon_with_holes_2` class. A call to this function is blocking, that + * is the program continues as soon as the user closes the window. This function + * requires `CGAL_Qt5`, and is only available if the macro + * `CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target + * `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition + * `CGAL_USE_BASIC_VIEWER`. + * \tparam PH an instance of the `CGAL::Polygon_with_holes_2` class. + * \param aph the polygon with holes to draw. + */ -opens a new window and draws `aph`, an instance of the `CGAL::Polygon_with_holes_2` class. A call to this function is blocking, that is the program continues as soon as the user closes the window. This function requires `CGAL_Qt5`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` is defined. -Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition `CGAL_USE_BASIC_VIEWER`. -\tparam PH an instance of the `CGAL::Polygon_with_holes_2` class. -\param aph the polygon with holes to draw. - -*/ -template +template void draw(const PH& aph); } /* namespace CGAL */ + #endif #ifdef CGAL_USE_BASIC_VIEWER + #include #include -#include -namespace CGAL -{ +namespace CGAL { // Viewer class for Polygon_with_holes_2 -template -class SimplePolygonWithHoles2ViewerQt : public Basic_viewer_qt -{ - typedef Basic_viewer_qt Base; - typedef typename P2::General_polygon_2::Point_2 Point; +template +class Pwh_2_basic_viewer_qt : public Basic_viewer_qt { + using Base = Basic_viewer_qt; + using Pwh = PolygonWidthHoles_2; + using Pgn = typename Pwh::Polygon_2; + using Pnt = typename Pgn::Point_2; public: - /// Construct the viewer without drawing anything. + /// Construct the viewer. + /// @param parent the active window to draw + /// @param pwh the polygon to view /// @param title the title of the window - SimplePolygonWithHoles2ViewerQt(QWidget* parent, - const char* title="Basic Polygon_with_holes_2 Viewer") : - Base(parent, title, true, true, true, false, false) + Pwh_2_basic_viewer_qt(QWidget* parent, const Pwh& pwh, + const char* title = "Basic Polygon_with_holes_2 Viewer") : + Base(parent, title, true, true, true, false, false), + m_pwh(pwh) { - clear(); + if (pwh.is_unbounded() && (0 == pwh.number_of_holes())) return; + + // mimic the computation of Camera::pixelGLRatio() + auto bbox = bounding_box(); + CGAL::qglviewer::Vec minv(bbox.xmin(), bbox.ymin(), 0); + CGAL::qglviewer::Vec maxv(bbox.xmax(), bbox.ymax(), 0); + auto diameter = (maxv - minv).norm(); + m_pixel_ratio = diameter / m_height; } - /// Construct the viewer. - /// @param ap2 the polygon to view - /// @param title the title of the window - SimplePolygonWithHoles2ViewerQt(QWidget* parent, const P2& ap2, - const char* title="Basic Polygon_with_holes_2 Viewer") : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, true, true, true, false, false) - { + /*! Intercept the resizing of the window. + */ + virtual void resizeGL(int width, int height) { + CGAL::QGLViewer::resizeGL(width, height); + m_width = width; + m_height = height; + CGAL::qglviewer::Vec p; + auto ratio = camera()->pixelGLRatio(p); + m_pixel_ratio = ratio; + add_elements(); + } + + /*! Obtain the pixel ratio. + */ + double pixel_ratio() const { return m_pixel_ratio; } + + /*! Compute the bounding box. + */ + CGAL::Bbox_2 bounding_box() { + if (! m_pwh.is_unbounded()) return m_pwh.outer_boundary().bbox(); + + Bbox_2 bbox; + for (auto it = m_pwh.holes_begin(); it != m_pwh.holes_end(); ++it) + bbox += it->bbox(); + return bbox; + } + + /*! Compute the elements of a polygon with holes. + */ + void add_elements() { clear(); - compute_elements(ap2); + CGAL::IO::Color c(75,160,255); + face_begin(c); + + const Pnt* point_in_face; + if (m_pwh.outer_boundary().is_empty()) { + Pgn pgn; + + double x = (double)m_width * 0.5 * m_pixel_ratio; + double y = (double)m_height * 0.5 * m_pixel_ratio; + pgn.push_back(Pnt(-x, -y)); + pgn.push_back(Pnt(x, -y)); + pgn.push_back(Pnt(x, y)); + pgn.push_back(Pnt(-x, y)); + compute_loop(pgn, false); + point_in_face = &(pgn.vertex(pgn.size()-1)); + } + else { + const auto& outer_boundary = m_pwh.outer_boundary(); + compute_loop(outer_boundary, false); + point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); + } + + for (auto it = m_pwh.holes_begin(); it != m_pwh.holes_end(); ++it) { + compute_loop(*it, true); + add_point_in_face(*point_in_face); + } + + face_end(); } protected: - void compute_one_loop_elements(const typename P2::General_polygon_2& p, bool hole) - { - if (hole) - { add_point_in_face(p.vertex(p.size()-1)); } + /*! Compute the face + */ + void compute_loop(const Pgn& p, bool hole) { + if (hole) add_point_in_face(p.vertex(p.size()-1)); - typename P2::General_polygon_2::Vertex_const_iterator prev; - for (typename P2::General_polygon_2::Vertex_const_iterator i=p.vertices_begin(); - i!=p.vertices_end(); ++i) - { - add_point(*i); // Add vertex - if (i!=p.vertices_begin()) - { add_segment(*prev, *i); } // Add segment with previous point - add_point_in_face(*i); // Add point in face - prev=i; + auto prev = p.vertices_begin(); + auto it = prev; + add_point(*it); + add_point_in_face(*it); + for (++it; it != p.vertices_end(); ++it) { + add_segment(*prev, *it); // add segment with previous point + add_point(*it); + add_point_in_face(*it); // add point in face + prev = it; } // Add the last segment between the last point and the first one add_segment(*prev, *(p.vertices_begin())); } - void compute_elements(const P2& p2) - { - if (p2.outer_boundary().is_empty()) return; - - CGAL::IO::Color c(75,160,255); - face_begin(c); - - compute_one_loop_elements(p2.outer_boundary(), false); - - for (typename P2::Hole_const_iterator it=p2.holes_begin(); it!=p2.holes_end(); ++it) - { - compute_one_loop_elements(*it, true); - add_point_in_face(p2.outer_boundary().vertex(p2.outer_boundary().size()-1)); - } - - face_end(); - } - - virtual void keyPressEvent(QKeyEvent *e) - { + virtual void keyPressEvent(QKeyEvent* e) { // Test key pressed: // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - // Call: * compute_elements() if the model changed, followed by + // Call: * add_elements() if the model changed, followed by // * redraw() if some viewing parameters changed that implies some // modifications of the buffers // (eg. type of normal, color/mono) @@ -128,27 +174,41 @@ protected: // Call the base method to process others/classicals key Base::keyPressEvent(e); } + +private: + //! The window width in pixels. + int m_width = CGAL_BASIC_VIEWER_INIT_SIZE_X; + + //! The window height in pixels. + int m_height = CGAL_BASIC_VIEWER_INIT_SIZE_Y; + + //! The ratio between pixel and opengl units (in world coordinate system). + double m_pixel_ratio = 1; + + //! The polygon with holes to draw. + const Pwh& m_pwh; }; // Specialization of draw function. template -void draw(const CGAL::Polygon_with_holes_2& ap2, - const char* title="Polygon_with_holes_2 Basic Viewer") +void draw(const CGAL::Polygon_with_holes_2& pwh, + const char* title = "Polygon_with_holes_2 Basic Viewer") { #if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; + bool cgal_test_suite = true; #else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); + bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif - if (!cgal_test_suite) - { + if (! cgal_test_suite) { + using Pwh = CGAL::Polygon_with_holes_2; + using Viewer = Pwh_2_basic_viewer_qt; CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"t2_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimplePolygonWithHoles2ViewerQt > - mainwindow(app.activeWindow(), ap2, title); + int argc = 1; + const char* argv[2] = {"t2_viewer", nullptr}; + QApplication app(argc, const_cast(argv)); + Viewer mainwindow(app.activeWindow(), pwh, title); + mainwindow.add_elements(); mainwindow.show(); app.exec(); }