++itVertex; // If we're here, we know that vertices is not empty.
for (; itVertex != vertices.end(); itVertex++)
{
xMax = itVertex->GetX(); // Vertices are sorted along the x-axis, so the last one stored will be the biggest.
float y = itVertex->GetY();
if (y < yMin) yMin = y;
if (y > yMax) yMax = y;
}
float dx = xMax - xMin;
float dy = yMax - yMin;
// Make the bounding box slightly bigger, just to feel safe.
float ddx = dx * 0.01F;
float ddy = dy * 0.01F;
xMin -= ddx;
xMax += ddx;
dx += 2 * ddx;
yMin -= ddy;
yMax += ddy;
dy += 2 * ddy;
// Create a 'super triangle', encompassing all the vertices. We choose an equilateral triangle with horizontal base.
// We could have made the 'super triangle' simply very big. However, the algorithm is quite sensitive to
// rounding errors, so it's better to make the 'super triangle' just big enough, like we do here.
vertex vSuper[3];
for (itVertex = vertices.begin(); itVertex != vertices.end(); itVertex++)
{
// First, remove all 'completed' triangles from the workset.
// A triangle is 'completed' if its circumcircle is entirely to the left of the current vertex.
// Vertices are sorted in x-direction (the set container does this automagically).
// Unless they are part of the 'super triangle', copy the 'completed' triangles to the output.
// The algorithm also works without this step, but it is an important optimalization for bigger numbers of vertices.
// It makes the algorithm about five times faster for 2000 vertices, and for 10000 vertices,
// it's thirty times faster. For smaller numbers, the difference is negligible.
tIterator itEnd = remove_if(workset.begin(), workset.end(), triangleIsCompleted(itVertex, output, vSuper));
edgeSet edges;
// A triangle is 'hot' if the current vertex v is inside the circumcircle.
// Remove all hot triangles, but keep their edges.
itEnd = remove_if(workset.begin(), itEnd, vertexIsInCircumCircle(itVertex, edges));
workset.erase(itEnd, workset.end()); // remove_if doesn't actually remove; we have to do this explicitly.
// Create new triangles from the edges and the current vertex.
for (edgeIterator it = edges.begin(); it != edges.end(); it++)
workset.insert(triangle(it->m_pV0, it->m_pV1, & (* itVertex)));
}
// Finally, remove all the triangles belonging to the 'super triangle' and move the remaining
// triangles tot the output; remove_copy_if lets us do that in one go.
tIterator where = output.begin();
remove_copy_if(workset.begin(), workset.end(), inserter(output, where), triangleHasVertex(vSuper));
}
void Delaunay::TrianglesToEdges(const triangleSet& triangles, edgeSet& edges)
{
for (ctIterator it = triangles.begin(); it != triangles.end(); ++it)
{
HandleEdge(it->GetVertex(0), it->GetVertex(1), edges);
HandleEdge(it->GetVertex(1), it->GetVertex(2), edges);
HandleEdge(it->GetVertex(2), it->GetVertex(0), edges);
}
}
m_R2 = dx * dx + dy * dy; // the radius of the circumcircle, squared
m_R = (float) sqrt(m_R2); // the proper radius
// Version 1.1: make m_R2 slightly higher to ensure that all edges
// of co-circular vertices will be caught.
// Note that this is a compromise. In fact, the algorithm isn't floatly
// suited for very many co-circular vertices.
m_R2 *= 1.000001f;
}
// Function object to check whether a triangle has one of the vertices in SuperTriangle.
// operator() returns true if it does.
class triangleHasVertex
{
public:
triangleHasVertex(const vertex SuperTriangle[3]) : m_pSuperTriangle(SuperTriangle) {}
bool operator()(const triangle& tri) const
{
for (int i = 0; i < 3; i++)
{
const vertex * p = tri.GetVertex(i);
if (p >= m_pSuperTriangle && p < (m_pSuperTriangle + 3)) return true;
}
return false;
}
protected:
const vertex * m_pSuperTriangle;
};
// Function object to check whether a triangle is 'completed', i.e. doesn't need to be checked
// again in the algorithm, i.e. it won't be changed anymore.
// Therefore it can be removed from the workset.
// A triangle is completed if the circumcircle is completely to the left of the current vertex.
// If a triangle is completed, it will be inserted in the output set, unless one or more of it's vertices
// belong to the 'super triangle'.
class triangleIsCompleted
{
public:
triangleIsCompleted(cvIterator itVertex, triangleSet& output, const vertex SuperTriangle[3])
: m_itVertex(itVertex)
, m_Output(output)
, m_pSuperTriangle(SuperTriangle)
{}
bool operator()(const triangle& tri) const
{
bool b = tri.IsLeftOf(m_itVertex);
if (b)
{
triangleHasVertex thv(m_pSuperTriangle);
if (! thv(tri)) m_Output.insert(tri);
}
return b;
}
// Function object to check whether vertex is in circumcircle of triangle.
// operator() returns true if it does.
// The edges of a 'hot' triangle are stored in the edgeSet edges.
class vertexIsInCircumCircle
{
public:
vertexIsInCircumCircle(cvIterator itVertex, edgeSet& edges) : m_itVertex(itVertex), m_Edges(edges) {}
bool operator()(const triangle& tri) const
{
bool b = tri.CCEncompasses(m_itVertex);
// Create a normalized edge, in which the smallest vertex comes first.
if (* p0 < * p1)
{
pVertex0 = p0;
pVertex1 = p1;
}
else
{
pVertex0 = p1;
pVertex1 = p0;
}
edge e(pVertex0, pVertex1);
// Check if this edge is already in the buffer
edgeIterator found = m_Edges.find(e);
if (found == m_Edges.end()) m_Edges.insert(e); // no, it isn't, so insert
else m_Edges.erase(found); // yes, it is, so erase it to eliminate double edges
}
This is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This file 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this application; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
********************************************************************************/
// Delaunay
// Class to perform Delaunay triangulation on a set of vertices
//
// Version 1.2 (C) 2005, Sjaak Priester, Amsterdam.
// - Removed stupid bug in SetY; function wasn't used, so no consequences. Thanks to squat.
//
// Version 1.1 (C) 2005, Sjaak Priester, Amsterdam.
// - Removed bug which gave incorrect results for co-circular vertices.
//
// Version 1.0 (C) 2004, Sjaak Priester, Amsterdam.
// mailto:sjaak@sjaakpriester.nl
// I designed this with GDI+ in mind. However, this particular code doesn't
// use GDI+ at all, only some of it's variable types.
// These definitions are substitutes for those of GDI+.
//typedef float float;
struct PointV
{
PointV() : X(0), Y(0) {propZ=0;}
PointV(const PointV& p) : X(p.X), Y(p.Y) {propZ=p.propZ;}
PointV(float x, float y, float z) : X(x), Y(y) {propZ=z;}
PointV operator+(const PointV& p) const { return PointV(X + p.X, Y + p.Y, propZ + p.propZ); }
PointV operator-(const PointV& p) const { return PointV(X - p.X, Y - p.Y, propZ - p.propZ); }
float X;
float Y;
float propZ; // 用于等值线的点值,add by zhaoyang.
};
const float float_EPSILON = 1.192092896e-07F; // = 2^-23; I've no idea why this is a good value, but GDI+ has it.
//#endif // _GDIPLUS_H
///////////////////
// vertex
class vertex
{
public:
vertex() : m_Pnt(0.0F, 0.0F, 0.0F) {}
vertex(const vertex& v) : m_Pnt(v.m_Pnt) {}
vertex(const PointV& pnt) : m_Pnt(pnt) {}
vertex(float x, float y, float z) : m_Pnt(x, y, z) {}
vertex(int x, int y, int z) : m_Pnt((float) x, (float) y, (float) z) {}
bool IsLeftOf(cvIterator itVertex) const
{
// returns true if * itVertex is to the right of the triangle's circumcircle
return itVertex->GetPoint().X > (m_Center.X + m_R);
}
bool CCEncompasses(cvIterator itVertex) const
{
// Returns true if * itVertex is in the triangle's circumcircle.
// A vertex exactly on the circle is also considered to be in the circle.
// These next few lines look like optimisation, however in practice they are not.
// They even seem to slow down the algorithm somewhat.
// Therefore, I've commented them out.
// First check boundary box.
// float x = itVertex->GetPoint().X;
//
// if (x > (m_Center.X + m_R)) return false;
// if (x < (m_Center.X - m_R)) return false;
//
// float y = itVertex->GetPoint().Y;
//
// if (y > (m_Center.Y + m_R)) return false;
// if (y < (m_Center.Y - m_R)) return false;
PointV dist = itVertex->GetPoint() - m_Center; // the distance between v and the circle center
float dist2 = dist.X * dist.X + dist.Y * dist.Y; // squared
return dist2 <= m_R2; // compare with squared radius
}
protected:
const vertex * m_Vertices[3]; // the three triangle vertices
PointV m_Center; // center of circumcircle
float m_R; // radius of circumcircle
float m_R2; // radius of circumcircle, squared
void SetCircumCircle();
};
// Changed in verion 1.1: collect triangles in a multiset.
// In version 1.0, I used a set, preventing the creation of multiple
// triangles with identical center points. Therefore, more than three
// co-circular vertices yielded incorrect results. Thanks to Roger Labbe.
typedef multiset<triangle> triangleSet;
typedef multiset<triangle>::iterator tIterator;
typedef multiset<triangle>::const_iterator ctIterator;
class Delaunay
{
public:
// Calculate the Delaunay triangulation for the given set of vertices.
void Triangulate(const vertexSet& vertices, triangleSet& output);
// Put the edges of the triangles in an edgeSet, eliminating double edges.
// This comes in useful for drawing the triangulation.
void TrianglesToEdges(const triangleSet& triangles, edgeSet& edges);
protected:
void HandleEdge(const vertex * p0, const vertex * p1, edgeSet& edges);
};