// Copyright (c) 2005 Tel-Aviv University (Israel). // All rights reserved. // // This file is part of CGAL (www.cgal.org); you may redistribute it under // the terms of the Q Public License version 1.0. // See the file LICENSE.QPL distributed with CGAL. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // // // Author(s) : Baruch Zukerman // (based on old version by Tali Zvi) #ifndef CGAL_BASIC_SWEEP_LINE_2_H #define CGAL_BASIC_SWEEP_LINE_2_H #include #include #include #include #include #include #include #include #include #ifndef VERBOSE #define CGAL_SL_DEBUG(a) #define CGAL_PRINT_INSERT(a) #define CGAL_PRINT_ERASE(a) #define CGAL_PRINT_NEW_EVENT(p, e) #define CGAL_PRINT(a) #else #define CGAL_SL_DEBUG(a) {a} #define CGAL_PRINT_INSERT(a) { std::cout << "+++ inserting "; \ (a)->Print(); \ std::cout << " currentPos = " \ << m_currentEvent->get_point() \ << "\n"; \ } #define CGAL_PRINT_ERASE(a) { std::cout << "--- erasing " ; \ (a)->Print(); } #define CGAL_PRINT_NEW_EVENT(p, e) \ { std::cout << "%%% a new event was created at " << (p) << std::endl; \ (e)->Print(); } #define CGAL_PRINT(a) { std::cout << a ; } #endif CGAL_BEGIN_NAMESPACE /*! Basic_Sweep_line_2 is a class that implements the sweep line algorithm for non-intersecting x-monotone curves. It extends the algorithm to support not only segments but general curves as well and isolated points. The curves are defined by the traits class that is one of the template arguments. The algorithm is also extended to support the following degenerate cases: - vertical segments General flow: After the initialization stage, the events are handled from left to right. For each event Handle left curves - iterate over the curves that intersect at the event point and defined to the left of the event. Handle right curves - iterate over the curves that intersect the event point and defined to the right of the event point. End Convensions through out the code: In order to make the code as readable as possible, some convensions were made in regards to variable naming: slIter - an iterator to the status line, always points to a curve. */ template < class Traits_, class SweepVisitor, class CurveWrap = Sweep_line_subcurve, typename SweepEvent = Sweep_line_event, typename Allocator = CGAL_ALLOCATOR(int) > class Basic_sweep_line_2 { public: typedef Traits_ Traits; typedef typename Traits::Point_2 Point_2; typedef typename Traits::X_monotone_curve_2 X_monotone_curve_2; typedef SweepEvent Event; typedef Event_less_functor EventLess; typedef Multiset EventQueue; typedef typename EventQueue::iterator EventQueueIter; typedef typename Event::SubCurveIter EventCurveIter; typedef Sweep_line_event Base_event; typedef typename Base_event::Attribute Attribute; typedef CurveWrap Subcurve; typedef Sweep_line_subcurve Base_subcurve; typedef Status_line_curve_less_functor StatusLineCurveLess; typedef Multiset StatusLine; typedef typename StatusLine::iterator StatusLineIter; typedef typename Allocator::template rebind EventAlloc_rebind; typedef typename EventAlloc_rebind::other EventAlloc; typedef typename Allocator::template rebind SubcurveAlloc_rebind; typedef typename SubcurveAlloc_rebind::other SubCurveAlloc; /*! * Constructor. * \param visitor A pointer to a sweep-line visitor object. */ Basic_sweep_line_2 (SweepVisitor* visitor) : m_traits(new Traits()), m_traitsOwner(true), m_statusLineCurveLess(m_traits, &m_currentEvent), m_queueEventLess(m_traits), m_queue(new EventQueue(m_queueEventLess)), m_statusLine(m_statusLineCurveLess), m_status_line_insert_hint(m_statusLine.begin()), m_num_of_subCurves(0), m_visitor(visitor) { m_visitor->attach(this); } /*! * Constructor. * \param traits A pointer to a sweep-line traits object. * \param visitor A pointer to a sweep-line visitor object. */ Basic_sweep_line_2(Traits *traits, SweepVisitor* visitor) : m_traits(traits), m_traitsOwner(false), m_statusLineCurveLess(m_traits, &m_currentEvent), m_queueEventLess(m_traits), m_queue(new EventQueue(m_queueEventLess)), m_statusLine(m_statusLineCurveLess), m_status_line_insert_hint(m_statusLine.begin()), m_num_of_subCurves(0), m_visitor(visitor) { m_visitor->attach(this); } /*! Destrcutor. */ virtual ~Basic_sweep_line_2() { if(m_traitsOwner) delete m_traits; delete m_queue; } /*! * Run the sweep-line with a range of curves. * \param curves_begin An iterator for the first curve in the range. * \param curves_end A past-the-end iterator for the range. * \pre The value-type of CurveInputIterator is X_monotone_curve_2. */ template void sweep(CurveInputIterator curves_begin, CurveInputIterator curves_end) { _init_sweep(curves_begin, curves_end); m_visitor ->after_init(); _sweep(); _complete_sweep(); m_visitor ->after_sweep(); } /*! * Run the sweep-line with a range of x-monotone curves and a range * of action event points(if a curve passed through an action point,it will * be splitted). * \param curves_begin An iterator for the first x-monotone curve in the * range. * \param curves_end A past-the-end iterator for this range. * \param points_begin An iterator for the first point in the range. * \param points_end A past-the-end iterator for this range. * \pre The value-type of XCurveInputIterator is the traits' * X_monotone_curve_2, and the value-type of PointInputIterator is the * traits' Point_2. */ template void sweep (CurveInputIterator curves_begin, CurveInputIterator curves_end, PointInputIterator action_points_begin, PointInputIterator action_points_end) { _init_sweep(curves_begin, curves_end); _init_points(action_points_begin, action_points_end, Base_event::ACTION); m_visitor ->after_init(); _sweep(); _complete_sweep(); m_visitor ->after_sweep(); } /*! * Run the sweep-line with a range of x-monotone curves, a range * of action event points (if a curve passed through an action point,it will * be splitted) and a range of query points (if a curve passed through a * query point,it will not be splitted). * \param curves_begin An iterator for the first x-monotone curve in the * range. * \param curves_end A past-the-end iterator for this range. * \param points_begin An iterator for the first point in the range. * \param points_end A past-the-end iterator for this range. * \pre The value-type of XCurveInputIterator is the traits' * X_monotone_curve_2, and the value-type of PointInputIterator is the * traits' Point_2. */ template void sweep (CurveInputIterator curves_begin, CurveInputIterator curves_end, ActionPointItr action_points_begin, ActionPointItr action_points_end, QueryPointItr query_points_begin, QueryPointItr query_points_end) { _init_sweep(curves_begin, curves_end); _init_points(action_points_begin, action_points_end, Base_event::ACTION); _init_points(query_points_begin, query_points_end, Base_event::QUERY); m_visitor ->after_init(); _sweep(); _complete_sweep(); m_visitor ->after_sweep(); } /*! Get an iterator for the first subcurve in the status line. */ StatusLineIter status_line_begin() { return m_statusLine.begin(); } /*! Get a past-the-end iterator for the subcurves in the status line. */ StatusLineIter status_line_end() { return m_statusLine.end(); } /*! Get the status line size */ unsigned int status_line_size() const { return m_statusLine.size(); } /*! return bool iff m_statusLine is empty */ bool is_status_line_empty() const { return (!m_statusLine.size()); } /*! Stop the sweep by erasing the X-strucure (except for the current event) * can be called by the visitor during 'arter_handle_event'. */ void stop_sweep() { EventQueueIter qiter= this ->m_queue->begin(); ++qiter; for(; qiter != this ->m_queue->end(); ++qiter) { this ->deallocate_event(*qiter); } this -> m_statusLine.clear(); m_status_line_insert_hint = this -> m_statusLine.begin(); CGAL_assertion(!m_queue->empty()); EventQueueIter second = m_queue->begin(); ++second; while(second != m_queue->end()) { EventQueueIter next = second; ++next; m_queue->erase(second); second = next; } } /*! Deallocate event object, it is a public method to allow the visitor * to manage the events deallocation (if he wants to) */ void deallocate_event(Event* event) { m_eventAlloc.destroy(event); m_eventAlloc.deallocate(event,1); } /*! Get the current event */ Event* current_event() { return m_currentEvent; } /*! Get the traits object */ Traits* traits() { return m_traits; } protected: /*! Preform the main sweep-line loop. */ void _sweep() { // Looping over the events in the queue. EventQueueIter eventIter = m_queue->begin(); while (eventIter != m_queue->end()) { // Get the next event from the queue. m_currentEvent = *eventIter; CGAL_PRINT("------------- " << m_currentEvent->get_point() << " --------------" << std::endl;); CGAL_SL_DEBUG(PrintStatusLine(); m_currentEvent->Print();); // Handle the subcurves that are to the left of the event point (i.e., // subcurves that we are done with). _handle_left_curves(); // Handle the subcurves to the right of the event point, reorder them // and test for intersections between them and their immediate neighbors // on the status line. _handle_right_curves(); // Inform the visitor about the event. if (m_visitor->after_handle_event(m_currentEvent, m_status_line_insert_hint, m_is_event_on_above)) { // It is possible to deallocate the event: deallocate_event(m_currentEvent); } // We are done with the current event - remove it from the queue. m_queue->erase(eventIter); eventIter = m_queue->begin(); } return; } /*! create Event object for each input point */ template void _init_points(PointInputIterator points_begin, PointInputIterator points_end, Attribute type) { for(PointInputIterator iter = points_begin; iter != points_end; ++iter) { _init_point(*iter, type); } } /*! for each curve create a Subcurve object and two Event objects */ template void _init_curves(CurveInputIterator curves_begin, CurveInputIterator curves_end) { unsigned int index = 0; for(CurveInputIterator iter = curves_begin; iter != curves_end; ++iter, ++index) { _init_curve(*iter, index); } } /*! Init the sweep algorithm */ template void _init_sweep(CurveInputIterator curves_begin, CurveInputIterator curves_end) { m_num_of_subCurves = std::distance(curves_begin, curves_end); _init_structures(); //init the curves _init_curves(curves_begin, curves_end); } /*! Init the data structures for the sweep algorithm */ virtual void _init_structures() { CGAL_assertion(m_queue->empty() && (m_statusLine.size() == 0)); //allocate all of the Subcure objects as one block m_subCurves = m_subCurveAlloc.allocate(m_num_of_subCurves); } /*! Compete the sweep (compete data strcures) */ virtual void _complete_sweep() { CGAL_assertion(m_queue->empty() && (m_statusLine.size() == 0)); for(unsigned int i=0 ; i < m_num_of_subCurves; ++i) m_subCurveAlloc.destroy(m_subCurves+i); if(m_num_of_subCurves) //if its zero, nothing to deallocate m_subCurveAlloc.deallocate(m_subCurves,m_num_of_subCurves); } /*! * Initialize an event associated with a point. * \param p The given point. */ void _init_point(const Point_2& pt, Attribute type) { const std::pair& pair_res = push_event(pt, type); if(! pair_res.second) m_visitor-> update_event(pair_res.first, pt); m_visitor -> init_event(pair_res.first); } /*! * Initialize an event associated with an x-monotone curve. * \param curve The given x-monotone curve. * \param indec Its unique index. */ void _init_curve(const X_monotone_curve_2 &curve,unsigned int index) { const Point_2 &left_end = m_traits->construct_min_vertex_2_object()(curve); const Point_2 &right_end = m_traits->construct_max_vertex_2_object()(curve); // Handle the right endpoint of the curve. const std::pair& pair_res1 = push_event(right_end, Base_event::RIGHT_END); Event* right_event = pair_res1.first; if(pair_res1.second == false) // the event already exist { m_visitor ->update_event(right_event, right_end, curve , false); //new notification function!! } // Handle the left endpoint of the curve. const std::pair& pair_res2 = push_event(left_end, Base_event::LEFT_END); Event* left_event = pair_res2.first; if(pair_res2.second == false) // the even already exist { m_visitor ->update_event(left_event, left_end, curve, true); //new notification function!! } // construct a Subcurve object m_subCurveAlloc.construct(m_subCurves+index, m_masterSubcurve); (m_subCurves+index)->init(curve, left_event, right_event); right_event->add_curve_to_left(m_subCurves+index); _add_curve_to_right(left_event, m_subCurves+index); return; } /*! Handle the subcurve to the left of the current event point. */ virtual void _handle_left_curves() { CGAL_PRINT("Handling left curve" << std::endl;); m_is_event_on_above = false; if(! m_currentEvent->has_left_curves()) { const std::pair& pair_res = m_statusLine.find_lower (m_currentEvent->get_point(), m_statusLineCurveLess); m_status_line_insert_hint = pair_res.first; m_is_event_on_above = pair_res.second; if(m_is_event_on_above) { // current event is on the interior of existing curve at the Y-str, // it can allowed only if the event is an isolated query point CGAL_assertion(!m_currentEvent -> has_right_curves() && m_currentEvent -> is_query()); //m_is_event_on_above = true; m_visitor->before_handle_event(m_currentEvent); } else m_visitor->before_handle_event(m_currentEvent); //nothing else to do (no left curves) return; } CGAL_PRINT("left curves before sorting: "<<"\n";); CGAL_SL_DEBUG(if (m_currentEvent->left_curves_begin() != m_currentEvent->left_curves_end() ) { m_currentEvent->Print(); }); // determine the order of left curves by the Y-structure _sort_left_curves(); m_visitor->before_handle_event(m_currentEvent); CGAL_PRINT("left curves after sorting: "<<"\n";); CGAL_SL_DEBUG(if (m_currentEvent->left_curves_begin() != m_currentEvent->left_curves_end() ) { m_currentEvent->Print(); }); EventCurveIter left_iter = m_currentEvent->left_curves_begin(); while(left_iter != m_currentEvent->left_curves_end()) { Subcurve *leftCurve = *left_iter; m_visitor->add_subcurve(leftCurve->get_last_curve(), leftCurve); ++left_iter; //remove curve from the status line _remove_curve_from_status_line(leftCurve); } CGAL_PRINT( "Handling left curve END" << std::endl;); return; } /*! * Sort the left subcurves of an event point according to their order in * their status line (no geometric comprasions are needed). */ void _sort_left_curves() { CGAL_assertion(m_currentEvent->has_left_curves()); Subcurve *curve = *(m_currentEvent->left_curves_begin()); StatusLineIter slIter = curve->get_hint(); CGAL_assertion(*slIter == curve); for (++slIter; slIter != m_statusLine.end(); ++slIter) { if(std::find(m_currentEvent->left_curves_begin(), m_currentEvent->left_curves_end(), *slIter) == m_currentEvent->left_curves_end()) break; } StatusLineIter end (slIter); slIter = curve->get_hint(); if(slIter == m_statusLine.begin()) { m_currentEvent->replace_left_curves(slIter,end); return; } --slIter; for(;slIter != m_statusLine.begin(); --slIter) { if( std::find(m_currentEvent->left_curves_begin(), m_currentEvent->left_curves_end(), *slIter) == m_currentEvent->left_curves_end()) { m_currentEvent->replace_left_curves(++slIter,end); return; } } if(std::find(m_currentEvent->left_curves_begin(), m_currentEvent->left_curves_end(), *slIter) == m_currentEvent->left_curves_end()) { m_currentEvent->replace_left_curves(++slIter,end);; } else { m_currentEvent->replace_left_curves(slIter,end); } } /*! Handle the subcurve to the left of the current event point. */ virtual void _handle_right_curves() { CGAL_PRINT("Handling right curves (" ;) CGAL_PRINT(m_currentEvent->get_point() << ")\n";) if(! m_currentEvent->has_right_curves()) return; // Loop over the curves to the right of the current event and handle them: // since we are at allways the beginning of a curve, we just insert // it to the status line. EventCurveIter currentOne = m_currentEvent->right_curves_begin(); EventCurveIter rightCurveEnd = m_currentEvent->right_curves_end(); while ( currentOne != rightCurveEnd ) { CGAL_PRINT_INSERT(*currentOne); StatusLineIter slIter = m_statusLine.insert_before(m_status_line_insert_hint, *currentOne); ((Subcurve*)(*currentOne))->set_hint(slIter); CGAL_SL_DEBUG(PrintStatusLine();); ++currentOne; } CGAL_SL_DEBUG(PrintStatusLine();); } /*! * Add a subcurve to the right of an event point. * \param event The event point. * \param curve The subcurve to add. * \return (true) if an overlap occured; (false) otherwise. */ virtual bool _add_curve_to_right (Event* event, Subcurve* curve, bool overlap_exist = false) { std::pair pair_res = event->add_curve_to_right(curve, m_traits); CGAL_assertion(!pair_res.first); return (false); } /*! Remove a curve from the status line. */ void _remove_curve_from_status_line (Subcurve *leftCurve); #ifdef VERBOSE void PrintEventQueue(); void PrintSubCurves(); void PrintStatusLine(); #endif protected: /*! a traits object */ Traits *m_traits; /*! indicates if the traits object was allocated by the sweep */ bool m_traitsOwner; /*! a pointer to the current event */ Event *m_currentEvent; /*! Y-str comprasion functor */ StatusLineCurveLess m_statusLineCurveLess; EventLess m_queueEventLess; /*! the queue of events (intersection points) to handle */ EventQueue *m_queue; /*! The subcurves array */ Subcurve *m_subCurves; /*! The status line (Y-str) */ StatusLine m_statusLine; /*! An iterator of the status line that is used as a hint for inserts. */ StatusLineIter m_status_line_insert_hint; /*! indicates if current event is on the interior of existing curve, it may * happen only with events that are associated with isolated query points */ bool m_is_event_on_above; /*! An allocator for the events objects */ EventAlloc m_eventAlloc; /*! An allocator for the Subcurve objects */ SubCurveAlloc m_subCurveAlloc; /*! a master Event (created once by the constructor) for the allocator's * usgae. */ Event m_masterEvent; /*! a master Subcurve (created once by the constructor) for the allocator's * usgae. */ Subcurve m_masterSubcurve; /*! The num of subcurves */ unsigned int m_num_of_subCurves; /*! a pointer to the visitor object which will be notidifed during sweep */ SweepVisitor* m_visitor; /*! Allocate an event object */ Event* allocate_event(const Point_2& pt, Attribute type) { Event *e = m_eventAlloc.allocate(1); m_eventAlloc.construct(e, m_masterEvent); e->init(pt, type); return e; } /*! Push event point to x-structure (m_queue) iff it doesnt exist */ std::pair push_event(const Point_2& pt, Attribute type) { Event* e; const std::pair& pair_res = m_queue->find_lower(pt, m_queueEventLess); bool exist = pair_res.second; if (! exist) { // We have a new event e = allocate_event(pt, type); m_queue->insert_before(pair_res.first, e); } else { // The event already exsits e = *(pair_res.first); e->set_attribute(type); } CGAL_PRINT_NEW_EVENT(pt, e); return (std::make_pair(e, !exist)); } }; /*! * Remove a curve from the status line for good. * * @param leftCurve a pointer to the curve that is about to be deleted * @return */ template inline void Basic_sweep_line_2:: _remove_curve_from_status_line(Subcurve *leftCurve) { CGAL_PRINT("remove_curve_from_status_line\n";); CGAL_SL_DEBUG(PrintStatusLine();); CGAL_SL_DEBUG(leftCurve->Print();); StatusLineIter sliter = leftCurve->get_hint(); m_status_line_insert_hint = sliter; ++m_status_line_insert_hint; CGAL_assertion(sliter!=m_statusLine.end()); m_statusLine.erase(sliter); CGAL_PRINT("remove_curve_from_status_line Done\n";) } //DEBUG UTILITIES #ifdef VERBOSE #include #endif CGAL_END_NAMESPACE #endif