/*****************************************************************************
*   Default curve drawing routine common to graphics drivers.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

#include <gl/gl.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "allocate.h"
#include "ip_cnvrt.h"
#include "cagd_lib.h"
#include "symb_lib.h"
#include "iritgrap.h"

#define DUMP_BIN TRUE

static int IsPointVisible(CagdRType *Pt);
static int IsPointVisibleNew(CagdRType *Pt);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Draw a single Curve object using current modes and transformations.	     M
*   Curve must be with either E3 or P3 point type and must be a NURB curve.  M
*   Piecewise linear approximation is cashed under "_isoline" and "_ctlpoly" M
* attributes of PObj.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     A curve object to draw.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGDrawCurve                                                              M
*****************************************************************************/
void IGDrawCurve(IPObjectStruct *PObj)
{
    static int
	Handler = -1;
    int i, j, n;
    IPObjectStruct
	*VarWidthObj = AttrGetObjectObjAttrib(PObj, "VarWidth");
    CagdCrvStruct *Crv,
	*Crvs = PObj -> U.Crvs;
    RealType
        WScale = AttrGetObjectRealAttrib(PObj, "_WidthScale");

    for (Crv = Crvs, n = 0; Crv != NULL; Crv = Crv -> Pnext, n++);
    if (VarWidthObj == NULL)
	printf("[OBJECT \"%s\"\n", PObj -> Name);

    if (Handler < 0) {
	Handler = IritPrsrOpenStreamFromFile(stdout, FALSE, DUMP_BIN, FALSE);
    }

    for (Crv = Crvs; Crv != NULL; Crv = Crv -> Pnext) {
	CagdRType TMin, TMax, TStart, t, dt, *R, Pt[3];
	CagdBType
	    Visible = FALSE;

	fprintf(stderr, "%d    \r", n--);

	CagdCrvDomain(Crv, &TMin, &TMax);
	TStart = TMin;
	dt = (TMax - TMin) / (IGGlblSamplesPerCurve - 1.0);

	if (VarWidthObj == NULL) {
	    for (i = 0; i < IGGlblSamplesPerCurve; i++) {
		t = TMin + dt * i;
		R = CagdCrvEval(Crv, t);
		CagdCoerceToE3(Pt, &R, -1, Crv -> PType);

		if (IsPointVisibleNew(Pt)) {
		    if (!Visible) {
			TStart = t;
			Visible = TRUE;
		    }
		    else {
		    }
		}
		else {
		    if (Visible) {
			/* End of visible domain. */
			if (TStart < t) {
			    char *Err;
			    CagdCrvStruct
				*Region = CagdCrvRegionFromCrv(Crv, TStart, t);
			
			    CagdCrvWriteToFile3(Region, stdout, 4, NULL, &Err);
			    CagdCrvFree(Region);
			}
			Visible = FALSE;
		    }
		    else {
		    }		
		}
	    }

	    if (Visible) {
		if (TStart < t) {
		    char *Err;
		    CagdCrvStruct
			*Region = CagdCrvRegionFromCrv(Crv, TStart, t);
		    
		    CagdCrvWriteToFile3(Region, stdout, 4, NULL, &Err);
		    CagdCrvFree(Region);
		}
	    }
	}
	else {
	    /* We do have a variable width curve. */
	    CagdRType LastPt[3];
	    CagdBType LastVisible,
	        CoercedDistCrv = FALSE;
	    CagdCrvStruct *DistCrv;
	    
	    if (VarWidthObj -> U.Crvs -> PType != CAGD_PT_E1_TYPE) {
		DistCrv = CagdCoerceCrvTo(VarWidthObj -> U.Crvs,
					  CAGD_PT_E1_TYPE);
		CoercedDistCrv = TRUE;
	    }
	    else 
		DistCrv = VarWidthObj -> U.Crvs;

	    t = TMin;
	    R = CagdCrvEval(Crv, t);
	    CagdCoerceToE3(LastPt, &R, -1, Crv -> PType);
	    LastVisible = IsPointVisibleNew(LastPt);

	    for (i = 1; i < IGGlblSamplesPerCurve; i++) {
		t = TMin + dt * i;
		R = CagdCrvEval(Crv, t);
		CagdCoerceToE3(Pt, &R, -1, Crv -> PType);

		if ((Visible = IsPointVisibleNew(Pt)) && LastVisible) {
#ifdef DUMP_POLY_TEXT		    
		    R = CagdCrvEval(DistCrv, t - dt / 2);

		    printf("[OBJECT [Width %lf] NONE\n    [POLYLINE 2\n",
			   R[1]);
		    printf("\t[%lf %lf %lf]\n\t[%lf %lf %lf]\n    ]\n]\n",
			   LastPt[0], LastPt[1], LastPt[2],
			   Pt[0], Pt[1], Pt[2]);
#else
		    IPObjectStruct *PObjPoly;
		    IPVertexStruct *V;

		    R = CagdCrvEval(DistCrv, t - dt / 2);

		    V = IPAllocVertex(0, 0, NULL,
				      IPAllocVertex(0,0, NULL, NULL));
		    PObjPoly =
			GenPOLYObject(IPAllocPolygon(0, 0, V, NULL));
		    IP_SET_POLYLINE_OBJ(PObjPoly);
		    AttrSetObjectRealAttrib(PObjPoly, "width", R[1] * WScale);
		    for (j = 0; j < 3; j++) {
			V -> Coord[j] = LastPt[j];
			V -> Pnext -> Coord[j] = Pt[j];
		    }
		    IritPrsrPutObjectToHandler(Handler, PObjPoly);
		    IPFreeObject(PObjPoly);
#endif /* DUMP_POLY_TEXT */
		}
		LastVisible = Visible;
		PT_COPY(LastPt, Pt);
	    }

	    if (CoercedDistCrv)
		CagdCrvFree(DistCrv);
	}
    }

    if (VarWidthObj == NULL)
	printf("]\n");
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Determines if point is visible or not. Modified from Agranov Genadi.     *
*                                                                            *
* PARAMETERS:                                                                *
*   Pt:       Point to test.                                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:      TRUE if visible, false otherwise.                              *
*****************************************************************************/
static int IsPointVisible(CagdRType *Pt)
{
    static int
	Initialized = FALSE;
    static long xsize, ysize, zbits;
    static double xsize_div2, ysize_div2, zmin, zmax;
    static double S[4][4];
    int i,j,k;
    unsigned long zvalue;
    double x, y, z, xs, ys, ws, xxx, yyy, zzz, www;

    if (!Initialized) {
	Matrix P, V;

	zbits = getgdesc(GD_BITS_NORM_ZBUFFER);
	if (zbits == 0) {
	    printf("There is no hardware/software z-buffer\n");
	    return 1;
	}
	zmin = getgdesc(GD_ZMIN);
	zmax = getgdesc(GD_ZMAX);
	getsize(&xsize, &ysize);
	xsize_div2 = (xsize - 1.0) / 2.0;
	ysize_div2 = (ysize - 1.0) / 2.0;
	mmode(MPROJECTION);
	getmatrix(P);
	mmode(MVIEWING);
	getmatrix(V);

	for (i = 0; i < 4; ++i)
	    for(j = 0; j < 4; ++j) {
		S[i][j]=0;
		for(k = 0; k < 4; ++k)
		    S[i][j] += V[i][k]*P[k][j];
	    }

	Initialized = TRUE;
    }

    xxx = Pt[0] * S[0][0]+ Pt[1] * S[1][0]+ Pt[2] * S[2][0] + S[3][0];
    yyy = Pt[0] * S[0][1]+ Pt[1] * S[1][1]+ Pt[2] * S[2][1] + S[3][1];
    zzz = Pt[0] * S[0][2]+ Pt[1] * S[1][2]+ Pt[2] * S[2][2] + S[3][2];
    www = Pt[0] * S[0][3]+ Pt[1] * S[1][3]+ Pt[2] * S[2][3] + S[3][3];

    www = 1 / www;
    xxx *= www;
    yyy *= www;
    zzz *= www;

    /* If point outside domain. */
    if (xxx > 1.0 || xxx < -1.0 || yyy > 1.0 || yyy < -1.0)
	return FALSE;

    /* xs,ys - screen coordinates */
    xs = xsize_div2 * (1.0 + xxx);
    ys = ysize_div2 * (1.0 + yyy);
    zzz = (zmin + zmax + zzz * (zmax - zmin)) / 2.0;
    /* zzz = (2.0 * zzz - zmax - zmin) / (zmax - zmin); */


    readsource(SRC_ZBUFFER);
    lrectread(xs, ys, xs, ys, &zvalue);

    return zzz < (long) zvalue;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Determines if point is visible or not. Modified from Agranov Genadi.     *
*                                                                            *
* PARAMETERS:                                                                *
*   Pt:       Point to test.                                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:      TRUE if visible, false otherwise.                              *
*****************************************************************************/
static int IsPointVisibleNew(CagdRType *Pt)
{
    static int Initialized = FALSE;
    static long xsize, ysize;
    static double xsize_div2, ysize_div2;
    static double S[4][4], w1, w2;
    static unsigned long zmax, zwork;
    unsigned long zbuffer;
    long zvalue;
    double x, y, z, xs, ys, ws, xxx, yyy, zzz, www;

    if (!Initialized) {
        Matrix P, V;
        int i,j,k;
        long zmin;

        if (!getgdesc(GD_BITS_NORM_ZBUFFER)) {
            printf("There is no hardware/software z-buffer\n");
            return 1;
        }
        zmin  = getgdesc(GD_ZMIN);
        zmax  = getgdesc(GD_ZMAX);
        zwork = zmax+zmax;
        w1= (double) zmax - (double) zmin;
        w2= (double) zmax + (double) zmin;
        getsize(&xsize, &ysize);
        xsize_div2 = (xsize - 1.0) / 2.0;
        ysize_div2 = (ysize - 1.0) / 2.0;
        mmode(MPROJECTION);
        getmatrix(P);
        mmode(MVIEWING);
        getmatrix(V);

        for (i = 0; i < 4; ++i)
            for(j = 0; j < 4; ++j) {
                S[i][j]=0;
                for(k = 0; k < 4; ++k)
                    S[i][j] += V[i][k]*P[k][j];
            }

        Initialized = TRUE;
    }

    xxx = Pt[0] * S[0][0]+ Pt[1] * S[1][0]+ Pt[2] * S[2][0] + S[3][0];
    yyy = Pt[0] * S[0][1]+ Pt[1] * S[1][1]+ Pt[2] * S[2][1] + S[3][1];
    zzz = Pt[0] * S[0][2]+ Pt[1] * S[1][2]+ Pt[2] * S[2][2] + S[3][2];
    www = Pt[0] * S[0][3]+ Pt[1] * S[1][3]+ Pt[2] * S[2][3] + S[3][3];

    www = 1 / www;
    xxx *= www;
    yyy *= www;
    zzz *= www;

    /* If point outside domain. */
    if (xxx > 1.0 || xxx < -1.0 || yyy > 1.0 || yyy < -1.0)
        return FALSE;

    /* xs,ys - screen coordinates */
    xs = xsize_div2 * (1.0 + xxx);
    ys = ysize_div2 * (1.0 + yyy);

    readsource(SRC_ZBUFFER);
    lrectread(xs, ys, xs, ys, &zbuffer);
    zvalue = (zbuffer > zmax) ? -((zwork - zbuffer) + 2) : zbuffer;
 
    return zzz < (2.0 * zvalue - w2 ) / w1;
}

