ftp.nice.ch/pub/next/science/mathematics/2DLab.3.1.s.tar.gz#/2DLab/vregion.c

This is vregion.c in view mode; [Download] [Up]

#include "headers.h"

#define LBND 0
#define BBND 1
#define RBND 2
#define TBND 3

/* left, right, top bottom */
#define NBOUND(x,y) (x >= Pxmax ? RBND : (x <= Pxmin ? LBND : (y >= Pymax ? TBND : (y <= Pymin ? BBND : -1))))

#define BBOX_THERE() ((pxmin == txmin) && (pxmax == txmax) && (pymin == tymin) && (pymax && pymax))
#define BBOX_NOT_THERE() (!BBOX_THERE())

static float Pxmin, Pxmax, Pymin, Pymax;	/* floating point tolerance values */
static float pxmin = 1E10, pxmax = -1E10, pymin = 1E10, pymax = -1E10;
/* space for theta after x*y* */
static float xminymin[3], xminymax[3], xmaxymax[3], xmaxymin[3];
/* these point to the x*y* */
static VERTTHETA *corner[4];

static POINT v, v1, v2, v3, v4;

static int numverts = 0, numvedges = 0, numtris;

/* sites[i].coord.x,y defined and sorted in main() */

VERT   GLsites[MAXVERTS];
static VERT verts[MAXVERTS];
static EDGE vedges[MAXEDGES];
static TRI  tris[MAXTRIS];
static int  inverse[MAXVERTS];

float randr(low,high)
float low, high;
{
    float value;

    value = low + (rand()/((1<<15)-1.0))*(high-low);
    return( value );
}

out_site(s)
struct Site *s;
{
}

out_bisector(e)
struct Edge *e;
{
}

out_ep(e)
struct Edge *e;
{
    if(!triangulate)
	clip_line(e);

    if(debug)	{
	printf("out_ep(): edge %d", e->edgenbr);
	printf(" ep %d", e->ep[le] != (struct Site *)NULL ? e->ep[le]->sitenbr : -1);
	printf(" ep %d", e->ep[re] != (struct Site *)NULL ? e->ep[re]->sitenbr : -1);
	printf(" reg %d", e->reg[le] != (struct Site *)NULL ? e->reg[le]->sitenbr : -1);
	printf(" reg %d\n", e->reg[re] != (struct Site *)NULL ? e->reg[re]->sitenbr : -1);
	}
}

out_vertex(v)
struct Site *v;
{
    if (!triangulate)	{
	verts[numverts].x = v->coord.x;
	verts[numverts].y = v->coord.y;
	if (numverts < MAXVERTS)
	    numverts++;
	else	{
	    fprintf (stderr, "\nvert list overflow!");
	    exit (-1);
	    }
	}

    if(debug)
	printf("vertex(%d) at %10.7f %10.7f\n", v->sitenbr, v->coord.x, v->coord.y);
}

out_triple(s1, s2, s3)
struct Site *s1, *s2, *s3;
{
    if(triangulate) {
	tris[numtris].v1 = (VERT *)&(sites[s1->sitenbr].coord);
	tris[numtris].v2 = (VERT *)&(sites[s2->sitenbr].coord);
	tris[numtris].v3 = (VERT *)&(sites[s3->sitenbr].coord);

	if (numtris < MAXTRIS)
	    numtris++;
	else	{
	    fprintf (stderr, "out_triple(): triangle list overflow!\n");
	    exit (-1);
	    }
	}
}

vdinit()
{
    numverts = numvedges = numtris = 0;
}

/* here, want to copy the gl sites INTO the voronoi array */
/* gl sites are written exactly once at beginning of time */
bboxinit()
{
int k;
float dx, dy, x, y;

    /* get tight bounding box */
    xmin =  1e10;
    xmax = -1e10;

    ymin =  1e10;
    ymax = -1e10;

    for(k = 0; k < nsites; k++) {	
	x = sites[k].coord.x = GLsites[k].x;
	y = sites[k].coord.y = GLsites[k].y;

        sites[k].refcnt = 0;
	sites[k].sitenbr = k;

	if (x < xmin) xmin = x;
	if (y < ymin) ymin = y;

	if (x > xmax) xmax = x;
	if (y > ymax) ymax = y;
	}

    /* NOW: xmin, ymin, xmax, ymax EXACT, as required by voronoi() */
    /* we shall fool with pxmin, pymin, pxmax, pymax, as used by clip() */

#define EPSILON 1.0

    /* compute 'loose' bounding box */
    dx = xmax - xmin;
    dx = MAX (dx, EPSILON);

    dy = ymax - ymin;
    dy = MAX (dy, EPSILON);

    pxmin = xmin - dx * 0.25;
    pymin = ymin - dy * 0.25;

    pxmax = xmax + dx * 0.25;
    pymax = ymax + dy * 0.25;

#ifdef STUPID_C_COMPILER
    printf ("/* xmin, ymin %10.7f %10.7f; xmax, ymax %10.7f %10.7f; */\n", xmin, ymin, xmax, ymax);
    printf ("/* pxmin, pymin %10.7f %10.7f; pxmax, pymax %10.7f %10.7f; crad %10.7f; */\n", pxmin, pymin, pxmax, pymax, cradius);
#endif
}

int clip_line(e)
struct Edge *e;
{
struct Site *s1, *s2;
struct Site *r1, *r2;
float x1,x2,y1,y2;

    if(e->a == 1.0 && e->b >= 0.0) {	
	s1 = e->ep[1];
	s2 = e->ep[0];

	r1 = e->reg[1];
	r2 = e->reg[0];
    } else {	
	s1 = e->ep[0];
	s2 = e->ep[1];

	r1 = e->reg[0];
	r2 = e->reg[1];
    }

    if(e->a == 1.0) {
	y1 = pymin;
	if (s1!=(struct Site *)NULL && s1->coord.y > pymin)
	    y1 = s1->coord.y;
	if(y1>pymax)
	    return;
	x1 = e->c - e->b * y1;
	y2 = pymax;
	if (s2!=(struct Site *)NULL && s2->coord.y < pymax)
	    y2 = s2->coord.y;
	if(y2<pymin)
	    return(0);
	x2 = e->c - e->b * y2;
	if ((x1> pxmax && x2>pxmax) || (x1<pxmin && x2<pxmin)) 
	    return;
	if(x1> pxmax) {
	    x1 = pxmax;
	    y1 = (e->c - x1)/e->b;
	}
	if(x1<pxmin) {
	    x1 = pxmin;
	    y1 = (e->c - x1)/e->b;
	}
	if(x2>pxmax) {
	    x2 = pxmax;
	    y2 = (e->c - x2)/e->b;
	}
	if(x2<pxmin) {
	    x2 = pxmin;
	    y2 = (e->c - x2)/e->b;
	}
    } else {
	x1 = pxmin;
	if (s1!=(struct Site *)NULL && s1->coord.x > pxmin) 
	    x1 = s1->coord.x;
	if(x1>pxmax)
	    return(0);
	y1 = e->c - e->a * x1;
	x2 = pxmax;
	if (s2!=(struct Site *)NULL && s2->coord.x < pxmax)
	    x2 = s2->coord.x;
	if(x2<pxmin)
	    return(0);
	y2 = e->c - e->a * x2;
	if ((y1> pymax && y2>pymax) || (y1<pymin && y2<pymin))
	    return(0);
	if(y1> pymax) {
	    y1 = pymax;
	    x1 = (e->c - y1)/e->a;
	}
	if(y1<pymin) {
	    y1 = pymin;
	    x1 = (e->c - y1)/e->a;
	}
	if(y2>pymax) {
	    y2 = pymax;
	    x2 = (e->c - y2)/e->a;
	}
	if(y2<pymin) {
	    y2 = pymin;
	    x2 = (e->c - y2)/e->a;
	}
    }

/*  fprintf (stderr, "clip_line(): edge %d is (%10.7f, %10.7f) to (%10.7f, %10.7f)\n", numvedges, x1, y1, x2, y2);*/

    if (!triangulate)	{
	vedges[numvedges].x1 = x1;
	vedges[numvedges].y1 = y1;

	vedges[numvedges].x2 = x2;
	vedges[numvedges].y2 = y2;

	vedges[numvedges].nbr1 = (r1 != NULL ? r1->sitenbr : -9998);
	vedges[numvedges].nbr2 = (r2 != NULL ? r2->sitenbr : -9999);

	if (r1 != NULL && r2 != NULL)	{
	    vedges[numvedges].xm = AVG (r1->coord.x, r2->coord.x);
	    vedges[numvedges].ym = AVG (r1->coord.y, r2->coord.y);
	    }

	if (debug)
	    printf ("clip_line puts edge induced by %d and %d\n", r1->sitenbr, r2->sitenbr);

	if (numvedges < MAXEDGES)
	    numvedges++;
	else	{
	    fprintf (stderr, "clip_line(): edge list overflow!\n");
	    exit (-1);
	    }
	}
}

/* load_vsites(): 
    accept the n voronoi sites (x_n, y_n)
    calculate and store the voronoi diagram over the n sites,
    clipping all infinite edges to bbox: [xmin, ymin, xmax, ymax].
    
    note: if (xmin,ymin,xmax,ymax are all == 0.0), OR
	if these do not enclose the data, a bounding box
	 will be computed over the input.

    returns:
	-1 if error
	 0 otherwise
*/
int
load_vsites (n, usites, uxmin, uymin, uxmax, uymax)
int n;
float usites[][2];	/* sites in x,y order */
float uxmin, uymin, uxmax, uymax;
{
int k, compute_bbox, sid, tid;
float dx, dy, x, y;

    if (n >= MAXSITES)	{
	fprintf (stderr, "load_vsites(): can't handle >= %d sites.\n", MAXSITES);
	return (-1);
	}

    compute_bbox = (uxmin == 0.0) && (uymin == 0.0) && (uxmax == 0.0) && (uymax == 0.0);

    /* copy the sites into GLsites and set global nsites */
    for (k = 0; k < n; k++) {
	GLsites[k].x = usites[k][0];
	GLsites[k].y = usites[k][1];
	GLsites[k].pid = k;
	}

    nsites = n;

    /* sort GL sites lexicographically by position */
    sortGLsites();

    /* copy sorted GLsites into voronoi alg sites */
    bboxinit();

    /* now, if user punted on bbox calculation, OR if user bbox does not truly enclose
       user data, we use our bbox (computed in initbbox).  otherwise we take the user's. */
    if (!(compute_bbox || uxmin > xmin || uymin > ymin || uxmax < xmax || uymax < ymax))	{
	pxmin = uxmin;
	pymin = uymin;

	pxmax = uxmax;
	pymax = uymax;
	}

    xminymax [0] = xminymin[0] = pxmin;
    xminymin [1] = xmaxymin[1] = pymin;

    xmaxymax [0] = xmaxymin[0] = pxmax;
    xmaxymax [1] = xminymax[1] = pymax;

    corner[0] = (VERTTHETA *)xminymin;
    corner[1] = (VERTTHETA *)xmaxymin;
    corner[2] = (VERTTHETA *)xmaxymax;
    corner[3] = (VERTTHETA *)xminymax;

    /* now: set the floating point tolerance values P*** to be 1 or 2 significant bits inside the p*** */
    /* be careful to use RELATIVE error; that is, to scale the tolerance to the bbox values themselves */
    /* now, if some user puts points way out in left field, our technique handles the ranges correctly */
    {
    float dx = (pxmax - pxmin) * (1.0 / (4096.0));      /* twelve binary digits out */
    float dy = (pymax - pymin) * (1.0 / (4096.0));      /* twelve binary digits out */

    Pxmin = pxmin + dx;
    Pxmax = pxmax - dx;

    Pymin = pymin + dy;
    Pymax = pymax - dy;
    }

    /* compute inverse of external->internal sid mapping */
    for (sid = 0; sid < nsites; sid++)
	inverse[sid] = GLsites[sid].pid;

    /* zero list lengths out */
    vdinit ();

    /* run the voronoi code, no triangulate */
    triangulate = FALSE;
    voronoi (nextone);

    /* RE-copy sorted GLsites into voronoi alg sites */
    bboxinit();

    /* run the voronoi code, do triangulate */
    triangulate = TRUE;
    voronoi (nextone);

    /* invert the integer id's in sites[], for find_dtriangles() */
    /* and restore the original vertex values (from GLsites)	 */
    for (sid = 0; sid < nsites; sid++)	{
        sites[sid].sitenbr = GLsites[sid].pid;
	sites[sid].coord.x = GLsites[sid].x;
	sites[sid].coord.y = GLsites[sid].y;
	}

    return (0);
}

static VERTTHETA vtlist[1024], slist[1024];
static int  vtnum;

int
vtcomp (vt1, vt2)
VERTTHETA *vt1, *vt2;
{
    if (vt1->theta < vt2->theta)
	return (-1);
    else if (vt1->theta > vt2->theta)
	return (1);
    else
	return (0);
}	

/*
    find_vregion(sid, plen, pverts)
	given a site id 'sid' from 0..nsites-1 inclusive, 
	returns the voronoi polygon associated with that site
	in the array 'pverts', and its length on call stack.

	the vertices are returned in counterclockwise order.

    returns:
	-1 if error condition
	plen > 2 [i.e., the # of verts in the voronoi polygon] otherwise
*/
int
find_vregion (vsid, pverts)
int vsid;
float pverts[][2];
{
int sid, b, k, vnum, bnd1, bnd2, bdiff, sleft, lag, lead, p1a, p1b, p2a, p2b;
float x1, y1, x2, y2, theta1, theta2, lasttheta, dtheta, h;

/* note that PUTV(v,sid,vid) has the side effect of incrementing vid */
#define PUTV(v,sid,vid)	{ pverts[vid][0] = (v)->x; pverts[vid][1] = (v)->y; vid++; }

    if (vsid < 0 || vsid >= nsites) {
	fprintf (stderr, "find_vregion(%d) called with illegal site id.\n", vsid);
	return (-1);
	}

    /* first thing is to convert user's 'virtual' site id to 
       a 'physical' site id, namely, an index into GLsites */
    for (sid = 0; sid < nsites; sid++)
	if (GLsites[sid].pid == vsid)
	    break;

    if (sid == nsites)	{
	fprintf (stderr, "find_vregion(%d) can't find requested site id.\n", vsid);
	return (-1);
	}

    for (k = 0; k < 4; k++)
	corner[k]->theta = atan2 (corner[k]->y - GLsites[sid].y, corner[k]->x - GLsites[sid].x);

    for (vtnum = 0, k = 0; k < numvedges; k++)
	if (vedges[k].nbr1 == sid || vedges[k].nbr2 == sid)   {
	    /* add both ends of the edge, and their thetas, to unsorted list (parent is edge k) */
	    slist[vtnum  ].e1 = slist[vtnum  ].e2 = k;
	    slist[vtnum  ].theta = atan2 (vedges[k].y1 - GLsites[sid].y, vedges[k].x1 - GLsites[sid].x);
	    slist[vtnum  ].x = vedges[k].x1;
	    slist[vtnum++].y = vedges[k].y1;

	    slist[vtnum  ].e1 = slist[vtnum  ].e2 = k;
            slist[vtnum  ].theta = atan2 (vedges[k].y2 - GLsites[sid].y, vedges[k].x2 - GLsites[sid].x);
	    slist[vtnum  ].x = vedges[k].x2;
	    slist[vtnum++].y = vedges[k].y2;
	    }

    /* now we have a list of vtnum thetas and vertices.  sort it on theta */
    qsort ((char *)slist, vtnum, sizeof (VERTTHETA), vtcomp);

    /* next, install the unique slist entries into vtlist */
    lag = lead = 0;
    vtlist[lag] = slist[lead];
    lasttheta = -10.0;

    while (lead < vtnum)	{
        if (fabs (slist[lead].theta - lasttheta) > 1E-4) {
	    lasttheta = slist[lead].theta;
	    vtlist[lag++] = slist[lead++];
	    }
	else    {
	    vtlist[lag-1].e2 = slist[lead++].e1;
	    }
	}

    vtnum = lag;
/*
    printf ("\n");
    for (k = 0; k < vtnum; k++)
        printf ("vtlist[%d]: x,y %f,%f; theta %f; edges %d %d\n",
		k, vtlist[k].x, vtlist[k].y, vtlist[k].theta, vtlist[k].e1, vtlist[k].e2);
*/

    for (vnum = 0, k = 0; k < vtnum; k++)	{
        if (fabs(vtlist[(k + vtnum - 1) % vtnum].theta - vtlist[k].theta) < 1E-4)   {
	    fprintf (stderr,"find_vregion(%d): vtlist %d, %d identical?\n", sid, (k + vtnum - 1) % vtnum, k);
	    return (-1);
	    }

        x1 = vtlist[(k + vtnum - 1) % vtnum].x;
        y1 = vtlist[(k + vtnum - 1) % vtnum].y;
	p1a = vtlist[(k + vtnum - 1) % vtnum].e1;
	p1b = vtlist[(k + vtnum - 1) % vtnum].e2;

	x2 = vtlist[k].x;
	y2 = vtlist[k].y;
	p2a = vtlist[k].e1;
	p2b = vtlist[k].e2;

	/* now: if the two vertices come from the same edge, output and continue */
	if (((p1a == p2a) || (p1a == p2b) || (p1b == p2a) || (p1b == p2b))  && (vtnum > 2))	{
	    PUTV (&vtlist[k], sid, vnum);
	    continue;
	    }

	/* otherwise must fill in missing corners between x1,y1 and x2,y2 */
	bnd1 = NBOUND(x1,y1);
	bnd2 = NBOUND(x2,y2);

	/* find number of CLOCKWISE steps around bbox */
	if (bnd1 >= 0 && bnd2 >= 0)
	    bdiff = ((bnd2 - bnd1) + 4) % 4;	/* from 0 to 3 */
	else
	    bdiff = 0;

	/* if they were on the same bounding box edge, output and continue */
	if (bdiff == 0) {		    
	    PUTV (&vtlist[k], sid, vnum);
	    continue;
	    }

	/* special case: exactly two vertices */
	if (vtnum == 2) {
	    theta1 = vtlist[0].theta;
	    theta2 = vtlist[1].theta;
	    dtheta = theta2 - theta1;    

	    /* add all corners OUTSIDE the angular range of the edge as seen from the site */
	    for (b = 0; b < 4; b++)	{
		if ((dtheta >= M_PI) && (corner[b]->theta > theta1) && (corner[b]->theta < theta2))
		    vtlist[vtnum++] = *corner[b];
		else if ((dtheta < M_PI) && ((corner[b]->theta < theta1) || (corner[b]->theta > theta2)))
		    vtlist[vtnum++] = *corner[b];
		}

	    /* resort the (small) vertex list by theta */
	    qsort ((char *)vtlist, vtnum, sizeof (VERTTHETA), vtcomp);

	    /* and output */
	    for (b = 0; b < vtnum; b++)
		PUTV (&vtlist[b], sid, vnum);

	    break;
	    }

	for (b = 0; b < bdiff; b++)
	    PUTV (corner[(bnd1 + b) % 4], sid, vnum);

	PUTV (&vtlist[k], sid, vnum);
	}	/* k 0 ..vtnum */

    return (vnum);
}

/*
    int
    find_dtriangles (**dtris)

    returns:
	-1 if error condition, *dtris == NULL
	o/wise, # of delaunay triangles, *dtris == array of TRIS (see voronoi.h)
*/
int
find_dtriangles (dtris)
TRI **dtris;
{
    if (numtris <= 0)	{
	*dtris = NULL;
	return (-1);
	}
    else    {
	*dtris = tris;
	return (numtris);
	}
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.