diff --git a/include/boost/polygon/detail/voronoi_structures.hpp b/include/boost/polygon/detail/voronoi_structures.hpp index 933903b..03274b9 100644 --- a/include/boost/polygon/detail/voronoi_structures.hpp +++ b/include/boost/polygon/detail/voronoi_structures.hpp @@ -14,9 +14,296 @@ #include #include +#include "boost/polygon/voronoi_geometry_type.hpp" + namespace boost { namespace polygon { namespace detail { +// Cartesian 2D point data structure. +template +class point_2d { +public: + typedef T coordinate_type; + + point_2d() {} + + point_2d(coordinate_type x, coordinate_type y) : + x_(x), + y_(y) {} + + bool operator==(const point_2d& that) const { + return (this->x_ == that.x()) && (this->y_ == that.y()); + } + + bool operator!=(const point_2d& that) const { + return (this->x_ != that.x()) || (this->y_ != that.y()); + } + + coordinate_type x() const { + return x_; + } + + coordinate_type y() const { + return y_; + } + + point_2d& x(coordinate_type x) { + x_ = x; + return *this; + } + + point_2d& y(coordinate_type y) { + y_ = y; + return *this; + } + +private: + coordinate_type x_; + coordinate_type y_; +}; + +// Site event type. +// Occurs when the sweepline sweeps over one of the initial sites: +// 1) point site +// 2) start-point of the segment site +// 3) endpoint of the segment site +// Implicit segment direction is defined: the start-point of +// the segment compares less than its endpoint. +// Each input segment is divided onto two site events: +// 1) One going from the start-point to the endpoint +// (is_inverse() = false) +// 2) Another going from the endpoint to the start-point +// (is_inverse() = true) +// In beach line data structure segment sites of the first +// type precede sites of the second type for the same segment. +// Members: +// point0_ - point site or segment's start-point +// point1_ - segment's endpoint if site is a segment +// sorted_index_ - the last bit encodes information if the site is inverse; +// the other bits encode site event index among the sorted site events +// initial_index_ - site index among the initial input set +// Note: for all sites is_inverse_ flag is equal to false by default. +template +class site_event { +public: + typedef T coordinate_type; + typedef point_2d point_type; + + site_event() : + point0_(0, 0), + point1_(0, 0), + sorted_index_(0), + flags_(0) {} + + site_event(coordinate_type x, coordinate_type y) : + point0_(x, y), + point1_(x, y), + sorted_index_(0), + flags_(0) {} + + site_event(const point_type& point) : + point0_(point), + point1_(point), + sorted_index_(0), + flags_(0) {} + + site_event(coordinate_type x1, coordinate_type y1, + coordinate_type x2, coordinate_type y2): + point0_(x1, y1), + point1_(x2, y2), + sorted_index_(0), + flags_(0) {} + + site_event(const point_type& point1, const point_type& point2) : + point0_(point1), + point1_(point2), + sorted_index_(0), + flags_(0) {} + + bool operator==(const site_event& that) const { + return (this->point0_ == that.point0_) && + (this->point1_ == that.point1_); + } + + bool operator!=(const site_event& that) const { + return (this->point0_ != that.point0_) || + (this->point1_ != that.point1_); + } + + coordinate_type x(bool oriented = false) const { + return x0(oriented); + } + + coordinate_type y(bool oriented = false) const { + return y0(oriented); + } + + coordinate_type x0(bool oriented = false) const { + if (!oriented) + return point0_.x(); + return is_inverse() ? point1_.x() : point0_.x(); + } + + coordinate_type y0(bool oriented = false) const { + if (!oriented) + return point0_.y(); + return is_inverse() ? point1_.y() : point0_.y(); + } + + coordinate_type x1(bool oriented = false) const { + if (!oriented) + return point1_.x(); + return is_inverse() ? point0_.x() : point1_.x(); + } + + coordinate_type y1(bool oriented = false) const { + if (!oriented) + return point1_.y(); + return is_inverse() ? point0_.y() : point1_.y(); + } + + const point_type& point0(bool oriented = false) const { + if (!oriented) + return point0_; + return is_inverse() ? point1_ : point0_; + } + + const point_type& point1(bool oriented = false) const { + if (!oriented) + return point1_; + return is_inverse() ? point0_ : point1_; + } + + std::size_t sorted_index() const { + return sorted_index_; + } + + site_event& sorted_index(std::size_t index) { + sorted_index_ = index; + return *this; + } + + std::size_t initial_index() const { + return initial_index_; + } + + site_event& initial_index(std::size_t index) { + initial_index_ = index; + return *this; + } + + bool is_inverse() const { + return (flags_ & IS_INVERSE) ? true : false; + } + + site_event& inverse() { + flags_ ^= IS_INVERSE; + return *this; + } + + SourceCategory source_category() const { + return static_cast(flags_ & SOURCE_CATEGORY_BITMASK); + } + + site_event& source_category(SourceCategory source_category) { + flags_ |= source_category; + return *this; + } + + bool is_point() const { + return (point0_.x() == point1_.x()) && (point0_.y() == point1_.y()); + } + + bool is_segment() const { + return (point0_.x() != point1_.x()) || (point0_.y() != point1_.y()); + } + +private: + enum Bits { + IS_INVERSE = 0x20 // 32 + }; + + point_type point0_; + point_type point1_; + std::size_t sorted_index_; + std::size_t initial_index_; + std::size_t flags_; +}; + +// Circle event type. +// Occurs when the sweepline sweeps over the rightmost point of the Voronoi +// circle (with the center at the intersection point of the bisectors). +// Circle event is made of the two consecutive nodes in the beach line data +// structure. In case another node was inserted during algorithm execution +// between the given two nodes circle event becomes inactive. +// Variables: +// center_x_ - center x-coordinate; +// center_y_ - center y-coordinate; +// lower_x_ - leftmost x-coordinate; +// is_active_ - states whether circle event is still active. +// NOTE: lower_y coordinate is always equal to center_y. +template +class circle_event { +public: + typedef T coordinate_type; + + circle_event() : is_active_(true) {} + + circle_event(coordinate_type c_x, + coordinate_type c_y, + coordinate_type lower_x) : + center_x_(c_x), + center_y_(c_y), + lower_x_(lower_x), + is_active_(true) {} + + coordinate_type x() const { + return center_x_; + } + + circle_event& x(coordinate_type center_x) { + center_x_ = center_x; + return *this; + } + + coordinate_type y() const { + return center_y_; + } + + circle_event& y(coordinate_type center_y) { + center_y_ = center_y; + return *this; + } + + coordinate_type lower_x() const { + return lower_x_; + } + + circle_event& lower_x(coordinate_type lower_x) { + lower_x_ = lower_x; + return *this; + } + + coordinate_type lower_y() const { + return center_y_; + } + + bool is_active() const { + return is_active_; + } + + circle_event& deactivate() { + is_active_ = false; + return *this; + } + +private: + coordinate_type center_x_; + coordinate_type center_y_; + coordinate_type lower_x_; + bool is_active_; +}; + // Event queue data structure, holds circle events. // During algorithm run, some of the circle events disappear (become // inactive). Priority queue data structure doesn't support diff --git a/include/boost/polygon/voronoi_builder.hpp b/include/boost/polygon/voronoi_builder.hpp index 088530b..c96c732 100644 --- a/include/boost/polygon/voronoi_builder.hpp +++ b/include/boost/polygon/voronoi_builder.hpp @@ -18,7 +18,7 @@ #include "detail/voronoi_predicates.hpp" #include "detail/voronoi_structures.hpp" -#include "voronoi_events.hpp" +#include "voronoi_geometry_type.hpp" namespace boost { namespace polygon { @@ -129,11 +129,11 @@ public: } private: - typedef point_2d point_type; - typedef site_event site_event_type; + typedef detail::point_2d point_type; + typedef detail::site_event site_event_type; typedef typename std::vector::const_iterator site_event_iterator_type; - typedef circle_event circle_event_type; + typedef detail::circle_event circle_event_type; typedef typename VP::template point_comparison_predicate point_comparison_predicate; typedef typename VP:: diff --git a/include/boost/polygon/voronoi_diagram.hpp b/include/boost/polygon/voronoi_diagram.hpp index daea710..bf6eeef 100644 --- a/include/boost/polygon/voronoi_diagram.hpp +++ b/include/boost/polygon/voronoi_diagram.hpp @@ -13,7 +13,9 @@ #include #include "detail/voronoi_ctypes.hpp" -#include "voronoi_events.hpp" +#include "detail/voronoi_structures.hpp" + +#include "voronoi_geometry_type.hpp" namespace boost { namespace polygon { @@ -351,7 +353,7 @@ public: } template - void _process_single_site(const site_event& site) { + void _process_single_site(const detail::site_event& site) { cells_.push_back(cell_type( site.initial_index(), site.source_category(), NULL)); } @@ -361,7 +363,8 @@ public: // Returns a pair of pointers to a new half-edges. template std::pair _insert_new_edge( - const site_event& site1, const site_event& site2) { + const detail::site_event& site1, + const detail::site_event& site2) { // Get sites' indexes. int site_index1 = site1.sorted_index(); int site_index2 = site2.sorted_index(); @@ -408,8 +411,8 @@ public: // new Voronoi vertex point. Returns a pair of pointers to a new half-edges. template std::pair _insert_new_edge( - const site_event& site1, const site_event& site3, - const circle_event& circle, void* data12, void* data23) { + const detail::site_event& site1, const detail::site_event& site3, + const detail::circle_event& circle, void* data12, void* data23) { edge_type* edge12 = static_cast(data12); edge_type* edge23 = static_cast(data23); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index d8858f9..b72ea7d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -26,7 +26,7 @@ test-suite voronoi-unit : [ run voronoi_builder_test.cpp ] [ run voronoi_ctypes_test.cpp ] - [ run voronoi_events_test.cpp ] + [ run voronoi_geometry_type_test.cpp ] [ run voronoi_predicates_test.cpp ] [ run voronoi_robust_fpt_test.cpp ] [ run voronoi_structures_test.cpp ] diff --git a/test/voronoi_predicates_test.cpp b/test/voronoi_predicates_test.cpp index 524e0e8..99f0388 100644 --- a/test/voronoi_predicates_test.cpp +++ b/test/voronoi_predicates_test.cpp @@ -17,7 +17,7 @@ #include using namespace boost::polygon::detail; -#include +#include using namespace boost::polygon; ulp_comparison ulp_cmp; diff --git a/test/voronoi_structures_test.cpp b/test/voronoi_structures_test.cpp index da1687d..9f9eb2b 100644 --- a/test/voronoi_structures_test.cpp +++ b/test/voronoi_structures_test.cpp @@ -13,10 +13,82 @@ #include using namespace boost::polygon::detail; +#include +using namespace boost::polygon; + +typedef point_2d point_type; +typedef site_event site_type; +typedef circle_event circle_type; typedef ordered_queue > ordered_queue_type; typedef beach_line_node_key node_key_type; typedef beach_line_node_data node_data_type; +BOOST_AUTO_TEST_CASE(point_2d_test1) { + point_type p(1, 2); + BOOST_CHECK_EQUAL(p.x(), 1); + BOOST_CHECK_EQUAL(p.y(), 2); + p.x(3); + BOOST_CHECK_EQUAL(p.x(), 3); + p.y(4); + BOOST_CHECK_EQUAL(p.y(), 4); +} + +BOOST_AUTO_TEST_CASE(site_event_test1) { + site_type s(1, 2); + s.sorted_index(1); + s.initial_index(2); + s.source_category(SOURCE_CATEGORY_SEGMENT_START_POINT); + BOOST_CHECK(s.x0() == 1 && s.x1() == 1); + BOOST_CHECK(s.y0() == 2 && s.y1() == 2); + BOOST_CHECK(s.is_point()); + BOOST_CHECK(!s.is_segment()); + BOOST_CHECK(!s.is_inverse()); + BOOST_CHECK(s.sorted_index() == 1); + BOOST_CHECK(s.initial_index() == 2); + BOOST_CHECK(s.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT); +} + +BOOST_AUTO_TEST_CASE(site_event_test2) { + site_type s(1, 2, 3, 4); + s.sorted_index(1); + s.initial_index(2); + s.source_category(SOURCE_CATEGORY_INITIAL_SEGMENT); + BOOST_CHECK(s.x0(true) == 1 && s.x0() == 1); + BOOST_CHECK(s.y0(true) == 2 && s.y0() == 2); + BOOST_CHECK(s.x1(true) == 3 && s.x1() == 3); + BOOST_CHECK(s.y1(true) == 4 && s.y1() == 4); + BOOST_CHECK(!s.is_point()); + BOOST_CHECK(s.is_segment()); + BOOST_CHECK(!s.is_inverse()); + BOOST_CHECK(s.source_category() == SOURCE_CATEGORY_INITIAL_SEGMENT); + + s.inverse(); + BOOST_CHECK(s.x1(true) == 1 && s.x0() == 1); + BOOST_CHECK(s.y1(true) == 2 && s.y0() == 2); + BOOST_CHECK(s.x0(true) == 3 && s.x1() == 3); + BOOST_CHECK(s.y0(true) == 4 && s.y1() == 4); + BOOST_CHECK(s.is_inverse()); + BOOST_CHECK(s.source_category() == SOURCE_CATEGORY_INITIAL_SEGMENT); +} + +BOOST_AUTO_TEST_CASE(circle_event_test) { + circle_type c(0, 1, 2); + BOOST_CHECK_EQUAL(c.x(), 0); + BOOST_CHECK_EQUAL(c.y(), 1); + BOOST_CHECK_EQUAL(c.lower_x(), 2); + BOOST_CHECK_EQUAL(c.lower_y(), 1); + BOOST_CHECK(c.is_active()); + c.x(3); + c.y(4); + c.lower_x(5); + BOOST_CHECK_EQUAL(c.x(), 3); + BOOST_CHECK_EQUAL(c.y(), 4); + BOOST_CHECK_EQUAL(c.lower_x(), 5); + BOOST_CHECK_EQUAL(c.lower_y(), 4); + c.deactivate(); + BOOST_CHECK(!c.is_active()); +} + BOOST_AUTO_TEST_CASE(ordered_queue_test) { ordered_queue_type q; BOOST_CHECK(q.empty());