// Copyright (c) 2005 Tel-Aviv University (Israel). // All rights reserved. // // This file is part of CGAL (www.cgal.org); you may redistribute it under // the terms of the Q Public License version 1.0. // See the file LICENSE.QPL distributed with CGAL. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // // // Author(s) : Kapelushnik Lior /*! \file * spherical arrangements of none intersecting arcs of great circles on a sphere */ #ifndef CGAL_SPHERE_DCEL_H #define CGAL_SPHERE_DCEL_H #include #include #include #include //temporary: #include // end temporary CGAL_BEGIN_NAMESPACE /* the class represents the spherical arrangement elements which are halfedge, vertex, face and circulators around vertex or connected compononent boundary _Kernel - the kernel to base uppon the spherical map and the internal cubical gaussian map _T_Dcel - the cubical gaussian map basic dcel structure template class */ template #endif class _T_Dcel = Spherical_cgm_arr_dcel> class SphereTopologicalMap { public: class Halfedge; class Vertex; class Face; class Halfedge_around_vertex_circulator; class Ccb_halfedge_circulator; // public definitions typedef CGAL::Cubical_gaussian_map_3<_Kernel, _T_Dcel> CGM; typedef _Kernel Kernel; typedef std::list HalfedgeList; typedef typename HalfedgeList::iterator Halfedge_iterator; typedef typename HalfedgeList::const_iterator Halfedge_const_iterator; typedef Halfedge_iterator Halfedge_handle; typedef Halfedge_const_iterator Halfedge_const_handle; typedef std::list VertexList; typedef typename VertexList::iterator Vertex_iterator; typedef typename VertexList::const_iterator Vertex_const_iterator; typedef Vertex_iterator Vertex_handle; typedef Vertex_const_iterator Vertex_const_handle; typedef std::list FaceList; typedef typename FaceList::iterator Face_iterator; typedef typename FaceList::const_iterator Face_const_iterator; typedef Face_iterator Face_handle; typedef Face_const_iterator Face_const_handle; typedef std::list HolesList; typedef typename HolesList::iterator Hole_iterator; /* represents a circulator around vertex that allows for circulating the incoming halfedges of a vertex */ class Halfedge_around_vertex_circulator: public Halfedge_handle { private: typedef Halfedge_handle Base; typedef Halfedge_around_vertex_circulator Self; // basic arrangement circulator typedef typename CGM::Arr_halfedge_around_vertex_const_circulator PM_Circulator; typedef typename CGM::Arr_halfedge_const_iterator Int_Halfedge_const_iterator; // cubical gaussian map circulator typedef typename CGM::Halfedge_around_vertex_const_circulator CGM_Circulator; // the corresponding cubical circulator around the vertex CGM_Circulator m_cubeCirculator; CGM *m_cgm; // pointer to the cubical gaussian map representing the spherical map public: /* empty constructor */ Halfedge_around_vertex_circulator() {} /* constructor from a cubical map circulator and a cubical map pointer inCir - a cubical map circulator cgm - a cubical gaussian map of the circulator */ Halfedge_around_vertex_circulator(CGM_Circulator &inCir, CGM *cgm): m_cubeCirculator(inCir), m_cgm(cgm) { Base * base = this; Halfedge_handle spEdge; // spEdge will hold the spherical map halfedge pointed by the cubical map // circulator halfedge, since the target of the cubical map is a real vertex // it is guaranteed that the cubical halfedge will have a pointer to // a spherical halfedge spEdge = *((Halfedge_handle *)(m_cubeCirculator->getSphereEdge())); // set circulator halfedge value to the spherical map halfedge *base = spEdge; } /* advance to next halfedge around vertex return value - the next halfedge around spherical vertex */ Self & operator++() { Base * base = this; ++m_cubeCirculator; // advance internal cgm circulator Halfedge_handle spEdge; // get spherical halfedge pointed by cgm advanced circulator spEdge = *((Halfedge_handle *)(m_cubeCirculator->getSphereEdge())); *base = spEdge; // set value to next halfedge return *this; } /* advance to next halfedge around vertex and return current halfedge as if the circulator was not advanced return value - the current circulator */ Self operator++(int) { Self tmp = *this; // save current circulator ++(*this); // advance circulator return tmp; // return circulator before advancing } /* test equality of two circulators circulator - circulator to compare object with return value - true if current object and circulator are the same */ bool operator==(const Self &circulator) const { // compare the halfedge values with the circulator return Base::operator==(circulator); } /* test inequality of two circulators circulator - circulator to compare object with return value - true if current object and circulator are not the same */ bool operator!=(const Self &circulator) const { // use composition of the equality operator return (!(*this == circulator)); } }; /* represents a circulator around a connected component boudary that allows for circulating a ccb of a face */ class Ccb_halfedge_circulator: public Halfedge_handle { typedef Halfedge_handle Base; typedef Ccb_halfedge_circulator Self; public: /* constructor from a regular halfedge to a ccb circulator i - a spherical map halfedge */ Ccb_halfedge_circulator(Halfedge_handle i): Base(i) {} /* empty constructor */ Ccb_halfedge_circulator(): Base() {}; /* test for equality of two ccb circulators i - the ccb circulator to compare with return value - true if the circulators are equal */ bool operator==(const Self &i) const { // compare the halfedge part of the two circulators return Base::operator==(i); } /* test for inequality of two ccb circulators i - the ccb circulator to compare with return value - true if the circulators are not equal */ bool operator!=(const Self &i) const { // by composition check if the two circulators are not equal return (!((*this) == i)); } /* advance the circulator return value - the next ccb circulator */ Self & operator++() { Base *base = this; // advancing is moving to the next halfedge *base = (*base)->next(); return *this; } /* advance the circulator and return the circulator before advancing return value - the circulator before advancing */ Self operator++(int) { Self tmp = *this; // remember circulator before advance is done ++(*this); // advance current circulator return tmp; // return the circulator before advancing } }; /* this class represents a spherical vertex */ class Vertex { public: // public definitions typedef typename CGM::Arr_vertex_iterator Int_Vertex_iterator; typedef typename CGM::Arr_halfedge_iterator Int_Halfedge_iterator; typedef typename _Kernel::Direction_3 Direction_3; /* constructor, inVer - the cubical vertex that represents the sphere vertex cgm - pointer to the cubical gaussian map holding the cgm vertex */ Vertex(Int_Vertex_iterator inVer, CGM *cgm): m_cubeVertex(inVer), m_cgm(cgm) {}; ~Vertex() { } /* get the sphere vertex handle pointing to the internal cgm vertex that represents the spherical vertex return value - a pointer to Vertex_handle as defined in the Spherical_map which points to the cubical vertex */ inline void *sphereVertexPointer() { return m_cubeVertex->getSphereVertex(); } /* test equality of vertices sphere_it - the other vertex to compare with return value - true if sphere_it and current vertex are the same */ bool operator==(const Vertex & sphere_it) { // two sphere vertices are equal if they are represented by the sane // cubical vertex return (m_cubeVertex == sphere_it.m_cubeVertex); } /* test inequality of vertices sphere_it - the other vertex to compare with return value - true if sphere_it and current vertex are not the same */ bool operator!=(const Vertex & sphere_it) { // use composition with the equality operator return (!(*this == sphere_it)); } /* the direction of the vertex (the intersection of the direction and the sphere is the vertex point) return value - the vertex direction */ inline Direction_3 direction() { // get direction stored in cubical map vertex return m_cubeVertex->get_direction(); } /* the direction of the vertex (the intersection of the direction and the sphere is the vertex point) return value - the vertex direction a const version */ inline const Direction_3 direction() const { // get direction stored in cubical map vertex return m_cubeVertex->get_direction(); } /* returns a circulator that allows to traverse the halfedges that have the vertex as their target return value - a halfedge circulator around the vertex */ Halfedge_around_vertex_circulator incident_halfedges() const { // build a cgm circulator around the corresponding cgm vertex typename CGM::Halfedge_around_vertex_const_circulator hec(m_cubeVertex-> incident_halfedges()); // return a circulator based on the cgm circulator return Halfedge_around_vertex_circulator(hec, m_cgm); } /* returns true if e is incident to v (i.e., if v is the source or the target of e). e - the halfedge to check incidence to return value - true if e is incident to the vertex */ bool is_incident_edge (Halfedge_const_handle e) { // the halfedge is incident if it's target or source is the vertex if ((e->target())->direction() == direction()) { return true; } if ((e->source())->direction() == direction()) { return true; } return false; } /* returns true if face f is incident to the vertex f - the face to check if the vertex is incident to return value - true if f is incident to the vertex */ bool is_incident_face(Face_const_handle f) { /* IntFaceList nearFaces; Int_face_handle nearFace; nearFace = f->m_cubeFace; connectedFaces(*m_cgm, nearFaces, nearFace); typename IntFaceList::iterator lit; */ /* see if one the the cgm faces of the sphere face is incident to the representative vertex */ /* for (lit = nearFaces.begin(); lit != nearFaces.end(); lit++) { if (m_cubeVertex->is_incident_face(*lit)) { return true; } } return false; */ Halfedge_around_vertex_circulator adjEdges=incident_halfedges(); Halfedge_around_vertex_circulator circEnd=adjEdges; // circulate the halfedges around the vertex, for each halfedge check if // it's pointed spherical face is f, if so f is incident, if all the surrounding // halfedges faces are not f, f is not incident to the vertex do { if (adjEdges->face() == f) { return true; } ++adjEdges; } while (adjEdges != circEnd); return false; } /* find the number of halfedges pointing to the vertex return value - the number of incoming halfedges pointing to the vertex */ unsigned int degree() const { Halfedge_around_vertex_circulator circ=incident_halfedges(); Halfedge_around_vertex_circulator startCirc = circ; unsigned int counter; counter = 0; // circulate the halfedges around the vertex and count do { ++counter; ++circ; } while (circ != startCirc); // return m_cgm->degree(m_cubeVertex); return counter; } private: // the cubical vertex projected by the sphere vertex Int_Vertex_iterator m_cubeVertex; CGM *m_cgm; // the cubical gaussian map holding the cubical corresponding vertex }; class Halfedge { public: // public definitions typedef typename CGM::Arr_halfedge_handle Int_halfedge_handle; typedef typename CGM::Arr_vertex_handle Int_vertex_handle; typedef Sphere_arc<_Kernel> Curve; /* constructor, create a new halfedge inEdge - a halfedge on the cubical gaussian map that is part of the sphere halfedge cgm - the cubical gaussian map holding the internal halfedge */ Halfedge(Int_halfedge_handle inEdge, CGM *cgm): m_cgm(cgm), m_cubeHalfedge(inEdge) {}; ~Halfedge() { } /* get the sphere halfedge handle pointing to the internal cgm halfedge that represents the spherical halfedge return value - a pointer to Halfedge_handle as defined in the Spherical_map which points to the cubical halfedge */ inline void *sphereHalfedgePointer() { return m_cubeHalfedge->getSphereEdge(); } /* returns the destination vertex of the spherical halfedge return value - the spherical handle to the target vertex of the halfedge */ Vertex_handle target() { Int_halfedge_handle lEdge; lEdge = lastEdge(); // get last cubical halfedge of the spherical halfedge Int_vertex_handle targ = lEdge->target(); // get the target of last halfedge // find the representative target vertex // which may be the last vertex or one of it's adjacent vertices while (!targ->is_rep()) { void *tmp = targ->get_adjacent_vertex(); targ = *((Int_vertex_handle *) (&tmp)); } return *((Vertex_handle *)(targ->getSphereVertex())); } /* returns the curve represented by the halfedge return value - the curve represented by the halfedge */ Curve curve() { // create a curve with the halfedge endpoints (short arc) return Curve(source()->direction(), target()->direction()); } /* returns the destination vertex of the spherical halfedge a const version of the none const target */ Vertex_const_handle target() const { Int_halfedge_handle lEdge; lEdge = lastEdge(); Int_vertex_handle targ = lEdge->target(); // find the representative target vertex while (!targ->is_rep()) { void *tmp = targ->get_adjacent_vertex(); targ = *((Int_vertex_handle *) (&tmp)); } return *((Vertex_const_handle *)(targ->getSphereVertex())); } /* returns the source vertex of the spherical halfedge return value - the spherical handle to the source vertex of the halfedge */ Vertex_handle source() { Halfedge_handle theTwin; theTwin = twin(); // source is the target of the twin return theTwin->target(); } /* returns the source vertex of the spherical halfedge a const version of the none const source */ Vertex_const_handle source() const { Halfedge_const_handle theTwin; theTwin = twin(); return theTwin->target(); } /* get the face next to the halfedge return value - a spherical face handle to the face adjacent of the halfedge */ Face_handle face() { // get the spherical face handle pointed by the adjacent face to the // cubical halfedge return *((Face_handle *)((m_cubeHalfedge->face())->getSphereFace())); } /* get the face next to the halfedge, a const version return value - a spherical face handle to the face adjacent of the halfedge */ Face_const_handle face() const { // get the spherical face handle pointed by the adjacent face to the // cubical halfedge return *((Face_handle *)((m_cubeHalfedge->face())->getSphereFace())); } /* the next halfedge around the face return value - next face halfedge */ Halfedge_handle next() { Int_halfedge_handle cur; // current halfedge of arc // find the last halfedge of the arc cur = lastEdge(); // found last edge, now find it's next cur = cur->next(); // if cube unreal halfedge, traverse adjacent until real halfedge while (!cur->is_real()) { cur = m_cgm->get_adjacent_halfedge_handle(cur); cur = cur->next(); } return Face::outHandle(cur, m_cgm); } /* the twin halfedge return value - the spherical twin halfedge */ Halfedge_handle twin() { Int_halfedge_handle twin; twin = m_cubeHalfedge->twin(); // get cubical twin if ((twin->face())->is_unbounded()) { // if cubical twin in unbounded face, twin is the adjacent on another // cubical face twin = m_cgm->get_adjacent_halfedge_handle(m_cubeHalfedge); } // find last cubical halfedge of the spherical halfedge while (!(twin->target()->getReal())) { twin = twin->next(); // advance cubical halfedge while (!twin->is_real()) { // traverse cubical unreal halfedges until a real halfedge twin = m_cgm->get_adjacent_halfedge_handle(twin); twin = twin->next(); } } return *((Halfedge_handle *)(twin->getSphereEdge())); } /* the twin halfedge a const version of the none const twin */ Halfedge_const_handle twin() const { Int_halfedge_handle twin; twin = m_cubeHalfedge->twin(); // get cubical twin if ((twin->face())->is_unbounded()) { // if cubical twin in unbounded face, twin is the adjacent on another // cubical face twin = m_cgm->get_adjacent_halfedge_handle(m_cubeHalfedge); } // find last cubical halfedge of the spherical halfedge while (!(twin->target()->getReal())) { twin = twin->next(); // advance cubical halfedge while (!twin->is_real()) { // traverse cubical unreal halfedges until a real halfedge twin = m_cgm->get_adjacent_halfedge_handle(twin); twin = twin->next(); } } return *((Halfedge_const_handle *)(twin->getSphereEdge())); } /* get a halfedge around ccb circulator starting at the halfedge return value - a ccb halfedge circulator starting from the halfedge */ Ccb_halfedge_circulator ccb() { Halfedge_handle thisHandle; thisHandle = *((Halfedge_handle *)(m_cubeHalfedge->getSphereEdge())); return Ccb_halfedge_circulator(thisHandle); } /* get the last cgm halfedge of the spherical halfedge return value - the last cgm halfedge of the spherical halfedge */ inline const Int_halfedge_handle cubeHalfedge() const { return m_cubeHalfedge; } private: CGM * m_cgm; // the internal cubical gaussian map // a cubical gaussian map halfedge corresponding the spherical halfedge, // the target of this cgm halfedge is real and it is part of the sphere halfedge arc Int_halfedge_handle m_cubeHalfedge; /* given a halfedge, finds the last edge of the arc that contains the halfedge return value - the last cgm halfedge that correspond the sphere halfedge, it's cgm target is a real vertex */ Int_halfedge_handle lastEdge() const { Int_halfedge_handle cur; cur = m_cubeHalfedge; // move to next real cgm halfedge until target is real while (!(cur->target()->getReal())) { cur = cur->next(); // traverse cubical unreal halfedges until a real halfedge while (!cur->is_real()) { cur = m_cgm->get_adjacent_halfedge_handle(cur); cur = cur->next(); } } return cur; } }; /* this class represents a spherical face */ class Face { public: // allow the spherical map to access m_doesOuterExist and set it's value template friend class Spherical_map; // public definitions typedef typename CGM::Arr_face_handle Int_face_handle; typedef typename CGM::Arr_halfedge_handle Int_halfedge_handle; typedef typename CGM::Arrangement::Hole_iterator Int_hole_iterator; /* constructor from a cubical face inFace - an internal cubical face that is part of the spherical face cgm - the cubical gaussian map containing the cgm face */ Face(Int_face_handle inFace, CGM *cgm): m_cgm(cgm), m_cubeFace(inFace), m_dirty(true), m_doesOuterExist(true) {} /* get the sphere face handle pointing to the internal cgm face that represents the spherical face (occupying part of the spherical face area) return value - a pointer to Face_handle as defined in the Spherical_map which points to a cubical face that is part of the spherical face */ inline void *sphereFacePointer() { return m_cubeFace->getSphereFace(); } /* get an iterator to first hole in face return value - an iterator to the first hole of the face */ Hole_iterator holes_begin() { update(); return m_holes.begin(); } /* get an iterator to past the end hole in face return value - an iterator to the past the end hole of the face */ Hole_iterator holes_end() { update(); return m_holes.end(); } /* get a halfedge on the face outer connected component boundary return value - a halfedge on the face outer ccb */ Halfedge_handle halfedge_on_outer_ccb() { update(); return m_outerCcb; } /* get a ccb circulator on the face outer connected component boundary return value - a ccb circulator on the face outer ccb */ Ccb_halfedge_circulator outer_ccb() { update(); return m_outerCcb; } /* find if the face has an outer ccb (are there any halfedges in the face) return value - true if the face has an outer ccb */ inline bool does_outer_ccb_exist() { return m_doesOuterExist; } /* find if a halfedge is on the face's outer ccb e - the halfedge to check if on outer ccb return value - true if e is a halfedge on the face outer ccb */ bool is_halfedge_on_outer_ccb(Halfedge_const_handle e) { update(); if (!m_doesOuterExist) { // no outer ccb, halfedge can't be on outer ccb return false; } Ccb_halfedge_circulator circ = m_outerCcb; // circulate on the halfedge ccb and check if e is one of the halfedges do { if (e==circ) { return true; // e found on the outer ccb } ++circ; } while (circ != m_outerCcb); // e not found on the outer ccb return false; } /* find if a halfedge is on a face's inner ccb e - the halfedge to check if on an inner ccb return value - true if e is a halfedge one of the face's inner ccb */ bool is_halfedge_on_inner_ccb(Halfedge_const_handle e) { if (e->face() == *((Face_const_handle *)m_cubeFace->getSphereFace())) { // if halfedge is adjacent to face, // using composition, if not on on outer ccb, on inner ccb return (!(this->is_halfedge_on_outer_ccb(e))); } else { // halfedge not adjacent to face return false; } } /* update the ccb structures of the face */ void update() { if (!m_dirty || !m_doesOuterExist) { // already updated or one face of entire cube return; } IntFaceList nearFaces; // find all cgm faces that are part of the spherical face connectedFaces(*m_cgm, nearFaces, m_cubeFace); typename IntFaceList::iterator lit; bool firstCcb = true; // seperate first ccb as outer ccb // loop over all cgm faces of the spherical face for (lit = nearFaces.begin(); lit != nearFaces.end(); lit++) { // process one internal cgm face // first process internal face outer ccb Int_halfedge_handle curIntHe = (*lit)->outer_ccb(); Int_halfedge_handle baseIntHe = curIntHe; // search for a real halfedge on the cgm face ccb // if halfedge is not real but it is marked, it is known // that the ccb have already been processed while (!curIntHe->is_real() && !curIntHe->getMark()) { curIntHe = curIntHe->next(); if (curIntHe == baseIntHe) { break; } } if (curIntHe->getMark() || !curIntHe->is_real()) { // the ccb has already been processed or isn't real, // any ccb will have one cgm face with real halfedge // and will be processed at laest in one cgm face } else { // process new ccb // get spherical handle Halfedge_handle curHe = outHandle(curIntHe, m_cgm); Ccb_halfedge_circulator currentCcb(curHe); markCcb(curIntHe); // mark ccb not to be processed again if (firstCcb) { // spherical outer ccb m_outerCcb = currentCcb; firstCcb = false; } else { // a hole m_holes.push_back(currentCcb); } } // process holes of internal face Int_hole_iterator holesIt; for (holesIt=(*lit)->holes_begin(); holesIt!= (*lit)->holes_end(); holesIt++) { // process each hole similar to the outer ccb processing Int_halfedge_handle curIntHe = (*holesIt); Int_halfedge_handle baseIntHe = curIntHe; // get a real or marked halfedge on the ccb if any while (!curIntHe->is_real() && !curIntHe->getMark()) { curIntHe = curIntHe->next(); if (curIntHe == baseIntHe) { break; } } if (curIntHe->getMark() || !curIntHe->is_real()) { // the ccb has already been processed or isn't real, // any ccb will have one cgm face with real halfedge // and will be processed at laest in one cgm face continue; } else { // process new ccb Halfedge_handle curHe = outHandle(curIntHe, m_cgm); Ccb_halfedge_circulator currentCcb(curHe); markCcb(curIntHe); // mark ccb not to be processed again if (firstCcb) { // spherical outer ccb m_outerCcb = currentCcb; firstCcb = false; } else { // a hole m_holes.push_back(currentCcb); } } } } m_dirty = false; } /* find the spherical halfedge from a cgm halfedge ihandle - a cubical halfedge cgm - the cgm holding the cubical halfedge return value - the spherical halfedge handle that ihandle is part of it */ static Halfedge_handle outHandle(Int_halfedge_handle ihandle, CGM *cgm) { // search for halfedeg with real target while (!(ihandle->target()->getReal())) { // advance ihandle = ihandle->next(); // if on cube unreal halfedge, traverse adjacents until real halfedge while (!ihandle->is_real()) { ihandle = cgm->get_adjacent_halfedge_handle(ihandle); ihandle = ihandle->next(); } } return *((Halfedge_handle *)(ihandle->getSphereEdge())); } private: /* mark a connected component boundary starting at ccbHandle ccbHandle - a cubical halfedge handle on the ccb to be marked */ void markCcb(Int_halfedge_handle ccbHandle) { Int_halfedge_handle cur; cur = ccbHandle; // traverse cgm ccb halfedges do { cur = cur->next(); // advance a halfedge // if on cube unreal halfedge, traverse adjacents until real halfedge while (!cur->is_real()) { cur = m_cgm->get_adjacent_halfedge_handle(cur); cur = cur->next(); } cur->setMark(); // mark cgm halfedge } while (!(cur == ccbHandle)); } CGM * m_cgm; // the internal cubical gaussian map // a cubical gaussian map face corresponding the spherical face, part of the // spherical face Int_face_handle m_cubeFace; // are ccb structures valid or needed to be updated bool m_dirty; bool m_doesOuterExist; // is there an outer ccb to the spherical face Ccb_halfedge_circulator m_outerCcb; HolesList m_holes; // list of face holes }; private: public: typedef typename CGM::Arr_ccb_halfedge_circulator Int_Ccb_halfedge_circulator; typedef typename CGM::Arr_face_handle Int_face_handle; typedef std::list IntFaceList; public: /* find all faces on cube that represents the same sphere face cgm - the cubical gaussian map faces - will hold the cubical faces that represents the sphere face startFace - one cubical face of the sphere face */ static void connectedFaces(CGM &cgm, IntFaceList &faces, Int_face_handle &startFace) { connectedFacesRec(cgm, faces, startFace); // find connected sub faces typename IntFaceList::iterator fit; // clear the sub-faces mark for (fit = faces.begin(); fit != faces.end(); fit++) { (*fit)->setMark(false); } } /* find all faces on cube that represents the same sphere face by recursivley traversing all adjacent faces, leave faces marked cgm - the cubical gaussian map faces - will hold the cubical faces that represents the sphere face startFace - one cubical face of the sphere face */ static void connectedFacesRec(CGM &cgm, IntFaceList &faces, Int_face_handle &startFace) { if (startFace->getMark() || startFace->is_unbounded()) { // face has already been processed or outside the cube return; } startFace->setMark(true); // mark face as processed faces.push_back(startFace); // add to list of faces Int_Ccb_halfedge_circulator ccbIt, ccbEnd; if (startFace->is_unbounded()) { // the cgm face should be on the cube std::cerr << "unbounded, no boudary" << std::endl; } ccbIt = startFace->outer_ccb(); ccbEnd = ccbIt; do { if (!ccbIt->is_real()) { // an unreal boundary, continue in adjacent face Int_face_handle nextFace = (cgm.get_adjacent_halfedge_handle(ccbIt))->face(); // process adjacent cgm face connectedFacesRec(cgm, faces, nextFace); } ccbIt++; } while (ccbIt != ccbEnd); }; }; CGAL_END_NAMESPACE #endif