From b6333ed4590ad6e3d1b731ccd28bed853643863f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 5 Oct 2020 18:29:34 +0200 Subject: [PATCH] Harmonize speeds of collinear input segments + caching for non-filtered kernels --- .../Straight_skeleton_builder_2_impl.h | 58 +- .../Straight_skeleton_builder_traits_2_aux.h | 135 +++- .../CGAL/Straight_skeleton_builder_2.h | 92 +-- .../CGAL/Straight_skeleton_builder_traits_2.h | 599 +++++++++++------- .../CGAL/Straight_skeleton_halfedge_base_2.h | 14 +- .../include/CGAL/Straight_skeleton_items_2.h | 3 +- .../constructions/Polygon_offset_cons_ftC2.h | 15 +- .../Straight_skeleton_cons_ftC2.h | 142 +++-- .../predicates/Polygon_offset_pred_ftC2.h | 27 +- .../predicates/Straight_skeleton_pred_ftC2.h | 71 ++- 10 files changed, 735 insertions(+), 421 deletions(-) diff --git a/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_2_impl.h b/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_2_impl.h index 7cf815d697c..3d22618cfff 100644 --- a/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_2_impl.h +++ b/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_2_impl.h @@ -189,7 +189,7 @@ Straight_skeleton_builder_2::IsPseudoSplitEvent( EventPtr const& aEvent // contains aNode as a vertex. // template -void Straight_skeleton_builder_2::CollectSplitEvent( Vertex_handle aNode, Triedge const& aTriedge, boost::optional bound) +void Straight_skeleton_builder_2::CollectSplitEvent( Vertex_handle aNode, Triedge const& aTriedge ) { CGAL_STSKEL_BUILDER_TRACE(3, "Collect SplitEvent for N" << aNode->id() << " triedge: " << aTriedge); @@ -204,7 +204,8 @@ void Straight_skeleton_builder_2::CollectSplitEvent( Vertex_handle aNod EventPtr lEvent = EventPtr( new SplitEvent (aTriedge,lTrisegment,aNode) ) ; // filter split event - if (CanSafelyIgnoreSplitEvent(lEvent, bound)) return; + if (CanSafelyIgnoreSplitEvent(lEvent)) + return; mVisitor.on_split_event_created(aNode) ; @@ -218,7 +219,7 @@ void Straight_skeleton_builder_2::CollectSplitEvent( Vertex_handle aNod // Tests the reflex wavefront emerging from 'aNode' against the other contour edges in search for split events. template -void Straight_skeleton_builder_2::CollectSplitEvents( Vertex_handle aNode, Triedge const& aPrevEventTriedge ) +void Straight_skeleton_builder_2::CollectSplitEvents( Vertex_handle aNode, Triedge const& aPrevEventTriedge ) { // lLBorder and lRBorder are the consecutive contour edges forming the reflex wavefront. Triedge const& lTriedge = GetVertexTriedge(aNode); @@ -231,9 +232,8 @@ void Straight_skeleton_builder_2::CollectSplitEvents( Vertex_handle aNo << " LBorder: E" << lLBorder->id() << " RBorder: E" << lRBorder->id() ); - boost::optional bound = - UpperBoundForValidSplitEvents(GetPrevInLAV(aNode), aNode, GetNextInLAV(aNode), - mContourHalfedges.begin(), mContourHalfedges.end()); + ComputeUpperBoundForValidSplitEvents(GetPrevInLAV(aNode), aNode, GetNextInLAV(aNode), + mContourHalfedges.begin(), mContourHalfedges.end()); for ( Halfedge_handle_vector_iterator i = mContourHalfedges.begin(); i != mContourHalfedges.end(); ++ i ) { @@ -245,7 +245,7 @@ void Straight_skeleton_builder_2::CollectSplitEvents( Vertex_handle aNo if ( lEventTriedge != aPrevEventTriedge ) { - CollectSplitEvent(aNode, lEventTriedge, bound ) ; + CollectSplitEvent(aNode, lEventTriedge) ; } } } @@ -556,11 +556,55 @@ void Straight_skeleton_builder_2::CreateContourBisectors() } } +// @todo certify calls to K() instead of supposing exact predicates from K +template +void Straight_skeleton_builder_2::HarmonizeSpeeds(boost::mpl::bool_) +{ + auto comparer = [&](Halfedge_handle lLH, Halfedge_handle lRH) -> bool + { + const Direction_2 lLD = CreateDirection(lLH) ; + const Direction_2 lRD = CreateDirection(lRH) ; + Comparison_result rRes = K().compare_angle_with_x_axis_2_object()(lLD, lRD) ; + + if ( rRes == EQUAL ) + { + if ( K().orientation_2_object()(lLH->vertex()->point(), + lLH->opposite()->vertex()->point(), + lRH->vertex()->point()) == EQUAL ) + return false; // collinear + + // parallel but not collinear, order arbitrarily (but consistently) + return K().less_xy_2_object()(lLH->vertex()->point(), lRH->vertex()->point()) ; + } + else + { + // not parallel + return ( rRes == SMALLER ) ; + } + } ; + + typedef std::set Ordered_halfedges; + Ordered_halfedges lOrdered_halfedges(comparer); + + for( Face_iterator fit = mSSkel->SSkel::Base::faces_begin(); fit != mSSkel->SSkel::Base::faces_end(); ++fit) + { + Halfedge_handle lBorder = fit->halfedge() ; + Segment_2 lS = CreateSegment ( lBorder ) ; + + std::pair rRes = lOrdered_halfedges.insert ( lBorder ) ; + if ( ! rRes.second ) // some collinear edge is already in the set + mTraits.InitializeLineCoeffs ( lBorder->id(), (*rRes.first)->id() ); + else + mTraits.InitializeLineCoeffs ( lS ); + } +} + template void Straight_skeleton_builder_2::InitPhase() { mVisitor.on_initialization_started(static_cast(mSSkel->size_of_vertices())); CreateContourBisectors(); + HarmonizeSpeeds(); CreateInitialEvents(); mVisitor.on_initialization_finished(); } diff --git a/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_traits_2_aux.h b/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_traits_2_aux.h index b7f777be2be..aaecb1a0703 100644 --- a/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_traits_2_aux.h +++ b/Straight_skeleton_2/include/CGAL/Straight_skeleton_2/Straight_skeleton_builder_traits_2_aux.h @@ -186,7 +186,24 @@ class Rational NT mN, mD ; } ; +template +struct Segment_2_with_ID + : public Segment_2 +{ + typedef Segment_2 Base; + typedef typename K::Point_2 Point_2; +public: + Segment_2_with_ID() : Base(), mID(-1) { } + Segment_2_with_ID(Base const& aS) : Base(aS), mID(-1) { } + Segment_2_with_ID(Base const& aS, const std::size_t aID) : Base(aS), mID(aID) { } + Segment_2_with_ID(Point_2 const& aP, Point_2 const& aQ, const std::size_t aID) : Base(aP, aQ), mID(aID) { } + +public: + std::size_t mID; +}; + +// @fixme this has to somehow be documented for the traits concept to make sense // // A straight skeleton event is the simultaneous collision of 3 offseted oriented straight line segments // e0*,e1*,e2* [e* denotes an _offseted_ edge]. @@ -211,21 +228,19 @@ class Rational template class Trisegment_2 : public Ref_counted_base { + typedef CGAL_SS_i::Segment_2_with_ID Segment_2_with_ID; + typedef Trisegment_2 Self; + public: - - typedef typename K::Segment_2 Segment_2 ; - typedef boost::intrusive_ptr Self_ptr ; -public: - - Trisegment_2 ( Segment_2 const& aE0 - , Segment_2 const& aE1 - , Segment_2 const& aE2 + Trisegment_2 ( Segment_2_with_ID const& aE0 + , Segment_2_with_ID const& aE1 + , Segment_2_with_ID const& aE2 , Trisegment_collinearity aCollinearity - , std::size_t id + , std::size_t aID ) - : id(id) + : mID(aID) { mCollinearity = aCollinearity ; @@ -252,22 +267,25 @@ public: } } + std::size_t& id() { return mID; } + const std::size_t& id() const { return mID; } + static Trisegment_2 null() { return Self_ptr() ; } Trisegment_collinearity collinearity() const { return mCollinearity ; } - Segment_2 const& e( unsigned idx ) const { CGAL_precondition(idx<3) ; return mE[idx] ; } + Segment_2_with_ID const& e( unsigned idx ) const { CGAL_precondition(idx<3) ; return mE[idx] ; } - Segment_2 const& e0() const { return e(0) ; } - Segment_2 const& e1() const { return e(1) ; } - Segment_2 const& e2() const { return e(2) ; } + Segment_2_with_ID const& e0() const { return e(0) ; } + Segment_2_with_ID const& e1() const { return e(1) ; } + Segment_2_with_ID const& e2() const { return e(2) ; } // If 2 out of the 3 edges are collinear they can be reclassified as 1 collinear edge (any of the 2) and 1 non-collinear. // These methods returns the edges according to that classification. // PRECONDITION: Exactly 2 out of 3 edges are collinear - Segment_2 const& collinear_edge () const { return e(mCSIdx) ; } - Segment_2 const& non_collinear_edge() const { return e(mNCSIdx) ; } - Segment_2 const& other_collinear_edge() const + Segment_2_with_ID const& collinear_edge () const { return e(mCSIdx) ; } + Segment_2_with_ID const& non_collinear_edge() const { return e(mNCSIdx) ; } + Segment_2_with_ID const& other_collinear_edge() const { switch ( mCollinearity ) { @@ -297,7 +315,7 @@ public: return c == TRISEGMENT_COLLINEARITY_01 ? LEFT : c == TRISEGMENT_COLLINEARITY_12 ? RIGHT : UNKNOWN ; } - friend std::ostream& operator << ( std::ostream& os, Trisegment_2 const& aTrisegment ) + friend std::ostream& operator << ( std::ostream& os, Self const& aTrisegment ) { return os << "[" << s2str(aTrisegment.e0()) << " " << s2str(aTrisegment.e1()) @@ -335,11 +353,11 @@ public: } } - std::size_t id; + std::size_t mID; private : - Segment_2 mE[3]; + Segment_2_with_ID mE[3]; Trisegment_collinearity mCollinearity ; unsigned mCSIdx, mNCSIdx ; @@ -347,13 +365,72 @@ private : Self_ptr mChildR ; } ; +template +struct No_cache +{ + bool IsCached ( std::size_t ) { return false; } + + Info const& Get ( std::size_t ) + { + CGAL_error(); + return Info(); + } + + void Set ( std::size_t, Info const& ) { } + void Reset ( std::size_t ) { } +}; + +//TODO: call reserve, but how? #input vertices + n*m estimation? +template +struct Info_cache +{ + std::vector mValues ; + std::vector mAlreadyComputed ; + + bool IsCached ( std::size_t i ) + { + return ( (mAlreadyComputed.size() > i) && mAlreadyComputed[i] ) ; + } + + Info const& Get(std::size_t i) + { + CGAL_precondition ( IsCached(i) ) ; + return mValues[i] ; + } + + void Set ( std::size_t i, Info const& aValue) + { + if (mValues.size() <= i ) + { + mValues.resize(i+1) ; + mAlreadyComputed.resize(i+1, false) ; + } + + mAlreadyComputed[i] = true ; + mValues[i] = aValue ; + } + + void Reset ( std::size_t i ) + { + if ( IsCached(i) ) // needed if approx info is set but not exact info + mAlreadyComputed[i] = false ; + } +}; + +template +using Time_cache = Info_cache< boost::optional< CGAL_SS_i::Rational< typename K::FT > > > ; + +template +using Coeff_cache = Info_cache< boost::optional< Line_2 > > ; + template struct Functor_base_2 { typedef typename K::FT FT ; typedef typename K::Point_2 Point_2 ; - typedef typename K::Vector_2 Vector_2 ; typedef typename K::Segment_2 Segment_2 ; + typedef typename K::Vector_2 Vector_2 ; + typedef CGAL_SS_i::Segment_2_with_ID Segment_2_with_ID ; typedef CGAL_SS_i::Trisegment_2 Trisegment_2 ; @@ -378,6 +455,9 @@ struct SS_converter : Converter typedef typename Source_kernel::Segment_2 Source_segment_2 ; typedef typename Target_kernel::Segment_2 Target_segment_2 ; + typedef Segment_2_with_ID Source_segment_2_with_ID ; + typedef Segment_2_with_ID Target_segment_2_with_ID ; + typedef Trisegment_2 Source_trisegment_2 ; typedef Trisegment_2 Target_trisegment_2 ; @@ -416,7 +496,13 @@ struct SS_converter : Converter return Target_vector_2(cvt_p(Source_point_2(CGAL::ORIGIN)), cvt_p(Source_point_2(CGAL::ORIGIN) + v) ) ; } - Target_segment_2 cvt_s( Source_segment_2 const& e) const { return Target_segment_2(cvt_p(e.source()), cvt_p(e.target()) ) ; } + Target_segment_2 cvt_s( Source_segment_2 const& e) const { + return Target_segment_2(cvt_p(e.source()), cvt_p(e.target())) ; + } + + Target_segment_2_with_ID cvt_s( Source_segment_2_with_ID const& e) const { + return Target_segment_2_with_ID(cvt_p(e.source()), cvt_p(e.target()), e.mID) ; + } Target_time_and_point_2 cvt_t_p( Source_time_and_point_2 const& v ) const { @@ -434,7 +520,7 @@ struct SS_converter : Converter ,cvt_s(tri->e1()) ,cvt_s(tri->e2()) ,tri->collinearity() - ,tri->id + ,tri->id() ) ) ; } @@ -473,6 +559,8 @@ struct SS_converter : Converter Target_segment_2 operator()( Source_segment_2 const& s) const { return cvt_s(s); } + Target_segment_2_with_ID operator()( Source_segment_2_with_ID const& s) const { return cvt_s(s); } + Target_trisegment_2_ptr operator()( Source_trisegment_2_ptr const& tri ) const { return cvt_trisegment(tri); @@ -507,6 +595,7 @@ struct SS_converter : Converter }; BOOST_MPL_HAS_XXX_TRAIT_DEF(Filters_split_events_tag) +BOOST_MPL_HAS_XXX_TRAIT_DEF(Segment_2_with_ID) } // namespace CGAL_SS_i diff --git a/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_2.h b/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_2.h index d5f7e4130b0..bfbd4306515 100644 --- a/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_2.h +++ b/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_2.h @@ -251,10 +251,6 @@ private : Vector_2 lV1 ( aEvent->point(), lEvent.seed0()->point() ); // @fixme ? is this correct? Vector_2 lV2 ( aEvent->point(), aEvent->point() + CreateVector(aEvent->triedge().e2()) ) ; -// std::cout << "aEvent->point(): " << aEvent->point() << std::endl; -// std::cout << "S LV1: " << lV1 << std::endl; -// std::cout << "S LV2: " << lV2 << std::endl; - return ComputeApproximateAngle(lV1, lV2) ; } @@ -268,21 +264,15 @@ private : if(lEvent.is_at_source_vertex()) { // is_at_source_vertex <=> opposite node is seed0 -// std::cout << "seed1: " << lEvent.seed1()->point() << std::endl; lV1 = Vector_2( aEvent->point(), lEvent.seed1()->point() ); lV2 = Vector_2( aEvent->point(), aEvent->point() + CreateVector(aEvent->triedge().e2()) ) ; } else { -// std::cout << "seed0: " << lEvent.seed0()->point() << std::endl; lV1 = Vector_2( aEvent->point(), lEvent.seed0()->point() ); lV2 = Vector_2( aEvent->point(), aEvent->point() - CreateVector(aEvent->triedge().e2()) ) ; } -// std::cout << "aEvent->point(): " << aEvent->point() << std::endl; -// std::cout << "PS LV1: " << lV1 << std::endl; -// std::cout << "PS LV2: " << lV2 << std::endl; - return ComputeApproximateAngle(lV1, lV2) ; } @@ -535,10 +525,6 @@ public: { CGAL_precondition( aA->type() != Event::cEdgeEvent || aB->type() != Event::cEdgeEvent ) ; - mBuilder->SetEventTimeAndPoint(*aA); // @fixme cache this - mBuilder->SetEventTimeAndPoint(*aB); - - // Below is a bit redundant since we recompute (certified) times if events are not simultaneous if ( ! mBuilder->AreEventsSimultaneous(aA,aB) ) return ( mBuilder->CompareEvents(aA,aB) == LARGER ) ; @@ -661,13 +647,30 @@ private : GetHalfedgeLAVList(GetVertexData(aV).mTriedge.e0()).remove(aV); } - Segment_2 CreateSegment ( Halfedge_const_handle aH ) const + typename K::Segment_2 CreateRawSegment ( Halfedge_const_handle aH ) const { Point_2 s = aH->opposite()->vertex()->point() ; Point_2 t = aH->vertex()->point() ; return K().construct_segment_2_object()(s,t); } + template // actually just equal to 'Traits', but gotta template for SFINAE to work + Segment_2 CreateSegment ( Halfedge_const_handle aH, + typename std::enable_if< + ! CGAL_SS_i::has_Segment_2_with_ID::value>::type* = nullptr ) const + { + CGAL_assertion(false); + return Segment_2(CreateRawSegment(aH)) ; + } + + template // actually just equal to 'Traits', but gotta template for SFINAE to work + Segment_2 CreateSegment ( Halfedge_const_handle aH, + typename std::enable_if< + CGAL_SS_i::has_Segment_2_with_ID::value>::type* = nullptr ) const + { + return Segment_2(CreateRawSegment(aH), aH->id()); + } + Vector_2 CreateVector ( Halfedge_const_handle aH ) const { Point_2 s = aH->opposite()->vertex()->point() ; @@ -684,9 +687,9 @@ private : { CGAL_precondition(aTriedge.is_valid() && aTriedge.is_skeleton()); - Trisegment_2_ptr r = Construct_ss_trisegment_2(mTraits)(CreateSegment(aTriedge.e0()) - ,CreateSegment(aTriedge.e1()) - ,CreateSegment(aTriedge.e2()) + Trisegment_2_ptr r = Construct_ss_trisegment_2(mTraits)(CreateSegment(aTriedge.e0()) + ,CreateSegment(aTriedge.e1()) + ,CreateSegment(aTriedge.e2()) ); CGAL_STSKEL_BUILDER_TRACE(5,"Trisegment for " << aTriedge << ":" << r ) ; @@ -828,8 +831,8 @@ private : bool IsOppositeEdgeFacingTheSplitSeed( Vertex_handle aSeed, Halfedge_handle aOpposite ) const { if ( aSeed->is_skeleton() ) - return Is_edge_facing_ss_node_2(mTraits)( GetTrisegment(aSeed), CreateSegment(aOpposite) ) ; - else return Is_edge_facing_ss_node_2(mTraits)( aSeed->point() , CreateSegment(aOpposite) ) ; + return Is_edge_facing_ss_node_2(mTraits)( GetTrisegment(aSeed), CreateSegment(aOpposite) ) ; + else return Is_edge_facing_ss_node_2(mTraits)( aSeed->point() , CreateSegment(aOpposite) ) ; } Oriented_side EventPointOrientedSide( Event const& aEvent @@ -840,8 +843,8 @@ private : ) const { return Oriented_side_of_event_point_wrt_bisector_2(mTraits)( aEvent.trisegment() - , CreateSegment(aE0) - , CreateSegment(aE1) + , CreateSegment(aE0) + , CreateSegment(aE1) , GetTrisegment(aV01) // Can be null , aE0isPrimary ) ; @@ -1012,9 +1015,9 @@ private : EventPtr IsPseudoSplitEvent( EventPtr const& aEvent, Vertex_handle_pair aOpp, Site const& aSite ) ; - void CollectSplitEvent( Vertex_handle aNode, Triedge const& aTriedge, boost::optional bound=boost::none) ; + void CollectSplitEvent( Vertex_handle aNode, Triedge const& aTriedge ) ; - void CollectSplitEvents( Vertex_handle aNode, Triedge const& aPrevEventTriedge ) ; + void CollectSplitEvents( Vertex_handle aNode, Triedge const& aPrevEventTriedge ) ; EventPtr FindEdgeEvent( Vertex_handle aLNode, Vertex_handle aRNode, Triedge const& aPrevEventTriedge ) ; @@ -1024,6 +1027,9 @@ private : void UpdatePQ( Vertex_handle aV, Triedge const& aPrevEventTriedge ) ; void CreateInitialEvents(); void CreateContourBisectors(); + void HarmonizeSpeeds(boost::mpl::bool_) { } + void HarmonizeSpeeds(boost::mpl::bool_); + void HarmonizeSpeeds() { return HarmonizeSpeeds(typename CGAL_SS_i::has_Segment_2_with_ID::type()); } void InitPhase(); void SetupNewNode( Vertex_handle aNode ); @@ -1206,43 +1212,41 @@ private : } // internal function to filter split event in case the traits is Filtered - bool CanSafelyIgnoreSplitEventImpl(const EventPtr& lEvent, const boost::optional& bound, boost::mpl::bool_) + bool CanSafelyIgnoreSplitEventImpl(const EventPtr&, boost::mpl::bool_) { return false; } - bool CanSafelyIgnoreSplitEventImpl(const EventPtr& lEvent, const boost::optional& bound, boost::mpl::bool_) + bool CanSafelyIgnoreSplitEventImpl(const EventPtr& lEvent, boost::mpl::bool_) { - return mTraits.can_safely_ignore_split_event(lEvent, bound); + return mTraits.CanSafelyIgnoreSplitEvent(lEvent); } - bool CanSafelyIgnoreSplitEvent(const EventPtr& lEvent, const boost::optional& bound) + bool CanSafelyIgnoreSplitEvent(const EventPtr& lEvent) { - return CanSafelyIgnoreSplitEventImpl(lEvent, bound, typename CGAL_SS_i::has_Filters_split_events_tag::type()); + return CanSafelyIgnoreSplitEventImpl(lEvent, typename CGAL_SS_i::has_Filters_split_events_tag::type()); } - boost::optional UpperBoundForValidSplitEventsImpl(Vertex_handle, Vertex_handle, Vertex_handle, - Halfedge_handle_vector_iterator , Halfedge_handle_vector_iterator, - boost::mpl::bool_) + void ComputeUpperBoundForValidSplitEventsImpl(Vertex_handle, Vertex_handle, Vertex_handle, + Halfedge_handle_vector_iterator, Halfedge_handle_vector_iterator, + boost::mpl::bool_) { - return boost::none; } - boost::optional UpperBoundForValidSplitEventsImpl(Vertex_handle lPrev, Vertex_handle aNode, Vertex_handle lNext, - Halfedge_handle_vector_iterator contour_halfedges_begin, - Halfedge_handle_vector_iterator contour_halfedges_end, - boost::mpl::bool_) + void ComputeUpperBoundForValidSplitEventsImpl(Vertex_handle lPrev, Vertex_handle aNode, Vertex_handle lNext, + Halfedge_handle_vector_iterator contour_halfedges_begin, + Halfedge_handle_vector_iterator contour_halfedges_end, + boost::mpl::bool_) { - return mTraits.upper_bound_for_valid_split_events(lPrev, aNode, lNext, contour_halfedges_begin, contour_halfedges_end); + return mTraits.ComputeFilteringBound(lPrev, aNode, lNext, contour_halfedges_begin, contour_halfedges_end); } - boost::optional - UpperBoundForValidSplitEvents(Vertex_handle lPrev, Vertex_handle aNode, Vertex_handle lNext, - Halfedge_handle_vector_iterator contour_halfedges_begin, - Halfedge_handle_vector_iterator contour_halfedges_end) + void ComputeUpperBoundForValidSplitEvents(Vertex_handle lPrev, Vertex_handle aNode, Vertex_handle lNext, + Halfedge_handle_vector_iterator contour_halfedges_begin, + Halfedge_handle_vector_iterator contour_halfedges_end) { - return UpperBoundForValidSplitEventsImpl(lPrev, aNode, lNext, contour_halfedges_begin, contour_halfedges_end, - typename CGAL_SS_i::has_Filters_split_events_tag::type()); + return ComputeUpperBoundForValidSplitEventsImpl(lPrev, aNode, lNext, contour_halfedges_begin, contour_halfedges_end, + typename CGAL_SS_i::has_Filters_split_events_tag::type()); } public: diff --git a/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_traits_2.h b/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_traits_2.h index 7be07d3bdff..e5a05a400e9 100644 --- a/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_traits_2.h +++ b/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_traits_2.h @@ -31,64 +31,27 @@ namespace CGAL { namespace CGAL_SS_i { -//TODO: call reserve, but how? #input vertices + n*m estimation? -template -struct Time_cache -{ - typedef boost::optional< Rational< typename K::FT > > Event_time; - std::vector event_times; - std::vector already_computed; - - bool is_time_cached(std::size_t i) - { - return already_computed.size()>i && already_computed[i]; - } - - const Event_time& time(std::size_t i) - { - CGAL_assertion(is_time_cached(i)); - return event_times[i]; - } - - void set_time(std::size_t i, const Event_time& time) - { - if (event_times.size()<=i){ - event_times.resize(i+1); - already_computed.resize(i+1, false); - } - already_computed[i]=true; - event_times[i]=time; - } - - void reset(std::size_t i) - { - if (is_time_cached(i)) // needed if approx time is set but not exact time - already_computed[i]=false; - } -}; - template struct Construct_ss_trisegment_2 : Functor_base_2 { typedef Functor_base_2 Base ; - typedef typename Base::Segment_2 Segment_2 ; - typedef typename Base::Trisegment_2 Trisegment_2 ; + typedef typename Base::Segment_2_with_ID Segment_2_with_ID ; typedef typename Base::Trisegment_2_ptr Trisegment_2_ptr ; typedef Trisegment_2_ptr result_type ; template Construct_ss_trisegment_2(const Traits& traits) - : nextID(traits.trisegment_id) + : mNext_ID(traits.trisegment_ID()) {} - result_type operator() ( Segment_2 const& aS0, Segment_2 const& aS1, Segment_2 const& aS2 ) const + result_type operator() ( Segment_2_with_ID const& aS0, Segment_2_with_ID const& aS1, Segment_2_with_ID const& aS2 ) const { - return construct_trisegment(aS0,aS1,aS2,nextID++); + return construct_trisegment(aS0,aS1,aS2,mNext_ID++) ; } - std::size_t& nextID; + std::size_t& mNext_ID ; }; template @@ -101,20 +64,22 @@ struct Do_ss_event_exist_2 : Functor_base_2 typedef Uncertain result_type ; - Do_ss_event_exist_2(Time_cache& time_cache) - : mTime_cache(time_cache) + Do_ss_event_exist_2(Time_cache& aTime_cache, Coeff_cache& aCoeff_cache) + : mTime_cache(aTime_cache), mCoeff_cache(aCoeff_cache) {} Uncertain operator() ( Trisegment_2_ptr const& aTrisegment, boost::optional aMaxTime ) const { - Uncertain rResult = exist_offset_lines_isec2(aTrisegment,aMaxTime, mTime_cache) ; + Uncertain rResult = exist_offset_lines_isec2(aTrisegment,aMaxTime,mTime_cache,mCoeff_cache) ; CGAL_STSKEL_ASSERT_PREDICATE_RESULT(rResult,K,"Exist_event",aTrisegment); return rResult ; } - Time_cache& mTime_cache; +private: + Time_cache& mTime_cache ; + Coeff_cache& mCoeff_cache ; }; template @@ -123,20 +88,27 @@ struct Is_edge_facing_ss_node_2 : Functor_base_2 typedef Functor_base_2 Base ; typedef typename Base::Point_2 Point_2 ; - typedef typename Base::Segment_2 Segment_2 ; + typedef typename Base::Segment_2_with_ID Segment_2_with_ID ; typedef typename Base::Trisegment_2_ptr Trisegment_2_ptr ; typedef Uncertain result_type ; - Uncertain operator() ( Point_2 const& aContourNode, Segment_2 const& aEdge ) const + Is_edge_facing_ss_node_2(Coeff_cache& aCoeff_cache) + : mCoeff_cache(aCoeff_cache) + { } + + Uncertain operator() ( Point_2 const& aContourNode, Segment_2_with_ID const& aEdge ) const { return is_edge_facing_pointC2(cgal_make_optional(aContourNode),aEdge) ; } - Uncertain operator() ( Trisegment_2_ptr const& aSkeletonNode, Segment_2 const& aEdge ) const + Uncertain operator() ( Trisegment_2_ptr const& aSkeletonNode, Segment_2_with_ID const& aEdge ) const { - return is_edge_facing_offset_lines_isecC2(aSkeletonNode,aEdge) ; + return is_edge_facing_offset_lines_isecC2(aSkeletonNode,aEdge,mCoeff_cache) ; } + +private: + Coeff_cache& mCoeff_cache ; }; template @@ -148,20 +120,22 @@ struct Compare_ss_event_times_2 : Functor_base_2 typedef Uncertain result_type ; - Compare_ss_event_times_2(Time_cache& time_cache) - : mTime_cache(time_cache) + Compare_ss_event_times_2(Time_cache& aTime_cache, Coeff_cache& aCoeff_cache) + : mTime_cache(aTime_cache), mCoeff_cache(aCoeff_cache) {} Uncertain operator() ( Trisegment_2_ptr const& aL, Trisegment_2_ptr const& aR ) const { - Uncertain rResult = compare_offset_lines_isec_timesC2(aL,aR, mTime_cache) ; + Uncertain rResult = compare_offset_lines_isec_timesC2(aL,aR,mTime_cache,mCoeff_cache) ; CGAL_STSKEL_ASSERT_PREDICATE_RESULT(rResult,K,"Compare_event_times","L: " << aL << "\nR:" << aR ); return rResult ; } - Time_cache& mTime_cache; +private: + Time_cache& mTime_cache ; + Coeff_cache& mCoeff_cache ; }; template @@ -192,25 +166,32 @@ struct Oriented_side_of_event_point_wrt_bisector_2 : Functor_base_2 { typedef Functor_base_2 Base ; - typedef typename Base::Segment_2 Segment_2 ; + typedef typename Base::Segment_2_with_ID Segment_2_with_ID ; typedef typename Base::Trisegment_2_ptr Trisegment_2_ptr ; typedef typename Base::Point_2 Point_2 ; typedef Uncertain result_type ; + Oriented_side_of_event_point_wrt_bisector_2(Coeff_cache& aCoeff_cache) + : mCoeff_cache(aCoeff_cache) + {} + Uncertain operator() ( Trisegment_2_ptr const& aEvent - , Segment_2 const& aE0 - , Segment_2 const& aE1 + , Segment_2_with_ID const& aE0 + , Segment_2_with_ID const& aE1 , Trisegment_2_ptr const& aE01Event , bool aE0isPrimary ) const { - Uncertain rResult = oriented_side_of_event_point_wrt_bisectorC2(aEvent,aE0,aE1,aE01Event,aE0isPrimary) ; + Uncertain rResult = oriented_side_of_event_point_wrt_bisectorC2(aEvent,aE0,aE1,aE01Event,aE0isPrimary,mCoeff_cache) ; CGAL_STSKEL_ASSERT_PREDICATE_RESULT(rResult,K,"Oriented_side_of_event_point_wrt_bisector_2","Event=" << aEvent << " E0=" << aE0 << " E1=" << aE1 ); return rResult ; } + +private: + Coeff_cache& mCoeff_cache ; }; @@ -223,21 +204,22 @@ struct Are_ss_events_simultaneous_2 : Functor_base_2 typedef Uncertain result_type ; - - Are_ss_events_simultaneous_2(Time_cache& time_cache) - : mTime_cache(time_cache) + Are_ss_events_simultaneous_2(Time_cache& aTime_cache, Coeff_cache& aCoeff_cache) + : mTime_cache(aTime_cache), mCoeff_cache(aCoeff_cache) {} Uncertain operator() ( Trisegment_2_ptr const& aA, Trisegment_2_ptr const& aB ) const { - Uncertain rResult = are_events_simultaneousC2(aA,aB,mTime_cache); + Uncertain rResult = are_events_simultaneousC2(aA,aB,mTime_cache,mCoeff_cache); CGAL_STSKEL_ASSERT_PREDICATE_RESULT(rResult,K,"Are_events_simultaneous","A=" << aA << "\nB=" << aB); return rResult ; } - Time_cache& mTime_cache; +private: + Time_cache& mTime_cache ; + Coeff_cache& mCoeff_cache ; }; template @@ -245,11 +227,11 @@ struct Are_ss_edges_collinear_2 : Functor_base_2 { typedef Functor_base_2 Base ; - typedef typename Base::Segment_2 Segment_2 ; + typedef typename Base::Segment_2_with_ID Segment_2_with_ID ; typedef Uncertain result_type ; - Uncertain operator() ( Segment_2 const& aA, Segment_2 const& aB ) const + Uncertain operator() ( Segment_2_with_ID const& aA, Segment_2_with_ID const& aB ) const { Uncertain rResult = are_edges_collinearC2(aA,aB); @@ -264,11 +246,11 @@ struct Are_ss_edges_parallel_2 : Functor_base_2 { typedef Functor_base_2 Base ; - typedef typename Base::Segment_2 Segment_2 ; + typedef typename Base::Segment_2_with_ID Segment_2_with_ID ; typedef Uncertain result_type ; - Uncertain operator() ( Segment_2 const& aA, Segment_2 const& aB ) const + Uncertain operator() ( Segment_2_with_ID const& aA, Segment_2_with_ID const& aB ) const { Uncertain rResult = are_edges_parallelC2(aA,aB); @@ -286,15 +268,15 @@ struct Construct_ss_event_time_and_point_2 : Functor_base_2 typedef typename Base::FT FT ; typedef typename Base::Point_2 Point_2 ; - typedef typename Base::Segment_2 Segment_2 ; + typedef typename Base::Segment_2_with_ID Segment_2_with_ID ; typedef typename Base::Trisegment_2_ptr Trisegment_2_ptr ; typedef boost::tuple rtype ; typedef boost::optional result_type ; - Construct_ss_event_time_and_point_2(Time_cache& time_cache) - :mTime_cache(time_cache) + Construct_ss_event_time_and_point_2(Time_cache& aTime_cache, Coeff_cache& aCoeff_cache) + : mTime_cache(aTime_cache), mCoeff_cache(aCoeff_cache) {} result_type operator() ( Trisegment_2_ptr const& aTrisegment ) const @@ -304,13 +286,13 @@ struct Construct_ss_event_time_and_point_2 : Functor_base_2 FT t(0) ; Point_2 i = ORIGIN ; - boost::optional< Rational > ot = compute_offset_lines_isec_timeC2(aTrisegment, mTime_cache); + boost::optional< Rational > ot = compute_offset_lines_isec_timeC2(aTrisegment,mTime_cache,mCoeff_cache); if ( !!ot && certainly( CGAL_NTS certified_is_not_zero(ot->d()) ) ) { t = ot->n() / ot->d(); - boost::optional oi = construct_offset_lines_isecC2(aTrisegment); + boost::optional oi = construct_offset_lines_isecC2(aTrisegment,mCoeff_cache); if ( oi ) { i = *oi ; @@ -329,9 +311,9 @@ struct Construct_ss_event_time_and_point_2 : Functor_base_2 if ( is_possibly_inexact_time_clearly_not_zero(t) ) { - Segment_2 const& e0 = aTrisegment->e0() ; - Segment_2 const& e1 = aTrisegment->e1() ; - Segment_2 const& e2 = aTrisegment->e2() ; + Segment_2_with_ID const& e0 = aTrisegment->e0() ; + Segment_2_with_ID const& e1 = aTrisegment->e1() ; + Segment_2_with_ID const& e2 = aTrisegment->e2() ; Point_2 const& e0s = e0.source(); Point_2 const& e0t = e0.target(); @@ -376,7 +358,9 @@ struct Construct_ss_event_time_and_point_2 : Functor_base_2 return rR ; } - Time_cache& mTime_cache; +private: + Time_cache& mTime_cache ; + Coeff_cache& mCoeff_cache ; }; } // namespace CGAL_SS_i @@ -405,10 +389,10 @@ struct Straight_skeleton_builder_traits_2_base typedef typename K::Point_2 Point_2 ; typedef typename K::Vector_2 Vector_2 ; typedef typename K::Direction_2 Direction_2 ; - typedef typename K::Segment_2 Segment_2 ; - - typedef CGAL_SS_i::Trisegment_2 Trisegment_2 ; + typedef CGAL_SS_i::Segment_2_with_ID Segment_2 ; + typedef CGAL_SS_i::Segment_2_with_ID Segment_2_with_ID ; // used in BOOST_MPL_HAS_XXX_TRAIT_DEF + typedef CGAL_SS_i::Trisegment_2 Trisegment_2 ; typedef typename Trisegment_2::Self_ptr Trisegment_2_ptr ; template F get( F const* = 0 ) const { return F(); } @@ -416,17 +400,17 @@ struct Straight_skeleton_builder_traits_2_base } ; -template class Straight_skeleton_builder_traits_2_impl ; +template +class Straight_skeleton_builder_traits_2_impl ; template -class Straight_skeleton_builder_traits_2_impl : public Straight_skeleton_builder_traits_2_base +class Straight_skeleton_builder_traits_2_impl + : public Straight_skeleton_builder_traits_2_base { typedef Straight_skeleton_builder_traits_2_functors Unfiltering ; + typedef Straight_skeleton_builder_traits_2_base Base ; public: - - mutable std::size_t trisegment_id = 0; - typedef Unfiltered_predicate_adaptor Do_ss_event_exist_2 ; @@ -454,43 +438,43 @@ public: typedef typename Unfiltering::Construct_ss_event_time_and_point_2 Construct_ss_event_time_and_point_2 ; typedef typename Unfiltering::Construct_ss_trisegment_2 Construct_ss_trisegment_2 ; - mutable CGAL_SS_i::Time_cache time_cache; - - void reset_trisegment(std::size_t i) const - { - time_cache.reset(i); - } - using Straight_skeleton_builder_traits_2_base::get; + Is_edge_facing_ss_node_2 + get(Is_edge_facing_ss_node_2 const* = 0 ) const + { + return Is_edge_facing_ss_node_2(mCoeff_cache); + } + Compare_ss_event_times_2 get(Compare_ss_event_times_2 const* = 0 ) const { - return Compare_ss_event_times_2(const_cast&>(time_cache)); + return Compare_ss_event_times_2(mTime_cache, mCoeff_cache); } - Compare_ss_event_angles_2 - get(Compare_ss_event_angles_2 const* = 0 ) const + + Oriented_side_of_event_point_wrt_bisector_2 + get(Oriented_side_of_event_point_wrt_bisector_2 const* = 0 ) const { - return Compare_ss_event_angles_2(); + return Oriented_side_of_event_point_wrt_bisector_2(mCoeff_cache); } Do_ss_event_exist_2 get(Do_ss_event_exist_2 const* = 0 ) const { - return Do_ss_event_exist_2(const_cast&>(time_cache)); + return Do_ss_event_exist_2(mTime_cache, mCoeff_cache); } Are_ss_events_simultaneous_2 get(Are_ss_events_simultaneous_2 const* = 0 ) const { - return Are_ss_events_simultaneous_2(const_cast&>(time_cache)); + return Are_ss_events_simultaneous_2(mTime_cache, mCoeff_cache); } Construct_ss_event_time_and_point_2 get(Construct_ss_event_time_and_point_2 const* = 0 ) const { - return Construct_ss_event_time_and_point_2(const_cast&>(time_cache)); + return Construct_ss_event_time_and_point_2(mTime_cache, mCoeff_cache); } Construct_ss_trisegment_2 @@ -498,10 +482,124 @@ public: { return Construct_ss_trisegment_2(*this); } + +// ID functions + std::size_t& trisegment_ID() const { return mTrisegment_ID ; } + + void reset_trisegment(std::size_t i) const + { + --mTrisegment_ID ; + mTime_cache.Reset(i) ; + } + +// functions to initialize (and harmonize) and cache speeds + void InitializeLineCoeffs ( CGAL_SS_i::Segment_2_with_ID const& aBorderS ) + { + CGAL_SS_i::compute_normalized_line_ceoffC2 ( aBorderS, mCoeff_cache ) ; + } + + void InitializeLineCoeffs ( std::size_t aID, std::size_t aOtherID ) + { + if ( mCoeff_cache.Get( aOtherID ) ) + mCoeff_cache.Set( aID, CGAL_SS_i::cgal_make_optional(*(mCoeff_cache.Get(aOtherID))) ) ; + else + mCoeff_cache.Set( aID, boost::none ) ; + } + + // functions and tag for filtering split events + struct Filters_split_events_tag{}; + + template + bool CanSafelyIgnoreSplitEvent(const EventPtr& lEvent) + { + return false; + + // filter event + if ( ! mFilteringBound ) + return false; + + typename Base::Trisegment_2_ptr tri = lEvent->trisegment() ; + boost::optional > lOptTime = + CGAL_SS_i::compute_offset_lines_isec_timeC2(tri, mTime_cache, mCoeff_cache); + + if ( lOptTime && lOptTime->to_nt() > *mFilteringBound ) + { + // avoid filling the cache vectors with times of trisegments that will be removed + reset_trisegment(tri->id()); + return true; + } + + return false; + } + + // @todo there shouldn't be any combinatorial structures such as vertices in the traits + template + void ComputeFilteringBound(Vertex_handle lPrev, Vertex_handle aNode, Vertex_handle lNext, + Halfedge_handle_vector_iterator contour_halfedges_begin, + Halfedge_handle_vector_iterator contour_halfedges_end) + { + mFilteringBound = boost::none; + + if ( ! aNode->is_contour() ) + return ; + + typedef typename K::Vector_2 Vector_2; + typedef typename K::Segment_2 Segment_2; + typedef typename K::Ray_2 Ray_2; + typedef typename K::Line_2 Line_2; + + Segment_2 s1(lPrev->point(), aNode->point()); + Segment_2 s2(aNode->point(), lNext->point()); + + // @todo? These are not input segments, but it might still worth caching (just gotta assign them an ID) + boost::optional< Line_2 > l1 = CGAL_SS_i::compute_normalized_line_ceoffC2(s1); + boost::optional< Line_2 > l2 = CGAL_SS_i::compute_normalized_line_ceoffC2(s2); + + Vector_2 lV1(l1->a(), l1->b()) ; + Vector_2 lV2(l2->a(), l2->b()) ; + Vector_2 lV12 = lV1 + lV2 ; + Ray_2 bisect_ray(aNode->point(), lV12) ; + + // F2C to_input; + // std::cout << "bisect " << aNode->point() << " " << aNode->point() + to_input(lV12) << "\n"; + + for ( Halfedge_handle_vector_iterator i = contour_halfedges_begin; i != contour_halfedges_end; ++ i ) + { + CGAL_assertion((*i)->vertex()->is_contour() && (*i)->opposite()->vertex()->is_contour() ); + Segment_2 s_h((*i)->opposite()->vertex()->point(), (*i)->vertex()->point()); + + auto inter = K().do_intersect_2_object()(s_h, bisect_ray); + auto orient = K().orientation_2_object()(s_h[0], s_h[1], aNode->point()); + + // we use segments of the input polygon intersected by the bisector and such that + // they are oriented such that the reflex vertex is on the left side of the segment + if (!is_certain(inter) || !is_certain(orient) || !inter || orient != LEFT_TURN) + continue; + + // Note that we don't need the normalization + boost::optional< Line_2 > lh = CGAL_SS_i::compute_normalized_line_ceoffC2(s_h); + + typename K::FT lBound = ( - lh->c() - lh->a()*aNode->point().x() - lh->b()*aNode->point().y() ) / + ( lh->a()*lV12.x() + lh->b()*lV12.y() ); + + if( ! is_finite(lBound) ) + continue; + + if ( ! mFilteringBound || *mFilteringBound > lBound ) + mFilteringBound = lBound; + } + } + +public: + mutable std::size_t mTrisegment_ID = 0 ; + mutable CGAL_SS_i::Time_cache mTime_cache ; + mutable CGAL_SS_i::Coeff_cache mCoeff_cache ; + boost::optional< typename K::FT > mFilteringBound ; } ; template -class Straight_skeleton_builder_traits_2_impl : public Straight_skeleton_builder_traits_2_base +class Straight_skeleton_builder_traits_2_impl + : public Straight_skeleton_builder_traits_2_base { typedef typename K::Exact_kernel EK ; typedef typename K::Approximate_kernel FK ; @@ -598,36 +696,53 @@ public: Compare_ss_event_times_2 get(Compare_ss_event_times_2 const* = 0 ) const { - return Compare_ss_event_times_2( typename Exact::Compare_ss_event_times_2(const_cast&>(mExact_traits.time_cache)), - typename Filtering::Compare_ss_event_times_2(const_cast&>(mApproximate_traits.time_cache)) ); + return Compare_ss_event_times_2( typename Exact::Compare_ss_event_times_2( + mExact_traits.mTime_cache, mExact_traits.mCoeff_cache), + typename Filtering::Compare_ss_event_times_2( + mApproximate_traits.mTime_cache, mApproximate_traits.mCoeff_cache) ); } - Compare_ss_event_angles_2 - get(Compare_ss_event_angles_2 const* = 0 ) const + Is_edge_facing_ss_node_2 + get(Is_edge_facing_ss_node_2 const* = 0 ) const { - return Compare_ss_event_angles_2( typename Exact::Compare_ss_event_angles_2(), - typename Filtering::Compare_ss_event_angles_2() ); + return Is_edge_facing_ss_node_2( typename Exact::Is_edge_facing_ss_node_2(mExact_traits.mCoeff_cache), + typename Filtering::Is_edge_facing_ss_node_2(mApproximate_traits.mCoeff_cache) ); + } + + Oriented_side_of_event_point_wrt_bisector_2 + get(Oriented_side_of_event_point_wrt_bisector_2 const* = 0 ) const + { + return Oriented_side_of_event_point_wrt_bisector_2( typename Exact::Oriented_side_of_event_point_wrt_bisector_2( + mExact_traits.mCoeff_cache), + typename Filtering::Oriented_side_of_event_point_wrt_bisector_2( + mApproximate_traits.mCoeff_cache) ); } Do_ss_event_exist_2 get(Do_ss_event_exist_2 const* = 0 ) const { - return Do_ss_event_exist_2( typename Exact::Do_ss_event_exist_2(const_cast&>(mExact_traits.time_cache)), - typename Filtering::Do_ss_event_exist_2(const_cast&>(mApproximate_traits.time_cache)) ); + return Do_ss_event_exist_2( typename Exact::Do_ss_event_exist_2( + mExact_traits.mTime_cache, mExact_traits.mCoeff_cache), + typename Filtering::Do_ss_event_exist_2( + mApproximate_traits.mTime_cache, mApproximate_traits.mCoeff_cache) ); } Are_ss_events_simultaneous_2 get(Are_ss_events_simultaneous_2 const* = 0 ) const { - return Are_ss_events_simultaneous_2( typename Exact::Are_ss_events_simultaneous_2(const_cast&>(mExact_traits.time_cache)), - typename Filtering::Are_ss_events_simultaneous_2(const_cast&>(mApproximate_traits.time_cache)) ); + return Are_ss_events_simultaneous_2( typename Exact::Are_ss_events_simultaneous_2( + mExact_traits.mTime_cache, mExact_traits.mCoeff_cache), + typename Filtering::Are_ss_events_simultaneous_2( + mApproximate_traits.mTime_cache, mApproximate_traits.mCoeff_cache) ); } Construct_ss_event_time_and_point_2 get(Construct_ss_event_time_and_point_2 const* = 0 ) const { - return Construct_ss_event_time_and_point_2( typename Exact::Construct_ss_event_time_and_point_2(const_cast&>(mExact_traits.time_cache)), - typename Filtering::Construct_ss_event_time_and_point_2(const_cast&>(mApproximate_traits.time_cache)) ); + return Construct_ss_event_time_and_point_2( typename Exact::Construct_ss_event_time_and_point_2( + mExact_traits.mTime_cache, mExact_traits.mCoeff_cache), + typename Filtering::Construct_ss_event_time_and_point_2( + mApproximate_traits.mTime_cache, mApproximate_traits.mCoeff_cache) ); } // constructor of trisegments using global id stored in the traits Construct_ss_trisegment_2 @@ -636,121 +751,165 @@ public: return Construct_ss_trisegment_2(*this); } -// functions and tag for filtering split events - struct Filters_split_events_tag{}; - - template - bool - can_safely_ignore_split_event(const EventPtr& lEvent, const boost::optional& bound) - { - // filter event - if (bound) - { - typedef FK Interval_kernel; - typename Interval_kernel::FT::Protector p; - Cartesian_converter to_interval; - typedef CGAL_SS_i::Trisegment_2 Target_trisegment_2 ; - typedef typename Target_trisegment_2::Self_ptr Target_trisegment_2_ptr; - Target_trisegment_2_ptr tri = new Target_trisegment_2(to_interval(lEvent->trisegment()->e0()) - ,to_interval(lEvent->trisegment()->e1()) - ,to_interval(lEvent->trisegment()->e2()) - ,lEvent->trisegment()->collinearity() - ,lEvent->trisegment()->id - ); - try - { - boost::optional > opt_time = CGAL_SS_i::compute_offset_lines_isec_timeC2(tri, const_cast&>(mApproximate_traits.time_cache)); - - if (opt_time && opt_time->to_nt().inf() > *bound) - { - reset_trisegment(tri->id); // avoid filling the cache vectors with times of trisegments that will be removed - return true; - } - } - catch(Uncertain_conversion_exception&) - {} - } - return false; - } - - template - boost::optional - upper_bound_for_valid_split_events(Vertex_handle lPrev, Vertex_handle aNode, Vertex_handle lNext, - Halfedge_handle_vector_iterator contour_halfedges_begin, - Halfedge_handle_vector_iterator contour_halfedges_end) - { - boost::optional bound; - if ( aNode->is_contour()) - { - - typedef FK Interval_kernel; - Cartesian_converter to_interval; - typedef typename Interval_kernel::Line_2 Line_2; - typedef typename Interval_kernel::Ray_2 Ray_2; - typedef typename Interval_kernel::Vector_2 Vector_2; - typedef typename Interval_kernel::Segment_2 Segment_2; - - typename Interval_kernel::FT::Protector protector; - - Segment_2 s1(to_interval(lPrev->point()), to_interval(aNode->point())); - Segment_2 s2(to_interval(aNode->point()), to_interval(lNext->point())); - - boost::optional< Line_2 > l1 = CGAL_SS_i::compute_normalized_line_ceoffC2(s1); - boost::optional< Line_2 > l2 = CGAL_SS_i::compute_normalized_line_ceoffC2(s2); - - Vector_2 v1(l1->a(), l1->b()); - Vector_2 v2(l2->a(), l2->b()); - Ray_2 bisect_ray(to_interval(aNode->point()), v1+v2); - - // Cartesian_converter to_input; - // std::cout << "bisect " << aNode->point() << " " << aNode->point() + to_input(v1+v2) << "\n"; - - for ( Halfedge_handle_vector_iterator i = contour_halfedges_begin; i != contour_halfedges_end; ++ i ) - { - try{ - CGAL_assertion((*i)->vertex()->is_contour() && (*i)->opposite()->vertex()->is_contour() ); - Segment_2 s_h(to_interval((*i)->opposite()->vertex()->point()), - to_interval((*i)->vertex()->point())); - - Uncertain inter = do_intersect(s_h, bisect_ray); - Uncertain orient = orientation(s_h[0], s_h[1], to_interval(aNode->point())); - - // we use segments of the input polygon intersected by the bisector and such that - // they are oriented such that the reflex vertex is on the left side of the segment - if (!is_certain(inter) || !is_certain(orient) || !inter || orient!=LEFT_TURN) continue; - - boost::optional< Line_2 > lh = CGAL_SS_i::compute_normalized_line_ceoffC2(s_h); // Note that we don't need the normalization - - typename Interval_kernel::FT h_bound = - ( - lh->c() -lh->a() * to_interval(aNode->point().x()) - lh->b() * to_interval(aNode->point().y()) ) / - (lh->a() * ( (v1+v2).x() ) + lh->b() * ( (v1+v2).y() )); - - if (!bound || *bound>h_bound.sup()) - bound=h_bound.sup(); - } - catch(CGAL::Uncertain_conversion_exception&) - {} - } - } - // if (bound) std::cout << "bound is " << *bound << "\n"; - return bound; - } - - // TODO: as soon as an exact value we could refine the interval one. Not sure if it is worth it - Exact mExact_traits; - Straight_skeleton_builder_traits_2_impl mApproximate_traits; // only used for the time_cache variable not the functor types - - mutable std::size_t trisegment_id = 0; +// ID functions + std::size_t& trisegment_ID() const { return mApproximate_traits.mTrisegment_ID ; } void reset_trisegment(std::size_t i) const { - if (i+1==trisegment_id) + if ( i+1 == trisegment_ID() ) { - --trisegment_id; - mExact_traits.time_cache.reset(i); - mApproximate_traits.time_cache.reset(i); + --trisegment_ID() ; + mExact_traits.mTime_cache.Reset(i) ; + mApproximate_traits.mTime_cache.Reset(i) ; } } + +// functions to initialize (and harmonize) and cache speeds + void InitializeLineCoeffs ( CGAL_SS_i::Segment_2_with_ID const& aBorderS ) + { +// std::cout << "Initialize " << aBorderS.mID << std::endl; + + C2E lToExact ; + C2F lToFiltered ; + + mApproximate_traits.InitializeLineCoeffs( lToFiltered(aBorderS) ); +// if ( mApproximate_traits.mCoeff_cache.Get ( aBorderS.mID ) ) +// std::cout << "Initialized (F) to: " << *(mApproximate_traits.mCoeff_cache.Get ( aBorderS.mID ) ) << std::endl; + + mExact_traits.InitializeLineCoeffs( lToExact(aBorderS) ); +// if ( mExact_traits.mCoeff_cache.Get ( aBorderS.mID ) ) +// std::cout << "Initialized (E) to: " << *(mExact_traits.mCoeff_cache.Get ( aBorderS.mID ) ) << std::endl; + } + + // This overload copies the coefficients from the halfedge `aOtherID` and caches them for the halfedge `aID` + void InitializeLineCoeffs ( std::size_t aID, std::size_t aOtherID ) + { +// std::cout << "Initialize (copy) " << aBorderS.mID << " using " << aOtherID << std::endl; + mApproximate_traits.InitializeLineCoeffs(aID, aOtherID) ; + mExact_traits.InitializeLineCoeffs(aID, aOtherID) ; + } + +// functions and tag for filtering split events + struct Filters_split_events_tag{}; + + // The kernel is filtered, and only the filtered part is used in the check (to avoid computing + // exact stuff and losing time in a check that is there to gain speed) + template + bool CanSafelyIgnoreSplitEvent(const EventPtr& lEvent) + { + // filter event + if ( ! mApproximate_traits.mFilteringBound ) + return false; + + typename FK::FT::Protector p; + + typedef CGAL_SS_i::Trisegment_2 Source_trisegment_2 ; + typedef typename Source_trisegment_2::Self_ptr Source_trisegment_2_ptr; + typedef CGAL_SS_i::Trisegment_2 Target_trisegment_2 ; + typedef typename Target_trisegment_2::Self_ptr Target_trisegment_2_ptr; + + C2F to_FK ; + Source_trisegment_2_ptr src_tri = lEvent->trisegment() ; + CGAL_assertion( src_tri != nullptr ) ; + Target_trisegment_2_ptr tri = to_FK.cvt_single_trisegment(src_tri) ; + + CGAL_postcondition( src_tri->collinearity() == tri->collinearity() ) ; + CGAL_postcondition( src_tri->id() == tri->id() ) ; + + try + { + boost::optional > lOptTime = + CGAL_SS_i::compute_offset_lines_isec_timeC2( + tri, mApproximate_traits.mTime_cache, mApproximate_traits.mCoeff_cache); + + if ( lOptTime && lOptTime->to_nt() > *mApproximate_traits.mFilteringBound ) + { + // avoid filling the cache vectors with times of trisegments that will be removed + reset_trisegment(tri->id()); + return true; + } + } + catch(Uncertain_conversion_exception&) + {} + + return false; + } + + // @todo there shouldn't be any combinatorial structures such as vertices in the traits + template + void ComputeFilteringBound(Vertex_handle lPrev, Vertex_handle aNode, Vertex_handle lNext, + Halfedge_handle_vector_iterator contour_halfedges_begin, + Halfedge_handle_vector_iterator contour_halfedges_end) + { + mApproximate_traits.mFilteringBound = boost::none; + + if ( ! aNode->is_contour() ) + return ; + + typedef typename FK::Point_2 Target_Point_2; + typedef typename FK::Vector_2 Target_Vector_2; + typedef typename FK::Segment_2 Target_Segment_2; + typedef typename FK::Ray_2 Target_Ray_2; + typedef typename FK::Line_2 Target_Line_2; + + typename FK::FT::Protector protector; + + C2F to_FK; + + Target_Point_2 laP = to_FK(aNode->point()); + Target_Segment_2 s1(to_FK(lPrev->point()), laP); + Target_Segment_2 s2(laP, to_FK(lNext->point())); + + // @todo? These are not input segments, but it might still worth caching (just gotta assign them an ID) + boost::optional< Target_Line_2 > l1 = CGAL_SS_i::compute_normalized_line_ceoffC2(s1); + boost::optional< Target_Line_2 > l2 = CGAL_SS_i::compute_normalized_line_ceoffC2(s2); + + Target_Vector_2 lV1(l1->a(), l1->b()) ; + Target_Vector_2 lV2(l2->a(), l2->b()) ; + Target_Vector_2 lV12 = lV1 + lV2 ; + Target_Ray_2 bisect_ray(laP, lV12) ; + + // F2C to_input; + // std::cout << "bisect " << aNode->point() << " " << aNode->point() + to_input(lV12) << "\n"; + + for ( Halfedge_handle_vector_iterator i = contour_halfedges_begin; i != contour_halfedges_end; ++ i ) + { + try + { + CGAL_assertion((*i)->vertex()->is_contour() && (*i)->opposite()->vertex()->is_contour() ); + Target_Segment_2 s_h(to_FK((*i)->opposite()->vertex()->point()), to_FK((*i)->vertex()->point())); + + Uncertain inter = FK().do_intersect_2_object()(s_h, bisect_ray); + Uncertain orient = FK().orientation_2_object()(s_h[0], s_h[1], laP); + + // we use segments of the input polygon intersected by the bisector and such that + // they are oriented such that the reflex vertex is on the left side of the segment + if (!is_certain(inter) || !is_certain(orient) || !inter || orient != LEFT_TURN) + continue; + + // Note that we don't need the normalization + boost::optional< Target_Line_2 > lh = CGAL_SS_i::compute_normalized_line_ceoffC2(s_h); + + typename FK::FT lBound = ( - lh->c() - lh->a()*laP.x() - lh->b()*laP.y() ) / + ( lh->a()*lV12.x() + lh->b()*lV12.y() ); + + if ( ! is_finite(lBound) ) + continue; + + if ( ! mApproximate_traits.mFilteringBound || *mApproximate_traits.mFilteringBound > lBound ) + mApproximate_traits.mFilteringBound = lBound ; + } + catch(CGAL::Uncertain_conversion_exception&) + {} + } + } + +public: + // TODO: as soon as an exact value we could refine the interval one. Not sure if it is worth it + Exact mExact_traits ; + + // Below is only used for the cached variables not the functor types + Straight_skeleton_builder_traits_2_impl mApproximate_traits ; } ; template diff --git a/Straight_skeleton_2/include/CGAL/Straight_skeleton_halfedge_base_2.h b/Straight_skeleton_2/include/CGAL/Straight_skeleton_halfedge_base_2.h index 932d21ec633..01a3256be7a 100644 --- a/Straight_skeleton_2/include/CGAL/Straight_skeleton_halfedge_base_2.h +++ b/Straight_skeleton_2/include/CGAL/Straight_skeleton_halfedge_base_2.h @@ -19,7 +19,7 @@ namespace CGAL { -template < class Refs, class S > +template < class Refs > class Straight_skeleton_halfedge_base_base_2 { public: @@ -38,9 +38,7 @@ public: typedef typename Refs::Vertex Vertex; typedef typename Refs::Face Face; - typedef Straight_skeleton_halfedge_base_base_2 Base_base ; - - typedef S Segment_2; + typedef Straight_skeleton_halfedge_base_base_2 Base_base ; protected: @@ -107,8 +105,8 @@ private: Sign mSlope ; }; -template < class Refs, class S > -class Straight_skeleton_halfedge_base_2 : public Straight_skeleton_halfedge_base_base_2 +template < class Refs > +class Straight_skeleton_halfedge_base_2 : public Straight_skeleton_halfedge_base_base_2 { public: @@ -116,8 +114,8 @@ public: typedef typename Refs::Halfedge_handle Halfedge_handle; typedef typename Refs::Face_handle Face_handle; - typedef Straight_skeleton_halfedge_base_base_2 Base_base ; - typedef Straight_skeleton_halfedge_base_2 Base ; + typedef Straight_skeleton_halfedge_base_base_2 Base_base ; + typedef Straight_skeleton_halfedge_base_2 Base ; Straight_skeleton_halfedge_base_2() {} diff --git a/Straight_skeleton_2/include/CGAL/Straight_skeleton_items_2.h b/Straight_skeleton_2/include/CGAL/Straight_skeleton_items_2.h index 29c67518869..c63d16e4212 100644 --- a/Straight_skeleton_2/include/CGAL/Straight_skeleton_items_2.h +++ b/Straight_skeleton_2/include/CGAL/Straight_skeleton_items_2.h @@ -35,8 +35,7 @@ public: template struct Halfedge_wrapper { - typedef typename Traits::Segment_2 Segment ; - typedef Straight_skeleton_halfedge_base_2 < Refs, Segment > Halfedge; + typedef Straight_skeleton_halfedge_base_2 < Refs > Halfedge; }; template diff --git a/Straight_skeleton_2/include/CGAL/constructions/Polygon_offset_cons_ftC2.h b/Straight_skeleton_2/include/CGAL/constructions/Polygon_offset_cons_ftC2.h index c97cc6d7c1d..43fcaa4ce66 100644 --- a/Straight_skeleton_2/include/CGAL/constructions/Polygon_offset_cons_ftC2.h +++ b/Straight_skeleton_2/include/CGAL/constructions/Polygon_offset_cons_ftC2.h @@ -31,11 +31,12 @@ namespace CGAL_SS_i { // // POSTCONDITION: In case of overflow an empty optional is returned. // -template -boost::optional< Point_2 > construct_offset_pointC2 ( typename K::FT const& t - , Segment_2 const& e0 - , Segment_2 const& e1 - , boost::intrusive_ptr< Trisegment_2 > const& tri) +template +boost::optional< Point_2 > construct_offset_pointC2 ( typename K::FT const& t + , Segment_2_with_ID const& e0 + , Segment_2_with_ID const& e1 + , boost::intrusive_ptr< Trisegment_2 > const& tri + , CoeffCache& aCoeff_cache) { typedef typename K::FT FT ; @@ -49,8 +50,8 @@ boost::optional< Point_2 > construct_offset_pointC2 ( typename K::FT const& CGAL_STSKEL_TRAITS_TRACE("Constructing offset point for t=" << t << " e0=" << s2str(e0) << " e1=" << s2str(e1) << " tri=" << tri ) ; - Optional_line_2 l0 = compute_normalized_line_ceoffC2(e0) ; - Optional_line_2 l1 = compute_normalized_line_ceoffC2(e1) ; + Optional_line_2 l0 = compute_normalized_line_ceoffC2(e0, aCoeff_cache) ; + Optional_line_2 l1 = compute_normalized_line_ceoffC2(e1, aCoeff_cache) ; bool ok = false ; diff --git a/Straight_skeleton_2/include/CGAL/constructions/Straight_skeleton_cons_ftC2.h b/Straight_skeleton_2/include/CGAL/constructions/Straight_skeleton_cons_ftC2.h index 0ecd2e5b40e..e50065741c2 100644 --- a/Straight_skeleton_2/include/CGAL/constructions/Straight_skeleton_cons_ftC2.h +++ b/Straight_skeleton_2/include/CGAL/constructions/Straight_skeleton_cons_ftC2.h @@ -28,7 +28,7 @@ namespace CGAL { namespace CGAL_SS_i { template -bool are_edges_collinear( Segment_2 const& e0, Segment_2 const& e1 ) +bool are_edges_collinear( Segment_2_with_ID const& e0, Segment_2_with_ID const& e1 ) { return ((e1.source() == e0.source()) || (e1.source() == e0.target()) || collinear(e0.source(),e0.target(),e1.source())) && ( (e1.target() == e0.source()) || (e1.target() == e0.target()) || (collinear(e0.source(),e0.target(),e1.target()))) ; @@ -36,23 +36,23 @@ bool are_edges_collinear( Segment_2 const& e0, Segment_2 const& e1 ) template inline -bool are_parallel_edges_equally_oriented( Segment_2 const& e0, Segment_2 const& e1 ) +bool are_parallel_edges_equally_oriented( Segment_2_with_ID const& e0, Segment_2_with_ID const& e1 ) { return angle(e0.source(), e0.target(), e1.source(), e1.target()) == ACUTE; } template -bool are_edges_orderly_collinear( Segment_2 const& e0, Segment_2 const& e1 ) +bool are_edges_orderly_collinear( Segment_2_with_ID const& e0, Segment_2_with_ID const& e1 ) { return are_edges_collinear(e0,e1) & are_parallel_edges_equally_oriented(e0,e1); } template -Trisegment_collinearity trisegment_collinearity_no_exact_constructions ( Segment_2 const& e0 - , Segment_2 const& e1 - , Segment_2 const& e2) +Trisegment_collinearity trisegment_collinearity_no_exact_constructions ( Segment_2_with_ID const& e0 + , Segment_2_with_ID const& e1 + , Segment_2_with_ID const& e2) { bool is_01 = are_edges_orderly_collinear(e0,e1); bool is_02 = are_edges_orderly_collinear(e0,e2); @@ -117,7 +117,7 @@ boost::optional< Line_2 > compute_normalized_line_ceoffC2( Segment_2 const typedef typename K::FT FT ; - FT a (0.0),b (0.0) ,c(0.0) ; + FT a(0), b(0), c(0) ; if(e.source().y() == e.target().y()) { @@ -199,6 +199,22 @@ boost::optional< Line_2 > compute_normalized_line_ceoffC2( Segment_2 const return cgal_make_optional( finite, K().construct_line_2_object()(a,b,c) ) ; } +template +boost::optional< Line_2 > compute_normalized_line_ceoffC2( Segment_2_with_ID const& e, + CoeffCache& aCoeff_cache ) +{ + CGAL_precondition ( e.mID != std::size_t(-1) ) ; + + if ( aCoeff_cache.IsCached(e.mID) ) + return aCoeff_cache.Get(e.mID) ; + + boost::optional< Line_2 > rRes = compute_normalized_line_ceoffC2 ( static_cast const&>(e) ) ; + + aCoeff_cache.Set(e.mID, rRes) ; + + return rRes ; +} + template Rational squared_distance_from_point_to_lineC2( FT const& px, FT const& py, FT const& sx, FT const& sy, FT const& tx, FT const& ty ) { @@ -219,11 +235,11 @@ Rational squared_distance_from_point_to_lineC2( FT const& px, FT const& py, // NOTE: If the collinearity cannot be determined reliably, a null trisegment is returned. // template -boost::intrusive_ptr< Trisegment_2 > construct_trisegment ( Segment_2 const& e0 - , Segment_2 const& e1 - , Segment_2 const& e2 - , std::size_t id - ) +boost::intrusive_ptr< Trisegment_2 > construct_trisegment ( Segment_2_with_ID const& e0 + , Segment_2_with_ID const& e1 + , Segment_2_with_ID const& e2 + , std::size_t id + ) { typedef Trisegment_2 Trisegment_2 ; typedef typename Trisegment_2::Self_ptr Trisegment_2_ptr ; @@ -250,8 +266,10 @@ boost::intrusive_ptr< Trisegment_2 > construct_trisegment ( Segment_2 cons // // NOTE: The segments (e0,e1,e2) are stored in the argument as the trisegment st.event() // -template -boost::optional< Rational< typename K::FT> > compute_normal_offset_lines_isec_timeC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri ) +template +boost::optional< Rational< typename K::FT> > +compute_normal_offset_lines_isec_timeC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + CoeffCache& aCoeff_cache ) { typedef typename K::FT FT ; @@ -280,9 +298,9 @@ boost::optional< Rational< typename K::FT> > compute_normal_offset_lines_isec_ti bool ok = false ; - Optional_line_2 l0 = compute_normalized_line_ceoffC2(tri->e0()) ; - Optional_line_2 l1 = compute_normalized_line_ceoffC2(tri->e1()) ; - Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->e2()) ; + Optional_line_2 l0 = compute_normalized_line_ceoffC2(tri->e0(), aCoeff_cache) ; + Optional_line_2 l1 = compute_normalized_line_ceoffC2(tri->e1(), aCoeff_cache) ; + Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->e2(), aCoeff_cache) ; if ( l0 && l1 && l2 ) { @@ -315,7 +333,8 @@ boost::optional< Rational< typename K::FT> > compute_normal_offset_lines_isec_ti // POSTCONDITION: In case of overflow an empty optional is returned. // template -boost::optional< Point_2 > compute_oriented_midpoint ( Segment_2 const& e0, Segment_2 const& e1 ) +boost::optional< Point_2 > compute_oriented_midpoint ( Segment_2_with_ID const& e0, + Segment_2_with_ID const& e1 ) { bool ok = false ; @@ -372,8 +391,10 @@ boost::optional< Point_2 > compute_oriented_midpoint ( Segment_2 const& e0 // so the degenerate seed is neither the left or the right seed. In this case, the SEED ID for the degenerate pseudo seed is UNKOWN. // If you request the point of such degenerate pseudo seed the oriented midpoint bettwen e0 and e2 is returned. // -template -boost::optional< Point_2 > compute_seed_pointC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, typename Trisegment_2::SEED_ID sid ) +template +boost::optional< Point_2 > compute_seed_pointC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + typename Trisegment_2::SEED_ID sid, + CoeffCache& aCoeff_cache) { boost::optional< Point_2 > p ; @@ -383,13 +404,13 @@ boost::optional< Point_2 > compute_seed_pointC2 ( boost::intrusive_ptr< Trise { case Trisegment_2::LEFT : - p = tri->child_l() ? construct_offset_lines_isecC2(tri->child_l()) // this can recurse + p = tri->child_l() ? construct_offset_lines_isecC2(tri->child_l(), aCoeff_cache) // this can recurse : compute_oriented_midpoint(tri->e0(),tri->e1()) ; break ; case Trisegment_2::RIGHT : - p = tri->child_r() ? construct_offset_lines_isecC2(tri->child_r()) // this can recurse + p = tri->child_r() ? construct_offset_lines_isecC2(tri->child_r(), aCoeff_cache) // this can recurse : compute_oriented_midpoint(tri->e1(),tri->e2()) ; break ; @@ -407,10 +428,11 @@ boost::optional< Point_2 > compute_seed_pointC2 ( boost::intrusive_ptr< Trise // Given the trisegment tree for an event which is known to have a normal collinearity returns the seed point // of the degenerate seed. // A normal collinearity occurs when e0,e1 or e1,e2 are collinear. -template -boost::optional< Point_2 > compute_degenerate_seed_pointC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri ) +template +boost::optional< Point_2 > compute_degenerate_seed_pointC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + CoeffCache& aCoeff_cache ) { - return compute_seed_pointC2( tri, tri->degenerate_seed_id() ) ; + return compute_seed_pointC2( tri, tri->degenerate_seed_id(), aCoeff_cache ) ; } // Given 3 oriented straight line segments: e0, e1, e2 @@ -423,8 +445,10 @@ boost::optional< Point_2 > compute_degenerate_seed_pointC2 ( boost::intrusive // // POSTCONDITION: In case of overflow an empty optional is returned. // -template -boost::optional< Rational< typename K::FT> > compute_degenerate_offset_lines_isec_timeC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri ) +template +boost::optional< Rational< typename K::FT> > +compute_degenerate_offset_lines_isec_timeC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + CoeffCache& aCoeff_cache ) { typedef typename K::FT FT ; @@ -473,12 +497,13 @@ boost::optional< Rational< typename K::FT> > compute_degenerate_offset_lines_ise // bool ok = false ; - Optional_line_2 l0 = compute_normalized_line_ceoffC2(tri->collinear_edge ()) ; - Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->non_collinear_edge()) ; + const Segment_2_with_ID& ce = tri->collinear_edge(); + Optional_line_2 l0 = compute_normalized_line_ceoffC2(ce, aCoeff_cache) ; + Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->non_collinear_edge(), aCoeff_cache) ; - Optional_point_2 q = compute_degenerate_seed_pointC2(tri); + Optional_point_2 q = compute_degenerate_seed_pointC2(tri, aCoeff_cache); - FT num(0.0), den(0.0) ; + FT num(0), den(0) ; if ( l0 && l2 && q ) { @@ -512,17 +537,23 @@ boost::optional< Rational< typename K::FT> > compute_degenerate_offset_lines_ise // // Calls the appropiate function depending on the collinearity of the edges. // -template -boost::optional< Rational< typename K::FT > > compute_offset_lines_isec_timeC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, TimeCache& cache) +template +boost::optional< Rational< typename K::FT > > compute_offset_lines_isec_timeC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + TimeCache& aTime_cache, + CoeffCache& aCoeff_cache) { - if (cache.is_time_cached(tri->id)) - return cache.time(tri->id); + if ( aTime_cache.IsCached(tri->id()) ) + return aTime_cache.Get(tri->id()) ; + CGAL_precondition ( tri->collinearity() != TRISEGMENT_COLLINEARITY_ALL ) ; - boost::optional< Rational< typename K::FT > > res = tri->collinearity() == TRISEGMENT_COLLINEARITY_NONE ? compute_normal_offset_lines_isec_timeC2 (tri) - : compute_degenerate_offset_lines_isec_timeC2(tri); - cache.set_time(tri->id, res); - return res; + boost::optional< Rational< typename K::FT > > rRes = + tri->collinearity() == TRISEGMENT_COLLINEARITY_NONE ? compute_normal_offset_lines_isec_timeC2 (tri, aCoeff_cache) + : compute_degenerate_offset_lines_isec_timeC2(tri, aCoeff_cache); + + aTime_cache.Set(tri->id(), rRes) ; + + return rRes ; } // Given 3 oriented line segments e0, e1 and e2 @@ -536,8 +567,9 @@ boost::optional< Rational< typename K::FT > > compute_offset_lines_isec_timeC2 ( // // POSTCONDITION: In case of overflow an empty optional is returned. // -template -boost::optional< Point_2 > construct_normal_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri ) +template +boost::optional< Point_2 > construct_normal_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + CoeffCache& aCoeff_cache) { typedef typename K::FT FT ; @@ -547,11 +579,11 @@ boost::optional< Point_2 > construct_normal_offset_lines_isecC2 ( boost::intr CGAL_STSKEL_TRAITS_TRACE("Computing normal offset lines isec point for: " << tri ) ; - FT x(0.0),y(0.0) ; + FT x(0), y(0) ; - Optional_line_2 l0 = compute_normalized_line_ceoffC2(tri->e0()) ; - Optional_line_2 l1 = compute_normalized_line_ceoffC2(tri->e1()) ; - Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->e2()) ; + Optional_line_2 l0 = compute_normalized_line_ceoffC2(tri->e0(), aCoeff_cache) ; + Optional_line_2 l1 = compute_normalized_line_ceoffC2(tri->e1(), aCoeff_cache) ; + Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->e2(), aCoeff_cache) ; bool ok = false ; @@ -595,8 +627,9 @@ boost::optional< Point_2 > construct_normal_offset_lines_isecC2 ( boost::intr // // POSTCONDITION: In case of overflow an empty optional is returned. // -template -boost::optional< Point_2 > construct_degenerate_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri ) +template +boost::optional< Point_2 > construct_degenerate_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + CoeffCache& aCoeff_cache) { typedef typename K::FT FT ; @@ -610,10 +643,10 @@ boost::optional< Point_2 > construct_degenerate_offset_lines_isecC2 ( boost:: FT x(0.0),y(0.0) ; - Optional_line_2 l0 = compute_normalized_line_ceoffC2(tri->collinear_edge ()) ; - Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->non_collinear_edge()) ; + Optional_line_2 l0 = compute_normalized_line_ceoffC2(tri->collinear_edge (), aCoeff_cache) ; + Optional_line_2 l2 = compute_normalized_line_ceoffC2(tri->non_collinear_edge(), aCoeff_cache) ; - Optional_point_2 q = compute_degenerate_seed_pointC2(tri); + Optional_point_2 q = compute_degenerate_seed_pointC2(tri, aCoeff_cache); bool ok = false ; @@ -655,13 +688,14 @@ boost::optional< Point_2 > construct_degenerate_offset_lines_isecC2 ( boost:: // // Calls the appropiate function depending on the collinearity of the edges. // -template -boost::optional< Point_2 > construct_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri ) +template +boost::optional< Point_2 > construct_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + CoeffCache& aCoeff_cache) { CGAL_precondition ( tri->collinearity() != TRISEGMENT_COLLINEARITY_ALL ) ; - return tri->collinearity() == TRISEGMENT_COLLINEARITY_NONE ? construct_normal_offset_lines_isecC2 (tri) - : construct_degenerate_offset_lines_isecC2(tri) ; + return tri->collinearity() == TRISEGMENT_COLLINEARITY_NONE ? construct_normal_offset_lines_isecC2 (tri, aCoeff_cache) + : construct_degenerate_offset_lines_isecC2(tri, aCoeff_cache) ; } } // namespace CGAL_SS_i diff --git a/Straight_skeleton_2/include/CGAL/predicates/Polygon_offset_pred_ftC2.h b/Straight_skeleton_2/include/CGAL/predicates/Polygon_offset_pred_ftC2.h index 09d9674f566..65eda795b09 100644 --- a/Straight_skeleton_2/include/CGAL/predicates/Polygon_offset_pred_ftC2.h +++ b/Straight_skeleton_2/include/CGAL/predicates/Polygon_offset_pred_ftC2.h @@ -24,29 +24,6 @@ namespace CGAL { namespace CGAL_SS_i { -template -struct No_cache -{ - typedef boost::optional< Rational< typename K::FT > > Event_time; - - bool is_time_cached(std::size_t) - { - return false; - } - - Event_time time(std::size_t) - { - CGAL_error(); - return Event_time(); - } - - void set_time(std::size_t, const Event_time&) - {} - - void reset(std::size_t) - {} -}; - // Given a triple of oriented straight line segments: (e0,e1,e2) such that // there exists a distance 'et' for which the offsets lines at 'et' (e0',e1',e2') intersect in a single point; // returns the relative order of 't' w.r.t 'et'. @@ -64,7 +41,9 @@ Uncertain compare_offset_against_isec_timeC2 ( typename K::FT Uncertain rResult = Uncertain::indeterminate(); - No_cache time_cache; + typedef boost::optional< CGAL_SS_i::Rational< typename K::FT > > Event_time; + No_cache time_cache; + Optional_rational et_ = compute_offset_lines_isec_timeC2(tri, time_cache); if ( et_ ) { diff --git a/Straight_skeleton_2/include/CGAL/predicates/Straight_skeleton_pred_ftC2.h b/Straight_skeleton_2/include/CGAL/predicates/Straight_skeleton_pred_ftC2.h index c1e86f0ae01..b2e4c36fa33 100644 --- a/Straight_skeleton_2/include/CGAL/predicates/Straight_skeleton_pred_ftC2.h +++ b/Straight_skeleton_2/include/CGAL/predicates/Straight_skeleton_pred_ftC2.h @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -70,7 +69,7 @@ Uncertain certified_collinear_are_ordered_along_lineC2( Point_2 const& // Returns true IFF segments e0,e1 share the same supporting line template -Uncertain are_edges_collinearC2( Segment_2 const& e0, Segment_2 const& e1 ) +Uncertain are_edges_collinearC2( Segment_2_with_ID const& e0, Segment_2_with_ID const& e1 ) { return certified_collinearC2(e0.source(),e0.target(),e1.source()) & certified_collinearC2(e0.source(),e0.target(),e1.target()) ; @@ -79,7 +78,7 @@ Uncertain are_edges_collinearC2( Segment_2 const& e0, Segment_2 cons // Returns true IFF the supporting lines for segments e0,e1 are parallel (or the same) template inline -Uncertain are_edges_parallelC2( Segment_2 const& e0, Segment_2 const& e1 ) +Uncertain are_edges_parallelC2( Segment_2_with_ID const& e0, Segment_2_with_ID const& e1 ) { Uncertain s = certified_sign_of_determinant2x2(e0.target().x() - e0.source().x() ,e0.target().y() - e0.source().y() @@ -96,7 +95,7 @@ Uncertain are_edges_parallelC2( Segment_2 const& e0, Segment_2 const // the three points are along the same line, in any order. template inline -Uncertain are_parallel_edges_equally_orientedC2( Segment_2 const& e0, Segment_2 const& e1 ) +Uncertain are_parallel_edges_equally_orientedC2( Segment_2_with_ID const& e0, Segment_2_with_ID const& e1 ) { return CGAL_NTS certified_sign( (e0.target() - e0.source()) * (e1.target() - e1.source()) ) == POSITIVE; } @@ -105,7 +104,7 @@ Uncertain are_parallel_edges_equally_orientedC2( Segment_2 const& e0, S // Returns true IFF segments e0,e1 share the same supporting line but do not overlap except at the vetices, and have the same orientation. // NOTE: If e1 goes back over e0 (a degenerate antenna or alley) this returns false. template -Uncertain are_edges_orderly_collinearC2( Segment_2 const& e0, Segment_2 const& e1 ) +Uncertain are_edges_orderly_collinearC2( Segment_2_with_ID const& e0, Segment_2_with_ID const& e1 ) { return are_edges_collinearC2(e0,e1) & are_parallel_edges_equally_orientedC2(e0,e1); } @@ -128,9 +127,9 @@ Uncertain certified_side_of_oriented_lineC2(const FT &a, const FT &b, cons // (encoded as a collinear count of -1) // template -Uncertain certified_trisegment_collinearity ( Segment_2 const& e0 - , Segment_2 const& e1 - , Segment_2 const& e2 +Uncertain certified_trisegment_collinearity ( Segment_2_with_ID const& e0 + , Segment_2_with_ID const& e1 + , Segment_2_with_ID const& e2 ) { Uncertain is_01 = are_edges_orderly_collinearC2(e0,e1); @@ -175,10 +174,12 @@ Uncertain certified_trisegment_collinearity ( Segment_2 // Those seeds are used to determine the actual position of the degenerate vertex in case of collinear edges (since that point is // not given by the collinear edges alone) // -template +template Uncertain exist_offset_lines_isec2 ( boost::intrusive_ptr< Trisegment_2 > const& tri , boost::optional const& aMaxTime - , TimeCache& time_cache ) + , TimeCache& aTime_cache + , CoeffCache& aCoeff_cache + ) { typedef Rational Rational ; @@ -191,7 +192,7 @@ Uncertain exist_offset_lines_isec2 ( boost::intrusive_ptr< Trisegment_2 { CGAL_STSKEL_TRAITS_TRACE( ( tri->collinearity() == TRISEGMENT_COLLINEARITY_NONE ? " normal edges" : " collinear edges" ) ) ; - Optional_rational t = compute_offset_lines_isec_timeC2(tri, time_cache) ; + Optional_rational t = compute_offset_lines_isec_timeC2(tri, aTime_cache, aCoeff_cache) ; if ( t ) { Uncertain d_is_zero = CGAL_NTS certified_is_zero(t->d()) ; @@ -236,10 +237,11 @@ Uncertain exist_offset_lines_isec2 ( boost::intrusive_ptr< Trisegment_2 // (m0',m1',m2') and (n0',n1',n2') intersect each in a single point; returns the relative order of mt w.r.t nt. // That is, indicates which offset triple intersects first (closer to the source lines) // PRECONDITION: There exist distances mt and nt for which each offset triple intersect at a single point. -template +template Uncertain compare_offset_lines_isec_timesC2 ( boost::intrusive_ptr< Trisegment_2 > const& m , boost::intrusive_ptr< Trisegment_2 > const& n - , TimeCache& time_cache + , TimeCache& aTime_cache + , CoeffCache& aCoeff_cache ) { typedef typename K::FT FT ; @@ -250,8 +252,8 @@ Uncertain compare_offset_lines_isec_timesC2 ( boost::intrusiv Uncertain rResult = Uncertain::indeterminate(); - Optional_rational mt_ = compute_offset_lines_isec_timeC2(m, time_cache); - Optional_rational nt_ = compute_offset_lines_isec_timeC2(n, time_cache); + Optional_rational mt_ = compute_offset_lines_isec_timeC2(m, aTime_cache, aCoeff_cache); + Optional_rational nt_ = compute_offset_lines_isec_timeC2(n, aTime_cache, aCoeff_cache); if ( mt_ && nt_ ) { @@ -306,7 +308,8 @@ Uncertain compare_isec_anglesC2 ( Vector_2 const& aBV1 // Returns true if the point aP is on the positive side of the line supporting the edge // template -Uncertain is_edge_facing_pointC2 ( boost::optional< Point_2 > const& aP, Segment_2 const& aEdge ) +Uncertain is_edge_facing_pointC2 ( boost::optional< Point_2 > const& aP, + Segment_2_with_ID const& aEdge ) { typedef typename K::FT FT ; @@ -323,10 +326,12 @@ Uncertain is_edge_facing_pointC2 ( boost::optional< Point_2 > const& aP // Given a triple of oriented straight line segments: (e0,e1,e2) such that their offsets // at some distance intersects in a point (x,y), returns true if (x,y) is on the positive side of the line supporting aEdge // -template -inline Uncertain is_edge_facing_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, Segment_2 const& aEdge ) +template +inline Uncertain is_edge_facing_offset_lines_isecC2 ( boost::intrusive_ptr< Trisegment_2 > const& tri, + Segment_2_with_ID const& aEdge, + CoeffCache& aCoeff_cache) { - return is_edge_facing_pointC2(construct_offset_lines_isecC2(tri),aEdge); + return is_edge_facing_pointC2(construct_offset_lines_isecC2(tri, aCoeff_cache), aEdge); } // Given an event trisegment and two oriented straight line segments e0 and e1, returns the oriented side of the event point @@ -390,13 +395,14 @@ inline Uncertain is_edge_facing_offset_lines_isecC2 ( boost::intrusive_ptr // If e0 and e1 are not consecutive in the input, v01_event is the event that defined they very first offset vertex. // If e0 and e1 are consecutive, v01_event is null. // -template +template Uncertain oriented_side_of_event_point_wrt_bisectorC2 ( boost::intrusive_ptr< Trisegment_2 > const& event - , Segment_2 const& e0 - , Segment_2 const& e1 + , Segment_2_with_ID const& e0 + , Segment_2_with_ID const& e1 , boost::intrusive_ptr< Trisegment_2 > const& v01_event // can be null , bool primary_is_0 + , CoeffCache& aCoeff_cache ) { typedef typename K::FT FT ; @@ -408,10 +414,10 @@ oriented_side_of_event_point_wrt_bisectorC2 ( boost::intrusive_ptr< Trisegment_2 try { - Point_2 p = validate(construct_offset_lines_isecC2(event)); + Point_2 p = validate(construct_offset_lines_isecC2(event, aCoeff_cache)); - Line_2 l0 = validate(compute_normalized_line_ceoffC2(e0)) ; - Line_2 l1 = validate(compute_normalized_line_ceoffC2(e1)) ; + Line_2 l0 = validate(compute_normalized_line_ceoffC2(e0, aCoeff_cache)) ; + Line_2 l1 = validate(compute_normalized_line_ceoffC2(e1, aCoeff_cache)) ; CGAL_STSKEL_TRAITS_TRACE("Getting oriented side of point " << p2str(p) << " w.r.t bisector [" @@ -430,7 +436,7 @@ oriented_side_of_event_point_wrt_bisectorC2 ( boost::intrusive_ptr< Trisegment_2 // We need to compute the actual bisector line. CGAL_assertion( v01_event || ( !v01_event && e0.target() == e1.source() ) ) ; - Point_2 v01 = v01_event ? validate( construct_offset_lines_isecC2(v01_event) ) : e1.source() ; + Point_2 v01 = v01_event ? validate( construct_offset_lines_isecC2(v01_event, aCoeff_cache) ) : e1.source() ; CGAL_STSKEL_TRAITS_TRACE("v01=" << p2str(v01) << ( v01_event ? " (from skelton node)" : "" ) ) ; @@ -515,10 +521,11 @@ oriented_side_of_event_point_wrt_bisectorC2 ( boost::intrusive_ptr< Trisegment_2 // PRECONDITIONS: // There exist single points at which the offset lines for 'l' and 'r' at 'tl', 'tr' intersect. // -template +template Uncertain are_events_simultaneousC2 ( boost::intrusive_ptr< Trisegment_2 > const& l, boost::intrusive_ptr< Trisegment_2 > const& r, - TimeCache& time_cache ) + TimeCache& aTime_cache, + CoeffCache& aCoeff_cache ) { typedef typename K::FT FT ; @@ -532,8 +539,8 @@ Uncertain are_events_simultaneousC2 ( boost::intrusive_ptr< Trisegment_2 rResult = Uncertain::indeterminate(); - Optional_rational lt_ = compute_offset_lines_isec_timeC2(l, time_cache); - Optional_rational rt_ = compute_offset_lines_isec_timeC2(r, time_cache); + Optional_rational lt_ = compute_offset_lines_isec_timeC2(l, aTime_cache, aCoeff_cache); + Optional_rational rt_ = compute_offset_lines_isec_timeC2(r, aTime_cache, aCoeff_cache); if ( lt_ && rt_ ) { @@ -548,8 +555,8 @@ Uncertain are_events_simultaneousC2 ( boost::intrusive_ptr< Trisegment_2x(),ri->x())