diff options
Diffstat (limited to 'include/ttl/ttl.h')
-rw-r--r-- | include/ttl/ttl.h | 1901 |
1 files changed, 1901 insertions, 0 deletions
diff --git a/include/ttl/ttl.h b/include/ttl/ttl.h new file mode 100644 index 0000000..bbcf86a --- /dev/null +++ b/include/ttl/ttl.h @@ -0,0 +1,1901 @@ +/* + * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT, + * Applied Mathematics, Norway. + * + * Contact information: E-mail: tor.dokken@sintef.no + * SINTEF ICT, Department of Applied Mathematics, + * P.O. Box 124 Blindern, + * 0314 Oslo, Norway. + * + * This file is part of TTL. + * + * TTL is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * TTL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with TTL. If not, see + * <http://www.gnu.org/licenses/>. + * + * In accordance with Section 7(b) of the GNU Affero General Public + * License, a covered work must retain the producer line in every data + * file that is created or manipulated using TTL. + * + * Other Usage + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial activities involving the TTL library without + * disclosing the source code of your own applications. + * + * This file may be used in accordance with the terms contained in a + * written agreement between you and SINTEF ICT. + */ + +#ifndef _TTL_H_ +#define _TTL_H_ + +#include <list> +#include <iterator> + +// Debugging +#ifdef DEBUG_TTL +static void errorAndExit( char* aMessage ) +{ + cout << "\n!!! ERROR: " << aMessage << " !!!\n" << endl; + exit(-1); +} +#endif + +// Next on TOPOLOGY: +// - get triangle strips +// - weighted graph, algorithms using a weight (real) for each edge, +// e.g. an "abstract length". Use for minimum spanning tree +// or some arithmetics on weights? +// - Circulators as defined in CGAL with more STL compliant code + +// - analyze in detail locateFace: e.g. detect 0-orbit in case of infinite loop +// around a node etc. + +/** + * \brief Main interface to TTL +* +* This namespace contains the basic generic algorithms for the TTL, +* the Triangulation Template Library.\n +* +* Examples of functionality are: +* - Incremental Delaunay triangulation +* - Constrained triangulation +* - Insert/remove nodes and constrained edges +* - Traversal operations +* - Misc. queries for extracting information for visualisation systems etc. +* +* \par General requirements and assumptions: +* - \e DART_TYPE and \e TRAITS_TYPE should be implemented in accordance with the description +* in \ref api. +* - A \b "Requires:" section in the documentation of a function template +* shows which functionality is required in \e TRAITS_TYPE to +* support that specific function.\n +* Functionalty required in \e DART_TYPE is the same (almost) for all +* function templates; see \ref api and the example referred to. +* - When a reference to a \e dart object is passed to a function in TTL, +* it is assumed that it is oriented \e counterclockwise (CCW) in a triangle +* unless it is explicitly mentioned that it can also be \e clockwise (CW). +* The same applies for a dart that is passed from a function in TTL to +* the users TRAITS_TYPE class (or struct). +* - When an edge (represented with a dart) is swapped, it is assumed that darts +* outside the quadrilateral where the edge is a diagonal are not affected by +* the swap. Thus, \ref hed::TTLtraits::swapEdge "TRAITS_TYPE::swapEdge" +* must be implemented in accordance with this rule. +* +* \par Glossary: +* - General terms are explained in \ref api. +* - \e CCW - counterclockwise +* - \e CW - clockwise +* - \e 0_orbit, \e 1_orbit and \e 2_orbit: A sequence of darts around +* a node, around an edge and in a triangle respectively; +* see get_0_orbit_interior and get_0_orbit_boundary +* - \e arc - In a triangulation an arc is equivalent with an edge +* +* \see +* \ref ttl_util and \ref api +* +* \author +* �yvind Hjelle, oyvindhj@ifi.uio.no +*/ + + +namespace ttl +{ +class TRIANGULATION_HELPER +{ +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +public: + TRIANGULATION_HELPER( hed::TRIANGULATION& aTriang ) : + m_triangulation( aTriang ) + { + } + + // Delaunay Triangulation + template <class TRAITS_TYPE, class DART_TYPE, class POINT_TYPE> + bool InsertNode( DART_TYPE& aDart, POINT_TYPE& aPoint ); + + template <class TRAITS_TYPE, class DART_TYPE> + void RemoveRectangularBoundary( DART_TYPE& aDart ); + + template <class TRAITS_TYPE, class DART_TYPE> + void RemoveNode( DART_TYPE& aDart ); + + template <class TRAITS_TYPE, class DART_TYPE> + void RemoveBoundaryNode( DART_TYPE& aDart ); + + template <class TRAITS_TYPE, class DART_TYPE> + void RemoveInteriorNode( DART_TYPE& aDart ); + + // Topological and Geometric Queries + // --------------------------------- + template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE> + static bool LocateFaceSimplest( const POINT_TYPE& aPoint, DART_TYPE& aDart ); + + template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE> + static bool LocateTriangle( const POINT_TYPE& aPoint, DART_TYPE& aDart ); + + template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE> + static bool InTriangle( const POINT_TYPE& aPoint, const DART_TYPE& aDart ); + + template <class DART_TYPE, class DART_LIST_TYPE> + static void GetBoundary( const DART_TYPE& aDart, DART_LIST_TYPE& aBoundary ); + + template <class DART_TYPE> + static bool IsBoundaryEdge( const DART_TYPE& aDart ); + + template <class DART_TYPE> + static bool IsBoundaryFace( const DART_TYPE& aDart ); + + template <class DART_TYPE> + static bool IsBoundaryNode( const DART_TYPE& aDart ); + + template <class DART_TYPE> + static int GetDegreeOfNode( const DART_TYPE& aDart ); + + template <class DART_TYPE, class DART_LIST_TYPE> + static void Get0OrbitInterior( const DART_TYPE& aDart, DART_LIST_TYPE& aOrbit ); + + template <class DART_TYPE, class DART_LIST_TYPE> + static void Get0OrbitBoundary( const DART_TYPE& aDart, DART_LIST_TYPE& aOrbit ); + + template <class DART_TYPE> + static bool Same0Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 ); + + template <class DART_TYPE> + static bool Same1Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 ); + + template <class DART_TYPE> + static bool Same2Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 ); + + template <class TRAITS_TYPE, class DART_TYPE> + static bool SwappableEdge( const DART_TYPE& aDart, bool aAllowDegeneracy = false ); + + template <class DART_TYPE> + static void PositionAtNextBoundaryEdge( DART_TYPE& aDart ); + + template <class TRAITS_TYPE, class DART_TYPE> + static bool ConvexBoundary( const DART_TYPE& aDart ); + + // Utilities for Delaunay Triangulation + // ------------------------------------ + template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE> + void OptimizeDelaunay( DART_LIST_TYPE& aElist ); + + template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE> + void OptimizeDelaunay( DART_LIST_TYPE& aElist, const typename DART_LIST_TYPE::iterator aEnd ); + + template <class TRAITS_TYPE, class DART_TYPE> + bool SwapTestDelaunay( const DART_TYPE& aDart, bool aCyclingCheck = false ) const; + + template <class TRAITS_TYPE, class DART_TYPE> + void RecSwapDelaunay( DART_TYPE& aDiagonal ); + + template <class TRAITS_TYPE, class DART_TYPE, class LIST_TYPE> + void SwapEdgesAwayFromInteriorNode( DART_TYPE& aDart, LIST_TYPE& aSwappedEdges ); + + template <class TRAITS_TYPE, class DART_TYPE, class LIST_TYPE> + void SwapEdgesAwayFromBoundaryNode( DART_TYPE& aDart, LIST_TYPE& aSwappedEdges ); + + template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE> + void SwapEdgeInList( const typename DART_LIST_TYPE::iterator& aIt, DART_LIST_TYPE& aElist ); + + // Constrained Triangulation + // ------------------------- + template <class TRAITS_TYPE, class DART_TYPE> + static DART_TYPE InsertConstraint( DART_TYPE& aDStart, DART_TYPE& aDEnd, bool aOptimizeDelaunay ); + +private: + hed::TRIANGULATION& m_triangulation; + + template <class TRAITS_TYPE, class FORWARD_ITERATOR, class DART_TYPE> + void insertNodes( FORWARD_ITERATOR aFirst, FORWARD_ITERATOR aLast, DART_TYPE& aDart ); + + template <class TOPOLOGY_ELEMENT_TYPE, class DART_TYPE> + static bool isMemberOfFace( const TOPOLOGY_ELEMENT_TYPE& aTopologyElement, const DART_TYPE& aDart ); + + template <class TRAITS_TYPE, class NODE_TYPE, class DART_TYPE> + static bool locateFaceWithNode( const NODE_TYPE& aNode, DART_TYPE& aDartIter ); + + template <class DART_TYPE> + static void getAdjacentTriangles( const DART_TYPE& aDart, DART_TYPE& aT1, DART_TYPE& aT2, + DART_TYPE& aT3 ); + + template <class DART_TYPE> + static void getNeighborNodes( const DART_TYPE& aDart, std::list<DART_TYPE>& aNodeList, + bool& aBoundary ); + + template <class TRAITS_TYPE, class DART_TYPE> + static bool degenerateTriangle( const DART_TYPE& aDart ); +}; + +#endif // DOXYGEN_SHOULD_SKIP_THIS + + + /** @name Delaunay Triangulation */ +//@{ +/** + * Inserts a new node in an existing Delaunay triangulation and + * swaps edges to obtain a new Delaunay triangulation. + * This is the basic function for incremental Delaunay triangulation. + * When starting from a set of points, an initial Delaunay triangulation + * can be created as two triangles forming a rectangle that contains + * all the points. + * After \c insertNode has been called repeatedly with all the points, + * removeRectangularBoundary can be called to remove triangles + * at the boundary of the triangulation so that the boundary + * form the convex hull of the points. + * + * Note that this incremetal scheme will run much faster if the points + * have been sorted lexicographically on \e x and \e y. + * + * \param aDart + * An arbitrary CCW dart in the tringulation.\n + * Output: A CCW dart incident to the new node. + * + * \param aPoint + * A point (node) to be inserted in the triangulation. + * + * \retval bool + * \c true if \e point was inserted; \c false if not.\n + * If \e point is outside the triangulation, or the input dart is not valid, + * \c false is returned. + * + * \require + * - \ref hed::TTLtraits::splitTriangle "TRAITS_TYPE::splitTriangle" (DART_TYPE&, const POINT_TYPE&) + * + * \using + * - locateTriangle + * - RecSwapDelaunay + * + * \note + * - For efficiency reasons \e dart should be close to the insertion \e point. + * + * \see + * removeRectangularBoundary + */ +template <class TRAITS_TYPE, class DART_TYPE, class POINT_TYPE> +bool TRIANGULATION_HELPER::InsertNode( DART_TYPE& aDart, POINT_TYPE& aPoint ) +{ + bool found = LocateTriangle<TRAITS_TYPE>( aPoint, aDart ); + + if( !found ) + { +#ifdef DEBUG_TTL + cout << "ERROR: Triangulation::insertNode: NO triangle found. /n"; +#endif + return false; + } + + // ??? can we hide the dart? this is not possible if one triangle only + m_triangulation.splitTriangle( aDart, aPoint ); + + DART_TYPE d1 = aDart; + d1.Alpha2().Alpha1().Alpha2().Alpha0().Alpha1(); + + DART_TYPE d2 = aDart; + d2.Alpha1().Alpha0().Alpha1(); + + // Preserve a dart as output incident to the node and CCW + DART_TYPE d3 = aDart; + d3.Alpha2(); + aDart = d3; // and see below + //DART_TYPE dsav = d3; + d3.Alpha0().Alpha1(); + + //if (!TRAITS_TYPE::fixedEdge(d1) && !IsBoundaryEdge(d1)) { + if( !IsBoundaryEdge( d1 ) ) + { + d1.Alpha2(); + RecSwapDelaunay<TRAITS_TYPE>( d1 ); + } + + //if (!TRAITS_TYPE::fixedEdge(d2) && !IsBoundaryEdge(d2)) { + if( !IsBoundaryEdge( d2 ) ) + { + d2.Alpha2(); + RecSwapDelaunay<TRAITS_TYPE>( d2 ); + } + + // Preserve the incoming dart as output incident to the node and CCW + //d = dsav.Alpha2(); + aDart.Alpha2(); + //if (!TRAITS_TYPE::fixedEdge(d3) && !IsBoundaryEdge(d3)) { + if( !IsBoundaryEdge( d3 ) ) + { + d3.Alpha2(); + RecSwapDelaunay<TRAITS_TYPE>( d3 ); + } + + return true; +} + +//------------------------------------------------------------------------------------------------ +// Private/Hidden function (might change later) +template <class TRAITS_TYPE, class FORWARD_ITERATOR, class DART_TYPE> +void TRIANGULATION_HELPER::insertNodes( FORWARD_ITERATOR aFirst, FORWARD_ITERATOR aLast, + DART_TYPE& aDart ) +{ + + // Assumes that the dereferenced point objects are pointers. + // References to the point objects are then passed to TTL. + + FORWARD_ITERATOR it; + for( it = aFirst; it != aLast; ++it ) + { + InsertNode<TRAITS_TYPE>( aDart, **it ); + } +} + + +/** Removes the rectangular boundary of a triangulation as a final step of an + * incremental Delaunay triangulation. + * The four nodes at the corners will be removed and the resulting triangulation + * will have a convex boundary and be Delaunay. + * + * \param dart + * A CCW dart at the boundary of the triangulation\n + * Output: A CCW dart at the new boundary + * + * \using + * - RemoveBoundaryNode + * + * \note + * - This function requires that the boundary of the m_triangulation is + * a rectangle with four nodes (one in each corner). + */ +template <class TRAITS_TYPE, class DART_TYPE> +void TRIANGULATION_HELPER::RemoveRectangularBoundary( DART_TYPE& aDart ) +{ + DART_TYPE d_next = aDart; + DART_TYPE d_iter; + + for( int i = 0; i < 4; i++ ) + { + d_iter = d_next; + d_next.Alpha0(); + PositionAtNextBoundaryEdge( d_next ); + RemoveBoundaryNode<TRAITS_TYPE>( d_iter ); + } + + aDart = d_next; // Return a dart at the new boundary +} + +/** Removes the node associated with \e dart and + * updates the triangulation to be Delaunay. + * + * \using + * - RemoveBoundaryNode if \e dart represents a node at the boundary + * - RemoveInteriorNode if \e dart represents an interior node + * + * \note + * - The node cannot belong to a fixed (constrained) edge that is not + * swappable. (An endless loop is likely to occur in this case). + */ +template <class TRAITS_TYPE, class DART_TYPE> +void TRIANGULATION_HELPER::RemoveNode( DART_TYPE& aDart ) +{ + + if( isBoundaryNode( aDart ) ) + RemoveBoundaryNode<TRAITS_TYPE>( aDart ); + else + RemoveInteriorNode<TRAITS_TYPE>( aDart ); +} + +/** Removes the boundary node associated with \e dart and + * updates the triangulation to be Delaunay. + * + * \using + * - SwapEdgesAwayFromBoundaryNode + * - OptimizeDelaunay + * + * \require + * - \ref hed::TTLtraits::removeBoundaryTriangle "TRAITS_TYPE::removeBoundaryTriangle" (Dart&) + */ +template <class TRAITS_TYPE, class DART_TYPE> +void TRIANGULATION_HELPER::RemoveBoundaryNode( DART_TYPE& aDart ) +{ + + // ... and update Delaunay + // - CCW dart must be given (for remove) + // - No dart is delivered back now (but this is possible if + // we assume that there is not only one triangle left in the m_triangulation. + + // Position at boundary edge and CCW + if( !IsBoundaryEdge( aDart ) ) + { + aDart.Alpha1(); // ensures that next function delivers back a CCW dart (if the given dart is CCW) + PositionAtNextBoundaryEdge( aDart ); + } + + std::list<DART_TYPE> swapped_edges; + SwapEdgesAwayFromBoundaryNode<TRAITS_TYPE>( aDart, swapped_edges ); + + // Remove boundary triangles and remove the new boundary from the list + // of swapped edges, see below. + DART_TYPE d_iter = aDart; + DART_TYPE dnext = aDart; + bool bend = false; + while( bend == false ) + { + dnext.Alpha1().Alpha2(); + if( IsBoundaryEdge( dnext ) ) + bend = true; // Stop when boundary + + // Generic: Also remove the new boundary from the list of swapped edges + DART_TYPE n_bedge = d_iter; + n_bedge.Alpha1().Alpha0().Alpha1().Alpha2(); // new boundary edge + + // ??? can we avoid find if we do this in swap away? + typename std::list<DART_TYPE>::iterator it; + it = find( swapped_edges.begin(), swapped_edges.end(), n_bedge ); + + if( it != swapped_edges.end() ) + swapped_edges.erase( it ); + + // Remove the boundary triangle + m_triangulation.removeBoundaryTriangle( d_iter ); + d_iter = dnext; + } + + // Optimize Delaunay + typedef std::list<DART_TYPE> DART_LIST_TYPE; + OptimizeDelaunay<TRAITS_TYPE, DART_TYPE, DART_LIST_TYPE>( swapped_edges ); +} + + +/** Removes the interior node associated with \e dart and + * updates the triangulation to be Delaunay. + * + * \using + * - SwapEdgesAwayFromInteriorNode + * - OptimizeDelaunay + * + * \require + * - \ref hed::TTLtraits::reverse_splitTriangle "TRAITS_TYPE::reverse_splitTriangle" (Dart&) + * + * \note + * - The node cannot belong to a fixed (constrained) edge that is not + * swappable. (An endless loop is likely to occur in this case). + */ +template <class TRAITS_TYPE, class DART_TYPE> +void TRIANGULATION_HELPER::RemoveInteriorNode( DART_TYPE& aDart ) +{ + // ... and update to Delaunay. + // Must allow degeneracy temporarily, see comments in swap edges away + // Assumes: + // - revese_splitTriangle does not affect darts + // outside the resulting triangle. + + // 1) Swaps edges away from the node until degree=3 (generic) + // 2) Removes the remaining 3 triangles and creates a new to fill the hole + // unsplitTriangle which is required + // 3) Runs LOP on the platelet to obtain a Delaunay m_triangulation + // (No dart is delivered as output) + + // Assumes dart is counterclockwise + + std::list<DART_TYPE> swapped_edges; + SwapEdgesAwayFromInteriorNode<TRAITS_TYPE>( aDart, swapped_edges ); + + // The reverse operation of split triangle: + // Make one triangle of the three triangles at the node associated with dart + // TRAITS_TYPE:: + m_triangulation.reverseSplitTriangle( aDart ); + + // ???? Not generic yet if we are very strict: + // When calling unsplit triangle, darts at the three opposite sides may + // change! + // Should we hide them longer away??? This is possible since they cannot + // be boundary edges. + // ----> Or should we just require that they are not changed??? + + // Make the swapped-away edges Delaunay. + // Note the theoretical result: if there are no edges in the list, + // the triangulation is Delaunay already + + OptimizeDelaunay<TRAITS_TYPE, DART_TYPE>( swapped_edges ); +} + +//@} // End of Delaunay Triangulation Group + +/** @name Topological and Geometric Queries */ +//@{ +//------------------------------------------------------------------------------------------------ +// Private/Hidden function (might change later) +template <class TOPOLOGY_ELEMENT_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::isMemberOfFace( const TOPOLOGY_ELEMENT_TYPE& aTopologyElement, + const DART_TYPE& aDart ) +{ + // Check if the given topology element (node, edge or face) is a member of the face + // Assumes: + // - DART_TYPE::isMember(TOPOLOGY_ELEMENT_TYPE) + + DART_TYPE dart_iter = aDart; + + do + { + if( dart_iter.isMember( aTopologyElement ) ) + return true; + dart_iter.Alpha0().Alpha1(); + } + while( dart_iter != aDart ); + + return false; +} + +//------------------------------------------------------------------------------------------------ +// Private/Hidden function (might change later) +template <class TRAITS_TYPE, class NODE_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::locateFaceWithNode( const NODE_TYPE& aNode, DART_TYPE& aDartIter ) +{ + // Locate a face in the topology structure with the given node as a member + // Assumes: + // - TRAITS_TYPE::Orient2D(DART_TYPE, DART_TYPE, NODE_TYPE) + // - DART_TYPE::isMember(NODE_TYPE) + // - Note that if false is returned, the node might still be in the + // topology structure. Application programmer + // should check all if by hypothesis the node is in the topology structure; + // see doc. on LocateTriangle. + + bool status = LocateFaceSimplest<TRAITS_TYPE>( aNode, aDartIter ); + + if( status == false ) + return status; + + // True was returned from LocateFaceSimplest, but if the located triangle is + // degenerate and the node is on the extension of the edges, + // the node might still be inside. Check if node is a member and return false + // if not. (Still the node might be in the topology structure, see doc. above + // and in locateTriangle(const POINT_TYPE& point, DART_TYPE& dart_iter) + + return isMemberOfFace( aNode, aDartIter ); +} + +/** Locates the face containing a given point. + * It is assumed that the tessellation (e.g. a triangulation) is \e regular in the sense that + * there are no holes, the boundary is convex and there are no degenerate faces. + * + * \param aPoint + * A point to be located + * + * \param aDart + * An arbitrary CCW dart in the triangulation\n + * Output: A CCW dart in the located face + * + * \retval bool + * \c true if a face is found; \c false if not. + * + * \require + * - \ref hed::TTLtraits::Orient2D "TRAITS_TYPE::Orient2D" (DART_TYPE&, DART_TYPE&, POINT_TYPE&) + * + * \note + * - If \c false is returned, \e point may still be inside a face if the tessellation is not + * \e regular as explained above. + * + * \see + * LocateTriangle + */ +template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::LocateFaceSimplest( const POINT_TYPE& aPoint, DART_TYPE& aDart ) +{ + // Not degenerate triangles if point is on the extension of the edges + // But inTriangle may be called in case of true (may update to inFace2) + // Convex boundary + // no holes + // convex faces (works for general convex faces) + // Not specialized for triangles, but ok? + // + // TRAITS_TYPE::orint2d(POINT_TYPE) is the half open half-plane defined + // by the dart: + // n1 = dart.node() + // n2 = dart.Alpha0().node + // Only the following gives true: + // ((n2->x()-n1->x())*(point.y()-n1->y()) >= (point.x()-n1->x())*(n2->y()-n1->y())) + + DART_TYPE dart_start; + dart_start = aDart; + DART_TYPE dart_prev; + + DART_TYPE d0; + for( ;; ) + { + d0 = aDart; + d0.Alpha0(); + + if( TRAITS_TYPE::Orient2D( aDart, d0, aPoint ) >= 0 ) + { + aDart.Alpha0().Alpha1(); + if( aDart == dart_start ) + return true; // left to all edges in face + } + else + { + dart_prev = aDart; + aDart.Alpha2(); + + if( aDart == dart_prev ) + return false; // iteration to outside boundary + + dart_start = aDart; + dart_start.Alpha0(); + + aDart.Alpha1(); // avoid twice on same edge and ccw in next + } + } +} + + +/** Locates the triangle containing a given point. + * It is assumed that the triangulation is \e regular in the sense that there + * are no holes and the boundary is convex. + * This function deals with degeneracy to some extent, but round-off errors may still + * lead to a wrong result if triangles are degenerate. + * + * \param point + * A point to be located + * + * \param dart + * An arbitrary CCW dart in the triangulation\n + * Output: A CCW dart in the located triangle + * + * \retval bool + * \c true if a triangle is found; \c false if not.\n + * If \e point is outside the m_triangulation, in which case \c false is returned, + * then the edge associated with \e dart will be at the boundary of the m_triangulation. + * + * \using + * - LocateFaceSimplest + * - InTriangle + */ +template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::LocateTriangle( const POINT_TYPE& aPoint, DART_TYPE& aDart ) +{ + // The purpose is to have a fast and stable procedure that + // i) avoids concluding that a point is inside a triangle if it is not inside + // ii) avoids infinite loops + + // Thus, if false is returned, the point might still be inside a triangle in + // the triangulation. But this will probably only occur in the following cases: + // i) There are holes in the triangulation which causes the procedure to stop. + // ii) The boundary of the m_triangulation is not convex. + // ii) There might be degenerate triangles interior to the triangulation, or on the + // the boundary, which in some cases might cause the procedure to stop there due + // to the logic of the algorithm. + + // It is the application programmer's responsibility to check further if false is + // returned. For example, if by hypothesis the point is inside a triangle + // in the triangulation and and false is returned, then all triangles in the + // triangulation should be checked by the application. This can be done using + // the function: + // bool inTriangle(const POINT_TYPE& point, const DART_TYPE& dart). + + // Assumes: + // - CrossProduct2D, ScalarProduct2D etc., see functions called + + bool status = LocateFaceSimplest<TRAITS_TYPE>( aPoint, aDart ); + + if( status == false ) + return status; + + // There may be degeneracy, i.e., the point might be outside the triangle + // on the extension of the edges of a degenerate triangle. + + // The next call returns true if inside a non-degenerate or a degenerate triangle, + // but false if the point coincides with the "supernode" in the case where all + // edges are degenerate. + return InTriangle<TRAITS_TYPE>( aPoint, aDart ); +} + +/** Checks if \e point is inside the triangle associated with \e dart. + * This function deals with degeneracy to some extent, but round-off errors may still + * lead to wrong result if the triangle is degenerate. + * + * \param aDart + * A CCW dart in the triangle + * + * \require + * - \ref hed::TTLtraits::CrossProduct2D "TRAITS_TYPE::CrossProduct2D" (DART_TYPE&, POINT_TYPE&) + * - \ref hed::TTLtraits::ScalarProduct2D "TRAITS_TYPE::ScalarProduct2D" (DART_TYPE&, POINT_TYPE&) + * + * \see + * InTriangleSimplest + */ +template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::InTriangle( const POINT_TYPE& aPoint, const DART_TYPE& aDart ) +{ + + // SHOULD WE INCLUDE A STRATEGY WITH EDGE X e_1 ETC? TO GUARANTEE THAT + // ONLY ON ONE EDGE? BUT THIS DOES NOT SOLVE PROBLEMS WITH + // notInE1 && notInE1.neghbour ? + + // Returns true if inside (but not necessarily strictly inside) + // Works for degenerate triangles, but not when all edges are degenerate, + // and the aPoint coincides with all nodes; + // then false is always returned. + + typedef typename TRAITS_TYPE::REAL_TYPE REAL_TYPE; + + DART_TYPE dart_iter = aDart; + + REAL_TYPE cr1 = TRAITS_TYPE::CrossProduct2D( dart_iter, aPoint ); + if( cr1 < 0 ) + return false; + + dart_iter.Alpha0().Alpha1(); + REAL_TYPE cr2 = TRAITS_TYPE::CrossProduct2D( dart_iter, aPoint ); + + if( cr2 < 0 ) + return false; + + dart_iter.Alpha0().Alpha1(); + REAL_TYPE cr3 = TRAITS_TYPE::CrossProduct2D( dart_iter, aPoint ); + if( cr3 < 0 ) + return false; + + // All cross products are >= 0 + // Check for degeneracy + if( cr1 != 0 || cr2 != 0 || cr3 != 0 ) + return true; // inside non-degenerate face + + // All cross-products are zero, i.e. degenerate triangle, check if inside + // Strategy: d.ScalarProduct2D >= 0 && alpha0(d).d.ScalarProduct2D >= 0 for one of + // the edges. But if all edges are degenerate and the aPoint is on (all) the nodes, + // then "false is returned". + + DART_TYPE dart_tmp = dart_iter; + REAL_TYPE sc1 = TRAITS_TYPE::ScalarProduct2D( dart_tmp, aPoint ); + REAL_TYPE sc2 = TRAITS_TYPE::ScalarProduct2D( dart_tmp.Alpha0(), aPoint ); + + if( sc1 >= 0 && sc2 >= 0 ) + { + // test for degenerate edge + if( sc1 != 0 || sc2 != 0 ) + return true; // interior to this edge or on a node (but see comment above) + } + + dart_tmp = dart_iter.Alpha0().Alpha1(); + sc1 = TRAITS_TYPE::ScalarProduct2D( dart_tmp, aPoint ); + sc2 = TRAITS_TYPE::ScalarProduct2D( dart_tmp.Alpha0(), aPoint ); + + if( sc1 >= 0 && sc2 >= 0 ) + { + // test for degenerate edge + if( sc1 != 0 || sc2 != 0 ) + return true; // interior to this edge or on a node (but see comment above) + } + + dart_tmp = dart_iter.Alpha1(); + sc1 = TRAITS_TYPE::ScalarProduct2D( dart_tmp, aPoint ); + sc2 = TRAITS_TYPE::ScalarProduct2D( dart_tmp.Alpha0(), aPoint ); + + if( sc1 >= 0 && sc2 >= 0 ) + { + // test for degenerate edge + if( sc1 != 0 || sc2 != 0 ) + return true; // interior to this edge or on a node (but see comment above) + } + + // Not on any of the edges of the degenerate triangle. + // The only possibility for the aPoint to be "inside" is that all edges are degenerate + // and the point coincide with all nodes. So false is returned in this case. + + return false; +} + + + //------------------------------------------------------------------------------------------------ +// Private/Hidden function (might change later) +template <class DART_TYPE> +void TRIANGULATION_HELPER::getAdjacentTriangles( const DART_TYPE& aDart, DART_TYPE& aT1, + DART_TYPE& aT2, DART_TYPE& aT3 ) +{ + + DART_TYPE dart_iter = aDart; + + // add first + if( dart_iter.Alpha2() != aDart ) + { + aT1 = dart_iter; + dart_iter = aDart; + } + + // add second + dart_iter.Alpha0(); + dart_iter.Alpha1(); + DART_TYPE dart_prev = dart_iter; + + if( ( dart_iter.Alpha2() ) != dart_prev ) + { + aT2 = dart_iter; + dart_iter = dart_prev; + } + + // add third + dart_iter.Alpha0(); + dart_iter.Alpha1(); + dart_prev = dart_iter; + + if( ( dart_iter.Alpha2() ) != dart_prev ) + aT3 = dart_iter; +} + +//------------------------------------------------------------------------------------------------ +/** Gets the boundary as sequence of darts, where the edges associated with the darts are boundary + * edges, given a dart with an associating edge at the boundary of a topology structure. + * The first dart in the sequence will be the given one, and the others will have the same + * orientation (CCW or CW) as the first. + * Assumes that the given dart is at the boundary. + * + * \param aDart + * A dart at the boundary (CCW or CW) + * + * \param aBoundary + * A sequence of darts, where the associated edges are the boundary edges + * + * \require + * - DART_LIST_TYPE::push_back (DART_TYPE&) + */ +template <class DART_TYPE, class DART_LIST_TYPE> +void TRIANGULATION_HELPER::GetBoundary( const DART_TYPE& aDart, DART_LIST_TYPE& aBoundary ) +{ + // assumes the given dart is at the boundary (by edge) + + DART_TYPE dart_iter( aDart ); + aBoundary.push_back( dart_iter ); // Given dart as first element + dart_iter.Alpha0(); + PositionAtNextBoundaryEdge( dart_iter ); + + while( dart_iter != aDart ) + { + aBoundary.push_back( dart_iter ); + dart_iter.Alpha0(); + PositionAtNextBoundaryEdge( dart_iter ); + } +} + +/** Checks if the edge associated with \e dart is at + * the boundary of the m_triangulation. + * + * \par Implements: + * \code + * DART_TYPE dart_iter = dart; + * if (dart_iter.Alpha2() == dart) + * return true; + * else + * return false; + * \endcode + */ +template <class DART_TYPE> +bool TRIANGULATION_HELPER::IsBoundaryEdge( const DART_TYPE& aDart ) +{ + DART_TYPE dart_iter = aDart; + + if( dart_iter.Alpha2() == aDart ) + return true; + else + return false; +} + +/** Checks if the face associated with \e dart is at + * the boundary of the m_triangulation. + */ +template <class DART_TYPE> +bool TRIANGULATION_HELPER::IsBoundaryFace( const DART_TYPE& aDart ) +{ + // Strategy: boundary if alpha2(d)=d + + DART_TYPE dart_iter( aDart ); + DART_TYPE dart_prev; + + do + { + dart_prev = dart_iter; + + if( dart_iter.Alpha2() == dart_prev ) + return true; + else + dart_iter = dart_prev; // back again + + dart_iter.Alpha0(); + dart_iter.Alpha1(); + + } while( dart_iter != aDart ); + + return false; +} + +/** Checks if the node associated with \e dart is at + * the boundary of the m_triangulation. + */ +template <class DART_TYPE> +bool TRIANGULATION_HELPER::IsBoundaryNode( const DART_TYPE& aDart ) +{ + // Strategy: boundary if alpha2(d)=d + + DART_TYPE dart_iter( aDart ); + DART_TYPE dart_prev; + + // If input dart is reached again, then internal node + // If alpha2(d)=d, then boundary + + do + { + dart_iter.Alpha1(); + dart_prev = dart_iter; + dart_iter.Alpha2(); + + if( dart_iter == dart_prev ) + return true; + + } while( dart_iter != aDart ); + + return false; +} + +/** Returns the degree of the node associated with \e dart. + * + * \par Definition: + * The \e degree (or valency) of a node \e V in a m_triangulation, + * is defined as the number of edges incident with \e V, i.e., + * the number of edges joining \e V with another node in the triangulation. + */ +template <class DART_TYPE> +int TRIANGULATION_HELPER::GetDegreeOfNode( const DART_TYPE& aDart ) +{ + DART_TYPE dart_iter( aDart ); + DART_TYPE dart_prev; + + // If input dart is reached again, then interior node + // If alpha2(d)=d, then boundary + + int degree = 0; + bool boundaryVisited = false; + do + { + dart_iter.Alpha1(); + degree++; + dart_prev = dart_iter; + + dart_iter.Alpha2(); + + if( dart_iter == dart_prev ) + { + if( !boundaryVisited ) + { + boundaryVisited = true; + // boundary is reached first time, count in the reversed direction + degree++; // count the start since it is not done above + dart_iter = aDart; + dart_iter.Alpha2(); + } else + return degree; + } + + } while( dart_iter != aDart ); + + return degree; +} + +// Modification of GetDegreeOfNode: +// Strategy, reverse the list and start in the other direction if the boundary +// is reached. NB. copying of darts but ok., or we could have collected pointers, +// but the memory management. + +// NOTE: not symmetry if we choose to collect opposite edges +// now we collect darts with radiating edges + +// Remember that we must also copy the node, but ok with push_back +// The size of the list will be the degree of the node + +// No CW/CCW since topology only + +// Each dart consists of an incident edge and an adjacent node. +// But note that this is only how we interpret the dart in this implementation. +// Given this list, how can we find the opposite edges: +// We can perform alpha1 on each, but for boundary nodes we will get one edge twice. +// But this is will always be the last dart! +// The darts in the list are in sequence and starts with the alpha0(dart) +// alpha0, alpha1 and alpha2 + +// Private/Hidden function +template <class DART_TYPE> +void TRIANGULATION_HELPER::getNeighborNodes( const DART_TYPE& aDart, + std::list<DART_TYPE>& aNodeList, bool& aBoundary ) +{ + DART_TYPE dart_iter( aDart ); + dart_iter.Alpha0(); // position the dart at an opposite node + + DART_TYPE dart_prev = dart_iter; + bool start_at_boundary = false; + dart_iter.Alpha2(); + + if( dart_iter == dart_prev ) + start_at_boundary = true; + else + dart_iter = dart_prev; // back again + + DART_TYPE dart_start = dart_iter; + + do + { + aNodeList.push_back( dart_iter ); + dart_iter.Alpha1(); + dart_iter.Alpha0(); + dart_iter.Alpha1(); + dart_prev = dart_iter; + dart_iter.Alpha2(); + + if( dart_iter == dart_prev ) + { + // boundary reached + aBoundary = true; + + if( start_at_boundary == true ) + { + // add the dart which now is positioned at the opposite boundary + aNodeList.push_back( dart_iter ); + return; + } + else + { + // call the function again such that we start at the boundary + // first clear the list and reposition to the initial node + dart_iter.Alpha0(); + aNodeList.clear(); + getNeighborNodes( dart_iter, aNodeList, aBoundary ); + + return; // after one recursive step + } + } + } + while( dart_iter != dart_start ); + + aBoundary = false; +} + +/** Gets the 0-orbit around an interior node. + * + * \param aDart + * A dart (CCW or CW) positioned at an \e interior node. + * + * \retval aOrbit + * Sequence of darts with one orbit for each arc. All the darts have the same + * orientation (CCW or CW) as \e dart, and \e dart is the first element + * in the sequence. + * + * \require + * - DART_LIST_TYPE::push_back (DART_TYPE&) + * + * \see + * Get0OrbitBoundary + */ +template <class DART_TYPE, class DART_LIST_TYPE> +void TRIANGULATION_HELPER::Get0OrbitInterior( const DART_TYPE& aDart, DART_LIST_TYPE& aOrbit ) +{ + DART_TYPE d_iter = aDart; + aOrbit.push_back( d_iter ); + d_iter.Alpha1().Alpha2(); + + while( d_iter != aDart ) + { + aOrbit.push_back( d_iter ); + d_iter.Alpha1().Alpha2(); + } +} + +/** Gets the 0-orbit around a node at the boundary + * + * \param aDart + * A dart (CCW or CW) positioned at a \e boundary \e node and at a \e boundary \e edge. + * + * \retval orbit + * Sequence of darts with one orbit for each arc. All the darts, \e exept \e the \e last one, + * have the same orientation (CCW or CW) as \e dart, and \e dart is the first element + * in the sequence. + * + * \require + * - DART_LIST_TYPE::push_back (DART_TYPE&) + * + * \note + * - The last dart in the sequence have opposite orientation compared to the others! + * + * \see + * Get0OrbitInterior + */ +template <class DART_TYPE, class DART_LIST_TYPE> +void TRIANGULATION_HELPER::Get0OrbitBoundary( const DART_TYPE& aDart, DART_LIST_TYPE& aOrbit ) +{ + DART_TYPE dart_prev; + DART_TYPE d_iter = aDart; + + do + { + aOrbit.push_back( d_iter ); + d_iter.Alpha1(); + dart_prev = d_iter; + d_iter.Alpha2(); + } + while( d_iter != dart_prev ); + + aOrbit.push_back( d_iter ); // the last one with opposite orientation +} + +/** Checks if the two darts belong to the same 0-orbit, i.e., + * if they share a node. + * \e d1 and/or \e d2 can be CCW or CW. + * + * (This function also examines if the the node associated with + * \e d1 is at the boundary, which slows down the function (slightly). + * If it is known that the node associated with \e d1 is an interior + * node and a faster version is needed, the user should implement his/her + * own version.) + */ +template <class DART_TYPE> +bool TRIANGULATION_HELPER::Same0Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 ) +{ + // Two copies of the same dart + DART_TYPE d_iter = aD2; + DART_TYPE d_end = aD2; + + if( isBoundaryNode( d_iter ) ) + { + // position at both boundary edges + PositionAtNextBoundaryEdge( d_iter ); + d_end.Alpha1(); + PositionAtNextBoundaryEdge( d_end ); + } + + for( ;; ) + { + if( d_iter == aD1 ) + return true; + + d_iter.Alpha1(); + + if( d_iter == aD1 ) + return true; + + d_iter.Alpha2(); + + if( d_iter == d_end ) + break; + } + + return false; +} + +/** Checks if the two darts belong to the same 1-orbit, i.e., + * if they share an edge. + * \e d1 and/or \e d2 can be CCW or CW. + */ +template <class DART_TYPE> +bool TRIANGULATION_HELPER::Same1Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 ) +{ + DART_TYPE d_iter = aD2; + + // (Also works at the boundary) + return ( d_iter == aD1 || d_iter.Alpha0() == aD1 || + d_iter.Alpha2() == aD1 || d_iter.Alpha0() == aD1 ); +} + +//------------------------------------------------------------------------------------------------ +/** Checks if the two darts belong to the same 2-orbit, i.e., + * if they lie in the same triangle. + * \e d1 and/or \e d2 can be CCW or CW + */ +template <class DART_TYPE> +bool TRIANGULATION_HELPER::Same2Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 ) +{ + DART_TYPE d_iter = aD2; + + return ( d_iter == aD1 || d_iter.Alpha0() == aD1 || d_iter.Alpha1() == aD1 || + d_iter.Alpha0() == aD1 || d_iter.Alpha1() == aD1 || d_iter.Alpha0() == aD1 ); +} + +// Private/Hidden function +template <class TRAITS_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::degenerateTriangle( const DART_TYPE& aDart ) +{ + // Check if triangle is degenerate + // Assumes CCW dart + + DART_TYPE d1 = aDart; + DART_TYPE d2 = d1; + d2.Alpha1(); + + return ( TRAITS_TYPE::CrossProduct2D( d1, d2 ) == 0 ); +} + +/** Checks if the edge associated with \e dart is swappable, i.e., if the edge + * is a diagonal in a \e strictly convex (or convex) quadrilateral. + * + * \param aAllowDegeneracy + * If set to true, the function will also return true if the numerical calculations + * indicate that the quadrilateral is convex only, and not necessarily strictly + * convex. + * + * \require + * - \ref hed::TTLtraits::CrossProduct2D "TRAITS_TYPE::CrossProduct2D" (Dart&, Dart&) + */ +template <class TRAITS_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::SwappableEdge( const DART_TYPE& aDart, bool aAllowDegeneracy ) +{ + // How "safe" is it? + + if( IsBoundaryEdge( aDart ) ) + return false; + + // "angles" are at the diagonal + DART_TYPE d1 = aDart; + d1.Alpha2().Alpha1(); + DART_TYPE d2 = aDart; + d2.Alpha1(); + + if( aAllowDegeneracy ) + { + if( TRAITS_TYPE::CrossProduct2D( d1, d2 ) < 0.0 ) + return false; + } + else + { + if( TRAITS_TYPE::CrossProduct2D( d1, d2 ) <= 0.0 ) + return false; + } + + // Opposite side (still angle at the diagonal) + d1 = aDart; + d1.Alpha0(); + d2 = d1; + d1.Alpha1(); + d2.Alpha2().Alpha1(); + + if( aAllowDegeneracy ) + { + if( TRAITS_TYPE::CrossProduct2D( d1, d2 ) < 0.0 ) + return false; + } + else + { + if( TRAITS_TYPE::CrossProduct2D( d1, d2 ) <= 0.0 ) + return false; + } + + return true; +} + +/** Given a \e dart, CCW or CW, positioned in a 0-orbit at the boundary of a tessellation. + * Position \e dart at a boundary edge in the same 0-orbit.\n + * If the given \e dart is CCW, \e dart is positioned at the left boundary edge + * and will be CW.\n + * If the given \e dart is CW, \e dart is positioned at the right boundary edge + * and will be CCW. + * + * \note + * - The given \e dart must have a source node at the boundary, otherwise an + * infinit loop occurs. + */ +template <class DART_TYPE> +void TRIANGULATION_HELPER::PositionAtNextBoundaryEdge( DART_TYPE& aDart ) +{ + DART_TYPE dart_prev; + + // If alpha2(d)=d, then boundary + + //old convention: dart.Alpha0(); + do + { + aDart.Alpha1(); + dart_prev = aDart; + aDart.Alpha2(); + } + while( aDart != dart_prev ); +} + +/** Checks if the boundary of a triangulation is convex. + * + * \param dart + * A CCW dart at the boundary of the m_triangulation + * + * \require + * - \ref hed::TTLtraits::CrossProduct2D "TRAITS_TYPE::CrossProduct2D" (const Dart&, const Dart&) + */ +template <class TRAITS_TYPE, class DART_TYPE> +bool TRIANGULATION_HELPER::ConvexBoundary( const DART_TYPE& aDart ) +{ + std::list<DART_TYPE> blist; + getBoundary( aDart, blist ); + + int no; + no = (int) blist.size(); + typename std::list<DART_TYPE>::const_iterator bit = blist.begin(); + DART_TYPE d1 = *bit; + ++bit; + DART_TYPE d2; + bool convex = true; + + for( ; bit != blist.end(); ++bit ) + { + d2 = *bit; + double crossProd = TRAITS_TYPE::CrossProduct2D( d1, d2 ); + + if( crossProd < 0.0 ) + { + //cout << "!!! Boundary is NOT convex: crossProd = " << crossProd << endl; + convex = false; + return convex; + } + + d1 = d2; + } + + // Check the last angle + d2 = *blist.begin(); + double crossProd = TRAITS_TYPE::CrossProduct2D( d1, d2 ); + + if( crossProd < 0.0 ) + { + //cout << "!!! Boundary is NOT convex: crossProd = " << crossProd << endl; + convex = false; + } + + //if (convex) + // cout << "\n---> Boundary is convex\n" << endl; + //cout << endl; + return convex; +} + +//@} // End of Topological and Geometric Queries Group + +/** @name Utilities for Delaunay Triangulation */ +//@{ +//------------------------------------------------------------------------------------------------ +/** Optimizes the edges in the given sequence according to the + * \e Delaunay criterion, i.e., such that the edge will fullfill the + * \e circumcircle criterion (or equivalently the \e MaxMin + * angle criterion) with respect to the quadrilaterals where + * they are diagonals. + * + * \param aElist + * The sequence of edges + * + * \require + * - \ref hed::TTLtraits::swapEdge "TRAITS_TYPE::swapEdge" (DART_TYPE& \e dart)\n + * \b Note: Must be implemented such that \e dart is delivered back in a position as + * seen if it was glued to the edge when swapping (rotating) the edge CCW + * + * \using + * - swapTestDelaunay + */ +template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE> +void TRIANGULATION_HELPER::OptimizeDelaunay( DART_LIST_TYPE& aElist ) +{ + OptimizeDelaunay<TRAITS_TYPE, DART_TYPE, DART_LIST_TYPE>( aElist, aElist.end() ); +} + +//------------------------------------------------------------------------------------------------ +template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE> +void TRIANGULATION_HELPER::OptimizeDelaunay( DART_LIST_TYPE& aElist, + const typename DART_LIST_TYPE::iterator aEnd ) +{ + // CCW darts + // Optimize here means Delaunay, but could be any criterion by + // requiring a "should swap" in the traits class, or give + // a function object? + // Assumes that elist has only one dart for each arc. + // Darts outside the quadrilateral are preserved + + // For some data structures it is possible to preserve + // all darts when swapping. Thus a preserve_darts_when swapping + // ccould be given to indicate this and we would gain performance by avoiding + // find in list. + + // Requires that swap retuns a dart in the "same position when rotated CCW" + // (A vector instead of a list may be better.) + + // First check that elist is not empty + if( aElist.empty() ) + return; + + // Avoid cycling by more extensive circumcircle test + bool cycling_check = true; + bool optimal = false; + typename DART_LIST_TYPE::iterator it; + + typename DART_LIST_TYPE::iterator end_opt = aEnd; + + // Hmm... The following code is trying to derefence an iterator that may + // be invalid. This may lead to debug error on Windows, so we comment out + // this code. Checking elist.empty() above will prevent some + // problems... + // + // last_opt is passed the end of the "active list" + //typename DART_LIST_TYPE::iterator end_opt; + //if (*end != NULL) + // end_opt = end; + //else + // end_opt = elist.end(); + + while( !optimal ) + { + optimal = true; + for( it = aElist.begin(); it != end_opt; ++it ) + { + if( SwapTestDelaunay<TRAITS_TYPE>( *it, cycling_check ) ) + { + // Preserve darts. Potential darts in the list are: + // - The current dart + // - the four CCW darts on the boundary of the quadrilateral + // (the current arc has only one dart) + + SwapEdgeInList<TRAITS_TYPE, DART_TYPE>( it, aElist ); + + optimal = false; + } // end if should swap + } // end for + } // end pass +} + +/** Checks if the edge associated with \e dart should be swapped according + * to the \e Delaunay criterion, i.e., the \e circumcircle criterion (or + * equivalently the \e MaxMin angle criterion). + * + * \param aCyclingCheck + * Must be set to \c true when used in connection with optimization algorithms, + * e.g., OptimizeDelaunay. This will avoid cycling and infinite loops in nearly + * neutral cases. + * + * \require + * - \ref hed::TTLtraits::ScalarProduct2D "TRAITS_TYPE::ScalarProduct2D" (DART_TYPE&, DART_TYPE&) + * - \ref hed::TTLtraits::CrossProduct2D "TRAITS_TYPE::CrossProduct2D" (DART_TYPE&, DART_TYPE&) + */ +template <class TRAITS_TYPE, class DART_TYPE> +#if ((_MSC_VER > 0) && (_MSC_VER < 1300))//#ifdef _MSC_VER +bool TRIANGULATION_HELPER::SwapTestDelaunay(const DART_TYPE& aDart, bool aCyclingCheck = false) const +{ +#else +bool TRIANGULATION_HELPER::SwapTestDelaunay( const DART_TYPE& aDart, bool aCyclingCheck ) const +{ +#endif + // The general strategy is taken from Cline & Renka. They claim that + // their algorithm insure numerical stability, but experiments show + // that this is not correct for neutral, or almost neutral cases. + // I have extended this strategy (without using tolerances) to avoid + // cycling and infinit loops when used in connection with LOP algorithms; + // see the comments below. + + typedef typename TRAITS_TYPE::REAL_TYPE REAL_TYPE; + + if( IsBoundaryEdge( aDart ) ) + return false; + + DART_TYPE v11 = aDart; + v11.Alpha1().Alpha0(); + DART_TYPE v12 = v11; + v12.Alpha1(); + + DART_TYPE v22 = aDart; + v22.Alpha2().Alpha1().Alpha0(); + DART_TYPE v21 = v22; + v21.Alpha1(); + + REAL_TYPE cos1 = TRAITS_TYPE::ScalarProduct2D( v11, v12 ); + REAL_TYPE cos2 = TRAITS_TYPE::ScalarProduct2D( v21, v22 ); + + // "Angles" are opposite to the diagonal. + // The diagonals should be swapped iff (t1+t2) .gt. 180 + // degrees. The following two tests insure numerical + // stability according to Cline & Renka. But experiments show + // that cycling may still happen; see the aditional test below. + if( cos1 >= 0 && cos2 >= 0 ) // both angles are grater or equual 90 + return false; + + if( cos1 < 0 && cos2 < 0 ) // both angles are less than 90 + return true; + + REAL_TYPE sin1 = TRAITS_TYPE::CrossProduct2D( v11, v12 ); + REAL_TYPE sin2 = TRAITS_TYPE::CrossProduct2D( v21, v22 ); + REAL_TYPE sin12 = sin1 * cos2 + cos1 * sin2; + + if( sin12 >= 0 ) // equality represents a neutral case + return false; + + if( aCyclingCheck ) + { + // situation so far is sin12 < 0. Test if this also + // happens for the swapped edge. + + // The numerical calculations so far indicate that the edge is + // not Delaunay and should not be swapped. But experiments show that + // in neutral cases, or almost neutral cases, it may happen that + // the swapped edge may again be found to be not Delaunay and thus + // be swapped if we return true here. This may lead to cycling and + // an infinte loop when used, e.g., in connection with OptimizeDelaunay. + // + // In an attempt to avoid this we test if the swapped edge will + // also be found to be not Delaunay by repeating the last test above + // for the swapped edge. + // We now rely on the general requirement for TRAITS_TYPE::swapEdge which + // should deliver CCW dart back in "the same position"; see the general + // description. This will insure numerical stability as the next calculation + // is the same as if this function was called again with the swapped edge. + // Cycling is thus impossible provided that the initial tests above does + // not result in ambiguity (and they should probably not do so). + + v11.Alpha0(); + v12.Alpha0(); + v21.Alpha0(); + v22.Alpha0(); + // as if the edge was swapped/rotated CCW + cos1 = TRAITS_TYPE::ScalarProduct2D( v22, v11 ); + cos2 = TRAITS_TYPE::ScalarProduct2D( v12, v21 ); + sin1 = TRAITS_TYPE::CrossProduct2D( v22, v11 ); + sin2 = TRAITS_TYPE::CrossProduct2D( v12, v21 ); + sin12 = sin1 * cos2 + cos1 * sin2; + + if( sin12 < 0 ) + { + // A neutral case, but the tests above lead to swapping + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------- +// +// x +//" / \ " +// / | \ Darts: +//oe2 / | \ oe2 = oppEdge2 +// x....|....x +// \ d| d/ d = diagonal (input and output) +// \ | / +// oe1 \ / oe1 = oppEdge1 +// x +// +//----------------------------------------------------------------------- +/** Recursively swaps edges in the triangulation according to the \e Delaunay criterion. + * + * \param aDiagonal + * A CCW dart representing the edge where the recursion starts from. + * + * \require + * - \ref hed::TTLtraits::swapEdge "TRAITS_TYPE::swapEdge" (DART_TYPE&)\n + * \b Note: Must be implemented such that the darts outside the quadrilateral + * are not affected by the swap. + * + * \using + * - Calls itself recursively + */ +template <class TRAITS_TYPE, class DART_TYPE> +void TRIANGULATION_HELPER::RecSwapDelaunay( DART_TYPE& aDiagonal ) +{ + if( !SwapTestDelaunay<TRAITS_TYPE>( aDiagonal ) ) + // ??? swapTestDelaunay also checks if boundary, so this can be optimized + return; + + // Get the other "edges" of the current triangle; see illustration above. + DART_TYPE oppEdge1 = aDiagonal; + oppEdge1.Alpha1(); + bool b1; + + if( IsBoundaryEdge( oppEdge1 ) ) + b1 = true; + else + { + b1 = false; + oppEdge1.Alpha2(); + } + + DART_TYPE oppEdge2 = aDiagonal; + oppEdge2.Alpha0().Alpha1().Alpha0(); + bool b2; + + if( IsBoundaryEdge( oppEdge2 ) ) + b2 = true; + else + { + b2 = false; + oppEdge2.Alpha2(); + } + + // Swap the given diagonal + m_triangulation.swapEdge( aDiagonal ); + + if( !b1 ) + RecSwapDelaunay<TRAITS_TYPE>( oppEdge1 ); + + if( !b2 ) + RecSwapDelaunay<TRAITS_TYPE>( oppEdge2 ); +} + +/** Swaps edges away from the (interior) node associated with + * \e dart such that that exactly three edges remain incident + * with the node. + * This function is used as a first step in RemoveInteriorNode + * + * \retval dart + * A CCW dart incident with the node + * + * \par Assumes: + * - The node associated with \e dart is interior to the + * triangulation. + * + * \require + * - \ref hed::TTLtraits::swapEdge "TRAITS_TYPE::swapEdge" (DART_TYPE& \e dart)\n + * \b Note: Must be implemented such that \e dart is delivered back in a position as + * seen if it was glued to the edge when swapping (rotating) the edge CCW + * + * \note + * - A degenerate triangle may be left at the node. + * - The function is not unique as it depends on which dart + * at the node that is given as input. + * + * \see + * SwapEdgesAwayFromBoundaryNode + */ +template <class TRAITS_TYPE, class DART_TYPE, class LIST_TYPE> +void TRIANGULATION_HELPER::SwapEdgesAwayFromInteriorNode( DART_TYPE& aDart, + LIST_TYPE& aSwappedEdges ) +{ + + // Same iteration as in fixEdgesAtCorner, but not boundary + DART_TYPE dnext = aDart; + + // Allow degeneracy, otherwise we might end up with degree=4. + // For example, the reverse operation of inserting a point on an + // existing edge gives a situation where all edges are non-swappable. + // Ideally, degeneracy in this case should be along the actual node, + // but there is no strategy for this now. + // ??? An alternative here is to wait with degeneracy till we get an + // infinite loop with degree > 3. + bool allowDegeneracy = true; + + int degree = getDegreeOfNode( aDart ); + DART_TYPE d_iter; + + while( degree > 3 ) + { + d_iter = dnext; + dnext.Alpha1().Alpha2(); + + if( SwappableEdge<TRAITS_TYPE>( d_iter, allowDegeneracy ) ) + { + m_triangulation.swapEdge( d_iter ); // swap the edge away + // Collect swapped edges in the list + // "Hide" the dart on the other side of the edge to avoid it being changed for + // other swaps + DART_TYPE swapped_edge = d_iter; // it was delivered back + swapped_edge.Alpha2().Alpha0(); // CCW (if not at boundary) + aSwappedEdges.push_back( swapped_edge ); + + degree--; + } + } + + // Output, incident to the node + aDart = dnext; +} + +/** Swaps edges away from the (boundary) node associated with + * \e dart in such a way that when removing the edges that remain incident + * with the node, the boundary of the triangulation will be convex. + * This function is used as a first step in RemoveBoundaryNode + * + * \retval dart + * A CCW dart incident with the node + * + * \require + * - \ref hed::TTLtraits::swapEdge "TRAITS_TYPE::swapEdge" (DART_TYPE& \e dart)\n + * \b Note: Must be implemented such that \e dart is delivered back in a position as + * seen if it was glued to the edge when swapping (rotating) the edge CCW + * + * \par Assumes: + * - The node associated with \e dart is at the boundary of the m_triangulation. + * + * \see + * SwapEdgesAwayFromInteriorNode + */ +template <class TRAITS_TYPE, class DART_TYPE, class LIST_TYPE> +void TRIANGULATION_HELPER::SwapEdgesAwayFromBoundaryNode( DART_TYPE& aDart, + LIST_TYPE& aSwappedEdges ) +{ + // All darts that are swappable. + // To treat collinear nodes at an existing boundary, we must allow degeneracy + // when swapping to the boundary. + // dart is CCW and at the boundary. + // The 0-orbit runs CCW + // Deliver the dart back in the "same position". + // Assume for the swap in the traits class: + // - A dart on the swapped edge is delivered back in a position as + // seen if it was glued to the edge when swapping (rotating) the edge CCW + + //int degree = getDegreeOfNode(dart); + + passes: + // Swap swappable edges that radiate from the node away + DART_TYPE d_iter = aDart; // ???? can simply use dart + d_iter.Alpha1().Alpha2(); // first not at boundary + DART_TYPE d_next = d_iter; + bool bend = false; + bool swapped_next_to_boundary = false; + bool swapped_in_pass = false; + + bool allowDegeneracy; // = true; + DART_TYPE tmp1, tmp2; + + while( !bend ) + { + d_next.Alpha1().Alpha2(); + + if( IsBoundaryEdge( d_next ) ) + bend = true; // then it is CW since alpha2 + + // To allow removing among collinear nodes at the boundary, + // degenerate triangles must be allowed + // (they will be removed when used in connection with RemoveBoundaryNode) + tmp1 = d_iter; + tmp1.Alpha1(); + tmp2 = d_iter; + tmp2.Alpha2().Alpha1(); // don't bother with boundary (checked later) + + if( IsBoundaryEdge( tmp1 ) && IsBoundaryEdge( tmp2 ) ) + allowDegeneracy = true; + else + allowDegeneracy = false; + + if( SwappableEdge<TRAITS_TYPE>( d_iter, allowDegeneracy ) ) + { + m_triangulation.swapEdge( d_iter ); + + // Collect swapped edges in the list + // "Hide" the dart on the other side of the edge to avoid it being changed for + // other swapps + DART_TYPE swapped_edge = d_iter; // it was delivered back + swapped_edge.Alpha2().Alpha0(); // CCW + aSwappedEdges.push_back( swapped_edge ); + + //degree--; // if degree is 2, or bend=true, we are done + swapped_in_pass = true; + if( bend ) + swapped_next_to_boundary = true; + } + + if( !bend ) + d_iter = d_next; + } + + // Deliver a dart as output in the same position as the incoming dart + if( swapped_next_to_boundary ) + { + // Assume that "swapping is CCW and dart is preserved in the same position + d_iter.Alpha1().Alpha0().Alpha1(); // CW and see below + } + else + { + d_iter.Alpha1(); // CW and see below + } + PositionAtNextBoundaryEdge( d_iter ); // CCW + + aDart = d_iter; // for next pass or output + + // If a dart was swapped in this iteration we must run it more + if( swapped_in_pass ) + goto passes; +} + +/** Swap the the edge associated with iterator \e it and update affected darts + * in \e elist accordingly. + * The darts affected by the swap are those in the same quadrilateral. + * Thus, if one want to preserve one or more of these darts on should + * keep them in \e elist. + */ +template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE> +void TRIANGULATION_HELPER::SwapEdgeInList( const typename DART_LIST_TYPE::iterator& aIt, + DART_LIST_TYPE& aElist ) +{ + + typename DART_LIST_TYPE::iterator it1, it2, it3, it4; + DART_TYPE dart( *aIt ); + + //typename TRAITS_TYPE::DART_TYPE d1 = dart; d1.Alpha2().Alpha1(); + //typename TRAITS_TYPE::DART_TYPE d2 = d1; d2.Alpha0().Alpha1(); + //typename TRAITS_TYPE::DART_TYPE d3 = dart; d3.Alpha0().Alpha1(); + //typename TRAITS_TYPE::DART_TYPE d4 = d3; d4.Alpha0().Alpha1(); + DART_TYPE d1 = dart; + d1.Alpha2().Alpha1(); + DART_TYPE d2 = d1; + d2.Alpha0().Alpha1(); + DART_TYPE d3 = dart; + d3.Alpha0().Alpha1(); + DART_TYPE d4 = d3; + d4.Alpha0().Alpha1(); + + // Find pinters to the darts that may change. + // ??? Note, this is not very efficient since we must use find, which is O(N), + // four times. + // - Solution?: replace elist with a vector of pair (dart,number) + // and avoid find? + // - make a function for swapping generically? + // - sould we use another container type or, + // - erase them and reinsert? + // - or use two lists? + it1 = find( aElist.begin(), aElist.end(), d1 ); + it2 = find( aElist.begin(), aElist.end(), d2 ); + it3 = find( aElist.begin(), aElist.end(), d3 ); + it4 = find( aElist.begin(), aElist.end(), d4 ); + + m_triangulation.swapEdge( dart ); + // Update the current dart which may have changed + *aIt = dart; + + // Update darts that may have changed again (if they were present) + // Note that dart is delivered back after swapping + if( it1 != aElist.end() ) + { + d1 = dart; + d1.Alpha1().Alpha0(); + *it1 = d1; + } + + if( it2 != aElist.end() ) + { + d2 = dart; + d2.Alpha2().Alpha1(); + *it2 = d2; + } + + if( it3 != aElist.end() ) + { + d3 = dart; + d3.Alpha2().Alpha1().Alpha0().Alpha1(); + *it3 = d3; + } + + if( it4 != aElist.end() ) + { + d4 = dart; + d4.Alpha0().Alpha1(); + *it4 = d4; + } +} + +//@} // End of Utilities for Delaunay Triangulation Group + +} +// End of ttl namespace scope (but other files may also contain functions for ttl) + +#endif // _TTL_H_ |