ftp.nice.ch/pub/next/tools/screen/backspace/old/Voronoi.N.bs.tar.gz#/VoronoiView/VoronoiViewPart.m

This is VoronoiViewPart.m in view mode; [Download] [Up]

#import "VoronoiView.h"
#import "Thinker.h"
#import <dpsclient/wraps.h>
#import <math.h>
#import <appkit/color.h>
#import <appkit/nextstd.h>
#import <libc.h>
#import <time.h>

#include "voronoi.h"   /* for the voronoi code */
#include "headers.h" /* more voronoi code */

/*
 * how many pixels wide should the sites and polygon edges be?
 */
#define WIDTH 2.0

/*
 * what is the maximum number of sites to be generated on any iteration?
 */
#define BS_MAXSITES 100

/* 
 * length of idle period in seconds
 */
#define IDLESECONDS 30

@implementation VoronoiView

/*
 * allocate a random number of sites (2D points) around which the
 * Voronoi diagram will be constructed.
 */

- initFrame:(NXRect *)frameRect
{
  self=[super initFrame:frameRect];
  nTotal=nLeft=0;
  sites=(NXPoint *)0;
  srandom(time(0));
  return self;
}

- setUpVoronoi
{
  NXRect r;
  int i;
  [self getBounds:&r];
  if (sites) NX_FREE(sites);
  nTotal=10+random()%(BS_MAXSITES-10);
  nLeft=nTotal-1;
  NX_MALLOC(sites,NXPoint,nTotal);
  for(i=0;i<nTotal;i++) {
     sites[i].x = 20 + r.origin.x + random()%((int)(r.size.width-40));
     sites[i].y = 20 + r.origin.y + random()%((int)(r.size.height-40));
     };
  /* set up voronoi diagram */
  if (load_vsites(nTotal,sites,r.origin.x+5,r.origin.y+5,
                  r.origin.x+r.size.width-5,r.origin.y+r.size.height-5)) {
    nLeft=-1;
    };
  return self;
}

- drawPolygon:(int)index
{
  NXPoint *temp;
  int nv,i;
  NX_MALLOC(temp,NXPoint,nTotal);
  nv=find_vregion(index,temp);
  if (nv < 1) {
    fprintf(stderr,"find_vregion %d failed\n",nLeft);
    nLeft=-1;
    return self;
    };
  NXSetColor(NXConvertHSBToColor(1.0-(1.0*index)/(nTotal-1.0),1.0,1.0));
  /* move the vertices of the voronoi polygon toward the site by 2%.
     This allows edges drawn multiple times to show up in multiple
     colors. */
  for(i=0;i<nv;i++) {
    temp[i].x += 0.02 * (sites[index].x - temp[i].x);
    temp[i].y += 0.02 * (sites[index].y - temp[i].y);
    };
  PSsetlinewidth(WIDTH);
  PSnewpath();
  PSmoveto(temp[0].x,temp[0].y);
  for(i=1;i<nv;i++) PSlineto(temp[i].x,temp[i].y);
  PSclosepath();
  PSstroke(); /* or even PSfill(); */
  NX_FREE(temp);
  return self;
}

- drawSites
{
  int i;
  /* draw the sites */
  for(i=0;i<nTotal;i++) {
    PSnewpath();
    NXSetColor(NXConvertHSBToColor((1.0*i)/(nTotal-1.0),1.0,1.0));
    PSarc(sites[i].x,sites[i].y,WIDTH,0.0,360.0);
    PSfill();/* or even PSstroke();*/
    };
  return self;
}


- drawSelf:(const NXRect *)rects :(int)rectCount
{
  int i;
  if (!rects || !rectCount) return self;
  PSsetgray(0);
  NXRectFill(rects);

  if (!sites) [self setUpVoronoi];

  [self drawSites];

  /* if some voronoi polygons have already been drawn, redraw them.
     this can happen if a window is moved around during or after the
     `active' phase of the oneStep method. */
  if (nLeft<nTotal-1) 
    for(i=nTotal-1;((i>nLeft)&&(i>=0));--i) [self drawPolygon:i];

  return self;
}

/* each time through oneStep, we draw one Voronoi polygon, unless they've
   already all been drawn, in which case we simply sleep for 1/10 second.
   note: nLeft does double duty.  When non-negative, it's the index of the
   polygon to draw (umm, it's actually the index of the SITE whose polygon
   is to be drawn, but you get what I mean.)  If negative, it counts the number
   of idle trips.  When it gets sufficiently negative, we restart the whole
   works (i.e. new sites, new Voronoi polygons, etc.)
*/
- oneStep
{
  if (nLeft>= 0)
    [self drawPolygon:nLeft--];
  else {
    if (nLeft-- <= -(IDLESECONDS*10)) {
      [[self setUpVoronoi] display];
      return self;
      };
    usleep(100000); /* sleep 0.1 second */
    };
  return self;
}

- (BOOL)useBufferedWindow {return NO;}

/**********************************
 **********************************
 *** voronoi code begins here
 *** This code was written by Seth Teller (at SGI?) and is
 *** available from many archive sites.  I squished it into
 *** one source file because I'm lazy.
 **********************************
 **********************************/

PQinsert(he, v, offset)
struct Halfedge *he;
struct Site *v;
float 	offset;
{
    struct Halfedge *last, *next;

    he->vertex = v;
    ref(v);
    he->ystar = v->coord.y + offset;
    last = &PQhash[PQbucket(he)];
    while ((next = last->PQnext) != (struct Halfedge *) NULL &&
      (he->ystar  > next->ystar  ||
	(he->ystar == next->ystar && v->coord.x > next->vertex->coord.x))) {
	last = next;
    }
    he->PQnext = last->PQnext; 
    last->PQnext = he;
    PQcount += 1;
}

PQdelete(he)
struct Halfedge *he;
{
    struct Halfedge *last;

    if(he-> vertex != (struct Site *) NULL) {	
	last = &PQhash[PQbucket(he)];
	while (last->PQnext != he) 
	    last = last->PQnext;
	last->PQnext = he->PQnext;
	PQcount -= 1;
	deref(he->vertex);
	he->vertex = (struct Site *) NULL;
    }
}

int PQbucket(he)
struct Halfedge *he;
{
    int bucket;

    bucket = (he->ystar - ymin)/deltay * PQhashsize;
    if (bucket<0) 
	bucket = 0;
    if (bucket>=PQhashsize) 
	bucket = PQhashsize-1 ;
    if (bucket < PQmin) 
	PQmin = bucket;
    return(bucket);
}

int PQempty()
{
    return(PQcount==0);
}

struct Point PQ_min()
{
    struct Point answer;

    while(PQhash[PQmin].PQnext == (struct Halfedge *)NULL) 
	PQmin += 1;
    answer.x = PQhash[PQmin].PQnext->vertex->coord.x;
    answer.y = PQhash[PQmin].PQnext->ystar;
    return (answer);
}

struct Halfedge *PQextractmin()
{
    struct Halfedge *curr;

    curr = PQhash[PQmin].PQnext;
    PQhash[PQmin].PQnext = curr->PQnext;
    PQcount -= 1;
    return(curr);
}

PQinitialize()
{
    int i; struct Point *s;

    PQcount = 0;
    PQmin = 0;
    PQhashsize = 4 * sqrt_nsites;
    PQhash = (struct Halfedge *) myalloc(PQhashsize * sizeof (struct Halfedge));
    for(i=0; i<PQhashsize; i+=1) 
	PQhash[i].PQnext = (struct Halfedge *)NULL;
}



int ntry, totalsearch;

ELinitialize()
{
    int i;

    freeinit(&hfl, sizeof **ELhash);
    ELhashsize = 2 * sqrt_nsites;
    ELhash = (struct Halfedge **) myalloc ( sizeof (*ELhash) * ELhashsize);
    for(i=0; i<ELhashsize; i +=1) 
	ELhash[i] = (struct Halfedge *)NULL;
    ELleftend = HEcreate( (struct Edge *)NULL, 0);
    ELrightend = HEcreate( (struct Edge *)NULL, 0);
    ELleftend->ELleft = (struct Halfedge *)NULL;
    ELleftend->ELright = ELrightend;
    ELrightend->ELleft = ELleftend;
    ELrightend->ELright = (struct Halfedge *)NULL;
    ELhash[0] = ELleftend;
    ELhash[ELhashsize-1] = ELrightend;
}

struct Halfedge *HEcreate(e, pm)
struct Edge *e;
int pm;
{
    struct Halfedge *answer;

    answer = (struct Halfedge *) getfree(&hfl);
    answer->ELedge = e;
    answer->ELpm = pm;
    answer->PQnext = (struct Halfedge *) NULL;
    answer->vertex = (struct Site *) NULL;
    answer->ELrefcnt = 0;
    return(answer);
}

ELinsert(lb, new)
struct	Halfedge *lb, *new;
{
    new->ELleft = lb;
    new->ELright = lb->ELright;
    (lb->ELright)->ELleft = new;
    lb->ELright = new;
}

/* Get entry from hash table, pruning any deleted nodes */
struct Halfedge *ELgethash(b)
int b;
{
    struct Halfedge *he;

    if(b<0 || b>=ELhashsize) 
	return((struct Halfedge *) NULL);
    he = ELhash[b]; 
    if (he == (struct Halfedge *)NULL || he->ELedge!=(struct Edge *)DELETED) 
	return (he);

/* Hash table points to deleted half edge.  Patch as necessary. */
    ELhash[b] = (struct Halfedge *) NULL;
    if ((he->ELrefcnt -= 1) == 0) makefree(he, &hfl);
	return ((struct Halfedge *) NULL);
}	

struct Halfedge *ELleftbnd(p)
struct Point *p;
{
    int i, bucket;
    struct Halfedge *he;

/* Use hash table to get close to desired halfedge */
    bucket = (p->x - xmin)/deltax * ELhashsize;
    if(bucket<0) 
	bucket =0;
    if(bucket>=ELhashsize) 
	bucket = ELhashsize - 1;
    he = ELgethash(bucket);
    if(he == (struct Halfedge *) NULL) {   
	for(i=1; 1 ; i += 1) {	
	    if ((he=ELgethash(bucket-i)) != (struct Halfedge *) NULL) 
		break;
	    if ((he=ELgethash(bucket+i)) != (struct Halfedge *) NULL) 
		break;
	}
	totalsearch += i;
    }
    ntry += 1;

/* Now search linear list of halfedges for the corect one */
    if (he==ELleftend  || (he != ELrightend && right_of(he,p))) {
	do {
	    he = he->ELright;
	} while (he!=ELrightend && right_of(he,p));
     	he = he->ELleft;
    } else 
	do {
	    he = he->ELleft;
	} while (he!=ELleftend && !right_of(he,p));

/* Update hash table and reference counts */
    if(bucket > 0 && bucket <ELhashsize-1) {	
	if(ELhash[bucket] != (struct Halfedge *) NULL) 
	    ELhash[bucket]->ELrefcnt -= 1;
	ELhash[bucket] = he;
	ELhash[bucket]->ELrefcnt += 1;
    }
    return (he);
}

/*
 *  This delete routine can't reclaim node, since pointers from hash
 *  table may be present.   
 */
ELdelete(he)
struct Halfedge *he;
{
    (he->ELleft)->ELright = he->ELright;
    (he->ELright)->ELleft = he->ELleft;
    he->ELedge = (struct Edge *)DELETED;
}

struct Halfedge *ELright(he)
struct Halfedge *he;
{
    return (he->ELright);
}

struct Halfedge *ELleft(he)
struct Halfedge *he;
{
    return (he->ELleft);
}

struct Site *leftreg(he)
struct Halfedge *he;
{
    if(he->ELedge == (struct Edge *)NULL) 
	return(bottomsite);
    return( he->ELpm == le ? he->ELedge->reg[le] : he->ELedge->reg[re]);
}

struct Site *rightreg(he)
struct Halfedge *he;
{
    if(he->ELedge == (struct Edge *)NULL) 
	return(bottomsite);
    return( he->ELpm == le ? he->ELedge->reg[re] : he->ELedge->reg[le]);
}


geominit()
{
    struct Edge e;
    float sn;

    freeinit (&efl, sizeof (struct Edge));
    nvertices = 0;
    nedges = 0;
    sn = nsites + 4;
    sqrt_nsites = sqrt(sn);
    deltay = ymax - ymin;
    deltax = xmax - xmin;
    siteidx = 0;
}

struct Edge *bisect(s1,s2)
struct	Site *s1,*s2;
{
    float dx,dy,adx,ady;
    struct Edge *newedge;

    newedge = (struct Edge *) getfree(&efl);

    newedge->reg[0] = s1;
    newedge->reg[1] = s2;
    ref(s1); 
    ref(s2);
    newedge->ep[0] = (struct Site *) NULL;
    newedge->ep[1] = (struct Site *) NULL;

    dx = s2->coord.x - s1->coord.x;
    dy = s2->coord.y - s1->coord.y;
    adx = dx>0 ? dx : -dx;
    ady = dy>0 ? dy : -dy;
    newedge->c = s1->coord.x * dx + s1->coord.y * dy + (dx*dx + dy*dy)*0.5;
    if (adx>ady) {	
	newedge->a = 1.0; 
	newedge->b = dy/dx; 
	newedge->c /= dx;
    } else {	
	newedge->b = 1.0; 
	newedge->a = dx/dy; 
	newedge->c /= dy;
    }
    newedge->edgenbr = nedges;
    out_bisector(newedge);
    nedges += 1;
    return(newedge);
}

struct Site *intersect(el1, el2, p)
struct Halfedge *el1, *el2;
struct Point *p;
{
    struct Edge *e1,*e2, *e;
    struct Halfedge *el;
    float d, xint, yint;
    int right_of_site;
    struct Site *v;

    e1 = el1->ELedge;
    e2 = el2->ELedge;
    if (e1 == (struct Edge*)NULL || e2 == (struct Edge*)NULL) 
	return ((struct Site *) NULL);
    if (e1->reg[1] == e2->reg[1]) 
	return ((struct Site *) NULL);
    d = e1->a * e2->b - e1->b * e2->a;
    if (-1.0e-10<d && d<1.0e-10) 
	return ((struct Site *) NULL);
    xint = (e1->c*e2->b - e2->c*e1->b)/d;
    yint = (e2->c*e1->a - e1->c*e2->a)/d;
    if ((e1->reg[1]->coord.y < e2->reg[1]->coord.y) ||
	    (e1->reg[1]->coord.y == e2->reg[1]->coord.y &&
		e1->reg[1]->coord.x < e2->reg[1]->coord.x) ) {	
	el = el1; 
	e = e1;
    } else {	
	el = el2; 
	e = e2;
    }
    right_of_site = xint >= e->reg[1]->coord.x;
    if ((right_of_site && el->ELpm == le) ||
	   (!right_of_site && el->ELpm == re)) 
	return ((struct Site *) NULL);
    v = (struct Site *) getfree(&sfl);
    v->refcnt = 0;
    v->coord.x = xint;
    v->coord.y = yint;
    return(v);
}

/* returns 1 if p is to right of halfedge e */
int right_of(el, p)
struct Halfedge *el;
struct Point *p;
{
    struct Edge *e;
    struct Site *topsite;
    int right_of_site, above, fast;
    float dxp, dyp, dxs, t1, t2, t3, yl;

    e = el->ELedge;
    topsite = e->reg[1];
    right_of_site = p->x > topsite->coord.x;
    if (right_of_site && el->ELpm == le) 
	return(1);
    if (!right_of_site && el->ELpm == re) 
	return (0);
    if (e->a == 1.0) {	
	dyp = p->y - topsite->coord.y;
	dxp = p->x - topsite->coord.x;
	fast = 0;
	if ((!right_of_site && e->b<0.0) || (right_of_site && e->b>=0.0) ) {	
	    above = dyp>= e->b*dxp;	
	    fast = above;
	} else {	
	    above = p->x + p->y*e->b > e->c;
	    if (e->b<0.0) 
		above = !above;
	    if (!above) 
		fast = 1;
	}
	if (!fast) {	
	    dxs = topsite->coord.x - (e->reg[0])->coord.x;
	    above = e->b * (dxp*dxp - dyp*dyp) <
				    dxs*dyp*(1.0+2.0*dxp/dxs + e->b*e->b);
	    if (e->b<0.0) 
		above = !above;
	}
    } else {	/*e->b==1.0 */
	yl = e->c - e->a*p->x;
	t1 = p->y - yl;
	t2 = p->x - topsite->coord.x;
	t3 = yl - topsite->coord.y;
	above = t1*t1 > t2*t2 + t3*t3;
    }
    return (el->ELpm==le ? above : !above);
}

void
v_endpoint(e, lr, s)
struct Edge *e;
int	lr;
struct Site *s;
{
    e->ep[lr] = s;
    ref(s);
    if (e->ep[re-lr]==(struct Site *)NULL) 
	return;
    out_ep(e);
    deref(e->reg[le]);
    deref(e->reg[re]);
    makefree(e, &efl);
}

float dist(s,t)
struct Site *s,*t;
{
    float dx,dy;
    dx = s->coord.x - t->coord.x;
    dy = s->coord.y - t->coord.y;
    return(sqrt(dx*dx + dy*dy));
}


int makevertex(v)
struct Site *v;
{
    v->sitenbr = nvertices;
    nvertices += 1;
    out_vertex(v);
}


deref(v)
struct	Site *v;
{
    v->refcnt -= 1;
    if (v->refcnt == 0) 
	makefree(v, &sfl);
}

ref(v)
struct Site *v;
{
    v->refcnt += 1;
}


freeinit(fl, size)
struct	Freelist *fl;
int	size;
{
    fl->head = (struct Freenode *) NULL;
    fl->nodesize = size;
}

char *getfree(fl)
struct	Freelist *fl;
{
    int i; struct Freenode *t;

    if(fl->head == (struct Freenode *) NULL) {	
	t = (struct Freenode *)myalloc(sqrt_nsites * fl->nodesize);
	for(i=0; i<sqrt_nsites; i+=1) 	
	    makefree((struct Freenode *)((char *)t+i*fl->nodesize), fl);
    }
    t = fl->head;
    fl->head = (fl->head)->nextfree;
    return((char *)t);
}

makefree(curr,fl)
struct Freenode *curr;
struct Freelist *fl;
{
    curr->nextfree = fl->head;
    fl->head = curr;
}

#define MAXALLOCS (8 * MAXSITES)
int total_alloc, num_allocs;
char *locations[MAXALLOCS];

char *myalloc(n)
unsigned n;
{
    char *t;
    if ((t=malloc(n)) == (char *) 0) {
	fprintf(stderr,"out of memory processing site %d (%d bytes in use)\n",
		siteidx, total_alloc);
    	exit(-1);
    }
    total_alloc += n;
    locations[num_allocs++] = t;
    return(t);
}

void 
myfreeall()
{
int k;

    for (k = 0; k < num_allocs; k++)
	free (locations [k]);
/*
    printf ("freed %d allocs (%d bytes)\n", num_allocs, total_alloc);
*/
    num_allocs = total_alloc = 0;
}


int triangulate = FALSE, sorted, plot, debug = FALSE, randmode, outputmode;
float xmin, xmax, ymin, ymax, deltax, deltay;

char execdir[80];

struct Site _sites[MAXSITES], *sites = _sites;

int nsites;
int siteidx;
int sqrt_nsites;
int nvertices;
struct Freelist sfl;
struct Site *bottomsite;

int nedges;
struct	Freelist efl;

struct Freelist	hfl;
struct Halfedge *ELleftend, *ELrightend;
int ELhashsize;
struct Halfedge **ELhash;

int PQhashsize;
struct Halfedge *PQhash;
int PQcount;
int PQmin;

/* sort sites on y, then x, coord */
int scomp(s1,s2)
struct Point *s1,*s2;
{
    if(s1->y < s2->y) return(-1);
    if(s1->y > s2->y) return(1);
    if(s1->x < s2->x) return(-1);
    if(s1->x > s2->x) return(1);
    return(0);
}

/* sort GLsites on y, then x, coord */
int GLscomp(s1,s2)
VERT *s1,*s2;
{
    if(s1->y < s2->y) return(-1);
    if(s1->y > s2->y) return(1);
    if(s1->x < s2->x) return(-1);
    if(s1->x > s2->x) return(1);
    return(0);
}

/* return a single in-storage site */
struct Site *
nextone()
{
    if(siteidx < nsites)
	return (&sites[siteidx++]);
    else
	return( (struct Site *)NULL);
}

sortGLsites()
{
float vx, vy;
int k;
char *a = NULL;
float randr(), dx, dy;

    qsort((char *)GLsites, nsites, sizeof (VERT), GLscomp);

    /* check: are there coincident points? */
    for (k = 1; k < nsites; k++)
	if ((GLsites[k-1].y == GLsites[k].y) && (GLsites[k-1].x == GLsites[k].x))   {
/*	    printf ("coincident sites at %d, %d!\n", k-1, k);*/
	    dx = GLsites[k].x * (1.0 / 4096.0);
	    dy = GLsites[k].y * (1.0 / 4096.0);
	    /* hack: jitter the last point randomly in its last significant digit */
	    GLsites[k-1].x += randr (-dx, dx);
	    GLsites[k-1].y += randr (-dy, dy);
	    }
}

/* implicit parameters: nsites, sqrt_nsites, xmin, xmax, ymin, ymax,
   deltax, deltay (can all be estimates).
   Performance suffers if they are wrong; better to make nsites,
   deltax, and deltay too big than too small.  (?) */
voronoi (nextsite)
struct Site *(*nextsite)();
{
struct Site *newsite, *bot, *top, *temp, *p;
struct Site *v;
struct Point newintstar;
int pm;
struct Halfedge *lbnd, *rbnd, *llbnd, *rrbnd, *bisector;
struct Edge *e;

    myfreeall ();

    if (nsites <= 1)
	return;

    freeinit(&sfl, sizeof (struct Site));

    /* bboxinit();   copies static array into nsites */
    geominit();	    /* internal use of deltax, deltay  */

    PQinitialize();
    bottomsite = (*nextsite)();
    out_site(bottomsite);
    ELinitialize();

    newsite = (*nextsite)();
    while(1) {
	if(!PQempty()) newintstar = PQ_min();
/* new site is smallest */
	if (newsite != (struct Site *)NULL && (PQempty() 
		 || newsite->coord.y < newintstar.y
			 || (newsite->coord.y == newintstar.y 
			     && newsite->coord.x < newintstar.x))) {
	    out_site(newsite);
	    lbnd = ELleftbnd(&(newsite->coord));
	    rbnd = ELright(lbnd);
	    bot = rightreg(lbnd);
	    e = bisect(bot, newsite);
	    bisector = HEcreate(e, le);
	    ELinsert(lbnd, bisector);
	    if ((p = intersect(lbnd, bisector)) != (struct Site *) NULL) {
		PQdelete(lbnd);
		PQinsert(lbnd, p, dist(p,newsite));
	    }
	    lbnd = bisector;
	    bisector = HEcreate(e, re);
	    ELinsert(lbnd, bisector);
	    if ((p = intersect(bisector, rbnd)) != (struct Site *) NULL) {
		PQinsert(bisector, p, dist(p,newsite));	
	    }
	    newsite = (*nextsite)();	
	} else if (!PQempty()) {
	/* intersection is smallest */
	    lbnd = PQextractmin();
	    llbnd = ELleft(lbnd);
	    rbnd = ELright(lbnd);
	    rrbnd = ELright(rbnd);
	    bot = leftreg(lbnd);
	    top = rightreg(rbnd);
	    out_triple(bot, top, rightreg(lbnd));
	    v = lbnd->vertex;
	    makevertex(v);
	    v_endpoint(lbnd->ELedge,lbnd->ELpm,v);
	    v_endpoint(rbnd->ELedge,rbnd->ELpm,v);
	    ELdelete(lbnd); 
	    PQdelete(rbnd);
	    ELdelete(rbnd); 
	    pm = le;
	    if (bot->coord.y > top->coord.y) {	
		temp = bot; 
		bot = top; 
		top = temp; 
		pm = re;
	    }
	    e = bisect(bot, top);
	    bisector = HEcreate(e, pm);
	    ELinsert(llbnd, bisector);
	    v_endpoint(e, re-pm, v);
	    deref(v);
	    if((p = intersect(llbnd, bisector)) != (struct Site *) NULL) {
		PQdelete(llbnd);
		PQinsert(llbnd, p, dist(p,bot));
	    }
	    if((p = intersect(bisector, rrbnd)) != (struct Site *) NULL) 
		PQinsert(bisector, p, dist(p,bot));
	} else 
	    break;
    }

    for(lbnd=ELright(ELleftend); lbnd != ELrightend; lbnd=ELright(lbnd)) {
	e = lbnd->ELedge;
	out_ep(e);
        }
}


#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 + (random()/((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);
	}
}
@end

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