package
{
//Credit to Paul Bourke (pbourke@swin.edu.au) for the original Fortran 77 Program :))
//Converted to a standalone C# 2.0 library by Morten Nielsen (www.iter.dk)
//Check out: http://astronomy.swin.edu.au/~pbourke/terrain/triangulate/
//You can use this code however you like providing the above credits remain in tact
/**
* @author nicoptere
* http://en.nicoptere.net/
*/
public class Delaunay
{
/**
* performs a Delaunay triangulation on a set of points
* @param Vertex the original set of points
* @return an array of triangl objects
*/
public static function Triangulate( Vertex:Array ):Array
{
//those will be used quite everywhere so I am storing them here not to declare them x times
var i:int;
var j:int;
var nv:int = Vertex.length;
if (nv < 3) return [];
var trimax:int = 4 * nv;
// Find the maximum and minimum vertex bounds.
// This is to allow calculation of the bounding supertriangle
var xmin:Number = Vertex[0].x;
var ymin:Number = Vertex[0].y;
var xmax:Number = xmin;
var ymax:Number = ymin;
for ( i = 1; i < nv; i++)
{
( Vertex[i] as Point ).id = i;
if (Vertex[i].x < xmin) xmin = Vertex[i].x;
if (Vertex[i].x > xmax) xmax = Vertex[i].x;
if (Vertex[i].y < ymin) ymin = Vertex[i].y;
if (Vertex[i].y > ymax) ymax = Vertex[i].y;
}
var dx:Number = xmax - xmin;
var dy:Number = ymax - ymin;
var dmax:Number = (dx > dy) ? dx : dy;
var xmid:Number = (xmax + xmin) * 0.5;
var ymid:Number = (ymax + ymin) * 0.5;
// Set up the supertriangle
// This is a triangle which encompasses all the sample points.
// The supertriangle coordinates are added to the end of the
// vertex list. The supertriangle is the first triangle in
// the triangle list.
Vertex.push(new Point( (xmid - 2 * dmax), (ymid - dmax), nv+1 ) );
Vertex.push(new Point( xmid, (ymid + 2 * dmax), nv+2 ) );
Vertex.push(new Point((xmid + 2 * dmax), (ymid - dmax), nv+3));
var triangles:Array = new Array();//array typé de triangles
triangles.push( new Triangle( Vertex[ nv ], Vertex[ nv + 1 ], Vertex[ nv + 2 ] ) ); //SuperTriangle placed at index 0
// Include each point one at a time into the existing mesh
for ( i = 0; i < nv; i++)
{
var Edges:Array = new Array(); //[trimax * 3];
// Set up the edge buffer.
// If the point (Vertex(i).x,Vertex(i).y) lies inside the circumcircle then the
// three edges of that triangle are added to the edge buffer and the triangle is removed from list.
for ( j = 0; j < triangles.length; j++ )
{
if ( InCircle( Vertex[ i ], triangles[ j ].p1, triangles[ j ].p2, triangles[ j ].p3 ) )
{
Edges.push(new Edge(triangles[j].p1, triangles[j].p2) );
Edges.push(new Edge(triangles[j].p2, triangles[j].p3) );
Edges.push(new Edge(triangles[j].p3, triangles[j].p1) );
triangles.splice( j,1 );
j--;
}
}
if ( i >= nv) continue; //In case we the last duplicate point we removed was the last in the array
// Remove duplicate edges
// Note: if all triangles are specified anticlockwise then all
// interior edges are opposite pointing in direction.
for ( j = Edges.length - 2; j >= 0; j--)
{
for (var k:int = Edges.length - 1; k >= j + 1; k--)
{
if ( Edges[ j ].Equals( Edges[ k ] ) )
{
Edges.splice( k, 1 );
Edges.splice( j, 1 );
k--;
continue;
}
}
}
// Form new triangles for the current point
// Skipping over any tagged edges.
// All edges are arranged in clockwise order.
for ( j = 0; j < Edges.length; j++)
{
if (triangles.length >= trimax )
{
// throw new ApplicationException("Exceeded maximum edges");
trace("Exceeded maximum edges");
}
triangles.push( new Triangle( Edges[ j ].p1, Edges[ j ].p2, Vertex[ i ] ));
}
Edges = [];
}
// Remove triangles with supertriangle vertices
// These are triangles which have a vertex number greater than nv
for ( i = triangles.length - 1; i >= 0; i--)
{
if ( triangles[ i ].p1.id >= nv || triangles[ i ].p2.id >= nv || triangles[ i ].p3.id >= nv)
{
triangles.splice(i, 1);
}
}
//Remove SuperTriangle vertices
Vertex.splice(Vertex.length - 1, 1);
Vertex.splice(Vertex.length - 1, 1);
Vertex.splice(Vertex.length - 1, 1);
triangles.concat();
return triangles;
}
///
/// Returns true if the point (p) lies inside the circumcircle made up by points (p1,p2,p3)
///
///
/// NOTE: A point on the edge is inside the circumcircle
///
/// Point to check
/// First point on circle
/// Second point on circle
/// Third point on circle
/// true if p is inside circle
private static function InCircle( p:Point, p1:Point, p2:Point, p3:Point ):Boolean
{
//Return TRUE if the point (xp,yp) lies inside the circumcircle
//made up by points (x1,y1) (x2,y2) (x3,y3)
//NOTE: A point on the edge is inside the circumcircle
var Epsilon:Number = Number.MIN_VALUE;
if ( Math.abs( p1.y - p2.y ) < Epsilon && Math.abs( p2.y - p3.y) < Epsilon)
{
//INCIRCUM - F - Points are coincident !!
return false;
}
var m1:Number;
var m2:Number;
var mx1:Number;
var mx2:Number;
var my1:Number;
var my2:Number;
var xc:Number;
var yc:Number;
if ( Math.abs(p2.y - p1.y) < Epsilon)
{
m2 = -(p3.x - p2.x) / (p3.y - p2.y);
mx2 = (p2.x + p3.x) * 0.5;
my2 = (p2.y + p3.y) * 0.5;
//Calculate CircumCircle center (xc,yc)
xc = (p2.x + p1.x) * 0.5;
yc = m2 * (xc - mx2) + my2;
}
else if ( Math.abs(p3.y - p2.y) < Epsilon)
{
m1 = -(p2.x - p1.x) / (p2.y - p1.y);
mx1 = (p1.x + p2.x) * 0.5;
my1 = (p1.y + p2.y) * 0.5;
//Calculate CircumCircle center (xc,yc)
xc = (p3.x + p2.x) * 0.5;
yc = m1 * (xc - mx1) + my1;
}
else
{
m1 = -(p2.x - p1.x) / (p2.y - p1.y);
m2 = -(p3.x - p2.x) / (p3.y - p2.y);
mx1 = (p1.x + p2.x) * 0.5;
mx2 = (p2.x + p3.x) * 0.5;
my1 = (p1.y + p2.y) * 0.5;
my2 = (p2.y + p3.y) * 0.5;
//Calculate CircumCircle center (xc,yc)
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
yc = m1 * (xc - mx1) + my1;
}
var dx:Number = p2.x - xc;
var dy:Number = p2.y - yc;
var rsqr:Number = dx * dx + dy * dy;
//double r = Math.Sqrt(rsqr); //Circumcircle radius
dx = p.x - xc;
dy = p.y - yc;
var drsqr:Number = dx * dx + dy * dy;
return ( drsqr <= rsqr );
}
}
}