Initial implementation of Constrained Delaunay Refinement
diff --git a/Include/tesselator.h b/Include/tesselator.h
index c27541e..1cc4166 100755
--- a/Include/tesselator.h
+++ b/Include/tesselator.h
@@ -107,11 +107,16 @@
 //         glEnd();
 //     }
 //
+// TESS_CONSTRAINED_DELAUNAY_TRIANGLES
+//   Similar to TESS_POLYGONS, but we output only triangles and we attempt to provide a valid
+//   Constrained Delaunay triangulation.
+
 enum TessElementType
 {
 	TESS_POLYGONS,
 	TESS_CONNECTED_POLYGONS,
 	TESS_BOUNDARY_CONTOURS,
+	TESS_CONSTRAINED_DELAUNAY_TRIANGLES,
 };
 
 typedef float TESSreal;
@@ -189,7 +194,7 @@
 //   tess - pointer to tesselator object.
 //   windingRule - winding rules used for tesselation, must be one of TessWindingRule.
 //   elementType - defines the tesselation result element type, must be one of TessElementType.
-//   polySize - defines maximum vertices per polygons if output is polygons.
+//   polySize - defines maximum vertices per polygons if output is polygons. If elementType is TESS_CONSTRAINED_DELAUNAY_TRIANGLES, this parameter is ignored.
 //   vertexSize - defines the number of coordinates in tesselation result vertex, must be 2 or 3.
 //   normal - defines the normal of the input contours, of null the normal is calculated automatically.
 // Returns:
diff --git a/Source/geom.c b/Source/geom.c
index a49ea17..4c49d58 100755
--- a/Source/geom.c
+++ b/Source/geom.c
@@ -33,6 +33,7 @@
 #include <assert.h>

 #include "mesh.h"

 #include "geom.h"

+#include <math.h>

 

 int tesvertLeq( TESSvertex *u, TESSvertex *v )

 {

@@ -259,3 +260,22 @@
 		v->t = Interpolate( z1, o2->t, z2, d2->t );

 	}

 }

+

+/*

+	Calculate the angle between v1-v2 and v1-v0

+ */

+TESSreal calcAngle(TESSvertex *v0, TESSvertex *v1, TESSvertex *v2) {

+	TESSreal a[2] = { v2->s - v1->s, v2->t - v1->t };

+	TESSreal b[2] = { v0->s - v1->s, v0->t - v1->t };

+	return acosf((a[0] * b[0] + a[1] * b[1]) /

+							 (sqrt(a[0] * a[0] + a[1] * a[1]) * sqrt(b[0] * b[0] + b[1] * b[1])));

+}

+

+/*

+	Returns 1 is edge is locally delaunay

+ */

+int tesedgeIsLocallyDelaunay( TESShalfEdge *e )

+{

+	return (calcAngle(e->Lnext->Org, e->Lnext->Lnext->Org, e->Org) +

+					calcAngle(e->Sym->Lnext->Org, e->Sym->Lnext->Lnext->Org, e->Sym->Org)) < (M_PI + 0.01);

+}

diff --git a/Source/geom.h b/Source/geom.h
index cf83897..c29a932 100755
--- a/Source/geom.h
+++ b/Source/geom.h
@@ -59,6 +59,7 @@
 

 #define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org )

 #define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst )

+#define EdgeIsInternal(e) e->Rface && e->Rface->inside

 

 #define ABS(x) ((x) < 0 ? -(x) : (x))

 #define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t))

@@ -72,5 +73,6 @@
 TESSreal	testransSign( TESSvertex *u, TESSvertex *v, TESSvertex *w );

 int tesvertCCW( TESSvertex *u, TESSvertex *v, TESSvertex *w );

 void tesedgeIntersect( TESSvertex *o1, TESSvertex *d1, TESSvertex *o2, TESSvertex *d2, TESSvertex *v );

+int tesedgeIsLocallyDelaunay( TESShalfEdge *e );

 

 #endif

diff --git a/Source/mesh.c b/Source/mesh.c
index 06a8ced..1b0ff9e 100755
--- a/Source/mesh.c
+++ b/Source/mesh.c
@@ -80,6 +80,7 @@
 	e->Lface = NULL;
 	e->winding = 0;
 	e->activeRegion = NULL;
+	e->mark = 0;
 
 	eSym->Sym = e;
 	eSym->Onext = eSym;
@@ -88,6 +89,7 @@
 	eSym->Lface = NULL;
 	eSym->winding = 0;
 	eSym->activeRegion = NULL;
+	eSym->mark = 0;
 
 	return e;
 }
@@ -748,6 +750,85 @@
 	return 1;
 }
 
+#include <stdio.h>
+
+void tessMeshFlipEdge( TESSmesh *mesh, TESShalfEdge *edge )
+{
+	assert(EdgeIsInternal(edge));
+
+	TESShalfEdge *a0 = edge;
+	TESShalfEdge *a1 = a0->Lnext;
+	TESShalfEdge *a2 = a1->Lnext;
+	assert(a2->Lnext == a0);
+	TESShalfEdge *b0 = edge->Sym;
+	TESShalfEdge *b1 = b0->Lnext;
+	TESShalfEdge *b2 = b1->Lnext;
+	assert(b2->Lnext == b0);
+
+	TESSvertex *aOrg = a0->Org;
+	TESSvertex *aOpp = a2->Org;
+	TESSvertex *bOrg = b0->Org;
+	TESSvertex *bOpp = b2->Org;
+
+	TESSface *fa = a0->Lface;
+	TESSface *fb = b0->Lface;
+
+	a0->Org = bOpp;
+	a0->Onext = b1->Sym;
+	b0->Org = aOpp;
+	b0->Onext = a1->Sym;
+	a2->Onext = b0;
+	b2->Onext = a0;
+    b1->Onext = a2->Sym;
+    a1->Onext = b2->Sym;
+
+	a0->Lnext = a2;
+	a2->Lnext = b1;
+	b1->Lnext = a0;
+
+	b0->Lnext = b2;
+	b2->Lnext = a1;
+	a1->Lnext = b0;
+
+	a1->Lface = fb;
+	b1->Lface = fa;
+
+	fa->anEdge = a0;
+	fb->anEdge = b0;
+
+	if (aOrg->anEdge == a0) aOrg->anEdge = b1;
+	if (bOrg->anEdge == b0) bOrg->anEdge = a1;
+
+	assert( a0->Lnext->Onext->Sym == a0 );
+	assert( a0->Onext->Sym->Lnext == a0 );
+	assert( a0->Org->anEdge->Org == a0->Org );
+
+
+	assert( a1->Lnext->Onext->Sym == a1 );
+	assert( a1->Onext->Sym->Lnext == a1 );
+	assert( a1->Org->anEdge->Org == a1->Org );
+
+	assert( a2->Lnext->Onext->Sym == a2 );
+	assert( a2->Onext->Sym->Lnext == a2 );
+	assert( a2->Org->anEdge->Org == a2->Org );
+
+	assert( b0->Lnext->Onext->Sym == b0 );
+	assert( b0->Onext->Sym->Lnext == b0 );
+	assert( b0->Org->anEdge->Org == b0->Org );
+
+	assert( b1->Lnext->Onext->Sym == b1 );
+	assert( b1->Onext->Sym->Lnext == b1 );
+	assert( b1->Org->anEdge->Org == b1->Org );
+
+	assert( b2->Lnext->Onext->Sym == b2 );
+	assert( b2->Onext->Sym->Lnext == b2 );
+	assert( b2->Org->anEdge->Org == b2->Org );
+
+	assert(aOrg->anEdge->Org == aOrg);
+	assert(bOrg->anEdge->Org == bOrg);
+
+	assert(a0->Oprev->Onext->Org == a0->Org);
+}
 
 #ifdef DELETE_BY_ZAPPING
 
diff --git a/Source/mesh.h b/Source/mesh.h
index c492916..479c66f 100755
--- a/Source/mesh.h
+++ b/Source/mesh.h
@@ -143,6 +143,7 @@
 	ActiveRegion *activeRegion;  /* a region with this upper edge (sweep.c) */
 	int winding;    /* change in winding number when crossing
 						  from the right face to the left face */
+	int mark; /* Used by the Edge Flip algorithm */
 };
 
 #define Rface   Sym->Lface
@@ -155,7 +156,6 @@
 #define Dnext   Rprev->Sym  /* 3 pointers */
 #define Rnext   Oprev->Sym  /* 3 pointers */
 
-
 struct TESSmesh {
 	TESSvertex vHead;      /* dummy header for vertex list */
 	TESSface fHead;      /* dummy header for face list */
@@ -258,6 +258,8 @@
 void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh );
 void tessMeshZapFace( TESSmesh *mesh, TESSface *fZap );
 
+void tessMeshFlipEdge( TESSmesh *mesh, TESShalfEdge *edge );
+
 #ifdef NDEBUG
 #define tessMeshCheckMesh( mesh )
 #else
diff --git a/Source/tess.c b/Source/tess.c
index 013b84c..e1d3b63 100755
--- a/Source/tess.c
+++ b/Source/tess.c
@@ -365,7 +365,6 @@
 	return 1;
 }
 
-
 /* tessMeshTessellateInterior( mesh ) tessellates each region of
 * the mesh which is marked "inside" the polygon.  Each such region
 * must be monotone.
@@ -382,6 +381,100 @@
 			if ( !tessMeshTessellateMonoRegion( mesh, f ) ) return 0;
 		}
 	}
+	return 1;
+}
+
+
+struct EdgeStackNode {
+	TESShalfEdge *edge;
+	struct EdgeStackNode *next;
+};
+
+struct EdgeStack {
+	struct EdgeStackNode *top;
+};
+
+void stackInit(struct EdgeStack *stack)
+{
+    stack->top = NULL;
+}
+
+int stackEmpty(struct EdgeStack *stack)
+{
+	return stack->top == NULL;
+}
+
+void stackPush(struct EdgeStack *stack, TESShalfEdge *e)
+{
+	struct EdgeStackNode *node = malloc(sizeof(struct EdgeStackNode));
+	node->edge = e;
+	node->next = stack->top;
+	stack->top = node;
+}
+
+TESShalfEdge *stackPop(struct EdgeStack *stack)
+{
+    TESShalfEdge *e = NULL;
+	struct EdgeStackNode *node = stack->top;
+	if (node) {
+		stack->top = node->next;
+		e = node->edge;
+            free(node);
+	}
+	return e;
+}
+
+/*
+	Starting with a valid triangulation, uses the Edge Flip algorithm to
+	refine the triangulation into a Constrained Delaunay Triangulation.
+*/
+int tessMeshRefineDelaunay( TESSmesh *mesh )
+{
+	/* At this point, we have a valid, but not optimal, triangulation.
+		 We refine the triangulation using the Edge Flip algorithm */
+
+/*
+	 1) Find all internal edges
+	 2) Mark all dual edges
+	 3) insert all dual edges into a queue
+*/
+	TESSface *f;
+	struct EdgeStack stack;
+    stackInit(&stack);
+	TESShalfEdge *e;
+	TESShalfEdge *edges[4];
+	for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
+		if ( f->inside) {
+			e = f->anEdge;
+			do {
+				e->mark = EdgeIsInternal(e); /* Mark internal edges */
+				if (e->mark && !e->Sym->mark) stackPush(&stack, e); /* Insert into queue */
+				e = e->Lnext;
+			} while (e != f->anEdge);
+		}
+	}
+
+	// Pop stack until we find a reversed edge
+	// Flip the reversed edge, and insert any of the four opposite edges
+	// which are internal and not already in the stack (!marked)
+	while (!stackEmpty(&stack)) {
+		e = stackPop(&stack);
+		e->mark = e->Sym->mark = 0;
+		if (!tesedgeIsLocallyDelaunay(e)) {
+			tessMeshFlipEdge(mesh, e);
+			// for each opposite edge
+			edges[0] = e->Lnext;
+			edges[1] = e->Lprev;
+			edges[2] = e->Sym->Lnext;
+			edges[3] = e->Sym->Lprev;
+			for (int i=0;i<3;i++) {
+				if (!edges[i]->mark && EdgeIsInternal(edges[i])) {
+					edges[i]->mark = edges[i]->Sym->mark = 1;
+					stackPush(&stack, edges[i]);
+				}
+			}
+		}
+	}
 
 	return 1;
 }
@@ -926,6 +1019,11 @@
 		rc = tessMeshSetWindingNumber( mesh, 1, TRUE );
 	} else {
 		rc = tessMeshTessellateInterior( mesh ); 
+		if (elementType == TESS_CONSTRAINED_DELAUNAY_TRIANGLES) {
+			rc = tessMeshRefineDelaunay( mesh );
+			elementType = TESS_POLYGONS;
+			polySize = 3;
+		}
 	}
 	if (rc == 0) longjmp(tess->env,1);  /* could've used a label */
 
diff --git a/alg_outline.md b/alg_outline.md
index 449ca2a..9a4a2bd 100644
--- a/alg_outline.md
+++ b/alg_outline.md
@@ -223,3 +223,11 @@
 The triangulation itself is not optimized to reduce the number of
 primitives; we just try to get a reasonable decomposition of the
 computed triangulation.
+
+Optionally, it's possible to output a Constrained Delaunay Triangulation.
+This is done by doing a delaunay refinement with the normal triangulation as
+a basis. The Edge Flip algorithm is used, which is guaranteed to terminate in O(n^2).
+
+Note: We don't use robust predicates to check if edges are locally
+delaunay, but currently us a naive epsilon of 0.01 radians to ensure
+termination.