// Copyright (c) 2005, 2006 Fernando Luis Cacciola Carballal. All rights reserved. // // This file is part of CGAL (www.cgal.org); you may redistribute it under // the terms of the Q Public License version 1.0. // See the file LICENSE.QPL distributed with CGAL. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // // Author(s) : Fernando Cacciola // #ifndef CGAL_STRAIGHT_SKELETON_BUILDER_2_C #define CGAL_STRAIGHT_SKELETON_BUILDER_2_C 1 #include #include #include #include CGAL_BEGIN_NAMESPACE namespace { template inline bool handle_assigned ( Handle const& aH ) { Handle null ; return aH != null ; } } template Straight_skeleton_builder_2::Straight_skeleton_builder_2 ( Traits const& aTraits ) : mTraits(aTraits) ,Equal (aTraits.get()) ,Left_turn(aTraits.get()) ,Collinear(aTraits.get()) ,mEventCompare(*this) ,mVertexID(0) ,mEdgeID(0) ,mEventID(0) ,mStepID(0) ,mPQ(mEventCompare) ,mSSkel( new SSkel() ) { } // This is defined out-of-class, here, just so it's easy to set a breakpoint template void Straight_skeleton_builder_2::throw_error ( char const* what ) const { throw straight_skeleton_exception(what); } // // Returns the 3 distinct defining contour edges of vertices aA and aB // (As long as the vertices are proceesed in the right order there is 1 common defining contour edge, // so there are 3 distinct contour edges given these 2 vertices) // template typename Straight_skeleton_builder_2::BorderTriple Straight_skeleton_builder_2::GetDefiningBorders( Vertex_handle aA, Vertex_handle aB ) { Halfedge_handle lAL = GetDefiningBorderA(aA); Halfedge_handle lAR = GetDefiningBorderB(aA) ; Halfedge_handle lBL = GetDefiningBorderA(aB); Halfedge_handle lBR = GetDefiningBorderB(aB); return BorderTriple(lAL, lAR, ( lAL == lBL || lAR == lBL ) ? lBR : lBL ) ; } // Tests whether there is an edge event between the 3 contour edges defining nodes 'aLnode' and 'aRNode'. // If such event exits and is not in the past, it's returned. Otherwise the result is null // template typename Straight_skeleton_builder_2::EventPtr Straight_skeleton_builder_2::FindEdgeEvent( Vertex_handle aLNode, Vertex_handle aRNode ) { EventPtr rResult ; Halfedge_handle lBorderA, lBorderB, lBorderC ; boost::tie(lBorderA,lBorderB,lBorderC) = GetDefiningBorders(aLNode,aRNode); if ( lBorderA != lBorderB && lBorderB != lBorderC ) { if ( ExistEvent(lBorderA,lBorderB,lBorderC) ) { bool lAccepted = true ; if ( aLNode->is_skeleton() && IsNewEventInThePast(lBorderA,lBorderB,lBorderC,aLNode) ) lAccepted = false ; if ( aRNode->is_skeleton() && IsNewEventInThePast(lBorderA,lBorderB,lBorderC,aRNode) ) lAccepted = false ; if ( lAccepted ) { rResult = EventPtr( new EdgeEvent( lBorderA, lBorderB, lBorderC, aLNode, aRNode ) ) ; #ifdef CGAL_STRAIGHT_SKELETON_ENABLE_TRACE SetEventTimeAndPoint(*rResult); #endif } } } return rResult ; } // Tests whether there is a split event between the contour edges (aReflexLBorder,aReflexRBorder,aOppositeBorder). // If such event exits and is not in the past, it's returned. Otherwise the result is null // 'aReflexLBorder' and 'aReflexRBorder' are consecutive contour edges which 'aNode' as the vertex. // 'aOppositeBorder' is some other edge in the polygon which, if the event exists, is split by the reflex wavefront. // // NOTE: 'aNode' can be a skeleton node (an interior split event produced by a previous vertex event). In that case, // the 'reflex borders' are not consecutive in the input polygon but they are in the corresponding offset polygon that // contains aNode as a vertex. // template void Straight_skeleton_builder_2::CollectSplitEvent( Vertex_handle aNode , Halfedge_handle aReflexLBorder , Halfedge_handle aReflexRBorder , Halfedge_handle aOppositeBorder ) { if ( ExistEvent(aReflexLBorder,aReflexRBorder,aOppositeBorder) ) { if ( ! ( aNode->is_skeleton() && IsNewEventInThePast(aReflexLBorder,aReflexRBorder,aOppositeBorder,aNode) ) ) { EventPtr lEvent( new SplitEvent( aReflexLBorder,aReflexRBorder,aOppositeBorder,aNode,aOppositeBorder) ) ; #ifdef CGAL_STRAIGHT_SKELETON_ENABLE_TRACE SetEventTimeAndPoint(*lEvent); #endif //mSplitEvents.push_back(lEvent); // A vertex event ocurrs when two split events collide each other. // Part of that condition is that the split contour edge is itself a reflex contour edge (there are other conditions). // This is called a "reflex split" (becasue the opposite edge is not really split in its interior but at it's reflex endpoint) // To speed up the search for vertex events, each 'aOppositeBorder' which is itself a reflex contour edge is // attached to this split event if ( IsReflex(aOppositeBorder->vertex()) ) AddReflexSplit(aNode,lEvent); EnqueEvent(lEvent); } } else { CGAL_SSBUILDER_TRACE(1,"Spit event for Seed N" << aNode->id() << " against E" << aOppositeBorder->id() << " does not exist." ) ; } } // 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 ) { // lLBorder and lRBorder are the consecutive contour edges forming the reflex wavefront. Halfedge_handle lLBorder = GetDefiningBorderA(aNode); Halfedge_handle lRBorder = GetDefiningBorderB(aNode) ; // For a strictly simple polygon, without antennas, it can be shown that the reflex wavefront cannot split // the edges adjacent to it (the prev and next of each wavefront edge), so these are excluded from the search. // (this is NOT an optimization, they must be excluded otherwise an illegal split event could be found) Vertex_handle lPrev = GetPrevInLAV(aNode) ; Vertex_handle lNext = GetNextInLAV(aNode) ; Halfedge_handle lLBorderP = lLBorder->opposite()->next()->opposite(); Halfedge_handle lLBorderN = lLBorder->opposite()->prev()->opposite(); Halfedge_handle lRBorderP = lRBorder->opposite()->next()->opposite(); Halfedge_handle lRBorderN = lRBorder->opposite()->prev()->opposite(); CGAL_SSBUILDER_TRACE(3 ,"Finding SplitEvent for N" << aNode->id() << " LBorder: E" << lLBorder->id() << " RBorder: E" << lRBorder->id() << " LBorderP: E" << lLBorderP->id() << " LBorderN: E" << lLBorderN->id() << " RBorderP: E" << lRBorderP->id() << " RBorderN: E" << lRBorderN->id() ); for ( Halfedge_handle_vector_iterator i = mContourHalfedges.begin(); i != mContourHalfedges.end(); ++ i ) { Halfedge_handle lOpposite = *i ; if ( lOpposite != lLBorder && lOpposite != lRBorder && lOpposite != lLBorderP && lOpposite != lLBorderN && lOpposite != lRBorderP && lOpposite != lRBorderN ) CollectSplitEvent(aNode, lLBorder, lRBorder, lOpposite ) ; } } // Finds and enques all the new potential events produced by the vertex wavefront emerging from 'aNode' (which can be a reflex wavefront). // This new events are simply stored in the priority queue, not processed. template void Straight_skeleton_builder_2::CollectNewEvents( Vertex_handle aNode ) { // A Straight Skeleton is the trace of the 'grassfire propagation' that corresponds to the inward move of all the vertices // of a polygon along their angular bisectors. // Since vertices are the common endpoints of contour edges, the propagation corresponds to contour edges moving inward, // shrinking and expanding as neccesasry to keep the vertices along the angular bisectors. // At each instant in time the current location of vertices (and edges) describe the current 'Offset polygon' // (with at time zero corresponds to the input polygon). // // An 'edge wavefront' is a moving contour edge. // A 'vertex wavefront' is the wavefront of two consecutive edge wavefronts (sharing a moving vertex). // // An 'Event' is the coallision of 2 wavefronts. // Each event changes the topology of the shrinking polygon; that is, at the event, the current polygon differs from the // inmediately previous polygon in the number of vertices. // // If 2 vertex wavefronts sharing a common edge collide, the event is called an edge event. At the time of the event, the current // polygon doex not have the common edge anynmore, and the two vertices become one. This new 'skeleton' vertex generates a new // vertex wavefront which can further collide with other wavefronts, producing for instance, more edge events. // // If a refex vertex wavefront collide with an edge wavefront, the event is called a split event. At the time of the event, the current // polygon is split in two unconnected polygons, each one containing a portion of the edge hit and split by the reflex wavefront. // // If 2 reflex wavefronts collide each other, the event is called a vertex event. At the time of the event, the current polygon // is split in two unconnected polygons. Each one contains a different combination of the colliding reflex edges. That is, if the // wavefront (edgea,edgeb) collides with (edgec,edged), the two resulting polygons will contain (edgea,edgec) and (edgeb,edged). // Furthermore, one of the new vertices can be a reflex vertex generating a reflex wavefront which can further produce more split or // vertex events (or edge events of course) // // Each vertex wavefront (reflex or not) results in one and only one event from a set of possible events. // It can result in a edge event against the vertex wavefronts emerging from the adjacent vertices (in the current polygon, not // in the input polygon); or it can result in a split event (or vertex event) against any other wavefront in the rest of // current polygon. // Adjacent vertices in the current polygon containing aNode (called LAV) Vertex_handle lPrev = GetPrevInLAV(aNode) ; Vertex_handle lNext = GetNextInLAV(aNode) ; CGAL_SSBUILDER_TRACE ( 2 , "Collecting new events generated by N" << aNode->id() << " at " << aNode->point() << " (Prev: N" << lPrev->id() << " Next: N" << lNext->id() << ")" ) ; if ( IsReflex(aNode) ) CollectSplitEvents(aNode) ; EventPtr lLEdgeEvent = FindEdgeEvent( lPrev , aNode ) ; EventPtr lREdgeEvent = FindEdgeEvent( aNode , lNext ) ; bool lAcceptL = !!lLEdgeEvent ; bool lAcceptR = !!lREdgeEvent ; // Altough one and only one of the potential events actually happens, the ocurrence of a particular candidate is // determined by all the previous events. That is, at this point we may find that the vertex wavefront collides, for instance, // with both adjacent vertex wavefronts, thus encountering 2 potential edge events; however, we cannot rule out one of these // potential edge events based on the other because it is, precisely, potential. // IOW, if event A happens before event B (for both A and B correspoding to the same wavefront), then for sure B won't happen; // but at this point we can't tell whether A will actually ocurr so we can't discard B just yet. // Both must be placed in the queue; if A is effectively processed, then B will naturally by ignored when it's pop off the queue. // But there is one exception to the "don't discard B yet" rule: // If A and B are coincident in time, their relative ordering in the queue is undetermined. Thus, // the 'wrong' event could be pop off and processed first. // In this case, and only this case, we rule out B (the event that can't ocurr if A does) // TODO: This may be incorrect still... the priority queue should resolve the "second level" ordering in case of time coincidence. if ( lLEdgeEvent && lREdgeEvent ) { Comparison_result lRel = CompareEvents(lLEdgeEvent,lREdgeEvent) ; if ( lRel == EQUAL ) { if ( CompareEventsDistanceToSeed(aNode,lLEdgeEvent,lREdgeEvent) == LARGER ) lAcceptL = false ; else lAcceptR = false ; CGAL_SSBUILDER_TRACE(3,"Both Left and Right Edge Events found with the same time:" << "LEvent:" << *lLEdgeEvent << '\n' << "REvent:" << *lREdgeEvent << '\n' << "Selecting the one closer to the seed: " << (lAcceptL ? "Left" : "Right") ); } } if ( lAcceptL ) EnqueEvent(lLEdgeEvent); if ( lAcceptR ) EnqueEvent(lREdgeEvent); } // Handles the special case of two simultaneous edge events, that is, two edges // collapsing along the line/point were they meet at the same time. // This ocurrs when the bisector emerging from vertex 'aA' is defined by the same pair of // contour edges as the bisector emerging from vertex 'aB' (but in opposite order). // template void Straight_skeleton_builder_2::HandleSimultaneousEdgeEvent( Vertex_handle aA, Vertex_handle aB ) { CGAL_SSBUILDER_TRACE ( 2, "Handling simultaneous EdgeEvent between N" << aA ->id() << " and N" << aB ->id() ) ; Halfedge_handle lOA = aA->primary_bisector() ; Halfedge_handle lOB = aB->primary_bisector() ; Halfedge_handle lIA = lOA->opposite(); Halfedge_handle lIB = lOB->opposite(); CGAL_SSBUILDER_TRACE ( 2 , "OA: B" << lOA->id() << '\n' << "IA: B" << lIA->id() << '\n' << "OB: B" << lOB->id() << '\n' << "IB: B" << lIB->id() ) ; SetIsProcessed(aA) ; SetIsProcessed(aB) ; mSLAV.remove(aA); mSLAV.remove(aB); CGAL_SSBUILDER_TRACE ( 3, 'N' << aA->id() << " processed\nN" << aB->id() << " processed" ) ; Halfedge_handle lOA_Prev = lOA->prev() ; Halfedge_handle lIA_Next = lIA->next() ; Halfedge_handle lOB_Prev = lOB->prev() ; Halfedge_handle lIB_Next = lIB->next() ; CGAL_SSBUILDER_TRACE ( 2 , "OA_Prev: B" << lOA_Prev->id() << '\n' << "IA_Next: B" << lIA_Next->id() << '\n' << "OB_Prev: B" << lOB_Prev->id() << '\n' << "IB_Next: B" << lIB_Next->id() ) ; lOB ->HBase_base::set_next( lIA_Next ); lIA_Next->HBase_base::set_prev( lOB ); lIB ->HBase_base::set_prev( lOA_Prev ); lOA_Prev->HBase_base::set_next( lIB ); lOB->HBase_base::set_vertex (aA); CGAL_SSBUILDER_SHOW ( DrawBisector(lOB) ; ) CGAL_SSBUILDER_TRACE ( 3, "B" << lOA->id() << " and B" << lIA->id() << " erased." ) ; mDanglingBisectors.push_back(lOA); // // The code above corrects the links for vertices aA/aB to the erased halfedges lOA and lIA. // However, any of these vertices (aA/aB) maybe one of the twin vertices of a split event. // If that's the case, the erased halfedge maybe be linked to a 'couple' of those vertices. // This situation is corrected below: if ( handle_assigned(lOA->vertex()) && lOA->vertex() != aA && lOA->vertex() != aB ) { lOA->vertex()->VBase::set_halfedge(lIB); CGAL_SSBUILDER_TRACE ( 1, "N" << lOA->vertex()->id() << " has B" << lOA->id() << " as it's halfedge. Replacing it with B" << lIB->id() ) ; } if ( handle_assigned(lIA->vertex()) && lIA->vertex() != aA && lIA->vertex() != aB ) { lIA->vertex()->VBase::set_halfedge(lOB); CGAL_SSBUILDER_TRACE ( 1, "N" << lIA->vertex()->id() << " has B" << lIA->id() << " as it's halfedge. Replacing it with B" << lOB->id() ) ; } CGAL_SSBUILDER_TRACE ( 2, "N" << aA->id() << " halfedge: B" << aA->halfedge()->id() ) ; CGAL_SSBUILDER_TRACE ( 2, "N" << aB->id() << " halfedge: B" << aB->halfedge()->id() ) ; CGAL_assertion( aA->primary_bisector() == lIB ) ; } // Returns true if the skeleton edges 'aA' and 'aB' are defined by the same pair of contour edges (but possibly in reverse order) // template bool Straight_skeleton_builder_2::AreBisectorsCoincident ( Halfedge_const_handle aA, Halfedge_const_handle aB ) const { CGAL_SSBUILDER_TRACE ( 3, "Testing for simultaneous EdgeEvents between B" << aA->id() << " and B" << aB->id() ) ; Halfedge_const_handle lA_LBorder = aA->defining_contour_edge(); Halfedge_const_handle lA_RBorder = aA->opposite()->defining_contour_edge(); Halfedge_const_handle lB_LBorder = aB->defining_contour_edge(); Halfedge_const_handle lB_RBorder = aB->opposite()->defining_contour_edge(); return ( lA_LBorder == lB_LBorder && lA_RBorder == lB_RBorder ) || ( lA_LBorder == lB_RBorder && lA_RBorder == lB_LBorder ) ; } template void Straight_skeleton_builder_2::UpdatePQ( Vertex_handle aNode ) { Vertex_handle lPrev = GetPrevInLAV(aNode) ; Vertex_handle lNext = GetNextInLAV(aNode) ; CGAL_SSBUILDER_TRACE ( 3, "Updating PQ for N" << aNode->id() << " Prev N" << lPrev->id() << " Next N" << lNext->id() ) ; Halfedge_handle lOBisector_P = lPrev->primary_bisector() ; Halfedge_handle lOBisector_C = aNode->primary_bisector() ; Halfedge_handle lOBisector_N = lNext->primary_bisector() ; if ( AreBisectorsCoincident(lOBisector_C,lOBisector_P) ) HandleSimultaneousEdgeEvent( aNode, lPrev ) ; else if ( AreBisectorsCoincident(lOBisector_C,lOBisector_N) ) HandleSimultaneousEdgeEvent( aNode, lNext ) ; else CollectNewEvents(aNode); } template void Straight_skeleton_builder_2::CreateInitialEvents() { CGAL_SSBUILDER_TRACE(0, "Creating initial events..."); for ( Vertex_iterator v = mSSkel->vertices_begin(); v != mSSkel->vertices_end(); ++ v ) UpdatePQ(v); } template typename Straight_skeleton_builder_2::EventPtr Straight_skeleton_builder_2::FindVertexEvent( EventPtr aE0, Vertex_handle aOV ) { EventPtr rResult ; EventPtr_Vector lReflexSplits = GetReflexSplits(aOV) ; for ( event_iterator i = lReflexSplits.begin(), ei = lReflexSplits.end() ; i != ei ; ++ i ) { EventPtr lE1 = *i ; CGAL_assertion(lE1->type() == Event::cSplitEvent ) ; if ( !lE1->is_excluded() && AreEventsSimultaneous(aE0,lE1) ) { CGAL_SSBUILDER_TRACE(2, "Vertex Event found with\n" << *aE0 << "\n" << *lE1 ); aE0->Exclude(); lE1->Exclude(); Halfedge_handle lBorderX[3], lBorderY[3]; lBorderX[0] = aE0->border_a(); lBorderX[1] = aE0->border_b(); lBorderX[2] = aE0->border_c(); lBorderY[0] = lE1->border_a(); lBorderY[1] = lE1->border_b(); lBorderY[2] = lE1->border_c(); Halfedge_handle lDistinct1, lDistinct2, lEqual1, lEqual2 ; boost::tie(lDistinct1, lDistinct2, lEqual1, lEqual2) = SortTwoDistinctAndTwoEqual(lBorderX,lBorderY); CGAL_SSBUILDER_TRACE ( 3 , "Distinct1 E" << lDistinct1->id() << " Distinct2 E" << lDistinct2->id() << " Equal1 E" << lEqual1->id() << " Equal2 E" << lEqual2->id() ) ; if ( ExistEvent(lDistinct1, lDistinct2, lEqual1 ) && ExistEvent(lEqual1 , lEqual2 , lDistinct1) ) { Vertex_handle lSeedX = aE0->seed0(); Vertex_handle lSeedY = lE1->seed0(); rResult = EventPtr( new VertexEvent( lDistinct1, lDistinct2, lEqual1 ,lEqual2, lSeedX, lSeedY ) ) ; rResult->SetTimeAndPoint(aE0->time(),aE0->point()); CGAL_SSBUILDER_SHOW ( SS_IO_AUX::ScopedPointDrawing lDrawA(rResult->point(),CGAL::BLUE,"Event"); ) } } } return rResult ; } template typename Straight_skeleton_builder_2::EventPtr Straight_skeleton_builder_2::FindVertexEvent( EventPtr aSplitEventPtr ) { EventPtr rResult ; SplitEvent& lSplitEvent = dynamic_cast(*aSplitEventPtr) ; Halfedge_handle lOppBorder = lSplitEvent.opposite_border() ; Vertex_handle lOV1 = lOppBorder->vertex(); if ( IsReflex(lOV1) ) rResult = FindVertexEvent(aSplitEventPtr,lOV1); if ( !rResult ) { Vertex_handle lOV2 = lOppBorder->opposite()->vertex(); if ( IsReflex(lOV2) ) rResult = FindVertexEvent(aSplitEventPtr,lOV2); } return rResult ; } template void Straight_skeleton_builder_2::CreateContourBisectors() { CGAL_SSBUILDER_TRACE(0, "Creating contour bisectors..."); for ( Vertex_iterator v = mSSkel->vertices_begin(); v != mSSkel->vertices_end(); ++ v ) { mSLAV.push_back(static_cast(v)); Vertex_handle lPrev = GetPrevInLAV(v) ; Vertex_handle lNext = GetNextInLAV(v) ; bool lCollinear = Collinear( lPrev->point(),v->point(),lNext->point() ) ; if ( lCollinear || !Left_turn( lPrev->point(),v->point(),lNext->point() ) ) { SetIsReflex(v); CGAL_SSBUILDER_TRACE(1,(lCollinear ? "COLLINEAR " : "Reflex ") << "vertex: N" << v->id() ); } Halfedge lOB(mEdgeID++), lIB(mEdgeID++); Halfedge_handle lOBisector = mSSkel->SSkel::Base::edges_push_back (lOB, lIB); Halfedge_handle lIBisector = lOBisector->opposite(); lOBisector->HBase_base::set_face(v->halfedge()->face()); lIBisector->HBase_base::set_face(v->halfedge()->next()->face()); lIBisector->HBase_base::set_vertex(v); Halfedge_handle lIBorder = v->halfedge() ; Halfedge_handle lOBorder = v->halfedge()->next() ; lIBorder ->HBase_base::set_next(lOBisector); lOBisector->HBase_base::set_prev(lIBorder); lOBorder ->HBase_base::set_prev(lIBisector); lIBisector->HBase_base::set_next(lOBorder); CGAL_SSBUILDER_TRACE(3 ,"Adding Contour Bisector at N:" << v->id() << "\n B" << lOBisector->id() << " (Out)\n B" << lIBisector->id() << " (In)" ) ; } } template void Straight_skeleton_builder_2::InitPhase() { CreateContourBisectors(); CreateInitialEvents(); } template typename Straight_skeleton_builder_2::Vertex_handle Straight_skeleton_builder_2::ConstructEdgeEventNode( EdgeEvent& aEvent ) { CGAL_SSBUILDER_TRACE ( 2, "Creating EdgeEvent Node" ) ; Vertex_handle lLSeed = aEvent.seed0() ; Vertex_handle lRSeed = aEvent.seed1() ; Vertex_handle lNewNode = mSSkel->SSkel::Base::vertices_push_back( Vertex( mVertexID++, aEvent.point(), aEvent.time()) ) ; mSLAV.push_back(lNewNode); mWrappedVertices.push_back( VertexWrapper(lNewNode) ) ; Halfedge_handle lLOBisector = lLSeed->primary_bisector(); Halfedge_handle lROBisector = lRSeed->primary_bisector(); Halfedge_handle lLIBisector = lLOBisector->opposite(); Halfedge_handle lRIBisector = lROBisector->opposite(); lNewNode->VBase::set_halfedge(lLOBisector); lLOBisector->HBase_base::set_vertex(lNewNode); lROBisector->HBase_base::set_vertex(lNewNode); lLIBisector->HBase_base::set_prev( lROBisector ) ; lROBisector->HBase_base::set_next( lLIBisector ) ; CGAL_SSBUILDER_SHOW( DrawBisector(lLOBisector); DrawBisector(lROBisector); ) ; CGAL_SSBUILDER_TRACE ( 3 , "LSeed: N" << lLSeed->id() << " proccesed\n" << "RSeed: N" << lRSeed->id() << " proccesed" ) ; SetIsProcessed(lLSeed) ; SetIsProcessed(lRSeed) ; mSLAV.remove(lLSeed); mSLAV.remove(lRSeed); Vertex_handle lLPrev = GetPrevInLAV(lLSeed) ; Vertex_handle lRNext = GetNextInLAV(lRSeed) ; SetPrevInLAV(lNewNode, lLPrev ) ; SetNextInLAV(lLPrev , lNewNode ) ; SetNextInLAV(lNewNode, lRNext ) ; SetPrevInLAV(lRNext , lNewNode ) ; CGAL_SSBUILDER_TRACE ( 2 , "LO: B" << lLOBisector->id() << " LI: B" << lLIBisector->id() << " RO: B" << lROBisector->id() << " RI: B" << lRIBisector->id() << '\n' << "New Node: N" << lNewNode->id() << " at " << lNewNode->point() << '\n' << "New Links: B" << lROBisector->id() << "->B" << lLIBisector->id() << '\n' << 'N' << lNewNode->id() << " inserted into LAV: N" << lLPrev->id() << "->N" << lNewNode->id() << "->N" << lRNext->id() << std::endl << 'N' << lLSeed->id() << " removed from LAV\n" << 'N' << lRSeed->id() << " removed from LAV" ); return lNewNode ; } template typename Straight_skeleton_builder_2::Vertex_handle Straight_skeleton_builder_2::LookupOnSLAV ( Halfedge_handle aBorder, Event const& aEvent ) { Vertex_handle rResult ; CGAL_SSBUILDER_TRACE ( 3, "Looking up for E" << aBorder->id() << " on SLAV. P=" << aEvent.point() ) ; #ifdef CGAL_STRAIGHT_SKELETON_ENABLE_TRACE bool lFound = false ; #endif 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 ) { #ifdef CGAL_STRAIGHT_SKELETON_ENABLE_TRACE lFound = true ; #endif Vertex_handle lPrev = GetPrevInLAV(v); Halfedge_handle lPrevBorder = GetDefiningBorderA(lPrev); Halfedge_handle lNextBorder = GetDefiningBorderB(v); CGAL_assertion(handle_assigned(lPrevBorder)); CGAL_assertion(handle_assigned(lNextBorder)); if ( IsEventInsideOffsetZone( aEvent.border_a(), aEvent.border_b(), aBorder, lPrevBorder, lNextBorder ) ) { rResult = v ; CGAL_SSBUILDER_TRACE ( 2 , 'E' << aBorder->id() << " found in SLAV: N" << lPrev->id() << "->N" << v->id() << " (E" << lPrevBorder->id() << "->E" << aBorder->id() << "->E" << lNextBorder->id() << ")" ) ; break ; } } } #ifdef CGAL_STRAIGHT_SKELETON_ENABLE_TRACE if ( !handle_assigned(rResult) ) { if ( !lFound ) { CGAL_SSBUILDER_TRACE(1,"Split event is no longer valid. Opposite edge vanished."); } else { CGAL_SSBUILDER_TRACE(1,"Split event is no longer valid. Not inside the opposite edge offset zone."); } } #endif return rResult ; } template typename Straight_skeleton_builder_2::Vertex_handle_pair Straight_skeleton_builder_2::ConstructSplitEventNodes( SplitEvent& aEvent, Vertex_handle aOppR ) { Vertex_handle_pair rResult; CGAL_SSBUILDER_TRACE ( 2, "Creating SplitEvent Nodes" ) ; Vertex_handle lOppL = GetPrevInLAV(aOppR) ; Vertex_handle lNodeA = mSSkel->SSkel::Base::vertices_push_back( Vertex( mVertexID++, aEvent.point(), aEvent.time() ) ) ; Vertex_handle lNodeB = mSSkel->SSkel::Base::vertices_push_back( Vertex( mVertexID++, aEvent.point(), aEvent.time() ) ) ; mSLAV.push_back(lNodeA); mSLAV.push_back(lNodeB); mWrappedVertices.push_back( VertexWrapper(lNodeA) ) ; mWrappedVertices.push_back( VertexWrapper(lNodeB) ) ; Vertex_handle lSeed = aEvent.seed0() ; Halfedge_handle lXOutBisector = lSeed->primary_bisector() ; Halfedge_handle lXInBisector = lXOutBisector->opposite(); lNodeA->VBase::set_halfedge(lXOutBisector); // lNodeB hafledge is set outside with the New In Bisector to the Right. lXOutBisector->HBase_base::set_vertex(lNodeA); CGAL_SSBUILDER_SHOW( DrawBisector(lXOutBisector); ) ; CGAL_SSBUILDER_TRACE ( 3, "Seed: N" << lSeed->id() << " proccesed" ) ; SetIsProcessed(lSeed) ; mSLAV.remove(lSeed); CGAL_SSBUILDER_TRACE ( 2, 'N' << lNodeA->id() << " and N" << lNodeB->id() << " inserted into LAV." ) ; Vertex_handle lPrev = GetPrevInLAV(lSeed) ; Vertex_handle lNext = GetNextInLAV(lSeed) ; SetNextInLAV(lPrev , lNodeA ) ; SetPrevInLAV(lNodeA, lPrev ) ; SetNextInLAV(lNodeA, aOppR ) ; SetPrevInLAV(aOppR , lNodeA ) ; SetNextInLAV(lOppL , lNodeB ) ; SetPrevInLAV(lNodeB, lOppL ) ; SetNextInLAV(lNodeB, lNext ) ; SetPrevInLAV(lNext , lNodeB ) ; CGAL_SSBUILDER_TRACE ( 2 , "Updated LAV: N" << lPrev->id() << "->N" << lNodeA->id() << "->N" << aOppR->id() << std::endl << "Updated LAV: N" << lOppL->id() << "->N" << lNodeB->id() << "->N" << lNext->id() << std::endl << 'N' << lSeed->id() << " removed from LAV" ); rResult = std::make_pair(lNodeA,lNodeB); mSplitNodes.push_back(rResult); return rResult ; } template typename Straight_skeleton_builder_2::Vertex_handle_pair Straight_skeleton_builder_2::ConstructVertexEventNodes( VertexEvent& aEvent ) { Vertex_handle_pair rResult; CGAL_SSBUILDER_TRACE ( 2, "Creating VertexEvent Nodes" ) ; Vertex_handle lLSeed = aEvent.seed0() ; Vertex_handle lRSeed = aEvent.seed1() ; Vertex_handle lNewNodeA = mSSkel->SSkel::Base::vertices_push_back( Vertex( mVertexID++, aEvent.point(), aEvent.time()) ) ; Vertex_handle lNewNodeB = mSSkel->SSkel::Base::vertices_push_back( Vertex( mVertexID++, aEvent.point(), aEvent.time()) ) ; mSLAV.push_back(lNewNodeA); mSLAV.push_back(lNewNodeB); mWrappedVertices.push_back( VertexWrapper(lNewNodeA) ) ; mWrappedVertices.push_back( VertexWrapper(lNewNodeB) ) ; Halfedge_handle lLOBisector = lLSeed->primary_bisector(); Halfedge_handle lROBisector = lRSeed->primary_bisector(); Halfedge_handle lLIBisector = lLOBisector->opposite(); Halfedge_handle lRIBisector = lROBisector->opposite(); lNewNodeA->VBase::set_halfedge(lLOBisector); lNewNodeB->VBase::set_halfedge(lROBisector); lLOBisector->HBase_base::set_vertex(lNewNodeA); lROBisector->HBase_base::set_vertex(lNewNodeB); lLIBisector->HBase_base::set_prev( lROBisector ) ; lROBisector->HBase_base::set_next( lLIBisector ) ; lLOBisector->HBase_base::set_next( lRIBisector ) ; lRIBisector->HBase_base::set_prev( lLOBisector ) ; CGAL_SSBUILDER_SHOW( DrawBisector(lLOBisector); DrawBisector(lROBisector); ) ; CGAL_SSBUILDER_TRACE ( 3 , "LSeed: N" << lLSeed->id() << " proccesed\n" << "RSeed: N" << lRSeed->id() << " proccesed" ) ; SetIsProcessed(lLSeed) ; SetIsProcessed(lRSeed) ; mSLAV.remove(lLSeed); mSLAV.remove(lRSeed); Vertex_handle lLPrev = GetPrevInLAV(lLSeed) ; Vertex_handle lLNext = GetNextInLAV(lLSeed) ; Vertex_handle lRPrev = GetPrevInLAV(lRSeed) ; Vertex_handle lRNext = GetNextInLAV(lRSeed) ; SetPrevInLAV(lNewNodeA, lLPrev ) ; SetNextInLAV(lLPrev , lNewNodeA ) ; SetNextInLAV(lNewNodeA, lRNext ) ; SetPrevInLAV(lRNext , lNewNodeA ) ; SetPrevInLAV(lNewNodeB, lRPrev ) ; SetNextInLAV(lRPrev , lNewNodeB ) ; SetNextInLAV(lNewNodeB, lLNext ) ; SetPrevInLAV(lLNext , lNewNodeB ) ; CGAL_SSBUILDER_TRACE ( 2 , "LO: B" << lLOBisector->id() << " LI: B" << lLIBisector->id() << " RO: B" << lROBisector->id() << " RI: B" << lRIBisector->id() << '\n' << "NewNodeA: N" << lNewNodeA->id() << " at " << lNewNodeA->point() << '\n' << "NewNodeB: N" << lNewNodeB->id() << " at " << lNewNodeB->point() << '\n' << "New Links: B" << lROBisector->id() << "->B" << lLIBisector->id() << '\n' << 'N' << lNewNodeA->id() << " and N" << lNewNodeB->id() << " inserted into LAV:\n" << 'N' << lLPrev->id() << "->N" << lNewNodeA->id() << "->N" << lRNext->id() << '\n' << 'N' << lRPrev->id() << "->N" << lNewNodeB->id() << "->N" << lLNext->id() << '\n' << 'N' << lLSeed->id() << " removed from LAV\n" << 'N' << lRSeed->id() << " removed from LAV" ); rResult = std::make_pair(lNewNodeA,lNewNodeB); mSplitNodes.push_back(rResult); return rResult ; } template bool Straight_skeleton_builder_2::IsProcessed( EventPtr aEvent ) { return IsProcessed(aEvent->seed0()) || IsProcessed(aEvent->seed1()) ; } template void Straight_skeleton_builder_2::HandleEdgeEvent( EventPtr aEvent ) { EdgeEvent& lEvent = dynamic_cast(*aEvent) ; Vertex_handle lLSeed = lEvent.seed0() ; Vertex_handle lRSeed = lEvent.seed1() ; Vertex_handle lNewNode = ConstructEdgeEventNode(lEvent); Halfedge_handle lLOBisector = lLSeed->primary_bisector() ; Halfedge_handle lROBisector = lRSeed->primary_bisector() ; Halfedge_handle lLIBisector = lLOBisector->opposite(); Halfedge_handle lRIBisector = lROBisector->opposite(); if ( !handle_assigned(lLOBisector->next()) && !handle_assigned(lRIBisector->prev()) ) { CGAL_SSBUILDER_TRACE(3,"Creating new Edge Event's Bisector"); Halfedge_handle lNOBisector = mSSkel->SSkel::Base::edges_push_back ( Halfedge(mEdgeID),Halfedge(mEdgeID+1) ); Halfedge_handle lNIBisector = lNOBisector->opposite(); mEdgeID += 2 ; lRIBisector->HBase_base::set_prev(lNIBisector); lNIBisector->HBase_base::set_next(lRIBisector); lNOBisector->HBase_base::set_face(lLOBisector->face()); lNIBisector->HBase_base::set_face(lRIBisector->face()); lNIBisector->HBase_base::set_vertex(lNewNode); lLOBisector->HBase_base::set_next(lNOBisector); lNOBisector->HBase_base::set_prev(lLOBisector); Halfedge_handle lDefiningBorderA = lNewNode->halfedge()->face()->halfedge(); Halfedge_handle lDefiningBorderB = lNewNode->halfedge()->opposite()->prev()->opposite()->face()->halfedge(); Halfedge_handle lDefiningBorderC = lNewNode->halfedge()->opposite()->prev()->face()->halfedge(); SetDefiningBorderA(lNewNode, lDefiningBorderA) ; SetDefiningBorderB(lNewNode, lDefiningBorderB) ; SetDefiningBorderC(lNewNode, lDefiningBorderC) ; CGAL_SSBUILDER_TRACE ( 2 , "NewNode N" << lNewNode->id() << " at " << lNewNode->point() << " defining borders: E" << lDefiningBorderA->id() << ",E" << lDefiningBorderB->id() << ",E" << lDefiningBorderC->id() << '\n' << "New Bisectors:\nB" << lNOBisector->id() << " [E" << lNOBisector->defining_contour_edge()->id() << ",E" << lNOBisector->opposite()->defining_contour_edge()->id() << "] (Out: Prev: B" << lNOBisector->prev()->id() << ")\nB" << lNIBisector->id() << " [E" << lNIBisector->defining_contour_edge()->id() << ",E" << lNIBisector->opposite()->defining_contour_edge()->id() << "] (In: Next: B" << lNIBisector->next()->id() << ")\n" << "N" << lNewNode->id() << " halfedge: " << lNewNode->halfedge()->id() << " primary bisector: B" << lNewNode->primary_bisector()->id() ) ; UpdatePQ(lNewNode); } else { Halfedge_handle lDefiningBorderA = lNewNode->halfedge()->face()->halfedge(); Halfedge_handle lDefiningBorderB = lNewNode->halfedge()->opposite()->prev()->opposite()->face()->halfedge(); Halfedge_handle lDefiningBorderC = lNewNode->halfedge()->opposite()->prev()->face()->halfedge(); SetDefiningBorderA(lNewNode, lDefiningBorderA) ; SetDefiningBorderB(lNewNode, lDefiningBorderB) ; SetDefiningBorderC(lNewNode, lDefiningBorderC) ; CGAL_SSBUILDER_TRACE(2 , "NewNode N" << lNewNode->id() << " at " << lNewNode->point() << " defining borders:" << " E" << lDefiningBorderA->id() << ",E" << lDefiningBorderB->id() << ",E" << lDefiningBorderC->id() << ".\nThis is a multiple node (A node with these defining edges already exist in the LAV)" ); } } template void Straight_skeleton_builder_2::HandleSplitEvent( EventPtr aEvent, Vertex_handle aOppR ) { SplitEvent& lEvent = dynamic_cast(*aEvent) ; Vertex_handle lSeed = lEvent.seed0(); Vertex_handle lNewNode_L, lNewNode_R ; boost::tie(lNewNode_L,lNewNode_R) = ConstructSplitEventNodes(lEvent,aOppR); Halfedge_handle lOppBorder = lEvent.opposite_border(); Halfedge_handle lReflexLBorder = GetDefiningBorderA(lSeed); Halfedge_handle lReflexRBorder = GetDefiningBorderB(lSeed); Halfedge_handle lNOBisector_L = mSSkel->SSkel::Base::edges_push_back ( Halfedge(mEdgeID++),Halfedge(mEdgeID++) ); Halfedge_handle lNOBisector_R = mSSkel->SSkel::Base::edges_push_back ( Halfedge(mEdgeID++),Halfedge(mEdgeID++) ); Halfedge_handle lNIBisector_L = lNOBisector_L->opposite(); Halfedge_handle lNIBisector_R = lNOBisector_R->opposite(); lNewNode_R->VBase::set_halfedge(lNIBisector_L) ; Halfedge_handle lXOBisector = lSeed->primary_bisector() ; Halfedge_handle lXIBisector = lXOBisector->opposite(); lNOBisector_L->HBase_base::set_face(lXOBisector->face()); lNIBisector_L->HBase_base::set_face(lOppBorder ->face()); lNOBisector_R->HBase_base::set_face(lOppBorder ->face()); lNIBisector_R->HBase_base::set_face(lXIBisector->face()); lNIBisector_L->HBase_base::set_vertex(lNewNode_R); lNIBisector_R->HBase_base::set_vertex(lNewNode_R); lXOBisector ->HBase_base::set_next(lNOBisector_L); lNOBisector_L->HBase_base::set_prev(lXOBisector); lXIBisector ->HBase_base::set_prev(lNIBisector_R); lNIBisector_R->HBase_base::set_next(lXIBisector); lNIBisector_L->HBase_base::set_next(lNOBisector_R); lNOBisector_R->HBase_base::set_prev(lNIBisector_L); Halfedge_handle lNewNodeLDefiningBorderA = lNewNode_L->halfedge()->face()->halfedge(); Halfedge_handle lNewNodeLDefiningBorderB = lNewNode_L->halfedge()->opposite()->prev()->opposite()->face()->halfedge(); Halfedge_handle lNewNodeLDefiningBorderC = lNewNode_L->halfedge()->opposite()->prev()->face()->halfedge(); Halfedge_handle lNewNodeRDefiningBorderA = lNewNode_R->halfedge()->face()->halfedge(); Halfedge_handle lNewNodeRDefiningBorderB = lNewNode_R->halfedge()->opposite()->prev()->opposite()->face()->halfedge(); Halfedge_handle lNewNodeRDefiningBorderC = lNewNode_R->halfedge()->opposite()->prev()->face()->halfedge(); SetDefiningBorderA(lNewNode_L, lNewNodeLDefiningBorderA) ; SetDefiningBorderB(lNewNode_L, lNewNodeLDefiningBorderB) ; SetDefiningBorderC(lNewNode_L, lNewNodeLDefiningBorderC) ; SetDefiningBorderA(lNewNode_R, lNewNodeRDefiningBorderA) ; SetDefiningBorderB(lNewNode_R, lNewNodeRDefiningBorderB) ; SetDefiningBorderC(lNewNode_R, lNewNodeRDefiningBorderC) ; CGAL_SSBUILDER_TRACE ( 2 , "New Node L: N" << lNewNode_L->id() << " at " << lNewNode_L->point() << " defining borders: E" << lNewNodeLDefiningBorderA->id() << ",E" << lNewNodeLDefiningBorderB->id() << ",E" << lNewNodeLDefiningBorderC->id() << '\n' << "New Node R: N" << lNewNode_R->id() << " at " << lNewNode_R->point() << " defining borders: E" << lNewNodeRDefiningBorderA->id() << ",E" << lNewNodeRDefiningBorderB->id() << '\n' << ",E" << lNewNodeRDefiningBorderC->id() << '\n' << "New Bisector OL:\nB" << lNOBisector_L->id() << "[E" << lNOBisector_L ->defining_contour_edge()->id() << ",E" << lNOBisector_L->opposite()->defining_contour_edge()->id() << "]" << " (Out: Prev: B" << lNOBisector_L->prev()->id() << ")\n" << "New Bisector IL:\nB" << lNIBisector_L->id() << "[E" << lNIBisector_L ->defining_contour_edge()->id() << ",E" << lNIBisector_L->opposite()->defining_contour_edge()->id() << "]" << " (In: Next: B" << lNIBisector_L->next()->id() << ")\n" << "New Bisector OR:\nB" << lNOBisector_R->id() << "[E" << lNOBisector_R ->defining_contour_edge()->id() << ",E" << lNOBisector_R->opposite()->defining_contour_edge()->id() << "]" << " (Out: Prev: B" << lNOBisector_R->prev()->id() << ")\n" << "New Bisector IR:\nB" << lNIBisector_R->id() << "[E" << lNIBisector_R ->defining_contour_edge()->id() << ",E" << lNIBisector_R->opposite()->defining_contour_edge()->id() << "] (In: Next: B" << lNIBisector_R->next()->id() << ")\n" << "N" << lNewNode_L->id() << " halfedge: " << lNewNode_L->halfedge()->id() << " primary bisector: B" << lNewNode_L->primary_bisector()->id() << '\n' << "N" << lNewNode_R->id() << " halfedge: " << lNewNode_R->halfedge()->id() << " primary bisector: B" << lNewNode_R->primary_bisector()->id() ) ; UpdatePQ(lNewNode_L); UpdatePQ(lNewNode_R); } template bool Straight_skeleton_builder_2::SetupVertexEventNode( 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() ; bool lCollinear = Collinear(p,q,r) ; if ( lCollinear || !Left_turn(p,q,r) ) { rR = true ; SetIsReflex(aNode); CGAL_SSBUILDER_TRACE(1, ( lCollinear ? "COLLINEAR ":"Reflex " ) << "*NEW* vertex: N" << aNode->id() ); } return rR ; } template void Straight_skeleton_builder_2::HandleVertexEvent( EventPtr aEvent ) { VertexEvent& lEvent = dynamic_cast(*aEvent) ; Vertex_handle lLSeed = lEvent.seed0() ; Vertex_handle lRSeed = lEvent.seed1() ; Vertex_handle lNewNode_L, lNewNode_R ; boost::tie(lNewNode_L,lNewNode_R) = ConstructVertexEventNodes(lEvent); Halfedge_handle lNBisector_LO = mSSkel->SSkel::Base::edges_push_back ( Halfedge(mEdgeID++),Halfedge(mEdgeID++) ); Halfedge_handle lNBisector_RO = mSSkel->SSkel::Base::edges_push_back ( Halfedge(mEdgeID++),Halfedge(mEdgeID++) ); Halfedge_handle lNBisector_LI = lNBisector_LO->opposite(); Halfedge_handle lNBisector_RI = lNBisector_RO->opposite(); Halfedge_handle lSBisector_LO = lLSeed->primary_bisector() ; Halfedge_handle lSBisector_LI = lSBisector_LO->opposite(); Halfedge_handle lSBisector_RO = lRSeed->primary_bisector() ; Halfedge_handle lSBisector_RI = lSBisector_RO->opposite(); lNBisector_LO->HBase_base::set_face(lSBisector_LO->face()); lNBisector_LI->HBase_base::set_face(lSBisector_RI->face()); lNBisector_RO->HBase_base::set_face(lSBisector_RO->face()); lNBisector_RI->HBase_base::set_face(lSBisector_LI->face()); lNBisector_LI->HBase_base::set_vertex(lNewNode_L); lNBisector_RI->HBase_base::set_vertex(lNewNode_R); lSBisector_LO->HBase_base::set_next(lNBisector_LO); lNBisector_LO->HBase_base::set_prev(lSBisector_LO); lSBisector_LI->HBase_base::set_prev(lNBisector_RI); lNBisector_RI->HBase_base::set_next(lSBisector_LI); lSBisector_RI->HBase_base::set_prev(lNBisector_LI); lNBisector_LI->HBase_base::set_next(lSBisector_RI); lSBisector_RO->HBase_base::set_next(lNBisector_RO); lNBisector_RO->HBase_base::set_prev(lSBisector_RO); lNewNode_L->VBase::set_halfedge(lSBisector_LO); lNewNode_R->VBase::set_halfedge(lSBisector_RO); Halfedge_handle lNewNodeLDefiningBorderA = lNewNode_L->halfedge()->face()->halfedge(); Halfedge_handle lNewNodeLDefiningBorderB = lNewNode_L->halfedge()->next()->opposite()->face()->halfedge(); Halfedge_handle lNewNodeLDefiningBorderC = lNewNode_L->halfedge()->opposite()->prev()->face()->halfedge(); Halfedge_handle lNewNodeRDefiningBorderA = lNewNode_R->halfedge()->face()->halfedge(); Halfedge_handle lNewNodeRDefiningBorderB = lNewNode_R->halfedge()->next()->opposite()->face()->halfedge(); Halfedge_handle lNewNodeRDefiningBorderC = lNewNode_R->halfedge()->opposite()->prev()->face()->halfedge(); SetDefiningBorderA(lNewNode_L, lNewNodeLDefiningBorderA) ; SetDefiningBorderB(lNewNode_L, lNewNodeLDefiningBorderB) ; SetDefiningBorderC(lNewNode_L, lNewNodeLDefiningBorderC) ; SetDefiningBorderA(lNewNode_R, lNewNodeRDefiningBorderA) ; SetDefiningBorderB(lNewNode_R, lNewNodeRDefiningBorderB) ; SetDefiningBorderC(lNewNode_R, lNewNodeRDefiningBorderC) ; CGAL_SSBUILDER_TRACE (2 , "New Node L: N" << lNewNode_L->id() << " at " << lNewNode_L->point() << " defining borders: E" << lNewNodeLDefiningBorderA->id() << ",E" << lNewNodeLDefiningBorderB->id() << ",E" << lNewNodeLDefiningBorderC->id() << '\n' << "New Node R: N" << lNewNode_R->id() << " at " << lNewNode_R->point() << " defining borders: E" << lNewNodeRDefiningBorderA->id() << ",E" << lNewNodeRDefiningBorderB->id() << ",E" << lNewNodeRDefiningBorderC->id() << '\n' << "New Bisector LO: B" << lNBisector_LO->id() << "[E" << lNBisector_LO ->defining_contour_edge()->id() << ",E" << lNBisector_LO->opposite()->defining_contour_edge()->id() << "]\n" << "New Bisector LI: B" << lNBisector_LI->id() << "[E" << lNBisector_LI ->defining_contour_edge()->id() << ",E" << lNBisector_LI->opposite()->defining_contour_edge()->id() << "]\n" << "New Bisector RO: B" << lNBisector_RO->id() << "[E" << lNBisector_RO ->defining_contour_edge()->id() << ",E" << lNBisector_RO->opposite()->defining_contour_edge()->id() << "]\n" << "New Bisector RI: B" << lNBisector_RI->id() << "[E" << lNBisector_RI ->defining_contour_edge()->id() << ",E" << lNBisector_RI->opposite()->defining_contour_edge()->id() << "]\n" << "N" << lNewNode_L->id() << " halfedge: " << lNewNode_L->halfedge()->id() << " primary bisector: B" << lNewNode_L->primary_bisector()->id() << '\n' << "N" << lNewNode_R->id() << " halfedge: " << lNewNode_R->halfedge()->id() << " primary bisector: B" << lNewNode_R->primary_bisector()->id() ) ; bool lNodeLIsNonConvex = SetupVertexEventNode(lNewNode_L,lNewNodeLDefiningBorderA,lNewNodeLDefiningBorderB) ; if ( !lNodeLIsNonConvex ) SetupVertexEventNode(lNewNode_R,lNewNodeRDefiningBorderA,lNewNodeRDefiningBorderB) ; UpdatePQ(lNewNode_L); UpdatePQ(lNewNode_R); } template void Straight_skeleton_builder_2::HandlePotentialSplitEvent( EventPtr aEvent ) { SplitEvent& lEvent = dynamic_cast(*aEvent); Halfedge_handle lOppBorder = lEvent.opposite_border() ; Vertex_handle lOppVertex = LookupOnSLAV(lOppBorder,lEvent); if ( handle_assigned(lOppVertex) ) { EventPtr lVertexEvent = FindVertexEvent(aEvent); if ( !lVertexEvent ) HandleSplitEvent (aEvent,lOppVertex); else HandleVertexEvent(lVertexEvent); } } template void Straight_skeleton_builder_2::Propagate() { CGAL_SSBUILDER_TRACE(0,"Propagating events..."); while ( !mPQ.empty() ) { EventPtr lEvent = PopEventFromPQ(); if ( !lEvent->is_excluded() && !IsProcessed(lEvent) ) { CGAL_SSBUILDER_TRACE (0,"\nStep: " << mStepID << " Event: " << *lEvent ) ; CGAL_SSBUILDER_SHOW ( SS_IO_AUX::ScopedPointDrawing lDraw(lEvent->point(),CGAL::BLUE,"Event"); ) SetEventTimeAndPoint(*lEvent) ; switch ( lEvent->type() ) { case Event::cEdgeEvent : HandleEdgeEvent (lEvent) ; break ; case Event::cSplitEvent : HandlePotentialSplitEvent(lEvent) ; break ; case Event::cVertexEvent: break ; // Avoids warning about unused enum value in switch } ++ mStepID ; } } } template void Straight_skeleton_builder_2::MergeSplitNodes ( Vertex_handle_pair aSplitNodes ) { Vertex_handle lLNode, lRNode ; boost::tie(lLNode,lRNode)=aSplitNodes; Halfedge_handle lIBisectorL1 = lLNode->primary_bisector()->opposite(); Halfedge_handle lIBisectorR1 = lRNode->primary_bisector()->opposite(); Halfedge_handle lIBisectorL2 = lIBisectorL1->next()->opposite(); Halfedge_handle lIBisectorR2 = lIBisectorR1->next()->opposite(); if ( lIBisectorL1->vertex() == lRNode ) lIBisectorL1->HBase_base::set_vertex(lLNode); if ( lIBisectorR1->vertex() == lRNode ) lIBisectorR1->HBase_base::set_vertex(lLNode); if ( lIBisectorL2->vertex() == lRNode ) lIBisectorL2->HBase_base::set_vertex(lLNode); if ( lIBisectorR2->vertex() == lRNode ) lIBisectorR2->HBase_base::set_vertex(lLNode); CGAL_SSBUILDER_TRACE(2 ,"SplitNodes: N" << lLNode->id() << " and N" << lRNode->id() << " merged.\n" << ". N" << lRNode->id() << " excluded.\n" << 'B' << lIBisectorL1->id() << " now linked to N" << lIBisectorL1->vertex()->id() << '\n' << 'B' << lIBisectorR1->id() << " now linked to N" << lIBisectorR1->vertex()->id() << '\n' << 'B' << lIBisectorL2->id() << " now linked to N" << lIBisectorL2->vertex()->id() << '\n' << 'B' << lIBisectorR2->id() << " now linked to N" << lIBisectorR2->vertex()->id() ); mSSkel->SSkel::Base::vertices_erase(lRNode); } template void Straight_skeleton_builder_2::MergeCoincidentNodes( Vertex_handle v0 , Vertex_handle v1 , Halfedge_handle_vector& rHalfedgesToRemove , Vertex_handle_vector& rVerticesToRemove ) { Halfedge_handle toremove0 ; Halfedge_handle_vector lToRelink ; Halfedge_around_vertex_circulator iebegin = v1->halfedge_around_vertex_begin(); Halfedge_around_vertex_circulator ie = iebegin ; do { Halfedge_handle iedge = *ie ; Vertex_handle v00 = iedge->opposite()->vertex(); if ( v00 != v0 ) lToRelink.push_back(iedge); else toremove0 = iedge ; ++ ie ; } while(ie != iebegin); // If there isn't any halfedge joining the two vertices, they can't be merged. if ( handle_assigned(toremove0) ) { CGAL_SSBUILDER_TRACE(0,"Merging N" << v0->id() << " and N" << v1->id() ); for ( Halfedge_handle_vector_iterator j = lToRelink.begin(), ej = lToRelink.end() ; j != ej ; ++ j ) (*j)->HBase_base::set_vertex(v0); Exclude(v1); rVerticesToRemove.push_back(v1); CGAL_SSBUILDER_TRACE(2, "N" << v1->id() << " removed." ); Halfedge_handle toremove1 = toremove0->opposite(); CGAL_SSBUILDER_TRACE(2, "New links: B" << toremove0->prev()->id() << "->" << toremove0->next()->id() << "\nNew links: B" << toremove1->prev()->id() << "->" << toremove1->next()->id() << "\nN" << v1->id() << " removed." ) ; toremove0->prev()->HBase_base::set_next(toremove0->next()); toremove0->next()->HBase_base::set_prev(toremove0->prev()); toremove1->prev()->HBase_base::set_next(toremove1->next()); toremove1->next()->HBase_base::set_prev(toremove1->prev()); if ( v0->halfedge() == toremove1 ) v0->VBase::set_halfedge(toremove1->prev()) ; CGAL_SSBUILDER_TRACE(2,"New bisector of N" << v0->id() << " is B" << toremove1->prev()->id() ) ; rHalfedgesToRemove.push_back(toremove0); return ; } else { CGAL_SSBUILDER_TRACE(0,"N" << v0->id() << " and N" << v1->id() << " are coincident but unconnected"); } } template void Straight_skeleton_builder_2::MergeCoincidentNodes() { typedef boost::adjacency_matrix Graph ; typedef boost::graph_traits::edge_descriptor edesc; Graph graph(mVertexID) ; Halfedge_handle_vector lHalfedgesToRemove ; Vertex_handle_vector lVerticesToRemove ; for ( Vertex_iterator vi0 = mSSkel->SSkel::Base::vertices_begin(), evi0 = mSSkel->SSkel::Base::vertices_end(); vi0 != evi0 ; ++ vi0 ) { Vertex_handle v0 = static_cast(vi0); if ( v0->is_skeleton() && !IsExcluded(v0) ) { for ( Vertex_iterator vi1 = mSSkel->SSkel::Base::vertices_begin(), evi1 = mSSkel->SSkel::Base::vertices_end(); vi1 != evi1 ; ++ vi1 ) { Vertex_handle v1 = static_cast(vi1); if ( v0 != v1 && v1->is_skeleton() && !IsExcluded(v1) ) { edesc ge ; bool linked ; tie(ge,linked) = boost::edge(v0->id(),v1->id(),graph); if ( !linked && AreSkeletonNodesCoincident(v0,v1) ) { MergeCoincidentNodes(v0,v1,lHalfedgesToRemove,lVerticesToRemove); boost::add_edge(v0->id(),v1->id(),graph); } } } } } for( Halfedge_handle_vector_iterator hi = lHalfedgesToRemove.begin(), ehi = lHalfedgesToRemove.end() ; hi != ehi ; ++ hi ) mSSkel->SSkel::Base::edges_erase(*hi); for( Vertex_handle_vector_iterator vi = lVerticesToRemove.begin(), evi = lVerticesToRemove.end() ; vi != evi ; ++ vi ) mSSkel->SSkel::Base::vertices_erase(*vi); } template void Straight_skeleton_builder_2::FinishUp() { std::for_each( mSplitNodes.begin() ,mSplitNodes.end () ,boost::bind(&Straight_skeleton_builder_2::MergeSplitNodes,this,_1) ) ; std::for_each( mDanglingBisectors.begin() ,mDanglingBisectors.end () ,boost::bind(&Straight_skeleton_builder_2::EraseBisector,this,_1) ) ; MergeCoincidentNodes(); } template void Straight_skeleton_builder_2::Run() { InitPhase(); Propagate(); FinishUp (); } template typename Straight_skeleton_builder_2::SSkelPtr Straight_skeleton_builder_2::construct_skeleton() { try { Run() ; } catch( std::exception const& e ) { CGAL_SSBUILDER_TRACE(0,"EXCEPTION THROWN (" << e.what() << ") during straight skeleton construction."); mSSkel = SSkelPtr() ; } if ( !CGAL::HalfedgeDS_const_decorator(*mSSkel).is_valid(false,3) ) { CGAL_SSBUILDER_TRACE(0,"Result inconsistent."); mSSkel = SSkelPtr() ; } return mSSkel ; } CGAL_END_NAMESPACE #endif // CGAL_STRAIGHT_SKELETON_BUILDER_2_C // // EOF //