1096 lines
25 KiB
C++
1096 lines
25 KiB
C++
/*
|
|
** delaunay.c : compute 2D delaunay triangulation in the plane.
|
|
** Copyright (C) 2005 Wael El Oraiby <wael.eloraiby@gmail.com>
|
|
**
|
|
**
|
|
** This program 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 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <QtGlobal>
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
|
|
#include "../vmisc/diagnostic.h"
|
|
#endif // QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
|
|
#include "delaunay.h"
|
|
|
|
QT_WARNING_PUSH
|
|
QT_WARNING_DISABLE_GCC("-Wold-style-cast")
|
|
// cppcheck-suppress unknownMacro
|
|
QT_WARNING_DISABLE_CLANG("-Wold-style-cast")
|
|
QT_WARNING_DISABLE_GCC("-Wcast-qual")
|
|
QT_WARNING_DISABLE_CLANG("-Wcast-qual")
|
|
QT_WARNING_DISABLE_GCC("-Walloc-zero")
|
|
|
|
#if PREDICATE == EXACT_PREDICATE
|
|
extern void exactinit();
|
|
extern real incircle(real* pa, real* pb, real* pc, real* pd);
|
|
#endif
|
|
|
|
#define ON_RIGHT 1
|
|
#define ON_SEG 0
|
|
#define ON_LEFT -1
|
|
|
|
#define OUTSIDE -1
|
|
#define ON_CIRCLE 0
|
|
#define INSIDE 1
|
|
|
|
struct delaunay_s;
|
|
struct face_s;
|
|
struct halfedge_s;
|
|
struct point2d_s;
|
|
|
|
|
|
#ifdef USE_DOUBLE
|
|
# define REAL_ZERO 0.0
|
|
# define REAL_ONE 1.0
|
|
# define REAL_TWO 2.0
|
|
# define REAL_FOUR 4.0
|
|
# define TOLERANCE (1024.0 * 1024.0)
|
|
#else
|
|
# define REAL_ZERO 0.0f
|
|
# define REAL_ONE 1.0f
|
|
# define REAL_TWO 2.0f
|
|
# define REAL_FOUR 4.0f
|
|
# define TOLERANCE (16.0f )
|
|
#endif
|
|
|
|
#define EPSILON (REAL_ONE / TOLERANCE)
|
|
|
|
typedef point2d_s point2d_t;
|
|
typedef face_s face_t;
|
|
typedef halfedge_s halfedge_t;
|
|
typedef delaunay_s delaunay_t;
|
|
typedef real mat3_t[3][3];
|
|
|
|
struct point2d_s
|
|
{
|
|
real x, y; /* point coordinates */
|
|
halfedge_t* he; /* point halfedge */
|
|
quint32 idx; /* point index in input buffer */
|
|
};
|
|
|
|
struct face_s
|
|
{
|
|
/* real radius;
|
|
real cx, cy;
|
|
point2d_t* p[3];
|
|
*/
|
|
halfedge_t* he; /* a pointing half edge */
|
|
quint32 num_verts; /* number of vertices on this face */
|
|
};
|
|
|
|
struct halfedge_s
|
|
{
|
|
point2d_t* vertex; /* vertex */
|
|
halfedge_t* pair; /* pair */
|
|
halfedge_t* next; /* next */
|
|
halfedge_t* prev; /* next^-1 */
|
|
face_t* face; /* halfedge face */
|
|
};
|
|
|
|
struct delaunay_s
|
|
{
|
|
halfedge_t* rightmost_he; /* right most halfedge */
|
|
halfedge_t* leftmost_he; /* left most halfedge */
|
|
point2d_t** points; /* pointer to points */
|
|
face_t* faces; /* faces of delaunay */
|
|
quint32 num_faces; /* face count */
|
|
quint32 start_point; /* start point index */
|
|
quint32 end_point; /* end point index */
|
|
};
|
|
|
|
|
|
/*
|
|
* 3x3 matrix determinant
|
|
*/
|
|
//static real det3( mat3_t *m )
|
|
//{
|
|
// real res;
|
|
|
|
// res = ((*m)[0][0]) * (((*m)[1][1]) * ((*m)[2][2]) - ((*m)[1][2]) * ((*m)[2][1]))
|
|
// - ((*m)[0][1]) * (((*m)[1][0]) * ((*m)[2][2]) - ((*m)[1][2]) * ((*m)[2][0]))
|
|
// + ((*m)[0][2]) * (((*m)[1][0]) * ((*m)[2][1]) - ((*m)[1][1]) * ((*m)[2][0]));
|
|
|
|
// return res;
|
|
//}
|
|
|
|
/*
|
|
* allocate a point
|
|
*/
|
|
static point2d_t* point_alloc()
|
|
{
|
|
point2d_t* p;
|
|
|
|
p = (point2d_t*)malloc(sizeof(point2d_t));
|
|
assert( p != nullptr );
|
|
// cppcheck-suppress memsetClassFloat
|
|
memset(p, 0, sizeof(point2d_t));
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* free a point
|
|
*/
|
|
static void point_free( point2d_t* p )
|
|
{
|
|
assert( p != nullptr );
|
|
free(p);
|
|
}
|
|
|
|
/*
|
|
* allocate a halfedge
|
|
*/
|
|
static halfedge_t* halfedge_alloc()
|
|
{
|
|
halfedge_t* d;
|
|
|
|
d = (halfedge_t*)malloc(sizeof(halfedge_t));
|
|
assert( d != nullptr );
|
|
memset(d, 0, sizeof(halfedge_t));
|
|
|
|
return d;
|
|
}
|
|
|
|
/*
|
|
* free a halfedge
|
|
*/
|
|
static void halfedge_free( halfedge_t* d )
|
|
{
|
|
assert( d != nullptr );
|
|
memset(d, 0, sizeof(halfedge_t));
|
|
free(d);
|
|
}
|
|
|
|
/*
|
|
* free all delaunay halfedges
|
|
*/
|
|
void del_free_halfedges( delaunay_t *del );
|
|
void del_free_halfedges( delaunay_t *del )
|
|
{
|
|
quint32 i;
|
|
halfedge_t *d, *sig;
|
|
|
|
/* if there is nothing to do */
|
|
if( del->points == nullptr )
|
|
return;
|
|
|
|
for( i = 0; i <= (del->end_point - del->start_point); i++ )
|
|
{
|
|
/* free all the halfedges around the point */
|
|
d = del->points[i]->he;
|
|
if( d != nullptr )
|
|
{
|
|
do {
|
|
sig = d->next;
|
|
halfedge_free( d );
|
|
d = sig;
|
|
} while( d != del->points[i]->he );
|
|
del->points[i]->he = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* allocate memory for a face
|
|
*/
|
|
//static face_t* face_alloc()
|
|
//{
|
|
// face_t *f = (face_t*)malloc(sizeof(face_t));
|
|
// assert( f != nullptr );
|
|
// memset(f, 0, sizeof(face_t));
|
|
// return f;
|
|
//}
|
|
|
|
/*
|
|
* free a face
|
|
*/
|
|
//static void face_free(face_t *f)
|
|
//{
|
|
// assert( f != nullptr );
|
|
// free(f);
|
|
//}
|
|
|
|
/*
|
|
* compare 2 points when sorting
|
|
*/
|
|
static int cmp_points( const void *_pt0, const void *_pt1 )
|
|
{
|
|
point2d_t *pt0, *pt1;
|
|
|
|
pt0 = (point2d_t*)(*((point2d_t**)_pt0));
|
|
pt1 = (point2d_t*)(*((point2d_t**)_pt1));
|
|
|
|
if( pt0->x < pt1->x )
|
|
return -1;
|
|
else if( pt0->x > pt1->x )
|
|
return 1;
|
|
else if( pt0->y < pt1->y )
|
|
return -1;
|
|
else if( pt0->y > pt1->y )
|
|
return 1;
|
|
assert(0 && "2 or more points share the same exact coordinate");
|
|
return 0; /* Should not be given! */
|
|
}
|
|
|
|
/*
|
|
* classify a point relative to a segment
|
|
*/
|
|
static int classify_point_seg( point2d_t *s, point2d_t *e, point2d_t *pt )
|
|
{
|
|
point2d_t se, spt;
|
|
real res;
|
|
|
|
se.x = e->x - s->x;
|
|
se.y = e->y - s->y;
|
|
|
|
spt.x = pt->x - s->x;
|
|
spt.y = pt->y - s->y;
|
|
|
|
res = (( se.x * spt.y ) - ( se.y * spt.x ));
|
|
if( res < REAL_ZERO )
|
|
return ON_RIGHT;
|
|
else if( res > REAL_ZERO )
|
|
return ON_LEFT;
|
|
|
|
return ON_SEG;
|
|
}
|
|
|
|
/*
|
|
* classify a point relative to a halfedge, -1 is left, 0 is on, 1 is right
|
|
*/
|
|
static int del_classify_point( halfedge_t *d, point2d_t *pt )
|
|
{
|
|
point2d_t *s, *e;
|
|
|
|
s = d->vertex;
|
|
e = d->pair->vertex;
|
|
|
|
return classify_point_seg(s, e, pt);
|
|
}
|
|
|
|
/*
|
|
* return the absolute value
|
|
*/
|
|
//static real dabs( real a )
|
|
//{
|
|
// if( a < REAL_ZERO )
|
|
// return (-a);
|
|
// return a;
|
|
//}
|
|
|
|
/*
|
|
* compute the circle given 3 points
|
|
*/
|
|
#if PREDICATE == LOOSE_PREDICATE
|
|
static void compute_circle( point2d_t *pt0, point2d_t *pt1, point2d_t *pt2, real *cx, real *cy, real *radius )
|
|
{
|
|
mat3_t ma, mbx, mby, mc;
|
|
real x0y0, x1y1, x2y2;
|
|
real a, bx, by, c;
|
|
|
|
/* calculate x0y0, .... */
|
|
x0y0 = pt0->x * pt0->x + pt0->y * pt0->y;
|
|
x1y1 = pt1->x * pt1->x + pt1->y * pt1->y;
|
|
x2y2 = pt2->x * pt2->x + pt2->y * pt2->y;
|
|
|
|
/* setup A matrix */
|
|
ma[0][0] = pt0->x;
|
|
ma[0][1] = pt0->y;
|
|
ma[1][0] = pt1->x;
|
|
ma[1][1] = pt1->y;
|
|
ma[2][0] = pt2->x;
|
|
ma[2][1] = pt2->y;
|
|
ma[0][2] = ma[1][2] = ma[2][2] = REAL_ONE;
|
|
|
|
/* setup Bx matrix */
|
|
mbx[0][0] = x0y0;
|
|
mbx[1][0] = x1y1;
|
|
mbx[2][0] = x2y2;
|
|
mbx[0][1] = pt0->y;
|
|
mbx[1][1] = pt1->y;
|
|
mbx[2][1] = pt2->y;
|
|
mbx[0][2] = mbx[1][2] = mbx[2][2] = REAL_ONE;
|
|
|
|
/* setup By matrix */
|
|
mby[0][0] = x0y0;
|
|
mby[1][0] = x1y1;
|
|
mby[2][0] = x2y2;
|
|
mby[0][1] = pt0->x;
|
|
mby[1][1] = pt1->x;
|
|
mby[2][1] = pt2->x;
|
|
mby[0][2] = mby[1][2] = mby[2][2] = REAL_ONE;
|
|
|
|
/* setup C matrix */
|
|
mc[0][0] = x0y0;
|
|
mc[1][0] = x1y1;
|
|
mc[2][0] = x2y2;
|
|
mc[0][1] = pt0->x;
|
|
mc[1][1] = pt1->x;
|
|
mc[2][1] = pt2->x;
|
|
mc[0][2] = pt0->y;
|
|
mc[1][2] = pt1->y;
|
|
mc[2][2] = pt2->y;
|
|
|
|
/* compute a, bx, by and c */
|
|
a = det3(&ma);
|
|
bx = det3(&mbx);
|
|
by = -det3(&mby);
|
|
c = -det3(&mc);
|
|
|
|
*cx = bx / (REAL_TWO * a);
|
|
*cy = by / (REAL_TWO * a);
|
|
*radius = sqrt(bx * bx + by * by - REAL_FOUR * a * c) / (REAL_TWO * dabs(a));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* test if a point is inside a circle given by 3 points, 1 if inside, 0 if outside
|
|
*/
|
|
static int in_circle( point2d_t *pt0, point2d_t *pt1, point2d_t *pt2, point2d_t *p )
|
|
{
|
|
#if PREDICATE == EXACT_PREDICATE
|
|
real res = incircle(&(pt0->x), &(pt1->x), &(pt2->x), &(p->x));
|
|
if( res > REAL_ZERO )
|
|
return INSIDE;
|
|
else if( res < REAL_ZERO )
|
|
return OUTSIDE;
|
|
|
|
return ON_CIRCLE;
|
|
#endif
|
|
#if PREDICATE == LOOSE_PREDICATE
|
|
real cx, cy, radius;
|
|
compute_circle(pt0, pt1, pt2, &cx, &cy, &radius);
|
|
|
|
real distance = sqrt((p->x - cx) * (p->x - cx) + (p->y - cy) * (p->y - cy));
|
|
if( distance < radius - EPSILON )
|
|
return INSIDE;
|
|
else if(distance > radius + EPSILON )
|
|
return OUTSIDE;
|
|
return ON_CIRCLE;
|
|
#endif
|
|
#if PREDICATE == FAST_PREDICATE
|
|
mat3_t ma, mbx, mby, mc;
|
|
real x0y0, x1y1, x2y2;
|
|
real a, bx, by, c, res;
|
|
|
|
/* calculate x0y0, .... */
|
|
x0y0 = pt0->x * pt0->x + pt0->y * pt0->y;
|
|
x1y1 = pt1->x * pt1->x + pt1->y * pt1->y;
|
|
x2y2 = pt2->x * pt2->x + pt2->y * pt2->y;
|
|
|
|
/* setup A matrix */
|
|
ma[0][0] = pt0->x;
|
|
ma[0][1] = pt0->y;
|
|
ma[1][0] = pt1->x;
|
|
ma[1][1] = pt1->y;
|
|
ma[2][0] = pt2->x;
|
|
ma[2][1] = pt2->y;
|
|
ma[0][2] = ma[1][2] = ma[2][2] = REAL_ONE;
|
|
|
|
/* setup Bx matrix */
|
|
mbx[0][0] = x0y0;
|
|
mbx[1][0] = x1y1;
|
|
mbx[2][0] = x2y2;
|
|
mbx[0][1] = pt0->y;
|
|
mbx[1][1] = pt1->y;
|
|
mbx[2][1] = pt2->y;
|
|
mbx[0][2] = mbx[1][2] = mbx[2][2] = REAL_ONE;
|
|
|
|
/* setup By matrix */
|
|
mby[0][0] = x0y0;
|
|
mby[1][0] = x1y1;
|
|
mby[2][0] = x2y2;
|
|
mby[0][1] = pt0->x;
|
|
mby[1][1] = pt1->x;
|
|
mby[2][1] = pt2->x;
|
|
mby[0][2] = mby[1][2] = mby[2][2] = REAL_ONE;
|
|
|
|
/* setup C matrix */
|
|
mc[0][0] = x0y0;
|
|
mc[1][0] = x1y1;
|
|
mc[2][0] = x2y2;
|
|
mc[0][1] = pt0->x;
|
|
mc[1][1] = pt1->x;
|
|
mc[2][1] = pt2->x;
|
|
mc[0][2] = pt0->y;
|
|
mc[1][2] = pt1->y;
|
|
mc[2][2] = pt2->y;
|
|
|
|
/* compute a, bx, by and c */
|
|
a = det3(&ma);
|
|
bx = det3(&mbx);
|
|
by = -det3(&mby);
|
|
c = -det3(&mc);
|
|
|
|
res = a * (p->x * p->x + p->y * p->y ) - bx * p->x - by * p->y + c;
|
|
|
|
|
|
if( res < REAL_ZERO )
|
|
return INSIDE;
|
|
else if( res > REAL_ZERO )
|
|
return OUTSIDE;
|
|
|
|
return ON_CIRCLE;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* initialize delaunay segment
|
|
*/
|
|
static int del_init_seg( delaunay_t *del, int start )
|
|
{
|
|
halfedge_t *d0, *d1;
|
|
point2d_t *pt0, *pt1;
|
|
|
|
/* init delaunay */
|
|
del->start_point = static_cast<quint32>(start);
|
|
del->end_point = static_cast<quint32>(start + 1);
|
|
|
|
/* setup pt0 and pt1 */
|
|
pt0 = del->points[start];
|
|
pt1 = del->points[start + 1];
|
|
|
|
/* allocate the halfedges and setup them */
|
|
d0 = halfedge_alloc();
|
|
d1 = halfedge_alloc();
|
|
|
|
d0->vertex = pt0;
|
|
d1->vertex = pt1;
|
|
|
|
d0->next = d0->prev = d0;
|
|
d1->next = d1->prev = d1;
|
|
|
|
d0->pair = d1;
|
|
d1->pair = d0;
|
|
|
|
pt0->he = d0;
|
|
pt1->he = d1;
|
|
|
|
del->rightmost_he = d1;
|
|
del->leftmost_he = d0;
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* initialize delaunay triangle
|
|
*/
|
|
static int del_init_tri( delaunay_t *del, int start )
|
|
{
|
|
halfedge_t *d0, *d1, *d2, *d3, *d4, *d5;
|
|
point2d_t *pt0, *pt1, *pt2;
|
|
|
|
/* initiate delaunay */
|
|
del->start_point = static_cast<quint32>(start);
|
|
del->end_point = static_cast<quint32>(start + 2);
|
|
|
|
/* setup the points */
|
|
pt0 = del->points[start];
|
|
pt1 = del->points[start + 1];
|
|
pt2 = del->points[start + 2];
|
|
|
|
/* allocate the 6 halfedges */
|
|
d0 = halfedge_alloc();
|
|
d1 = halfedge_alloc();
|
|
d2 = halfedge_alloc();
|
|
d3 = halfedge_alloc();
|
|
d4 = halfedge_alloc();
|
|
d5 = halfedge_alloc();
|
|
|
|
if( classify_point_seg(pt0, pt2, pt1) == ON_LEFT ) /* first case */
|
|
{
|
|
/* set halfedges points */
|
|
d0->vertex = pt0;
|
|
d1->vertex = pt2;
|
|
d2->vertex = pt1;
|
|
|
|
d3->vertex = pt2;
|
|
d4->vertex = pt1;
|
|
d5->vertex = pt0;
|
|
|
|
/* set points halfedges */
|
|
pt0->he = d0;
|
|
pt1->he = d2;
|
|
pt2->he = d1;
|
|
|
|
/* next and next -1 setup */
|
|
d0->next = d5;
|
|
d0->prev = d5;
|
|
|
|
d1->next = d3;
|
|
d1->prev = d3;
|
|
|
|
d2->next = d4;
|
|
d2->prev = d4;
|
|
|
|
d3->next = d1;
|
|
d3->prev = d1;
|
|
|
|
d4->next = d2;
|
|
d4->prev = d2;
|
|
|
|
d5->next = d0;
|
|
d5->prev = d0;
|
|
|
|
/* set halfedges pair */
|
|
d0->pair = d3;
|
|
d3->pair = d0;
|
|
|
|
d1->pair = d4;
|
|
d4->pair = d1;
|
|
|
|
d2->pair = d5;
|
|
d5->pair = d2;
|
|
|
|
del->rightmost_he = d1;
|
|
del->leftmost_he = d0;
|
|
|
|
} else /* 2nd case */
|
|
{
|
|
/* set halfedges points */
|
|
d0->vertex = pt0;
|
|
d1->vertex = pt1;
|
|
d2->vertex = pt2;
|
|
|
|
d3->vertex = pt1;
|
|
d4->vertex = pt2;
|
|
d5->vertex = pt0;
|
|
|
|
/* set points halfedges */
|
|
pt0->he = d0;
|
|
pt1->he = d1;
|
|
pt2->he = d2;
|
|
|
|
/* next and next -1 setup */
|
|
d0->next = d5;
|
|
d0->prev = d5;
|
|
|
|
d1->next = d3;
|
|
d1->prev = d3;
|
|
|
|
d2->next = d4;
|
|
d2->prev = d4;
|
|
|
|
d3->next = d1;
|
|
d3->prev = d1;
|
|
|
|
d4->next = d2;
|
|
d4->prev = d2;
|
|
|
|
d5->next = d0;
|
|
d5->prev = d0;
|
|
|
|
/* set halfedges pair */
|
|
d0->pair = d3;
|
|
d3->pair = d0;
|
|
|
|
d1->pair = d4;
|
|
d4->pair = d1;
|
|
|
|
d2->pair = d5;
|
|
d5->pair = d2;
|
|
|
|
del->rightmost_he = d2;
|
|
del->leftmost_he = d0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* remove an edge given a halfedge
|
|
*/
|
|
static void del_remove_edge( halfedge_t *d )
|
|
{
|
|
halfedge_t *next, *prev, *pair, *orig_pair;
|
|
|
|
orig_pair = d->pair;
|
|
|
|
next = d->next;
|
|
prev = d->prev;
|
|
pair = d->pair;
|
|
|
|
assert(next != nullptr);
|
|
assert(prev != nullptr);
|
|
|
|
next->prev = prev;
|
|
prev->next = next;
|
|
|
|
|
|
/* check to see if we have already removed pair */
|
|
if( pair )
|
|
pair->pair = nullptr;
|
|
|
|
/* check to see if the vertex points to this halfedge */
|
|
if( d->vertex->he == d )
|
|
d->vertex->he = next;
|
|
|
|
d->vertex = nullptr;
|
|
d->next = nullptr;
|
|
d->prev = nullptr;
|
|
d->pair = nullptr;
|
|
|
|
next = orig_pair->next;
|
|
prev = orig_pair->prev;
|
|
pair = orig_pair->pair;
|
|
|
|
assert(next != nullptr);
|
|
assert(prev != nullptr);
|
|
|
|
next->prev = prev;
|
|
prev->next = next;
|
|
|
|
|
|
/* check to see if we have already removed pair */
|
|
if( pair )
|
|
pair->pair = nullptr;
|
|
|
|
/* check to see if the vertex points to this halfedge */
|
|
if( orig_pair->vertex->he == orig_pair )
|
|
orig_pair->vertex->he = next;
|
|
|
|
orig_pair->vertex = nullptr;
|
|
orig_pair->next = nullptr;
|
|
orig_pair->prev = nullptr;
|
|
orig_pair->pair = nullptr;
|
|
|
|
|
|
/* finally free the halfedges */
|
|
halfedge_free(d);
|
|
halfedge_free(orig_pair);
|
|
}
|
|
|
|
/*
|
|
* pass through all the halfedges on the left side and validate them
|
|
*/
|
|
static halfedge_t* del_valid_left( halfedge_t* b )
|
|
{
|
|
point2d_t *g, *d, *u, *v;
|
|
halfedge_t *c, *du, *dg;
|
|
|
|
g = b->vertex; /* base halfedge point */
|
|
dg = b;
|
|
|
|
d = b->pair->vertex; /* pair(halfedge) point */
|
|
b = b->next;
|
|
|
|
u = b->pair->vertex; /* next(pair(halfedge)) point */
|
|
du = b->pair;
|
|
|
|
v = b->next->pair->vertex; /* pair(next(next(halfedge)) point */
|
|
|
|
if( classify_point_seg(g, d, u) == ON_LEFT )
|
|
{
|
|
/* 3 points aren't colinear */
|
|
/* as long as the 4 points belong to the same circle, do the cleaning */
|
|
assert( v != u && "1: floating point precision error");
|
|
while( v != d && v != g && in_circle(g, d, u, v) == INSIDE )
|
|
{
|
|
c = b->next;
|
|
du = b->next->pair;
|
|
del_remove_edge(b);
|
|
b = c;
|
|
u = du->vertex;
|
|
v = b->next->pair->vertex;
|
|
}
|
|
|
|
assert( v != u && "2: floating point precision error");
|
|
if( v != d && v != g && in_circle(g, d, u, v) == ON_CIRCLE )
|
|
{
|
|
du = du->prev;
|
|
del_remove_edge(b);
|
|
}
|
|
} else /* treat the case where the 3 points are colinear */
|
|
du = dg;
|
|
|
|
assert(du->pair);
|
|
return du;
|
|
}
|
|
|
|
/*
|
|
* pass through all the halfedges on the right side and validate them
|
|
*/
|
|
static halfedge_t* del_valid_right( halfedge_t *b )
|
|
{
|
|
point2d_t *rv, *lv, *u, *v;
|
|
halfedge_t *c, *dd, *du;
|
|
|
|
b = b->pair;
|
|
rv = b->vertex;
|
|
dd = b;
|
|
lv = b->pair->vertex;
|
|
b = b->prev;
|
|
u = b->pair->vertex;
|
|
du = b->pair;
|
|
|
|
v = b->prev->pair->vertex;
|
|
|
|
if( classify_point_seg(lv, rv, u) == ON_LEFT )
|
|
{
|
|
assert( v != u && "1: floating point precision error");
|
|
while( v != lv && v != rv && in_circle(lv, rv, u, v) == INSIDE )
|
|
{
|
|
c = b->prev;
|
|
du = c->pair;
|
|
del_remove_edge(b);
|
|
b = c;
|
|
u = du->vertex;
|
|
v = b->prev->pair->vertex;
|
|
}
|
|
|
|
assert( v != u && "1: floating point precision error");
|
|
if( v != lv && v != rv && in_circle(lv, rv, u, v) == ON_CIRCLE )
|
|
{
|
|
du = du->next;
|
|
del_remove_edge(b);
|
|
}
|
|
} else
|
|
du = dd;
|
|
|
|
assert(du->pair);
|
|
return du;
|
|
}
|
|
|
|
|
|
/*
|
|
* validate a link
|
|
*/
|
|
static halfedge_t* del_valid_link( halfedge_t *b )
|
|
{
|
|
point2d_t *g, *g_p, *d, *d_p;
|
|
halfedge_t *gd, *dd, *new_gd, *new_dd;
|
|
|
|
g = b->vertex;
|
|
gd = del_valid_left(b);
|
|
g_p = gd->vertex;
|
|
|
|
assert(b->pair);
|
|
d = b->pair->vertex;
|
|
dd = del_valid_right(b);
|
|
d_p = dd->vertex;
|
|
assert(b->pair);
|
|
|
|
if( g != g_p && d != d_p )
|
|
{
|
|
int a = in_circle(g, d, g_p, d_p);
|
|
|
|
if( a != ON_CIRCLE )
|
|
{
|
|
if( a == INSIDE )
|
|
{
|
|
// cppcheck-suppress unreadVariable
|
|
g_p = g;
|
|
gd = b;
|
|
}
|
|
else
|
|
{
|
|
// cppcheck-suppress unreadVariable
|
|
d_p = d;
|
|
dd = b->pair;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* create the 2 halfedges */
|
|
new_gd = halfedge_alloc();
|
|
new_dd = halfedge_alloc();
|
|
|
|
/* setup new_gd and new_dd */
|
|
|
|
new_gd->vertex = gd->vertex;
|
|
new_gd->pair = new_dd;
|
|
new_gd->prev = gd;
|
|
new_gd->next = gd->next;
|
|
gd->next->prev = new_gd;
|
|
gd->next = new_gd;
|
|
|
|
new_dd->vertex = dd->vertex;
|
|
new_dd->pair = new_gd;
|
|
new_dd->prev = dd->prev;
|
|
dd->prev->next = new_dd;
|
|
new_dd->next = dd;
|
|
dd->prev = new_dd;
|
|
|
|
return new_gd;
|
|
}
|
|
|
|
/*
|
|
* find the lower tangent between the two delaunay, going from left to right (returns the left half edge)
|
|
*/
|
|
static halfedge_t* del_get_lower_tangent( delaunay_t *left, delaunay_t *right )
|
|
{
|
|
halfedge_t *right_d, *left_d, *new_ld, *new_rd;
|
|
int sl, sr;
|
|
|
|
left_d = left->rightmost_he;
|
|
right_d = right->leftmost_he;
|
|
|
|
do {
|
|
point2d_t *pl = left_d->prev->pair->vertex;
|
|
point2d_t *pr = right_d->pair->vertex;
|
|
|
|
if( (sl = classify_point_seg(left_d->vertex, right_d->vertex, pl)) == ON_RIGHT ) {
|
|
left_d = left_d->prev->pair;
|
|
}
|
|
|
|
if( (sr = classify_point_seg(left_d->vertex, right_d->vertex, pr)) == ON_RIGHT ) {
|
|
right_d = right_d->pair->next;
|
|
}
|
|
|
|
} while( sl == ON_RIGHT || sr == ON_RIGHT );
|
|
|
|
/* create the 2 halfedges */
|
|
new_ld = halfedge_alloc();
|
|
new_rd = halfedge_alloc();
|
|
|
|
/* setup new_gd and new_dd */
|
|
new_ld->vertex = left_d->vertex;
|
|
new_ld->pair = new_rd;
|
|
new_ld->prev = left_d->prev;
|
|
left_d->prev->next = new_ld;
|
|
new_ld->next = left_d;
|
|
left_d->prev = new_ld;
|
|
|
|
new_rd->vertex = right_d->vertex;
|
|
new_rd->pair = new_ld;
|
|
new_rd->prev = right_d->prev;
|
|
right_d->prev->next = new_rd;
|
|
new_rd->next = right_d;
|
|
right_d->prev = new_rd;
|
|
|
|
return new_ld;
|
|
}
|
|
|
|
/*
|
|
* link the 2 delaunay together
|
|
*/
|
|
static void del_link( delaunay_t *result, delaunay_t *left, delaunay_t *right )
|
|
{
|
|
point2d_t *u, *v, *ml, *mr;
|
|
halfedge_t *base;
|
|
|
|
assert( left->points == right->points );
|
|
|
|
/* save the most right point and the most left point */
|
|
ml = left->leftmost_he->vertex;
|
|
mr = right->rightmost_he->vertex;
|
|
|
|
base = del_get_lower_tangent(left, right);
|
|
|
|
u = base->next->pair->vertex;
|
|
v = base->pair->prev->pair->vertex;
|
|
|
|
while( del_classify_point(base, u) == ON_LEFT ||
|
|
del_classify_point(base, v) == ON_LEFT )
|
|
{
|
|
base = del_valid_link(base);
|
|
u = base->next->pair->vertex;
|
|
v = base->pair->prev->pair->vertex;
|
|
}
|
|
|
|
right->rightmost_he = mr->he;
|
|
left->leftmost_he = ml->he;
|
|
|
|
/* TODO: this part is not needed, and can be optimized */
|
|
while( del_classify_point( right->rightmost_he, right->rightmost_he->prev->pair->vertex ) == ON_RIGHT )
|
|
right->rightmost_he = right->rightmost_he->prev;
|
|
|
|
while( del_classify_point( left->leftmost_he, left->leftmost_he->prev->pair->vertex ) == ON_RIGHT )
|
|
left->leftmost_he = left->leftmost_he->prev;
|
|
|
|
result->leftmost_he = left->leftmost_he;
|
|
result->rightmost_he = right->rightmost_he;
|
|
result->points = left->points;
|
|
result->start_point = left->start_point;
|
|
result->end_point = right->end_point;
|
|
}
|
|
|
|
/*
|
|
* divide and conquer delaunay
|
|
*/
|
|
void del_divide_and_conquer( delaunay_t *del, int start, int end );
|
|
void del_divide_and_conquer( delaunay_t *del, int start, int end )
|
|
{
|
|
delaunay_t left, right;
|
|
|
|
int n = (end - start + 1);
|
|
|
|
if( n > 3 )
|
|
{
|
|
int i = (n / 2) + (n & 1);
|
|
left.points = del->points;
|
|
right.points = del->points;
|
|
del_divide_and_conquer( &left, start, start + i - 1 );
|
|
del_divide_and_conquer( &right, start + i, end );
|
|
del_link( del, &left, &right );
|
|
} else
|
|
if( n == 3 )
|
|
del_init_tri( del, start );
|
|
else
|
|
if( n == 2 )
|
|
del_init_seg( del, start );
|
|
}
|
|
|
|
static void build_halfedge_face( delaunay_t *del, halfedge_t *d )
|
|
{
|
|
halfedge_t *curr;
|
|
|
|
/* test if the halfedge has already a pointing face */
|
|
if( d->face != nullptr )
|
|
return;
|
|
|
|
del->faces = (face_t*)realloc(del->faces, (del->num_faces + 1) * sizeof(face_t));
|
|
|
|
face_t *f = &(del->faces[del->num_faces]);
|
|
curr = d;
|
|
f->he = d;
|
|
f->num_verts = 0;
|
|
do {
|
|
curr->face = f;
|
|
(f->num_verts)++;
|
|
curr = curr->pair->prev;
|
|
} while( curr != d );
|
|
|
|
(del->num_faces)++;
|
|
|
|
/* if( d->face.radius < 0.0 )
|
|
{
|
|
d->face.p[0] = d->vertex;
|
|
d->face.p[1] = d->pair->vertex;
|
|
d->face.p[2] = d->pair->prev->pair->vertex;
|
|
|
|
if( classify_point_seg( d->face.p[0], d->face.p[1], d->face.p[2] ) == ON_LEFT )
|
|
{
|
|
compute_circle(d->face.p[0], d->face.p[1], d->face.p[2], &(d->face.cx), &(d->face.cy), &(d->face.radius));
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* build the faces for all the halfedge
|
|
*/
|
|
void del_build_faces( delaunay_t *del );
|
|
void del_build_faces( delaunay_t *del )
|
|
{
|
|
quint32 i;
|
|
halfedge_t *curr;
|
|
|
|
del->num_faces = 0;
|
|
del->faces = nullptr;
|
|
|
|
/* build external face first */
|
|
build_halfedge_face(del, del->rightmost_he->pair);
|
|
|
|
for( i = del->start_point; i <= del->end_point; i++ )
|
|
{
|
|
curr = del->points[i]->he;
|
|
|
|
do {
|
|
build_halfedge_face( del, curr );
|
|
curr = curr->next;
|
|
} while( curr != del->points[i]->he );
|
|
}
|
|
}
|
|
|
|
/*
|
|
*/
|
|
delaunay2d_t* delaunay2d_from(del_point2d_t *points, quint32 num_points) {
|
|
delaunay2d_t* res = nullptr;
|
|
delaunay_t del;
|
|
quint32 i;
|
|
quint32* faces = nullptr;
|
|
|
|
del.num_faces = 0; //Warning using uninitialized value
|
|
|
|
#if PREDICATE == EXACT_PREDICATE
|
|
exactinit();
|
|
#endif
|
|
|
|
/* allocate the points */
|
|
del.points = (point2d_t**)malloc(num_points * sizeof(point2d_t*));
|
|
assert( del.points != nullptr );
|
|
memset(del.points, 0, num_points * sizeof(point2d_t*));
|
|
|
|
/* copy the points */
|
|
for( i = 0; i < num_points; i++ )
|
|
{
|
|
del.points[i] = point_alloc();
|
|
del.points[i]->idx = i;
|
|
del.points[i]->x = points[i].x;
|
|
del.points[i]->y = points[i].y;
|
|
}
|
|
|
|
qsort(del.points, num_points, sizeof(point2d_t*), cmp_points);
|
|
|
|
if( num_points >= 3 ) {
|
|
quint32 fbuff_size = 0;
|
|
quint32 j = 0;
|
|
|
|
del_divide_and_conquer( &del, 0, static_cast<int>(num_points - 1) );
|
|
del_build_faces( &del );
|
|
|
|
for( i = 0; i < del.num_faces; i++ )
|
|
fbuff_size += del.faces[i].num_verts + 1;
|
|
|
|
faces = (quint32*)malloc(sizeof(quint32) * fbuff_size);
|
|
|
|
for( i = 0; i < del.num_faces; i++ )
|
|
{
|
|
halfedge_t *curr;
|
|
|
|
faces[j] = del.faces[i].num_verts;
|
|
j++;
|
|
|
|
curr = del.faces[i].he;
|
|
do {
|
|
faces[j] = curr->vertex->idx;
|
|
j++;
|
|
curr = curr->pair->prev;
|
|
} while( curr != del.faces[i].he );
|
|
}
|
|
|
|
del_free_halfedges( &del );
|
|
|
|
free(del.faces);
|
|
for( i = 0; i < num_points; i++ )
|
|
point_free(del.points[i]);
|
|
|
|
free(del.points);
|
|
}
|
|
|
|
res = (delaunay2d_t*)malloc(sizeof(delaunay2d_t));
|
|
res->num_points = num_points;
|
|
res->points = (del_point2d_t*)malloc(sizeof(del_point2d_t) * num_points);
|
|
memcpy(res->points, points, sizeof(del_point2d_t) * num_points);
|
|
res->num_faces = del.num_faces;
|
|
res->faces = faces;
|
|
|
|
return res;
|
|
}
|
|
|
|
void delaunay2d_release(delaunay2d_t *del) {
|
|
free(del->faces);
|
|
free(del->points);
|
|
free(del);
|
|
}
|
|
|
|
QT_WARNING_POP
|