Mercurial > hg > vamp-build-and-test
diff DEPENDENCIES/generic/include/boost/polygon/voronoi_builder.hpp @ 16:2665513ce2d3
Add boost headers
author | Chris Cannam |
---|---|
date | Tue, 05 Aug 2014 11:11:38 +0100 |
parents | |
children | c530137014c0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DEPENDENCIES/generic/include/boost/polygon/voronoi_builder.hpp Tue Aug 05 11:11:38 2014 +0100 @@ -0,0 +1,517 @@ +// Boost.Polygon library voronoi_builder.hpp header file + +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_POLYGON_VORONOI_BUILDER +#define BOOST_POLYGON_VORONOI_BUILDER + +#include <algorithm> +#include <map> +#include <queue> +#include <utility> +#include <vector> + +#include "detail/voronoi_ctypes.hpp" +#include "detail/voronoi_predicates.hpp" +#include "detail/voronoi_structures.hpp" + +#include "voronoi_geometry_type.hpp" + +namespace boost { +namespace polygon { +// GENERAL INFO: +// The sweepline algorithm implementation to compute Voronoi diagram of +// points and non-intersecting segments (except endpoints). +// Complexity - O(N*logN), memory usage - O(N), where N is the total number +// of input geometries. Input geometries should have integer coordinate type. +// +// IMPLEMENTATION DETAILS: +// Each input point creates one site event. Each input segment creates three +// site events: two for its endpoints and one for the segment itself (this is +// made to simplify output construction). All the site events are constructed +// and sorted at the algorithm initialization step. Priority queue is used to +// dynamically hold circle events. At each step of the algorithm execution the +// leftmost event is retrieved by comparing the current site event and the +// topmost element from the circle event queue. STL map (red-black tree) +// container was chosen to hold state of the beach line. The keys of the map +// correspond to the neighboring sites that form a bisector and values map to +// the corresponding Voronoi edges in the output data structure. +template <typename T, + typename CTT = detail::voronoi_ctype_traits<T>, + typename VP = detail::voronoi_predicates<CTT> > +class voronoi_builder { + public: + typedef typename CTT::int_type int_type; + typedef typename CTT::fpt_type fpt_type; + + voronoi_builder() : index_(0) {} + + // Each point creates a single site event. + std::size_t insert_point(const int_type& x, const int_type& y) { + site_events_.push_back(site_event_type(x, y)); + site_events_.back().initial_index(index_); + site_events_.back().source_category(SOURCE_CATEGORY_SINGLE_POINT); + return index_++; + } + + // Each segment creates three site events that correspond to: + // 1) the start point of the segment; + // 2) the end point of the segment; + // 3) the segment itself defined by its start point. + std::size_t insert_segment( + const int_type& x1, const int_type& y1, + const int_type& x2, const int_type& y2) { + // Set up start point site. + point_type p1(x1, y1); + site_events_.push_back(site_event_type(p1)); + site_events_.back().initial_index(index_); + site_events_.back().source_category(SOURCE_CATEGORY_SEGMENT_START_POINT); + + // Set up end point site. + point_type p2(x2, y2); + site_events_.push_back(site_event_type(p2)); + site_events_.back().initial_index(index_); + site_events_.back().source_category(SOURCE_CATEGORY_SEGMENT_END_POINT); + + // Set up segment site. + if (point_comparison_(p1, p2)) { + site_events_.push_back(site_event_type(p1, p2)); + site_events_.back().source_category(SOURCE_CATEGORY_INITIAL_SEGMENT); + } else { + site_events_.push_back(site_event_type(p2, p1)); + site_events_.back().source_category(SOURCE_CATEGORY_REVERSE_SEGMENT); + } + site_events_.back().initial_index(index_); + return index_++; + } + + // Run sweepline algorithm and fill output data structure. + template <typename OUTPUT> + void construct(OUTPUT* output) { + // Init structures. + output->_reserve(site_events_.size()); + init_sites_queue(); + init_beach_line(output); + + // The algorithm stops when there are no events to process. + event_comparison_predicate event_comparison; + while (!circle_events_.empty() || + !(site_event_iterator_ == site_events_.end())) { + if (circle_events_.empty()) { + process_site_event(output); + } else if (site_event_iterator_ == site_events_.end()) { + process_circle_event(output); + } else { + if (event_comparison(*site_event_iterator_, + circle_events_.top().first)) { + process_site_event(output); + } else { + process_circle_event(output); + } + } + while (!circle_events_.empty() && + !circle_events_.top().first.is_active()) { + circle_events_.pop(); + } + } + beach_line_.clear(); + + // Finish construction. + output->_build(); + } + + void clear() { + index_ = 0; + site_events_.clear(); + } + + private: + typedef detail::point_2d<int_type> point_type; + typedef detail::site_event<int_type> site_event_type; + typedef typename std::vector<site_event_type>::const_iterator + site_event_iterator_type; + typedef detail::circle_event<fpt_type> circle_event_type; + typedef typename VP::template point_comparison_predicate<point_type> + point_comparison_predicate; + typedef typename VP:: + template event_comparison_predicate<site_event_type, circle_event_type> + event_comparison_predicate; + typedef typename VP:: + template circle_formation_predicate<site_event_type, circle_event_type> + circle_formation_predicate_type; + typedef void edge_type; + typedef detail::beach_line_node_key<site_event_type> key_type; + typedef detail::beach_line_node_data<edge_type, circle_event_type> + value_type; + typedef typename VP::template node_comparison_predicate<key_type> + node_comparer_type; + typedef std::map< key_type, value_type, node_comparer_type > beach_line_type; + typedef typename beach_line_type::iterator beach_line_iterator; + typedef std::pair<circle_event_type, beach_line_iterator> event_type; + typedef struct { + bool operator()(const event_type& lhs, const event_type& rhs) const { + return predicate(rhs.first, lhs.first); + } + event_comparison_predicate predicate; + } event_comparison_type; + typedef detail::ordered_queue<event_type, event_comparison_type> + circle_event_queue_type; + typedef std::pair<point_type, beach_line_iterator> end_point_type; + + void init_sites_queue() { + // Sort site events. + std::sort(site_events_.begin(), site_events_.end(), + event_comparison_predicate()); + + // Remove duplicates. + site_events_.erase(std::unique( + site_events_.begin(), site_events_.end()), site_events_.end()); + + // Index sites. + for (std::size_t cur = 0; cur < site_events_.size(); ++cur) { + site_events_[cur].sorted_index(cur); + } + + // Init site iterator. + site_event_iterator_ = site_events_.begin(); + } + + template <typename OUTPUT> + void init_beach_line(OUTPUT* output) { + if (site_events_.empty()) + return; + if (site_events_.size() == 1) { + // Handle single site event case. + output->_process_single_site(site_events_[0]); + ++site_event_iterator_; + } else { + int skip = 0; + + while (site_event_iterator_ != site_events_.end() && + VP::is_vertical(site_event_iterator_->point0(), + site_events_.begin()->point0()) && + VP::is_vertical(*site_event_iterator_)) { + ++site_event_iterator_; + ++skip; + } + + if (skip == 1) { + // Init beach line with the first two sites. + init_beach_line_default(output); + } else { + // Init beach line with collinear vertical sites. + init_beach_line_collinear_sites(output); + } + } + } + + // Init beach line with the two first sites. + // The first site is always a point. + template <typename OUTPUT> + void init_beach_line_default(OUTPUT* output) { + // Get the first and the second site event. + site_event_iterator_type it_first = site_events_.begin(); + site_event_iterator_type it_second = site_events_.begin(); + ++it_second; + insert_new_arc( + *it_first, *it_first, *it_second, beach_line_.end(), output); + // The second site was already processed. Move the iterator. + ++site_event_iterator_; + } + + // Init beach line with collinear sites. + template <typename OUTPUT> + void init_beach_line_collinear_sites(OUTPUT* output) { + site_event_iterator_type it_first = site_events_.begin(); + site_event_iterator_type it_second = site_events_.begin(); + ++it_second; + while (it_second != site_event_iterator_) { + // Create a new beach line node. + key_type new_node(*it_first, *it_second); + + // Update the output. + edge_type* edge = output->_insert_new_edge(*it_first, *it_second).first; + + // Insert a new bisector into the beach line. + beach_line_.insert(beach_line_.end(), + std::pair<key_type, value_type>(new_node, value_type(edge))); + + // Update iterators. + ++it_first; + ++it_second; + } + } + + void deactivate_circle_event(value_type* value) { + if (value->circle_event()) { + value->circle_event()->deactivate(); + value->circle_event(NULL); + } + } + + template <typename OUTPUT> + void process_site_event(OUTPUT* output) { + // Get next site event to process. + site_event_type site_event = *site_event_iterator_; + + // Move site iterator. + site_event_iterator_type last = site_event_iterator_ + 1; + + // If a new site is an end point of some segment, + // remove temporary nodes from the beach line data structure. + if (!site_event.is_segment()) { + while (!end_points_.empty() && + end_points_.top().first == site_event.point0()) { + beach_line_iterator b_it = end_points_.top().second; + end_points_.pop(); + beach_line_.erase(b_it); + } + } else { + while (last != site_events_.end() && + last->is_segment() && last->point0() == site_event.point0()) + ++last; + } + + // Find the node in the binary search tree with left arc + // lying above the new site point. + key_type new_key(*site_event_iterator_); + beach_line_iterator right_it = beach_line_.lower_bound(new_key); + + for (; site_event_iterator_ != last; ++site_event_iterator_) { + site_event = *site_event_iterator_; + beach_line_iterator left_it = right_it; + + // Do further processing depending on the above node position. + // For any two neighboring nodes the second site of the first node + // is the same as the first site of the second node. + if (right_it == beach_line_.end()) { + // The above arc corresponds to the second arc of the last node. + // Move the iterator to the last node. + --left_it; + + // Get the second site of the last node + const site_event_type& site_arc = left_it->first.right_site(); + + // Insert new nodes into the beach line. Update the output. + right_it = insert_new_arc( + site_arc, site_arc, site_event, right_it, output); + + // Add a candidate circle to the circle event queue. + // There could be only one new circle event formed by + // a new bisector and the one on the left. + activate_circle_event(left_it->first.left_site(), + left_it->first.right_site(), + site_event, right_it); + } else if (right_it == beach_line_.begin()) { + // The above arc corresponds to the first site of the first node. + const site_event_type& site_arc = right_it->first.left_site(); + + // Insert new nodes into the beach line. Update the output. + left_it = insert_new_arc( + site_arc, site_arc, site_event, right_it, output); + + // If the site event is a segment, update its direction. + if (site_event.is_segment()) { + site_event.inverse(); + } + + // Add a candidate circle to the circle event queue. + // There could be only one new circle event formed by + // a new bisector and the one on the right. + activate_circle_event(site_event, right_it->first.left_site(), + right_it->first.right_site(), right_it); + right_it = left_it; + } else { + // The above arc corresponds neither to the first, + // nor to the last site in the beach line. + const site_event_type& site_arc2 = right_it->first.left_site(); + const site_event_type& site3 = right_it->first.right_site(); + + // Remove the candidate circle from the event queue. + deactivate_circle_event(&right_it->second); + --left_it; + const site_event_type& site_arc1 = left_it->first.right_site(); + const site_event_type& site1 = left_it->first.left_site(); + + // Insert new nodes into the beach line. Update the output. + beach_line_iterator new_node_it = + insert_new_arc(site_arc1, site_arc2, site_event, right_it, output); + + // Add candidate circles to the circle event queue. + // There could be up to two circle events formed by + // a new bisector and the one on the left or right. + activate_circle_event(site1, site_arc1, site_event, new_node_it); + + // If the site event is a segment, update its direction. + if (site_event.is_segment()) { + site_event.inverse(); + } + activate_circle_event(site_event, site_arc2, site3, right_it); + right_it = new_node_it; + } + } + } + + // In general case circle event is made of the three consecutive sites + // that form two bisectors in the beach line data structure. + // Let circle event sites be A, B, C, two bisectors that define + // circle event are (A, B), (B, C). During circle event processing + // we remove (A, B), (B, C) and insert (A, C). As beach line comparison + // works correctly only if one of the nodes is a new one we remove + // (B, C) bisector and change (A, B) bisector to the (A, C). That's + // why we use const_cast there and take all the responsibility that + // map data structure keeps correct ordering. + template <typename OUTPUT> + void process_circle_event(OUTPUT* output) { + // Get the topmost circle event. + const event_type& e = circle_events_.top(); + const circle_event_type& circle_event = e.first; + beach_line_iterator it_first = e.second; + beach_line_iterator it_last = it_first; + + // Get the C site. + site_event_type site3 = it_first->first.right_site(); + + // Get the half-edge corresponding to the second bisector - (B, C). + edge_type* bisector2 = it_first->second.edge(); + + // Get the half-edge corresponding to the first bisector - (A, B). + --it_first; + edge_type* bisector1 = it_first->second.edge(); + + // Get the A site. + site_event_type site1 = it_first->first.left_site(); + + if (!site1.is_segment() && site3.is_segment() && + site3.point1() == site1.point0()) { + site3.inverse(); + } + + // Change the (A, B) bisector node to the (A, C) bisector node. + const_cast<key_type&>(it_first->first).right_site(site3); + + // Insert the new bisector into the beach line. + it_first->second.edge(output->_insert_new_edge( + site1, site3, circle_event, bisector1, bisector2).first); + + // Remove the (B, C) bisector node from the beach line. + beach_line_.erase(it_last); + it_last = it_first; + + // Pop the topmost circle event from the event queue. + circle_events_.pop(); + + // Check new triplets formed by the neighboring arcs + // to the left for potential circle events. + if (it_first != beach_line_.begin()) { + deactivate_circle_event(&it_first->second); + --it_first; + const site_event_type& site_l1 = it_first->first.left_site(); + activate_circle_event(site_l1, site1, site3, it_last); + } + + // Check the new triplet formed by the neighboring arcs + // to the right for potential circle events. + ++it_last; + if (it_last != beach_line_.end()) { + deactivate_circle_event(&it_last->second); + const site_event_type& site_r1 = it_last->first.right_site(); + activate_circle_event(site1, site3, site_r1, it_last); + } + } + + // Insert new nodes into the beach line. Update the output. + template <typename OUTPUT> + beach_line_iterator insert_new_arc( + const site_event_type& site_arc1, const site_event_type &site_arc2, + const site_event_type& site_event, beach_line_iterator position, + OUTPUT* output) { + // Create two new bisectors with opposite directions. + key_type new_left_node(site_arc1, site_event); + key_type new_right_node(site_event, site_arc2); + + // Set correct orientation for the first site of the second node. + if (site_event.is_segment()) { + new_right_node.left_site().inverse(); + } + + // Update the output. + std::pair<edge_type*, edge_type*> edges = + output->_insert_new_edge(site_arc2, site_event); + position = beach_line_.insert(position, + typename beach_line_type::value_type( + new_right_node, value_type(edges.second))); + + if (site_event.is_segment()) { + // Update the beach line with temporary bisector, that will + // disappear after processing site event corresponding to the + // second endpoint of the segment site. + key_type new_node(site_event, site_event); + new_node.right_site().inverse(); + position = beach_line_.insert(position, + typename beach_line_type::value_type(new_node, value_type(NULL))); + + // Update the data structure that holds temporary bisectors. + end_points_.push(std::make_pair(site_event.point1(), position)); + } + + position = beach_line_.insert(position, + typename beach_line_type::value_type( + new_left_node, value_type(edges.first))); + + return position; + } + + // Add a new circle event to the event queue. + // bisector_node corresponds to the (site2, site3) bisector. + void activate_circle_event(const site_event_type& site1, + const site_event_type& site2, + const site_event_type& site3, + beach_line_iterator bisector_node) { + circle_event_type c_event; + // Check if the three input sites create a circle event. + if (circle_formation_predicate_(site1, site2, site3, c_event)) { + // Add the new circle event to the circle events queue. + // Update bisector's circle event iterator to point to the + // new circle event in the circle event queue. + event_type& e = circle_events_.push( + std::pair<circle_event_type, beach_line_iterator>( + c_event, bisector_node)); + bisector_node->second.circle_event(&e.first); + } + } + + private: + point_comparison_predicate point_comparison_; + struct end_point_comparison { + bool operator() (const end_point_type& end1, + const end_point_type& end2) const { + return point_comparison(end2.first, end1.first); + } + point_comparison_predicate point_comparison; + }; + + std::vector<site_event_type> site_events_; + site_event_iterator_type site_event_iterator_; + std::priority_queue< end_point_type, std::vector<end_point_type>, + end_point_comparison > end_points_; + circle_event_queue_type circle_events_; + beach_line_type beach_line_; + circle_formation_predicate_type circle_formation_predicate_; + std::size_t index_; + + // Disallow copy constructor and operator= + voronoi_builder(const voronoi_builder&); + void operator=(const voronoi_builder&); +}; + +typedef voronoi_builder<detail::int32> default_voronoi_builder; +} // polygon +} // boost + +#endif // BOOST_POLYGON_VORONOI_BUILDER