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 229b972e2bb..9d6350bb062 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 @@ -502,7 +502,14 @@ void Straight_skeleton_builder_2::CreateContourBisectors() Vertex_handle lPrev = GetPrevInLAV(v) ; Vertex_handle lNext = GetNextInLAV(v) ; - if ( lOrientation == RIGHT_TURN ) + Orientation lOrientation = CGAL::orientation(lPrev->point(),v->point(),lNext->point()); + if ( lOrientation == COLLINEAR ) + { + SetIsDegenerate(v); + CGAL_STSKEL_BUILDER_TRACE(1, "COLLINEAR vertex: N" << v->id() ); + CGAL_STSKEL_STATS_CODE(++sDegenerateVertexCount); + } + else if ( lOrientation == RIGHT_TURN ) { mReflexVertices.push_back(v); SetIsReflex(v); @@ -606,14 +613,14 @@ Straight_skeleton_builder_2::LookupOnSLAV ( Halfedge_handle aBorder, Even { Vertex_handle rResult ; - CGAL_STSKEL_BUILDER_TRACE ( 3, "ALT Looking up for E" << aBorder->id() << " on SLAV. P=" << aEvent->point() ) ; + CGAL_STSKEL_BUILDER_TRACE ( 3, "Looking up for E" << aBorder->id() << " on SLAV. P=" << aEvent->point() ) ; CGAL_STSKEL_DEBUG_CODE( bool lFound = false ; ) for ( typename std::list::const_iterator vi = mSLAV.begin(); vi != mSLAV.end(); ++ vi ) { Vertex_handle v = *vi; - + if ( handle_assigned(GetPrevInLAV(v)) && handle_assigned(GetNextInLAV(v)) && GetDefiningBorderA(v) == aBorder @@ -622,19 +629,23 @@ Straight_skeleton_builder_2::LookupOnSLAV ( Halfedge_handle aBorder, Even CGAL_STSKEL_DEBUG_CODE( lFound = true ; ) Vertex_handle lPrev = GetPrevInLAV(v); + Vertex_handle lNext = GetNextInLAV(v); + Halfedge_handle lPrevBorder = GetDefiningBorderA(lPrev); - Halfedge_handle lNextBorder = GetDefiningBorderB(v); + Halfedge_handle lNextBorder = GetDefiningBorderA(lNext); CGAL_assertion(handle_assigned(lPrevBorder)); CGAL_assertion(handle_assigned(lNextBorder)); - if ( IsEventInsideOffsetZone( aEvent->border_a(), aEvent->border_b(), aBorder, lPrevBorder, lNextBorder ) ) + CGAL_STSKEL_BUILDER_TRACE ( 3 + , "Subedge found in SLAV: N" << lPrev->id() << "->N" << v->id() + << " (E" << lPrevBorder->id() << "->E" << aBorder->id() << "->E" << lNextBorder->id() << ")" + ) ; + + if ( IsEventInsideOffsetZone( aEvent->border_a(), aEvent->border_b(), lPrevBorder, aBorder, lNextBorder ) ) { rResult = v ; - CGAL_STSKEL_BUILDER_TRACE ( 2 - , "ALT E" << aBorder->id() << " found in SLAV: N" << lPrev->id() << "->N" << v->id() - << " (E" << lPrevBorder->id() << "->E" << aBorder->id() << "->E" << lNextBorder->id() << ")" - ) ; + CGAL_STSKEL_BUILDER_TRACE ( 3, "That's the correct subedge. Found") ; break ; } } @@ -645,11 +656,11 @@ Straight_skeleton_builder_2::LookupOnSLAV ( Halfedge_handle aBorder, Even { if ( !lFound ) { - CGAL_STSKEL_BUILDER_TRACE(1,"ALT Split event is no longer valid. Opposite edge vanished."); + CGAL_STSKEL_BUILDER_TRACE(1,"Split event is no longer valid. Opposite edge vanished."); } else { - CGAL_STSKEL_BUILDER_TRACE(1,"ALT Split event is no longer valid. Not inside the opposite edge offset zone."); + CGAL_STSKEL_BUILDER_TRACE(1,"Split event is no longer valid. Not inside the opposite edge offset zone."); } } #endif @@ -1002,13 +1013,11 @@ void Straight_skeleton_builder_2::HandleSplitEvent( EventPtr aEvent, Vert } template -bool Straight_skeleton_builder_2::SetupPseudoSplitEventNode( Vertex_handle aNode +void Straight_skeleton_builder_2::SetupPseudoSplitEventNode( Vertex_handle aNode , Halfedge_handle aDefiningBorderA , Halfedge_handle aDefiningBorderB ) { - bool rR = false ; - Point_2 p = aDefiningBorderA->opposite()->vertex()->point() ; Point_2 q = aDefiningBorderA->opposite()->prev()->vertex()->point() ; Point_2 r = aDefiningBorderB->opposite()->prev()->vertex()->point() ; @@ -1020,16 +1029,12 @@ bool Straight_skeleton_builder_2::SetupPseudoSplitEventNode( Vertex_handl CGAL_STSKEL_BUILDER_TRACE(1, "COLLINEAR *NEW* vertex: N" << aNode->id() ); CGAL_STSKEL_STATS_CODE(++sDegenerateVertexCount); } - - if ( lOrientation == RIGHT_TURN ) + else if ( lOrientation == RIGHT_TURN ) { - rR = true ; mReflexVertices.push_back(aNode); SetIsReflex(aNode); CGAL_STSKEL_BUILDER_TRACE(1, "Reflex *NEW* vertex: N" << aNode->id() ); } - - return rR ; } template @@ -1122,9 +1127,8 @@ void Straight_skeleton_builder_2::HandlePseudoSplitEvent( EventPtr aEvent << " primary bisector: B" << lNewNode_R->primary_bisector()->id() ) ; - bool lNodeLIsNonConvex = SetupPseudoSplitEventNode(lNewNode_L,lNewNodeLDefiningBorderA,lNewNodeLDefiningBorderB) ; - if ( !lNodeLIsNonConvex ) - SetupPseudoSplitEventNode(lNewNode_R,lNewNodeRDefiningBorderA,lNewNodeRDefiningBorderB) ; + SetupPseudoSplitEventNode(lNewNode_L,lNewNodeLDefiningBorderA,lNewNodeLDefiningBorderB) ; + SetupPseudoSplitEventNode(lNewNode_R,lNewNodeRDefiningBorderA,lNewNodeRDefiningBorderB) ; UpdatePQ(lNewNode_L); UpdatePQ(lNewNode_R); @@ -1136,7 +1140,7 @@ void Straight_skeleton_builder_2::HandleSplitOrPseudoSplitEvent( EventPtr Vertex_handle lOppR = LookupOnSLAV(aEvent->border_c(),aEvent); if ( handle_assigned(lOppR) ) { - EventPtr lPseudoSplitEvent ; //= IsPseudoSplitEvent(aEvent,lOppR); + EventPtr lPseudoSplitEvent = IsPseudoSplitEvent(aEvent,lOppR); if ( lPseudoSplitEvent ) HandlePseudoSplitEvent(lPseudoSplitEvent); else HandleSplitEvent (aEvent,lOppR); @@ -1569,7 +1573,6 @@ std::cerr << "EXCEPTION THROWN (" << e.what() << ") during straight skeleton con mSSkel = SSkelPtr() ; } - if ( !!mSSkel && !CGAL::HalfedgeDS_const_decorator(*mSSkel).is_valid(false,3) ) { @@ -1581,6 +1584,7 @@ std::cerr << "Result incosistent." << std::endl ; mSSkel = SSkelPtr() ; } + #ifdef CGAL_STRAIGHT_SKELETON_STATS 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 5e9aed69375..cab94279751 100644 --- a/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_2.h +++ b/Straight_skeleton_2/include/CGAL/Straight_skeleton_builder_2.h @@ -152,6 +152,12 @@ private : if ( !handle_assigned(aB->opposite()) ) throw straight_skeleton_exception("opposite() missing!"); + if ( !handle_assigned(aA->opposite()->vertex()) ) + throw straight_skeleton_exception("opposite()->vertex() missing!"); + + if ( !handle_assigned(aB->opposite()->vertex()) ) + throw straight_skeleton_exception("opposite()->vertex() missing!"); + Point_2 o = aA->vertex()->point(); Point_2 a = aA->opposite()->vertex()->point(); Point_2 b = aB->opposite()->vertex()->point(); @@ -495,8 +501,8 @@ private : bool IsEventInsideOffsetZone( Halfedge_const_handle aReflexL , Halfedge_const_handle aReflexR - , Halfedge_const_handle aOpposite , Halfedge_const_handle aOppositePrev + , Halfedge_const_handle aOpposite , Halfedge_const_handle aOppositeNext ) const { @@ -654,7 +660,7 @@ private : void CreateContourBisectors(); void InitPhase(); - bool SetupPseudoSplitEventNode( Vertex_handle aNode + void SetupPseudoSplitEventNode( Vertex_handle aNode , Halfedge_handle aDefiningBorderA , Halfedge_handle aDefiningBorderB ); @@ -924,7 +930,7 @@ public: for ( Point_iterator curr = begin ; curr != end ; ++ curr ) { Point_iterator next = ( curr == last ? begin : CGAL::successor(curr) ) ; - if ( !CGAL::collinear(*prev,*curr,*next) ) + //if ( !CGAL::collinear(*prev,*curr,*next) ) lList1.push_back(*curr); prev = curr ; } 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 41b570d9dda..80233f1613f 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 @@ -218,8 +218,8 @@ struct Construct_ss_event_time_and_point_2 : Functor_base_2 Sorted_triedge_2 sorted = collinear_sort(triedge); - if ( !sorted.is_indeterminate() ) - { + //if ( !sorted.is_indeterminate() ) + //{ CGAL_assertion(sorted.collinear_count() < 3) ; optional< Rational > qt = compute_offset_lines_isec_timeC2(sorted); @@ -228,7 +228,7 @@ struct Construct_ss_event_time_and_point_2 : Functor_base_2 if ( qt ) t = cgal_make_optional(qt->n() / qt->d()) ; - } + //} if ( !t ) t = cgal_make_optional( FT(0) ); @@ -382,7 +382,6 @@ public: > Are_ss_edges_collinear_2 ; - /* typedef Filtered_construction< typename Unfiltering::Construct_ss_event_time_and_point_2 , typename Exact ::Construct_ss_event_time_and_point_2 , typename Filtering ::Construct_ss_event_time_and_point_2 @@ -392,9 +391,8 @@ public: , F2C > Construct_ss_event_time_and_point_2 ; // This uses internally a predicate so must be filtered - */ - typedef typename Unfiltering::Construct_ss_event_time_and_point_2 Construct_ss_event_time_and_point_2 ; + //typedef typename Unfiltering::Construct_ss_event_time_and_point_2 Construct_ss_event_time_and_point_2 ; typedef typename Unfiltering::Construct_ss_triedge_2 Construct_ss_triedge_2 ; diff --git a/Straight_skeleton_2/include/CGAL/certified_numeric_predicates.h b/Straight_skeleton_2/include/CGAL/certified_numeric_predicates.h index 0d9145d5f0f..901d78253d7 100644 --- a/Straight_skeleton_2/include/CGAL/certified_numeric_predicates.h +++ b/Straight_skeleton_2/include/CGAL/certified_numeric_predicates.h @@ -25,6 +25,16 @@ CGAL_BEGIN_NAMESPACE +inline bool certainly( bool c ) { return c ; } + +inline +bool certainly( Uncertain const& c ) +{ + if ( CGAL_NTS is_indeterminate(c) ) + return false ; + else return static_cast(c) ; +} + inline Uncertain logical_or ( Uncertain a, Uncertain b ) { return a | b ; } inline Uncertain logical_and( Uncertain a, Uncertain b ) { return a & b ; } 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 3872b0a08c9..d0bfced8b20 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 @@ -129,7 +129,7 @@ optional< Line_2 > compute_normalized_line_ceoffC2( Segment_2 const& e ) CGAL_STSKEL_TRAITS_TRACE("Line coefficients for line:\npx=" << e.source().x() << "\npy=" << e.source().y() << "\nqx=" << e.target().x() << "\nqy=" << e.target().y() - << "\na="<< a << "\nb=" << b << "\nc=" << c << "\nl:" << l + << "\na="<< a << "\nb=" << b << "\nc=" << c << "\nl2:" << l2 ) ; } 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 8b089c25796..7f2c43769be 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 @@ -28,6 +28,9 @@ CGAL_BEGIN_NAMESPACE namespace CGAL_SS_i { +// Just like the uncertified collinear() returns true IFF r lies in the line p->q +// NOTE: r might be in the ray from p or q containing q or p, that is, there is no ordering implied, just that +// the three points are along the same line, in any order. template Uncertain certified_collinearC2( Point_2 const& p , Point_2 const& q @@ -39,23 +42,62 @@ Uncertain certified_collinearC2( Point_2 const& p ); } +// Just like the uncertified collinear_are_ordered_along_lineC2() returns true IFF, given p,q,r along the same line, +// q in the closed segment [p,r]. template -Uncertain are_edges_collinearC2( Segment_2 const& e0, Segment_2 const& e1 ) +Uncertain certified_collinear_are_ordered_along_lineC2( Point_2 const& p + , Point_2 const& q + , Point_2 const& r + ) { - return CGAL_NTS logical_and( certified_collinearC2(e0.source(),e0.target(),e1.source()) - , certified_collinearC2(e0.source(),e0.target(),e1.target()) - ) ; + if ( CGAL_NTS certainly(p.x() < q.x()) ) return !(r.x() < q.x()); + if ( CGAL_NTS certainly(q.x() < p.x()) ) return !(q.x() < r.x()); + if ( CGAL_NTS certainly(p.y() < q.y()) ) return !(r.y() < q.y()); + if ( CGAL_NTS certainly(q.y() < p.y()) ) return !(q.y() < r.y()); + + if ( CGAL_NTS certainly(p.x() == q.x()) && CGAL_NTS certainly(p.y() == q.y()) ) return make_uncertain(true); + + return Uncertain::indeterminate(); } +// Returns true IFF segments e0,e1 share the same supporting line, 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_parallelC2( Segment_2 const& e0, Segment_2 const& e1 ) +Uncertain are_edges_orderly_collinearC2( Segment_2 const& e0, Segment_2 const& e1 ) +{ + return certified_collinearC2(e0.source(),e0.target(),e1.source()) + & + ( certified_collinear_are_ordered_along_lineC2(e0.source(),e0.target(),e1.source()) + | certified_collinear_are_ordered_along_lineC2(e1.source(),e0.source(),e0.target()) + ) + & certified_collinearC2(e0.source(),e0.target(),e1.target()) + & + ( certified_collinear_are_ordered_along_lineC2(e0.source(),e0.target(),e1.target()) + | certified_collinear_are_ordered_along_lineC2(e1.target(),e0.source(),e0.target()) + ); +} + +// Returns true IFF segments e0,e1 do not share the same supporting line but are parallel, or share the +// supporting line but are not orderly collinear (that is, one doesn't follow the other) +template +Uncertain are_edges_parallel_but_not_orderly_collinearC2( Segment_2 const& e0, Segment_2 const& e1 ) { Uncertain s = certified_sign_of_determinant2x2(e0.target().x() - e0.source().x() ,e0.target().y() - e0.source().y() ,e1.target().x() - e1.source().x() ,e1.target().y() - e1.source().y() ) ; - return s == Uncertain(ZERO); + + Uncertain parallel = ( s == Uncertain(ZERO) ) ; + + return parallel & !are_edges_orderly_collinearC2(e0,e1); +} + +template +inline +Uncertain certified_side_of_oriented_lineC2(const FT &a, const FT &b, const FT &c, const FT &x, const FT &y) +{ + return CGAL_NTS certified_sign(a*x+b*y+c); } // @@ -74,13 +116,13 @@ Sorted_triedge_2 collinear_sort ( Triedge_2 const& triedge ) int idx0=0, idx1=1, idx2=2 ; - Uncertain is_01 = are_edges_collinearC2(triedge.e0(),triedge.e1()); + Uncertain is_01 = are_edges_orderly_collinearC2(triedge.e0(),triedge.e1()); if ( !CGAL_NTS is_indeterminate(is_01) ) { - Uncertain is_02 = are_edges_collinearC2(triedge.e0(),triedge.e2()); + Uncertain is_02 = are_edges_orderly_collinearC2(triedge.e0(),triedge.e2()); if ( !CGAL_NTS is_indeterminate(is_02) ) { - Uncertain is_12 = are_edges_collinearC2(triedge.e1(),triedge.e2()); + Uncertain is_12 = are_edges_orderly_collinearC2(triedge.e1(),triedge.e2()); if ( !CGAL_NTS is_indeterminate(is_12) ) { if ( CGAL_NTS logical_and(is_01 , !is_02 , !is_12 ) ) @@ -120,21 +162,6 @@ Sorted_triedge_2 collinear_sort ( Triedge_2 const& triedge ) return Sorted_triedge_2(triedge.e(idx0),triedge.e(idx1),triedge.e(idx2),lCollinearCount); } -// Returns true if the offset-zone for the triedge 'zone' is degenerate. -// -// Since an offset zone [e0->e1->e2] is the region inside the polygon where the three edges remain connected as they -// move inward, if e0 and/or e2 is parallel to e1 then the zone is degenerate (collapsed to the bisecting line of the parallel edges) -// -// (that is, the parallel zone edges, since they share a vertex, have already collide and can't move any further) -// -template -Uncertain is_offset_zone_degenerate ( Triedge_2 const& zone ) -{ - Uncertain is_01 = are_edges_parallelC2(zone.e0(),zone.e1()); - Uncertain is_12 = are_edges_parallelC2(zone.e1(),zone.e2()); - - return CGAL_NTS logical_or(is_01,is_12); -} // Given 3 oriented straight line segments: e0, e1, e2 // returns true if there exist some positive offset distance 't' for which the @@ -313,31 +340,62 @@ compare_offset_lines_isec_dist_to_pointC2 ( Triedge_2 const& s return rResult ; } +// Returns true if the offset-zone for the triedge 'zone' is degenerate. +// +// Since an offset zone [e0->e1->e2] is the region inside the polygon where the three edges remain connected as they +// move inward, if e0 and/or e2 is parallel to e1 then the zone is degenerate (collapsed to the bisecting line of the parallel edges) +// +// (that is, the parallel zone edges, since they share a vertex, have already collide and can't move any further) +// +template +Uncertain is_offset_zone_degenerate ( Triedge_2 const& zone ) +{ + return are_edges_parallel_but_not_orderly_collinearC2(zone.e0(),zone.e1()) + | are_edges_parallel_but_not_orderly_collinearC2(zone.e1(),zone.e2()) ; +} // 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 inside the passed offset zone. // -// An offset zone [z0->z1->z2] is a region where the offset edges z0',z1',z2' remain connected in the absence of topological changes. +// An offset zone [z0,z1,z2], where zi is a line, is a region where the corresponding offset edges (oriented segment) +// Z0'->Z1'->Z2' remain connected in the absence of topological changes. // It is the area (bounded or not) to the left of z1, to the right of the angular bisector (z0,z1) // and to the left of the angular bisector (z1,z2). -// This area represents all the possible offset edges z1'. -// If the event involves z1' (that is, z1 is one of (e0,e1,e2)) then a neccesary condition for the event to exist is -// that the point of coallision hits z1' as bounded by the vertices shared with z0' and z2', -// and not just the line supporting z1'. -// This condition is equivalent to the condition that (x,y) be in the offset zone [z0->z1->z2] as described above -// (which refers to the input edges and not the offset edges). +// This area represents all the possible offset edges Z1'. // -// This condition is neccesary but not sufficient becasue z1' might end up connected with edges other than z0' and z2' +// This predicate tells whether a split event, at (x,y), against z1, is effectively splitting the segment Z1' +// instead of hitting the supporting offseted line z1' but outside the segment. +// +// Events are defined in term of intersecting offset _lines_, not segments, thus if the event involves z1' +// (that is, z1 is one of (e0,e1,e2)) then a neccesary condition for the event to actually exist is +// that the point of coallision hits a segment of z1' as bounded by the vertices shared with z0' and z2', +// and not just the line z1'. +// This condition is equivalent to the condition that (x,y) be in the offset zone [z0,z1,z2] as described above +// (which refers to the input _lines_ and not the offset edges). +// +// This condition is neccesary but not sufficient becasue z1' might end up connected with lines other than z0' and z2' // after some further event, so this predicate is valid only in the context of an event at a known time 't' such // that it is known that at time t, z1' is indeed connected to z0' and z2'. // -// If z0 and/or z2 are parallel to z1 then the offset zone is "degenerate" and the event is conventionally considered -// NOT inside the zone, so this predicate returns false in that case. +// During the shrinking process, edges can anihiliate one another; that is, collide not in a single point +// but along a line segment (reach a common supporting line simultaneously). +// This is possible if and only if the edges are parallel but not collinear. +// Exactly at the time when such an anhiliation event ocurrs, the two initially parallel edges become connected +// in the offset polygon and form a degenerate alley or anntenna. Right after that the degenerate edges +// collapse and dissapears from the offset polygon and the rest of the process continues normally +// (or not if this is the very last event). +// Since Z0'->Z1'->Z2' are 3 edges _known_ to be connected at the time of the event defined by (e0,e1,e2), +// it might very well happen that z0 or z2 (or both) are parallel to z1. If that happens they never share +// a vertex except when they collapse in a common line, which is only an instant in the sense that +// right after that both edges dissappears from the wavefront. +// If z0 and/or z2 are parallel but not collinear to z1 they are connected in the offset polygons only when +// they collapsed into each other and in that instant Z1' is not (cannot) split by any opposite edge, thus, +// if z0 and/or z2 are parallel but not collinear to z2 the offset zone is "degenerate" and no split event is inside. // // PRECONDITIONS: // There exist a single point at which the offset lines for e0,e1,e2 at 't' intersect. -// 'z1' must be one of (e0,e1,e2); that is, (x,y) must be exactly over the offseted 'z1' at time 't'. +// 'z1' must be one of (e0,e1,e2); that is, (x,y) must be exactly over the offseted z1' at time 't'. // template Uncertain @@ -372,70 +430,149 @@ is_offset_lines_isec_inside_offset_zoneC2 ( Triedge_2 const& event, Triedge_2 // Construct intersection point (x,y) Optional_point_2 i = construct_offset_lines_isecC2(e_sorted); - if ( zl && zc && zr && i) + if ( zl && zc && zr && i ) // all properly computed { - // Calculate scaled (signed) distance from (x,y) to 'zc' + // Let L and R be the vertices formed by zl',zc' and zc',zr' resp. + // Let ZC1 be the oriented segment of zc' bounded first by L then by R. + // Let ZC0 be the ray of zc' before L, and ZC2 the ray of zc' after R. + // Let ZL0 be tha ray of zl' before L and ZL1 the ray of zl' after R + // Let ZR0 be tha ray of zl' before L and ZR1 the ray of zl' after R + + // At the time of the event, each offseted edge zl', zc' and zr' are made of : + // + // zl': ZL0->L->ZL1 + // zc': ZC0->L->ZC1->R->ZC2 + // zr': ZR0->R->ZR1 + + // Here we need to determine if "i", known to be along zc', is inside ZC1 instead of ZC0 (and instead of ZC2) + + // Since all edges move at the same speed, there are 3 cases to consider: + + // (1) If L is convex, ZL1 is "behind" ZC1 while ZL0 is "before" ZC0. + // (2) If L is reflex, ZL1 is "before" ZC1 while ZL0 is "behind" ZC0. + // (3) If L is degenerate, that is, zl and zc are collinear, none if behind or before any other + + // If L is case (1) then "i" is inside ZC1 and not ZC0 if the signed distance to zc is smaller than to zl + // If L is case (2) then "i" is inside ZC1 and not ZC0 if the signed distance to zc is larger than to zl + // If L is case (3) then "i" is inside ZC1 if its to the right of a line perpendicular to zc passing through L* + // where L* is a pseudo-vertex between collinear edges (the oriented midpoint between them) + + // (likewise for R) + + // sdc : scaled (signed) distance from (x,y) to 'zc' FT sdc = zc->a() * i->x() + zc->b() * i->y() + zc->c() ; CGAL_STSKEL_TRAITS_TRACE("\nsdc=" << sdc ) ; // NOTE: - // if (x,y) is not on the positive side of 'ec' it isn't on it's offset zone. - // Also, if (x,y) is over 'ec' (its signed distance to ec is not certainly positive) then by definition is not on its _offset_ + // if "i" is not on the positive side of 'zc' it isn't on it's offset zone. + // Also, if "i" is _over_ 'zc' (its signed distance to ec is not certainly positive) then by definition is not on its _offset_ // zone either. Uncertain cok = CGAL_NTS is_finite(sdc) ? CGAL_NTS certified_is_positive(sdc) : Uncertain::indeterminate() ; if ( ! CGAL_NTS is_indeterminate(cok) ) { if ( cok == true ) { - CGAL_STSKEL_TRAITS_TRACE("\nright side of ec." ) ; + CGAL_STSKEL_TRAITS_TRACE("\ncorrect side of zc." ) ; - // Determine if the vertices (el,ec) and (ec,er) are reflex. - Uncertain lcx = CGAL_NTS certified_is_smaller(zl->a()*zc->b(),zc->a()*zl->b()); - Uncertain crx = CGAL_NTS certified_is_smaller(zc->a()*zr->b(),zr->a()*zc->b()); + Uncertain lc_collinear = are_edges_orderly_collinearC2(zone.e0(),zone.e1()); + Uncertain cr_collinear = are_edges_orderly_collinearC2(zone.e1(),zone.e2()); - if ( ! CGAL_NTS is_indeterminate(lcx) && ! CGAL_NTS is_indeterminate(crx) ) + if ( ! CGAL_NTS is_indeterminate(lc_collinear) && ! CGAL_NTS is_indeterminate(cr_collinear) ) { - CGAL_STSKEL_TRAITS_TRACE("\n(el,ec) reflex:" << lcx ) ; - CGAL_STSKEL_TRAITS_TRACE("\n(ec,er) reflex:" << crx ) ; - - // Calculate scaled (signed) distances from (x,y) to 'el' and 'er' - FT sdl = zl->a() * i->x() + zl->b() * i->y() + zl->c() ; - FT sdr = zr->a() * i->x() + zr->b() * i->y() + zr->c() ; - - if ( CGAL_NTS is_finite(sdl) && CGAL_NTS is_finite(sdc) ) + Uncertain lok, rok ; + + if ( !lc_collinear ) { - CGAL_STSKEL_TRAITS_TRACE("\nsdl=" << sdl ) ; - CGAL_STSKEL_TRAITS_TRACE("\nsdr=" << sdr ) ; - - // Is (x,y) to the right|left of the bisectors (el,ec) and (ec,er)? - // It depends on whether the vertex ((el,ec) and (ec,er)) is relfex or not. - // If it is reflex, then (x,y) is to the right|left of the bisector if sdl|sdr <= sdc; otherwise, if sdc <= sdl|srd - - Uncertain lok = lcx ? CGAL_NTS certified_is_smaller_or_equal(sdl,sdc) - : CGAL_NTS certified_is_smaller_or_equal(sdc,sdl) ; - - Uncertain rok = crx ? CGAL_NTS certified_is_smaller_or_equal(sdr,sdc) - : CGAL_NTS certified_is_smaller_or_equal(sdc,sdr) ; - - CGAL_STSKEL_TRAITS_TRACE("\nlok:" << lok) ; - CGAL_STSKEL_TRAITS_TRACE("\nrok:" << rok) ; - - r = CGAL_NTS logical_and(lok , rok) ; + CGAL_STSKEL_TRAITS_TRACE("\nl:(zl,zc) is " << ( lc_reflex == SMALLER ? "reflex" : "non-reflex") ) ; + + // sld: scaled (signed) distances from "i" to 'zl' + FT sdl = zl->a() * i->x() + zl->b() * i->y() + zl->c() ; + + if ( CGAL_NTS is_finite(sdl) ) + { + CGAL_STSKEL_TRAITS_TRACE("\nsdl=" << sdl ) ; + + Uncertain lc_reflex = CGAL_NTS certified_is_smaller(zl->a()*zc->b(),zc->a()*zl->b()); + + if ( ! CGAL_NTS is_indeterminate(lc_reflex) ) + lok = ( lc_reflex ? CGAL_NTS certified_is_smaller_or_equal(sdl,sdc) + : CGAL_NTS certified_is_larger_or_equal (sdl,sdc) + ) ; + } + else + { + CGAL_STSKEL_TRAITS_TRACE("\nOverflow detected." ) ; + } } else { - CGAL_STSKEL_TRAITS_TRACE("\nOverflow detected." ) ; + CGAL_STSKEL_TRAITS_TRACE("\nl:(zl,zc) is DEGENERATE") ; + Optional_point_2 l = compute_oriented_midpoint(zone.e0(),zone.e1()); + if ( l ) + { + FT na, nb, nc ; + perpendicular_through_pointC2(zc->a(),zc->b(),l->x(),l->y(),na, nb, nc); + lok = certified_side_of_oriented_lineC2(na,nb,nc,i->x(),i->y()) != make_uncertain(POSITIVE); + } + else + { + CGAL_STSKEL_TRAITS_TRACE("\nOverflow detected." ) ; + } } + + if ( !cr_collinear ) + { + CGAL_STSKEL_TRAITS_TRACE("\nr:(zc,zr) is " << ( cr_reflex == SMALLER ? "reflex" : "non-reflex") ) ; + + // slr: scaled (signed) distances from "i" to 'zr' + FT sdr = zr->a() * i->x() + zr->b() * i->y() + zr->c() ; + + if ( CGAL_NTS is_finite(sdr) ) + { + CGAL_STSKEL_TRAITS_TRACE("\nsdr=" << sdr ) ; + + Uncertain cr_reflex = CGAL_NTS certified_is_smaller(zc->a()*zr->b(),zr->a()*zc->b()); + + if ( ! CGAL_NTS is_indeterminate(cr_reflex) ) + rok = ( cr_reflex ? CGAL_NTS certified_is_smaller_or_equal(sdr,sdc) + : CGAL_NTS certified_is_larger_or_equal (sdr,sdc) + ) ; + } + else + { + CGAL_STSKEL_TRAITS_TRACE("\nOverflow detected." ) ; + } + } + else + { + CGAL_STSKEL_TRAITS_TRACE("\nr:(zc,zr) is DEGENERATE") ; + Optional_point_2 r = compute_oriented_midpoint(zone.e1(),zone.e2()); + if ( r ) + { + FT na, nb, nc ; + perpendicular_through_pointC2(zc->a(),zc->b(),r->x(),r->y(),na, nb, nc); + rok = certified_side_of_oriented_lineC2(na,nb,nc,i->x(),i->y()) != make_uncertain(NEGATIVE); + } + else + { + CGAL_STSKEL_TRAITS_TRACE("\nOverflow detected." ) ; + } + } + + CGAL_STSKEL_TRAITS_TRACE("\nlok:" << lok) ; + CGAL_STSKEL_TRAITS_TRACE("\nrok:" << rok) ; + + r = CGAL_NTS logical_and(lok , rok) ; } else { - CGAL_STSKEL_TRAITS_TRACE("\nUnable to reliably determine side-of-line." ) ; + CGAL_STSKEL_TRAITS_TRACE("\nUnable to reliably determine reflexivity of zone vertices." ) ; } } else { - CGAL_STSKEL_TRAITS_TRACE("\nWRONG side of ec." ) ; + CGAL_STSKEL_TRAITS_TRACE("\nWRONG side of zc." ) ; r = make_uncertain(false); } } @@ -488,7 +625,7 @@ Uncertain are_events_simultaneousC2 ( Triedge_2 const& l, Triedge_2 Uncertain rResult = Uncertain::indeterminate(); - Sorted_triedge_2 l_sorted = collinear_sort(l); + Sorted_triedge_2 l_sorted = collinear_sort(l); Sorted_triedge_2 r_sorted = collinear_sort(r); if ( ! ( l_sorted.is_indeterminate() || r_sorted.is_indeterminate() ) )