/*
 * Copyright (c) 1986, 1987, 1988, 1989 by Sun Microsystems, Inc.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both that copyright
 * notice and this permission notice are retained, and that the name
 * of Sun Microsystems, Inc., not be used in advertising or publicity
 * pertaining to this software without specific, written prior permission.
 * Sun Microsystems, Inc., makes no representations about the suitability
 * of this software or the interface defined in this software for any
 * purpose. It is provided "as is" without express or implied warranty.
 */
#ifndef lint
static char sccsid[] = "@(#)outputclip.c 1.1 94/10/31 Copyr 1984 Sun Micro";
#endif

/*
 * Copyright (c) 1984 by Sun Microsystems, Inc.
 */
/* Output clipping routines clip to viewport of the segment being drawn */
/*----------------------------------------------------------------------*/
#include "coretypes.h"
#include "corevars.h"
#include <sys/types.h>

/*----------------------------------------------------------------------*/
static mkwec(v, wec, outcode, vwprt)
ipt_type *v;
int wec[], *outcode;
porttype *vwprt;
	{
	*outcode = 0;
	if ((wec[0] = v->x - vwprt->xmin) < 0) *outcode |= 1;
	if ((wec[1] = vwprt->xmax - v->x) < 0) *outcode |= 2;
	if ((wec[2] = v->y - vwprt->ymin) < 0) *outcode |= 4;
	if ((wec[3] = vwprt->ymax - v->y) < 0) *outcode |= 8;
	}


int _core_oclpvec2(v1, v2, vwprt)
ipt_type *v1, *v2;
porttype *vwprt;
	{		/* 2-D Clipper. Clips vector from v1 to v2 to
			   viewport in x and y.  Z is interpolated.
			   Cohen-Sutherland algorithm with window-edge
			   coordinates adapted from Newman & Sproull (2nd Ed.).
			*/

	int wec1[4], wec2[4], outcodes, outcode1, outcode2;
	u_int t1, t2, t;
	int i, j, n, d;
	int dx, dy, dz;
	int shiftx, shifty, shiftz;
	short sdx, sdy, sdz;

	mkwec(v1, wec1, &outcode1, vwprt);
	mkwec(v2, wec2, &outcode2, vwprt);
	if (!(outcodes = outcode1 | outcode2))
		return(TRUE);
	if (outcode1 & outcode2)
		return(FALSE);
	t1 = 0;
	t2 = 65536;
	j = 1;
	for (i = 0; i < 4; i++)
		{
		if (outcodes & j)
			{
				/* The following several lines accomplish the
				   computation t = wec1[i] / (wec1[i] - wec2[i])
				   where t is a fixed-point number with the
				   bottom 16 bits the fractional part.  Thus
				   the code maintains adequate precision and
				   guards against overflow.
				   Note that the numerator and denominator have
				   the same sign and that abs(n) <= abs(d).
				*/
			if ((n = wec1[i]) < 0)
				{
				n = -n;
				d = wec2[i] - wec1[i];
				}
			else
				d = wec1[i] - wec2[i];
			while (n > 65535)
				{
				n >>= 1;
				d >>= 1;
				}
			t = ((u_int) n * 65536) / (u_int) d;
			if (outcode1 & j)
				{
				if (t > t1)
					t1 = t;
				}
			else
				{
				if (t < t2)
					t2 = t;
				}
			}
		j <<= 1;
		}
	if (t2 < t1)
		return(FALSE);
					/* Compute magnitudes and signs of
					   deltas in preparation for computing
					   clipped endpoints below.
					   Also scale deltas to prevent overflow
					   below.
					*/
	if ((dx = v2->x - v1->x) < 0)
		{
		dx = -dx;
		sdx = 1;
		}
	else 
		sdx = 0;
	shiftx = 16;
	while (dx > 65535)
		{
		dx >>= 1;
		shiftx--;
		}
	if ((dy = v2->y - v1->y) < 0)
		{
		dy = -dy;
		sdy = 1;
		}
	else 
		sdy = 0;
	shifty = 16;
	while (dy > 65535)
		{
		dy >>= 1;
		shifty--;
		}
	if ((dz = v2->z - v1->z) < 0)
		{
		dz = -dz;
		sdz = 1;
		}
	else 
		sdz = 0;
	shiftz = 16;
	while (dz > 65535)
		{
		dz >>= 1;
		shiftz--;
		}

	if (t2 != 65536)
		{
		register int d;
					/* The following lines compute
					   v2->x = v1->x + ((t2 * dx) >> 16)
					   The scaling done above prevents
					   overflow for large dx.
					*/
		(u_int) d = (t2 * (u_int) dx) >> shiftx;
		if (sdx)
			d = -d;
		v2->x = v1->x + d;

					/* v2->y = v1->y + ((t2 * dy) >> 16) */
		(u_int) d = (t2 * (u_int) dy) >> shifty;
		if (sdy)
			d = -d;
		v2->y = v1->y + d;

					/* v2->z = v1->z + ((t2 * dz) >> 16) */
		(u_int) d = (t2 * (u_int) dz) >> shiftz;
		if (sdz)
			d = -d;
		v2->z = v1->z + d;

					/* Now clamp clipped coords to viewport
					   to prevent horrible effects from
					   roundoff error.
					*/
		if (v2->x < vwprt->xmin)
			v2->x = vwprt->xmin;
		else if (v2->x > vwprt->xmax)
			v2->x = vwprt->xmax;
		
		if (v2->y < vwprt->ymin)
			v2->y = vwprt->ymin;
		else if (v2->y > vwprt->ymax)
			v2->y = vwprt->ymax;
		}
	if (t1 != 0)
		{
		register int d;

					/* v1->x += (t1 * dx) >> 16 */
		(u_int) d = (t1 * (u_int) dx) >> shiftx;
		if (sdx)
			d = -d;
		v1->x += d;

					/* v1->y += (t1 * dy) >> 16 */
		(u_int) d = (t1 * (u_int) dy) >> shifty;
		if (sdy)
			d = -d;
		v1->y += d;

					/* v1->z += (t1 * dz) >> 16 */
		(u_int) d = (t1 * (u_int) dz) >> shiftz;
		if (sdz)
			d = -d;
		v1->z += d;

					/* Now clamp clipped coords to viewport
					   to prevent horrible effects from
					   roundoff error.
					*/
		if (v1->x < vwprt->xmin)
			v1->x = vwprt->xmin;
		else if (v1->x > vwprt->xmax)
			v1->x = vwprt->xmax;
		
		if (v1->y < vwprt->ymin)
			v1->y = vwprt->ymin;
		else if (v1->y > vwprt->ymax)
			v1->y = vwprt->ymax;
		}
	_core_cpchang = TRUE;
	return(TRUE);
	}
/*--------------------------------------*/
_core_oclippt2(x,y,vwprt) register int x,y; register porttype *vwprt;
{			/* return true if point is in view window */

    if (  (x < vwprt->xmin) || (x >= vwprt->xmax)
        ||(y < vwprt->ymin) || (y >= vwprt->ymax))
        return(FALSE);
    else return(TRUE);
    }

/*-----------------------------------------------------------------------
	Polygon clipper for 2-D polygons.  Uses Sutherland Hodgeman algorithm
	in 'Reentrant Polygon Clipping', CACM 17:1, January 1974.
	Two function calls are included _core_oclpvtx2( p) must be called for
	each vertex of a polygon and _core_oclpend2() is called to terminate the
	polygon.  Output vertices are written to the global array 'vtxlist'.
	*/

/*------------globals---------------------*/
#define LEFTPLANE	0
#define RIGHTPLANE	1
#define BOTTOM		2
#define TOP		3
#define VTXSIZE		7

static int oprocvtx(), opolclos(), ooutvtx(), opolout();

static struct clipplane {
    struct clipplane *nextplane;	/* ptr to data for next clip plane */
    int (*oprocvtx)();			/* vertex processing routine */
    int (*opolclos)();			/* close-polygon-entry routine */
    short firstvtx;			/* TRUE if this is 1st vertex  */
    short planeno;			/* clip plane number for this plane */
    short nout;				/* no. vtxs output by this stage */
    vtxtype f,				/* 1st vertex of polygon */
	    s,				/* saved previous vtx of current pol */
	    i;				/* intersect of edge with clip plane */
    } planes[4]	= {	/* 4 clipping planes */
	    {&planes[1], oprocvtx, opolclos, TRUE, LEFTPLANE},
	    {&planes[2], oprocvtx, opolclos, TRUE, RIGHTPLANE},
	    {&planes[3], oprocvtx, opolclos, TRUE, BOTTOM},
	    {&planes[0], ooutvtx, opolout, TRUE, TOP}
	    };

/*-------------------------------------------
printplanes()
{
    int i;
    for (i=0; i<4; i++) {
	fprintf(stderr, " %d %d %d\n", planes[i].firstvtx, planes[i].planeno,
	planes[i].nout);
	fprintf(stderr, " %d %d %d %d\n", planes[i].f.x,planes[i].f.y,
	planes[i].f.z,planes[i].f.w);
	fprintf(stderr, " %d %d %d %d\n", planes[i].s.x,planes[i].s.y,
	planes[i].s.z,planes[i].s.w);
	fprintf(stderr, " %d %d %d %d\n", planes[i].i.x,planes[i].i.y,
	planes[i].i.z,planes[i].i.w);
	}
    }
---------------------------------------------*/
static porttype *vwprt;
/*------------------------------------*/
_core_oclpvtx2( p,vwp) vtxtype *p; porttype *vwp;
{				/* hand this routine each vertex */
    vwprt = vwp;
    oprocvtx( p, &planes[0]);
    }
/*------------------------------------*/
_core_oclpend2()			/* this routine terminates polygon */
{
    opolclos(  &planes[0]);
    }

/*------------------------------------*/
static oprocvtx( p, plane) vtxtype *p; struct clipplane *plane;
{
    if (plane->firstvtx) {
	plane->f = *p;			/* f is first vertex of polygon */
	plane->s = *p;			/* s is previous 'saved' vertex */
	}
    else {
	if (olinecross( plane, p)) {	/* if line sp crosses plane */
	    plane->s = *p;		/* compute intersect vtx i  */
	    (*(plane->oprocvtx))( &plane->i, plane->nextplane);
	    }
	else plane->s = *p;
	}
    if ( ovisible( plane)) {		/* if s on visible side of plane */
	(*(plane->oprocvtx))( &plane->s, plane->nextplane);
	plane->nout++;
	}
    plane->firstvtx = FALSE;
    }

/*-----------------------------------------*/
static opolclos( pl) register struct clipplane *pl;
{
    if (pl->nout ) {
	if (olinecross( pl, &pl->f)) {		/* handle last edge */
	    (*(pl->oprocvtx))( &pl->i, pl->nextplane);
	    }
	}
    pl->firstvtx = TRUE;
    pl->nout = 0;
    (*(pl->opolclos))( pl->nextplane);
    }

/*--------------------------------------------*/
static ooutvtx( p, pl) vtxtype *p; struct clipplane *pl;
{			/* output routine from last stage of clipper */
					/* Now clamp clipped coords to viewport
					   to prevent horrible effects from
					   roundoff error.
					*/
	if (p->x < vwprt->xmin)
		p->x = vwprt->xmin;
	else if (p->x > vwprt->xmax)
		p->x = vwprt->xmax;

	if (p->y < vwprt->ymin)
		p->y = vwprt->ymin;
	else if (p->y > vwprt->ymax)
		p->y = vwprt->ymax;
	pl->nout++;
	_core_vtxlist[_core_vtxcount++] = *p;
}
/*--------------------------------------------*/
static opolout( pl) struct clipplane *pl;
{
    if (pl->nout ) {	/* later may want to notify about end of polygon */
        }
    pl->nout = 0;
    }
	
/*----------------------------------------*/
static olinecross( pl, p) struct clipplane *pl; vtxtype *p;
{
    register int shift;
    register u_int alpha, n, d;
    int svis, pvis, i;
    int *ptri, *ptrs, *ptrp;
    short sd;

    switch( pl->planeno) {
	case LEFTPLANE:
	    svis = pl->s.x - vwprt->xmin;		/* x-(-w) */
	    pvis = p->x - vwprt->xmin;
	    break;
	case RIGHTPLANE:
	    svis = vwprt->xmax - pl->s.x;		/* -(x-w) */
	    pvis = vwprt->xmax - p->x;
	    break;
	case BOTTOM:
	    svis = pl->s.y - vwprt->ymin;		/* y-(-w) */
	    pvis = p->y - vwprt->ymin;
	    break;
	case TOP:
	    svis = vwprt->ymax - pl->s.y;		/* -(y-w) */
	    pvis = vwprt->ymax - p->y;
	    break;
	}
    if ((svis ^ pvis) & 0x80000000) {		/* if opposite signs then on */
	if (((int) n = svis) < 0)		/* opposite sides of plane */
		{
		(int) n = - (int) n;
		(int) d = pvis - svis;
		}
	else
		(int) d = svis - pvis;
	while (n > 65535)
		{
		n >>= 1;
		d >>= 1;
		}
	alpha = (n << 16) / d;
	ptri = &pl->i.x;
	ptrs = &pl->s.x;
	ptrp = &p->x;
	for (i=0; i<VTXSIZE; i++){		/* compute intersection vrtx */
	    if (((int) d = *ptrp++ - *ptrs) < 0)
		{
		(int) d = - (int) d;
		sd = 1;
		}
	    else
		sd = 0;
	    shift = 16;
	    while (d > 65535)
		{
		d >>= 1;
		shift--;
		}
	    d = (alpha * d) >> shift;
	    if (sd)
		(int) d = - (int) d;
	    *ptri++ = *ptrs++ + (int) d;
	    }
	return( TRUE);
	}
    else return( FALSE);
    }
/*-------------------------------------*/
static ovisible( pl) struct clipplane *pl;
{
    int svis;

    switch( pl->planeno) {
	case LEFTPLANE:
	    svis = pl->s.x - vwprt->xmin;		/* x-(-w) */
	    break;
	case RIGHTPLANE:
	    svis = vwprt->xmax - pl->s.x;		/* -(x-w) */
	    break;
	case BOTTOM:
	    svis = pl->s.y - vwprt->ymin;		/* y-(-w) */
	    break;
	case TOP:
	    svis = vwprt->ymax - pl->s.y;		/* -(y-w) */
	    break;
	}
    if (svis >= 0) return( TRUE);
    else return( FALSE);
    }
