home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Resource Library: Graphics
/
graphics-16000.iso
/
general
/
raytrace
/
renaisnc
/
delaunay.lha
/
Delaunay
/
DCEL.h
< prev
next >
Wrap
C/C++ Source or Header
|
1992-01-04
|
12KB
|
406 lines
/*
File: DCEL.h
Author: K.R. Sloan
Last Modified: 30 August 1988
Purpose: Doubly-Connected Edge List (improved),
along with a few utilities for triangulating planar
point sets
*/
#define ArraySize 200000
#include <stdio.h>
#define true (-1)
#define false (0)
/*
These forward declarations are needed to make the compiler
happy.
*/
typedef struct dcelPoint Point;
typedef struct dcelPSE PointSetElement;
typedef PointSetElement *PointSetPointer;
typedef struct dcelEE EdgeEnd;
typedef struct dcelE Edge;
typedef Edge *EdgePointer;
typedef struct dcelT Triangle;
typedef Triangle *TrianglePointer;
/*
Points are stored in an array, so that an application can pun
on the array index and keep auxiliary information. We only care about
the x,y coordinates, and the Edges emanating from this vertex
*/
struct dcelPoint
{
REAL x, y;
Edge *Edges;
};
/*
On the other hand, we occasionally need a "set of points". For that,
we use PointSetElements, arranged in a circular, doubly-linked list.
*/
struct dcelPSE
{
int P;
PointSetElement *Previous, *Next;
};
/*
Edges have two ends. Each end has a vertex, links for a circular,
doubly-linked list, and an associated Triangle.
In addition, we will need sets of Edges - yet another circular,
doubly-linked list.
*/
struct dcelEE
{
int V; /* Vertex at this End */
Triangle *T; /* Triangle on the LEFT, seen from this End */
Edge *Previous, *Next;
};
struct dcelE
{
EdgeEnd Head;
EdgeEnd Tail;
Edge *Previous, *Next;
};
/*
Triangles have three vertices, three edges, and a figure-of-merit.
Like everything else, they can be collected on circular, doubly-connected
linked lists.
For the truly ambitious: generalize the Triangle to Facet. Give up
the efficiency of immediate access to the vertices and edges, in favor
of arbitrary k-gons (yup, more circular, doubly connected linked lists.
Me, I like triangles...
*/
struct dcelT
{
REAL Merit; /* See Rho, and Shrink */
int P1, P2, P3; /* turning to the RIGHT */
Edge *E1, *E2, *E3; /* Ei = <Pi,P(i+1)mod3> */
Triangle *Previous, *Next;
};
/*
Thus, by convention:
LOOKING FROM THE TAIL LOOKING FROM THE HEAD
------------------------ ---------------------
Head.V Tail.V
^ |
| |
Tail.T | Head.T Head.T | Tail.T
| |
| \/
Tail.V Head.V
*/
/*
Allocation routines
*/
/*
extern PointSetPointer NewPoint();
extern Edge *NewEdge();
extern Triangle *NewTriangle();
*/
/* for Animation */
extern int ANIMATING;
/* for error messages */
extern int DCELVERBOSE;
/* data */
extern Point Points[ArraySize];
/*
Triangulation information - a Hull, a set of Triangles, a set of
"Suspect" edges, and a "ProbeRoot" (a vertex used as a hint to the
Search procedure.
Only one region here. You want more? Looks easy, go do it.
*/
extern PointSetPointer Hull;
extern TrianglePointer Triangles;
extern EdgePointer Suspects;
extern int ProbeRoot;
extern REAL AnimateColor, AnimateErase;
extern void Animate();
/*
Add and Delete Suspect handle a queue of edges.
AddSuspect allows the same edge to be added multiple times.
The first addition puts the edge at the end of the queue.
Subsequent additions do nothing.
RemoveSuspect may be called even when the edge is not in the queue.
*/
extern void AddSuspect();
extern void RemoveSuspect();
/*
FindEdge tries to find an existing edge (P1,P2) in the edge list for P1.
*/
extern Edge *FindEdge ();
/*
Does the path A -> B -> C turn to the left?
*/
extern int LeftTurn();
/*
This little gem is due to Rob Fowler. Given two points A, and B
we calculate a number in [0.0, 8.0) which is a monotonic function of
the direction from A to B.
(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0) correspond to
( 0, 45, 90, 135, 180, 225, 270, 315, 360) degrees, measured
counter-clockwise from the positive x axis.
*/
extern REAL FowlerAngle();
/*
FindNeighbors locates the edges which would be the left
and right neighbors of the edge O-P, in the list of edges
emanating from O.
ASSUMPTION: there must be at least 1 Edge emanating from O; check
before you call...
*/
extern void FindNeighbors ();
/*
AddEdge inserts the edge P1-P2, and the information that this edge
has the Triangle T on its left.
*/
extern Edge *AddEdge ();
/*
RemoveEdge removes the Triangle T as support for Edge E.
If there is no triangle on the other side of E, then E is destroyed.
*/
extern void RemoveEdge();
/*
AddTriangle creates the triangle A,B,C - with figure of merit R.
(See Rho and Shrink for the definition and use of R)
*/
extern void AddTriangle ();
/*
RemoveTriangle destroys T
*/
extern void RemoveTriangle();
/*
InsideTriangle decides if a point P is Inside, or OnEdge
of the triangle defined by A, B, C.
If P is on an edge, V1 and V2 identify that edge
*/
extern void InsideTriangle ();
/*
Given (the GLOBAL variable) ProbeRoot - a vertex in a
CONVEX, TRIANGULATED region
of the plane, Search attempts to find the Triangle containing P.
This is accomplished by traversing the triangulation from ProbeRoot
towards P until an enclosing triangle is found, or P is shown to be
outside the convex hull.
If an enclosing triangle is found, Search also determines if P is
strictly Inside, or OnEdge. If OnEdge, then V1 and V2 identify
the edge.
LeftNeighbor and RightNeighbor are two edges emanating from a vertex
in the triangulated region which bracket the point P. If P is Inside
or OnEdge, then these are edges of the enclosing triangle. If no
enclosing triangle is found, then these are edges from the convex hull
of the triangulated region. In the latter case, V1 identifies the
shared vertex.
Thus, if P is known to be inside the convex hull, then Search will
always return the enclosing triangle.
If P is OUTSIDE the convex hull, then V1 is visible from P, and the
Neighboring edges are the hull edges emanating from V1.
*/
extern Triangle *Search ( );
/*
Rho calculates 4r^2, where r is the radius of the circle passing
through A, B, C
The calculation of Rho depends on the observations that:
2
( abc ) 2 2 2 2
Rho = 4*r*r= (------- ) = a b c / (a cross c)
(a cross c )
2 2 2
and that a , b , c , and (a cross c) are all easy to calculate.
2
a is the square of the length of the side opposite vertex A
2
b is the square of the length of the side opposite vertex B
2
c is the square of the length of the side opposite vertex C
*/
extern REAL Rho();
/*
Shrink is the absolute HOT SPOT - so we've optimized as much as
is seemly. Given a pair of triangles ABC and BAD, we want to know if
the pair ADC, BCD would be better. If it is, then we make the switch.
To determine which local configuration is best, we calculate
Rabc = the square of the radius of the circle through ABC
Rbad = the square of the radius of teh circle through BAD
Radc = the square of the radius of the circle through ADC
Rbcd = the square of the radius of the circle through BCD
Rabc and Rbad are associated with the current configuration;
Radc and Rbcd are associated with the proposed configuration.
The calculation of the R's depends on the observations that:
2
( abc )
4*r*r= (------- )
(a cross c )
so, we get:
2 2 2 2
Rabc = a c e / (a cross c)
2 2 2 2
Racd = a d f / (a cross d)
2 2 2 2
Rbad = b d e / (b cross d)
2 2 2 2
Rbcd = b c f / (b cross c)
where
a is the edge connecting A and C
b is the edge connecting B and D
c is the edge connecting C and B
d is the edge connecting D and A
e is the existing diagonal connecting A and B
f is the candidate diagonal connecting C and D
Of the four circles, the smallest one is guaranteed to EXCLUDE the fourth
point - that is the configuration we want. So, we first pick the minimum
from each pair ([Rabc, Rabd] and [Racd, Rbcd]) and then compare the minima.
if min(Rabc,Rabd) > min(Racd, Rbcd) then SWAP else DO NOT SWAP.
Finally, this criterion is not valid when the quadrilateral ADBC is not
convex. So, we first check the angles DAC and CBD:
If (a cross d) and (b cross c) are both positive
then all is well. If either (a cross d) of (b cross c) are negative,
then all of the above is moot, and no swap is possible.
Here, it is important that:
a is the DIRECTED edge from A to C
b is the DIRECTED edge from B to D
c is the DIRECTED edge from C to B
d is the DIRECTED edge from D to A
Rabc and Rbad have already been calculated.
Radc and Rbcd need to be calculated - we do it ourself, for speed.
*/
extern void Shrink ();
/*
Optimize is called to clean up a triangulation.
On entry: the queue pointed at by Suspect contains "suspect" edges.
Optimize considers each suspect edge, in turn. For each such edge,
the local quadrilateral is examined. If the suspect edge is OK,
then it is simply removed from the suspect queue. If not, then the
two triangles which share this edge are removed, and two other triangles
are inserted. The four outer edges of these two triangles then become
suspect, and are added to the end of the queue.
Parameters? WE DON'T GOT TO SHOW NO STINKING PARAMETERS!
*/
extern void Optimize();
/*
InsertPoint adds a new point.
The current version only understands how to do that when P is
inside an already triangulated convex region.
In case of error, return false.
Coming soon, a version that will do on-line insertions
*/
extern int InsertPoint ();
/*
ConvexTriangles will triangulate a CONVEX polygon.
*/
extern void ConvexTriangles();
/*
Is A in PointSet P ?
*/
extern int InPointSet();
/*
Is A Inside Convex Polygon H
*/
extern int InSideHull();
/*
given three points (x1,y1,x2,y3,x3,y3) calculate the center
of the circle thru those points.
WARNING: if the points are colinear, we print a message, and
return something reasonble. Best not to call this routine
with colinear points, eh?
*/
extern void VoronoiPt();
/* ------------------------------------------------------------------------ */
/* Allocation routines */
#define POINT_GLOB_SIZE 1024
extern PointSetPointer PointFreeList;
#define NewPoint() \
(PointSetPointer) New( sizeof(PointSetElement),POINT_GLOB_SIZE,&PointFreeList )
#define DisposePoint(p) \
Dispose( p, &PointFreeList );
#define EDGE_GLOB_SIZE 1024
extern EdgePointer EdgeFreeList;
#define NewEdge() \
(EdgePointer) New( sizeof(Edge), EDGE_GLOB_SIZE, &EdgeFreeList )
#define DisposeEdge(e) \
Dispose( e, &EdgeFreeList );
#define TRIANGLE_GLOB_SIZE 1024
extern TrianglePointer TriangleFreeList;
#define NewTriangle() \
(TrianglePointer) New( sizeof(Triangle),TRIANGLE_GLOB_SIZE,&TriangleFreeList)
#define DisposeTriangle(t) \
Dispose( t, &TriangleFreeList );