2013-11-25 15:50:03 +00:00
|
|
|
|
/*
|
|
|
|
|
* 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
|
2014-04-07 11:32:09 +00:00
|
|
|
|
static void errorAndExit( char* aMessage )
|
|
|
|
|
{
|
|
|
|
|
cout << "\n!!! ERROR: " << aMessage << " !!!\n" << endl;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
exit(-1);
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
#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.
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/**
|
|
|
|
|
* \brief Main interface to TTL
|
2013-11-25 15:50:03 +00:00
|
|
|
|
*
|
2014-04-07 11:32:09 +00:00
|
|
|
|
* This namespace contains the basic generic algorithms for the TTL,
|
|
|
|
|
* the Triangulation Template Library.\n
|
2013-11-25 15:50:03 +00:00
|
|
|
|
*
|
2014-04-07 11:32:09 +00:00
|
|
|
|
* 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.
|
2013-11-25 15:50:03 +00:00
|
|
|
|
*
|
2014-04-07 11:32:09 +00:00
|
|
|
|
* \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.
|
2013-11-25 15:50:03 +00:00
|
|
|
|
*
|
2014-04-07 11:32:09 +00:00
|
|
|
|
* \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
|
2013-11-25 15:50:03 +00:00
|
|
|
|
*
|
2014-04-07 11:32:09 +00:00
|
|
|
|
* \see
|
|
|
|
|
* \ref ttl_util and \ref api
|
2013-11-25 15:50:03 +00:00
|
|
|
|
*
|
2014-04-07 11:32:09 +00:00
|
|
|
|
* \author
|
|
|
|
|
* <EFBFBD>yvind Hjelle, oyvindhj@ifi.uio.no
|
2013-11-25 15:50:03 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
namespace ttl
|
|
|
|
|
{
|
|
|
|
|
class TRIANGULATION_HELPER
|
2014-01-27 10:42:47 +00:00
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
|
|
|
|
|
2014-01-27 10:42:47 +00:00
|
|
|
|
public:
|
2014-04-07 11:32:09 +00:00
|
|
|
|
TRIANGULATION_HELPER( hed::TRIANGULATION& aTriang ) :
|
|
|
|
|
m_triangulation( aTriang )
|
|
|
|
|
{
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// Delaunay Triangulation
|
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE, class POINT_TYPE>
|
|
|
|
|
bool InsertNode( DART_TYPE& aDart, POINT_TYPE& aPoint );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
void RemoveRectangularBoundary( DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
void RemoveNode( DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
void RemoveBoundaryNode( DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
void RemoveInteriorNode( DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// Topological and Geometric Queries
|
|
|
|
|
// ---------------------------------
|
|
|
|
|
template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE>
|
|
|
|
|
static bool LocateFaceSimplest( const POINT_TYPE& aPoint, DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE>
|
|
|
|
|
static bool LocateTriangle( const POINT_TYPE& aPoint, DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE>
|
|
|
|
|
static bool InTriangleSimplest( const POINT_TYPE& aPoint, const DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE>
|
|
|
|
|
static bool InTriangle( const POINT_TYPE& aPoint, const DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE, class DART_LIST_TYPE>
|
|
|
|
|
static void GetBoundary( const DART_TYPE& aDart, DART_LIST_TYPE& aBoundary );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static bool IsBoundaryEdge( const DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static bool IsBoundaryFace( const DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static bool IsBoundaryNode( const DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static int GetDegreeOfNode( const DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE, class DART_LIST_TYPE>
|
|
|
|
|
static void Get0OrbitInterior( const DART_TYPE& aDart, DART_LIST_TYPE& aOrbit );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE, class DART_LIST_TYPE>
|
|
|
|
|
static void Get0OrbitBoundary( const DART_TYPE& aDart, DART_LIST_TYPE& aOrbit );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static bool Same0Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static bool Same1Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static bool Same2Orbit( const DART_TYPE& aD1, const DART_TYPE& aD2 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
static bool SwappableEdge( const DART_TYPE& aDart, bool aAllowDegeneracy = false );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static void PositionAtNextBoundaryEdge( DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
static bool ConvexBoundary( const DART_TYPE& aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// Utilities for Delaunay Triangulation
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE>
|
|
|
|
|
void OptimizeDelaunay( DART_LIST_TYPE& aElist );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE>
|
|
|
|
|
void OptimizeDelaunay( DART_LIST_TYPE& aElist, const typename DART_LIST_TYPE::iterator aEnd );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
bool SwapTestDelaunay( const DART_TYPE& aDart, bool aCyclingCheck = false ) const;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
void RecSwapDelaunay( DART_TYPE& aDiagonal );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE, class LIST_TYPE>
|
|
|
|
|
void SwapEdgesAwayFromInteriorNode( DART_TYPE& aDart, LIST_TYPE& aSwappedEdges );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE, class LIST_TYPE>
|
|
|
|
|
void SwapEdgesAwayFromBoundaryNode( DART_TYPE& aDart, LIST_TYPE& aSwappedEdges );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE, class DART_LIST_TYPE>
|
|
|
|
|
void SwapEdgeInList( const typename DART_LIST_TYPE::iterator& aIt, DART_LIST_TYPE& aElist );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// Constrained Triangulation
|
|
|
|
|
// -------------------------
|
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
static DART_TYPE InsertConstraint( DART_TYPE& aDStart, DART_TYPE& aDEnd, bool aOptimizeDelaunay );
|
2014-01-27 10:42:47 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2014-04-07 11:32:09 +00:00
|
|
|
|
hed::TRIANGULATION& m_triangulation;
|
2014-01-27 10:42:47 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class FORWARD_ITERATOR, class DART_TYPE>
|
|
|
|
|
void insertNodes( FORWARD_ITERATOR aFirst, FORWARD_ITERATOR aLast, DART_TYPE& aDart );
|
2014-01-27 10:42:47 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TOPOLOGY_ELEMENT_TYPE, class DART_TYPE>
|
|
|
|
|
static bool isMemberOfFace( const TOPOLOGY_ELEMENT_TYPE& aTopologyElement, const DART_TYPE& aDart );
|
2014-01-27 10:42:47 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class NODE_TYPE, class DART_TYPE>
|
|
|
|
|
static bool locateFaceWithNode( const NODE_TYPE& aNode, DART_TYPE& aDartIter );
|
2014-01-27 10:42:47 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static void getAdjacentTriangles( const DART_TYPE& aDart, DART_TYPE& aT1, DART_TYPE& aT2,
|
|
|
|
|
DART_TYPE& aT3 );
|
2014-01-27 10:42:47 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class DART_TYPE>
|
|
|
|
|
static void getNeighborNodes( const DART_TYPE& aDart, std::list<DART_TYPE>& aNodeList,
|
|
|
|
|
bool& aBoundary );
|
2014-01-27 10:42:47 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
template <class TRAITS_TYPE, class DART_TYPE>
|
|
|
|
|
static bool degenerateTriangle( const DART_TYPE& aDart );
|
2014-01-27 10:42:47 +00:00
|
|
|
|
};
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @name Delaunay Triangulation */
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//@{
|
|
|
|
|
/**
|
|
|
|
|
* 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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( !found )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
#ifdef DEBUG_TTL
|
2014-04-07 11:32:09 +00:00
|
|
|
|
cout << "ERROR: Triangulation::insertNode: NO triangle found. /n";
|
2013-11-25 15:50:03 +00:00
|
|
|
|
#endif
|
2014-04-07 11:32:09 +00:00
|
|
|
|
return false;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// ??? can we hide the dart? this is not possible if one triangle only
|
2014-04-07 11:32:09 +00:00
|
|
|
|
m_triangulation.splitTriangle( aDart, aPoint );
|
|
|
|
|
|
|
|
|
|
DART_TYPE d1 = aDart;
|
|
|
|
|
d1.Alpha2().Alpha1().Alpha2().Alpha0().Alpha1();
|
|
|
|
|
|
|
|
|
|
DART_TYPE d2 = aDart;
|
|
|
|
|
d2.Alpha1().Alpha0().Alpha1();
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Preserve a dart as output incident to the node and CCW
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
//if (!TRAITS_TYPE::fixedEdge(d2) && !IsBoundaryEdge(d2)) {
|
|
|
|
|
if( !IsBoundaryEdge( d2 ) )
|
|
|
|
|
{
|
|
|
|
|
d2.Alpha2();
|
|
|
|
|
RecSwapDelaunay<TRAITS_TYPE>( d2 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Preserve the incoming dart as output incident to the node and CCW
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//d = dsav.Alpha2();
|
|
|
|
|
aDart.Alpha2();
|
|
|
|
|
//if (!TRAITS_TYPE::fixedEdge(d3) && !IsBoundaryEdge(d3)) {
|
|
|
|
|
if( !IsBoundaryEdge( d3 ) )
|
|
|
|
|
{
|
|
|
|
|
d3.Alpha2();
|
|
|
|
|
RecSwapDelaunay<TRAITS_TYPE>( d3 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return true;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
|
|
|
// 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// Assumes that the dereferenced point objects are pointers.
|
|
|
|
|
// References to the point objects are then passed to TTL.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
FORWARD_ITERATOR it;
|
|
|
|
|
for( it = aFirst; it != aLast; ++it )
|
|
|
|
|
{
|
|
|
|
|
InsertNode<TRAITS_TYPE>( aDart, **it );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
aDart = d_next; // Return a dart at the new boundary
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
else
|
2014-04-07 11:32:09 +00:00
|
|
|
|
RemoveInteriorNode<TRAITS_TYPE>( aDart );
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// ... and update Delaunay
|
|
|
|
|
// - CCW dart must be given (for remove)
|
|
|
|
|
// - No dart is delivered back now (but this is possible if
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// we assume that there is not only one triangle left in the m_triangulation.
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Position at boundary edge and CCW
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( !IsBoundaryEdge( aDart ) )
|
|
|
|
|
{
|
|
|
|
|
aDart.Alpha1(); // ensures that next function delivers back a CCW dart (if the given dart is CCW)
|
|
|
|
|
PositionAtNextBoundaryEdge( aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
std::list<DART_TYPE> swapped_edges;
|
|
|
|
|
SwapEdgesAwayFromBoundaryNode<TRAITS_TYPE>( aDart, swapped_edges );
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Remove boundary triangles and remove the new boundary from the list
|
|
|
|
|
// of swapped edges, see below.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
DART_TYPE d_iter = aDart;
|
|
|
|
|
DART_TYPE dnext = aDart;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
bool bend = false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Optimize Delaunay
|
2014-04-07 11:32:09 +00:00
|
|
|
|
typedef std::list<DART_TYPE> DART_LIST_TYPE;
|
|
|
|
|
OptimizeDelaunay<TRAITS_TYPE, DART_TYPE, DART_LIST_TYPE>( swapped_edges );
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// ... 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.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// 3) Runs LOP on the platelet to obtain a Delaunay m_triangulation
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// (No dart is delivered as output)
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Assumes dart is counterclockwise
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
std::list<DART_TYPE> swapped_edges;
|
|
|
|
|
SwapEdgesAwayFromInteriorNode<TRAITS_TYPE>( aDart, swapped_edges );
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// The reverse operation of split triangle:
|
|
|
|
|
// Make one triangle of the three triangles at the node associated with dart
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// TRAITS_TYPE::
|
|
|
|
|
m_triangulation.reverseSplitTriangle( aDart );
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// ???? 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???
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Make the swapped-away edges Delaunay.
|
|
|
|
|
// Note the theoretical result: if there are no edges in the list,
|
|
|
|
|
// the triangulation is Delaunay already
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
OptimizeDelaunay<TRAITS_TYPE, DART_TYPE>( swapped_edges );
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//@} // End of Delaunay Triangulation Group
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** @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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Check if the given topology element (node, edge or face) is a member of the face
|
|
|
|
|
// Assumes:
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// - DART_TYPE::isMember(TOPOLOGY_ELEMENT_TYPE)
|
|
|
|
|
|
|
|
|
|
DART_TYPE dart_iter = aDart;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if( dart_iter.isMember( aTopologyElement ) )
|
|
|
|
|
return true;
|
|
|
|
|
dart_iter.Alpha0().Alpha1();
|
|
|
|
|
}
|
|
|
|
|
while( dart_iter != aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
|
|
|
// 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Locate a face in the topology structure with the given node as a member
|
|
|
|
|
// Assumes:
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// - TRAITS_TYPE::Orient2D(DART_TYPE, DART_TYPE, NODE_TYPE)
|
|
|
|
|
// - DART_TYPE::isMember(NODE_TYPE)
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// - 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;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// 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
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// and in locateTriangle(const POINT_TYPE& point, DART_TYPE& dart_iter)
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
return isMemberOfFace( aNode, aDartIter );
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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?
|
|
|
|
|
//
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// TRAITS_TYPE::orint2d(POINT_TYPE) is the half open half-plane defined
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// by the dart:
|
|
|
|
|
// n1 = dart.node()
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// n2 = dart.Alpha0().node
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Only the following gives true:
|
|
|
|
|
// ((n2->x()-n1->x())*(point.y()-n1->y()) >= (point.x()-n1->x())*(n2->y()-n1->y()))
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// ii) The boundary of the m_triangulation is not convex.
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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:
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// bool inTriangle(const POINT_TYPE& point, const DART_TYPE& dart).
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Assumes:
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// - CrossProduct2D, ScalarProduct2D etc., see functions called
|
|
|
|
|
|
|
|
|
|
bool status = LocateFaceSimplest<TRAITS_TYPE>( aPoint, aDart );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( status == false )
|
|
|
|
|
return status;
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// There may be degeneracy, i.e., the point might be outside the triangle
|
|
|
|
|
// on the extension of the edges of a degenerate triangle.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
return InTriangle<TRAITS_TYPE>( aPoint, aDart );
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
|
|
|
/** Checks if \e point is inside the triangle associated with \e dart.
|
|
|
|
|
* A fast and simple function that does not deal with degeneracy.
|
|
|
|
|
*
|
|
|
|
|
* \param aDart
|
|
|
|
|
* A CCW dart in the triangle
|
|
|
|
|
*
|
|
|
|
|
* \require
|
|
|
|
|
* - \ref hed::TTLtraits::Orient2D "TRAITS_TYPE::Orient2D" (DART_TYPE&, DART_TYPE&, POINT_TYPE&)
|
|
|
|
|
*
|
|
|
|
|
* \see
|
|
|
|
|
* InTriangle for a more robust function
|
|
|
|
|
*/
|
|
|
|
|
template <class TRAITS_TYPE, class POINT_TYPE, class DART_TYPE>
|
|
|
|
|
bool TRIANGULATION_HELPER::InTriangleSimplest( const POINT_TYPE& aPoint, const DART_TYPE& aDart )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Fast and simple: Do not deal with degenerate faces, i.e., if there is
|
|
|
|
|
// degeneracy, true will be returned if the point is on the extension of the
|
|
|
|
|
// edges of a degenerate triangle
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
DART_TYPE d_iter = aDart;
|
|
|
|
|
DART_TYPE d0 = d_iter;
|
|
|
|
|
d0.Alpha0();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( !TRAITS_TYPE::Orient2D( d_iter, d0, aPoint ) >= 0 )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
d_iter.Alpha0().Alpha1();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
d0 = d_iter;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
d0.Alpha0();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( !TRAITS_TYPE::Orient2D( d_iter, d0, aPoint ) >= 0 )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
d_iter.Alpha0().Alpha1();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
d0 = d_iter;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
d0.Alpha0();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( !TRAITS_TYPE::Orient2D( d_iter, d0, aPoint ) >= 0 )
|
|
|
|
|
return false;
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return true;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// 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 ?
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Returns true if inside (but not necessarily strictly inside)
|
|
|
|
|
// Works for degenerate triangles, but not when all edges are degenerate,
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// and the aPoint coincides with all nodes;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// then false is always returned.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// All cross products are >= 0
|
|
|
|
|
// Check for degeneracy
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( cr1 != 0 || cr2 != 0 || cr3 != 0 )
|
|
|
|
|
return true; // inside non-degenerate face
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// All cross-products are zero, i.e. degenerate triangle, check if inside
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// 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,
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// then "false is returned".
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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)
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
dart_tmp = dart_iter.Alpha0().Alpha1();
|
|
|
|
|
sc1 = TRAITS_TYPE::ScalarProduct2D( dart_tmp, aPoint );
|
|
|
|
|
sc2 = TRAITS_TYPE::ScalarProduct2D( dart_tmp.Alpha0(), aPoint );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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)
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
dart_tmp = dart_iter.Alpha1();
|
|
|
|
|
sc1 = TRAITS_TYPE::ScalarProduct2D( dart_tmp, aPoint );
|
|
|
|
|
sc2 = TRAITS_TYPE::ScalarProduct2D( dart_tmp.Alpha0(), aPoint );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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)
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Not on any of the edges of the degenerate triangle.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// The only possibility for the aPoint to be "inside" is that all edges are degenerate
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// and the point coincide with all nodes. So false is returned in this case.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// 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;
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// add first
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( dart_iter.Alpha2() != aDart )
|
|
|
|
|
{
|
|
|
|
|
aT1 = dart_iter;
|
|
|
|
|
dart_iter = aDart;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// add second
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// add third
|
2014-04-07 11:32:09 +00:00
|
|
|
|
dart_iter.Alpha0();
|
|
|
|
|
dart_iter.Alpha1();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
dart_prev = dart_iter;
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( ( dart_iter.Alpha2() ) != dart_prev )
|
|
|
|
|
aT3 = dart_iter;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// assumes the given dart is at the boundary (by edge)
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
DART_TYPE dart_iter( aDart );
|
|
|
|
|
aBoundary.push_back( dart_iter ); // Given dart as first element
|
|
|
|
|
dart_iter.Alpha0();
|
|
|
|
|
PositionAtNextBoundaryEdge( dart_iter );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
while( dart_iter != aDart )
|
|
|
|
|
{
|
|
|
|
|
aBoundary.push_back( dart_iter );
|
|
|
|
|
dart_iter.Alpha0();
|
|
|
|
|
PositionAtNextBoundaryEdge( dart_iter );
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( dart_iter.Alpha2() == aDart )
|
|
|
|
|
return true;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
else
|
2014-04-07 11:32:09 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Strategy: boundary if alpha2(d)=d
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
DART_TYPE dart_iter( aDart );
|
|
|
|
|
DART_TYPE dart_prev;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
dart_prev = dart_iter;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// If input dart is reached again, then interior node
|
|
|
|
|
// If alpha2(d)=d, then boundary
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
int degree = 0;
|
|
|
|
|
bool boundaryVisited = false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
} while( dart_iter != aDart );
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return degree;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// 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.
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// 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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
bool start_at_boundary = false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
dart_iter.Alpha2();
|
|
|
|
|
|
|
|
|
|
if( dart_iter == dart_prev )
|
|
|
|
|
start_at_boundary = true;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
else
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
|
|
|
|
while( dart_iter != dart_start );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
aBoundary = false;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
aOrbit.push_back( d_iter ); // the last one with opposite orientation
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Two copies of the same dart
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
for( ;; )
|
|
|
|
|
{
|
|
|
|
|
if( d_iter == aD1 )
|
|
|
|
|
return true;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
d_iter.Alpha1();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( d_iter == aD1 )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
d_iter.Alpha2();
|
|
|
|
|
|
|
|
|
|
if( d_iter == d_end )
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
return false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// (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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
return ( d_iter == aD1 || d_iter.Alpha0() == aD1 || d_iter.Alpha1() == aD1 ||
|
|
|
|
|
d_iter.Alpha0() == aD1 || d_iter.Alpha1() == aD1 || d_iter.Alpha0() == aD1 );
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// 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
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
DART_TYPE d1 = aDart;
|
|
|
|
|
DART_TYPE d2 = d1;
|
|
|
|
|
d2.Alpha1();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// How "safe" is it?
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
if( IsBoundaryEdge( aDart ) )
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
// "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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if( TRAITS_TYPE::CrossProduct2D( d1, d2 ) <= 0.0 )
|
|
|
|
|
return false;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Opposite side (still angle at the diagonal)
|
2014-04-07 11:32:09 +00:00
|
|
|
|
d1 = aDart;
|
|
|
|
|
d1.Alpha0();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
d2 = d1;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
d1.Alpha1();
|
|
|
|
|
d2.Alpha2().Alpha1();
|
|
|
|
|
|
|
|
|
|
if( aAllowDegeneracy )
|
|
|
|
|
{
|
|
|
|
|
if( TRAITS_TYPE::CrossProduct2D( d1, d2 ) < 0.0 )
|
|
|
|
|
return false;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if( TRAITS_TYPE::CrossProduct2D( d1, d2 ) <= 0.0 )
|
|
|
|
|
return false;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// If alpha2(d)=d, then boundary
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
int no;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
no = (int) blist.size();
|
|
|
|
|
typename std::list<DART_TYPE>::const_iterator bit = blist.begin();
|
|
|
|
|
DART_TYPE d1 = *bit;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
++bit;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
DART_TYPE d2;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
bool convex = true;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Check the last angle
|
|
|
|
|
d2 = *blist.begin();
|
2014-04-07 11:32:09 +00:00
|
|
|
|
double crossProd = TRAITS_TYPE::CrossProduct2D( d1, d2 );
|
|
|
|
|
|
|
|
|
|
if( crossProd < 0.0 )
|
|
|
|
|
{
|
|
|
|
|
//cout << "!!! Boundary is NOT convex: crossProd = " << crossProd << endl;
|
|
|
|
|
convex = false;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
//if (convex)
|
|
|
|
|
// cout << "\n---> Boundary is convex\n" << endl;
|
|
|
|
|
//cout << endl;
|
|
|
|
|
return convex;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//@} // 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() );
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
|
|
|
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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Requires that swap retuns a dart in the "same position when rotated CCW"
|
|
|
|
|
// (A vector instead of a list may be better.)
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// First check that elist is not empty
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( aElist.empty() )
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Avoid cycling by more extensive circumcircle test
|
|
|
|
|
bool cycling_check = true;
|
|
|
|
|
bool optimal = false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
typename DART_LIST_TYPE::iterator it;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
typename DART_LIST_TYPE::iterator end_opt = aEnd;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// 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"
|
2014-04-07 11:32:09 +00:00
|
|
|
|
//typename DART_LIST_TYPE::iterator end_opt;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
//if (*end != NULL)
|
|
|
|
|
// end_opt = end;
|
|
|
|
|
//else
|
|
|
|
|
// end_opt = elist.end();
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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
|
2013-11-25 15:50:03 +00:00
|
|
|
|
} // end pass
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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>
|
2013-11-25 15:50:03 +00:00
|
|
|
|
#if ((_MSC_VER > 0) && (_MSC_VER < 1300))//#ifdef _MSC_VER
|
2014-04-07 11:32:09 +00:00
|
|
|
|
bool TRIANGULATION_HELPER::SwapTestDelaunay(const DART_TYPE& aDart, bool aCyclingCheck = false) const
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
#else
|
2014-04-07 11:32:09 +00:00
|
|
|
|
bool TRIANGULATION_HELPER::SwapTestDelaunay( const DART_TYPE& aDart, bool aCyclingCheck ) const
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
#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.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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 );
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// "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.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( cos1 >= 0 && cos2 >= 0 ) // both angles are grater or equual 90
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return false;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
return true;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// 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;
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Get the other "edges" of the current triangle; see illustration above.
|
2014-04-07 11:32:09 +00:00
|
|
|
|
DART_TYPE oppEdge1 = aDiagonal;
|
|
|
|
|
oppEdge1.Alpha1();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
bool b1;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
if( IsBoundaryEdge( oppEdge1 ) )
|
|
|
|
|
b1 = true;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
b1 = false;
|
|
|
|
|
oppEdge1.Alpha2();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
DART_TYPE oppEdge2 = aDiagonal;
|
|
|
|
|
oppEdge2.Alpha0().Alpha1().Alpha0();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
bool b2;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
if( IsBoundaryEdge( oppEdge2 ) )
|
|
|
|
|
b2 = true;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
b2 = false;
|
|
|
|
|
oppEdge2.Alpha2();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Swap the given diagonal
|
2014-04-07 11:32:09 +00:00
|
|
|
|
m_triangulation.swapEdge( aDiagonal );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// Same iteration as in fixEdgesAtCorner, but not boundary
|
2014-04-07 11:32:09 +00:00
|
|
|
|
DART_TYPE dnext = aDart;
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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;
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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--;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
// Output, incident to the node
|
|
|
|
|
aDart = dnext;
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
/** 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 )
|
|
|
|
|
{
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// 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
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
2014-01-27 10:42:47 +00:00
|
|
|
|
//int degree = getDegreeOfNode(dart);
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
2013-11-25 15:50:03 +00:00
|
|
|
|
else
|
2014-04-07 11:32:09 +00:00
|
|
|
|
{
|
|
|
|
|
d_iter.Alpha1(); // CW and see below
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
PositionAtNextBoundaryEdge( d_iter ); // CCW
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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();
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
// 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?
|
2014-04-07 11:32:09 +00:00
|
|
|
|
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 );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Update the current dart which may have changed
|
2014-04-07 11:32:09 +00:00
|
|
|
|
*aIt = dart;
|
|
|
|
|
|
2013-11-25 15:50:03 +00:00
|
|
|
|
// Update darts that may have changed again (if they were present)
|
|
|
|
|
// Note that dart is delivered back after swapping
|
2014-04-07 11:32:09 +00:00
|
|
|
|
if( it1 != aElist.end() )
|
|
|
|
|
{
|
|
|
|
|
d1 = dart;
|
|
|
|
|
d1.Alpha1().Alpha0();
|
|
|
|
|
*it1 = d1;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
if( it2 != aElist.end() )
|
|
|
|
|
{
|
|
|
|
|
d2 = dart;
|
|
|
|
|
d2.Alpha2().Alpha1();
|
|
|
|
|
*it2 = d2;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
if( it3 != aElist.end() )
|
|
|
|
|
{
|
|
|
|
|
d3 = dart;
|
|
|
|
|
d3.Alpha2().Alpha1().Alpha0().Alpha1();
|
|
|
|
|
*it3 = d3;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
|
|
|
|
|
if( it4 != aElist.end() )
|
|
|
|
|
{
|
|
|
|
|
d4 = dart;
|
|
|
|
|
d4.Alpha0().Alpha1();
|
|
|
|
|
*it4 = d4;
|
2013-11-25 15:50:03 +00:00
|
|
|
|
}
|
2014-04-07 11:32:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//@} // End of Utilities for Delaunay Triangulation Group
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// End of ttl namespace scope (but other files may also contain functions for ttl)
|
2013-11-25 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
#endif // _TTL_H_
|