/*****************************************************************************
*   Program to draw 3D object as wireframe after removing the hidden lines.  *
* This porgram works in object space, and if redirect stdout to a file, dump *
* the visible polylines into it instead of drawing them on current device.   *
* This may be used to display the results on any device (a plotter !?) later.*
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Gershon Elber				Ver 3.0, Aug. 1990   *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include "program.h"
#include "getarg.h"
#include "genmat.h"
#include "config.h"
#include "ip_cnvrt.h"

#ifdef NO_CONCAT_STR
static char *VersionStr =
	"Poly3D-H	Version 6.0,	Gershon Elber,\n\
	(C) Copyright 1989/90-95 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr = "Poly3D-H	" VERSION ",	Gershon Elber,	"
	__DATE__ ",  " __TIME__ "\n" COPYRIGHT ", Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char *CtrlStr =
#ifdef DOUBLE
	"poly3d-h b%- m%- i%- e%-#Edges!d H%- 4%- W%-Width!F F%-PolyOpti|FineNess!d!F q%- o%-OutName!s c%- z%- DFiles!*s";
#else
	"poly3d-h b%- m%- i%- e%-#Edges!d H%- 4%- W%-Width!f F%-PolyOpti|FineNess!d!f q%- o%-OutName!s c%- z%- DFiles!*s";
#endif /* DOUBLE */

int NumOfPolygons = 0;		      /* Total number of polygons to handle. */
MatrixType GlblViewMat;				  /* Current view of object. */

/* Data structures used by the hidden line modules: */
int EdgeCount = 0;
EdgeStruct *EdgeHashTable[EDGE_HASH_TABLE_SIZE];
IPPolygonStruct *PolyHashTable[POLY_HASH_TABLE_SIZE];

/* The following are setable variables (via configuration file poly3d-h.cfg).*/
int GlblMore = FALSE,
    GlblClipScreen = TRUE,
    GlblFourPerFlat = FALSE,
    GlblOptimalPolyApprox = FALSE,
    GlblQuiet = FALSE,
    GlblOutputHasRGB = FALSE,
    GlblOutputRGB[3] = { 255, 255, 255 },
    GlblOutputColor = VISIBLE_COLOR,
    GlblNumEdge = 0,
    GlblBackFacing = FALSE,
    GlblInternal = FALSE,
    GlblOutputHiddenData = FALSE;

RealType
    GlblFineNess = DEFAULT_FINENESS,
    GlblOutputWidth = VISIBLE_WIDTH;

static ConfigStruct SetUp[] =
{
    { "Internal",    "-i", (VoidPtr) &GlblInternal,	SU_BOOLEAN_TYPE },
    { "BackFacing",  "-b", (VoidPtr) &GlblBackFacing,	SU_BOOLEAN_TYPE },
    { "More",	     "-m", (VoidPtr) &GlblMore,		SU_BOOLEAN_TYPE },
    { "ClipScreen",  "-c", (VoidPtr) &GlblClipScreen,	SU_BOOLEAN_TYPE },
    { "Quiet",	     "-q", (VoidPtr) &GlblQuiet,	SU_BOOLEAN_TYPE },
    { "FourPerFlat", "-4", (VoidPtr) &GlblFourPerFlat,	SU_BOOLEAN_TYPE },
    { "DumpHidden",  "-H", (VoidPtr) &GlblOutputHiddenData,SU_BOOLEAN_TYPE },
    { "PolyOpti",    "-F", (VoidPtr) &GlblOptimalPolyApprox,SU_INTEGER_TYPE },
    { "NumOfEdges",  "-e", (VoidPtr) &GlblNumEdge,	SU_INTEGER_TYPE },
    { "LineWidth",   "-W", (VoidPtr) &GlblOutputWidth,	SU_REAL_TYPE },
    { "FineNess",    "-F", (VoidPtr) &GlblFineNess,     SU_REAL_TYPE }
};
#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of poly3d-h - Read command line and do what is needed...	     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv:  Command line.                                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   main                                                                     M
*****************************************************************************/
void main(int argc, char **argv)
{
    int Error,
	EdgesFlag = FALSE,
	VerFlag = FALSE,
	OutFlag = FALSE,
	WidthFlag = FALSE,
	NumFiles = FALSE,
	OptPolyApproxFlag = FALSE;
    char
	*OutFileName = NULL,
	**FileNames = NULL;
    FILE *OutFile;
    IPObjectStruct *PObjects, *PTmpObj;

    Config("poly3d-h", SetUp, NUM_SET_UP);   /* Read config. file if exists. */

    if ((Error = GAGetArgs(argc, argv, CtrlStr,
			   &GlblBackFacing, &GlblMore, &GlblInternal,
			   &EdgesFlag, &GlblNumEdge, &GlblOutputHiddenData,
			   &GlblFourPerFlat, &WidthFlag,
			   &GlblOutputWidth, &OptPolyApproxFlag,
			   &GlblOptimalPolyApprox, &GlblFineNess,
			   &GlblQuiet, &OutFlag, &OutFileName,
			   &GlblClipScreen, &VerFlag, &NumFiles,
			   &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	Poly3dhExit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s\n\n", VersionStr);
	GAPrintHowTo(CtrlStr);
	ConfigPrint(SetUp, NUM_SET_UP);
	Poly3dhExit(0);
    }

    if (!NumFiles) {
	fprintf(stderr, "No data file names were given, exit\n");
	GAPrintHowTo(CtrlStr);
	Poly3dhExit(1);
    }

    if (GlblQuiet)
	GlblMore = FALSE;

    if (GlblOutputHiddenData && GlblBackFacing) {
	fprintf(stderr, "Warning: You have activated both output of hidden data (-H) and back facing\n");
	fprintf(stderr, "\t elimination (-b). Chances are you did not want to do that.\n");
    }

    /* Get the data files: */
    if ((PObjects = IritPrsrGetDataFiles(FileNames, NumFiles, TRUE,
					 GlblMore)) == NULL)
	Poly3dhExit(1);

    /* If has color/width attribute in geometries - uses them. Assume */
    /* all objects share the same attribute (should be fixed).        */
    for (PTmpObj = PObjects; PTmpObj != NULL; PTmpObj = PTmpObj -> Pnext) {
	if (IP_IS_GEOM_OBJ(PTmpObj)) {
	    int Color;
	    RealType Width;

	    if ((Width = AttrGetObjectRealAttrib(PTmpObj, "width")) <
							IP_ATTR_BAD_REAL)
		GlblOutputWidth = Width;

	    if (!GlblOutputHasRGB)
		GlblOutputHasRGB = AttrGetObjectRGBColor(PTmpObj,
							 &GlblOutputRGB[0],
							 &GlblOutputRGB[1],
							 &GlblOutputRGB[2]);
	    if ((Color = AttrGetObjectColor(PTmpObj)) != IP_ATTR_NO_COLOR)
		GlblOutputColor = Color;
	}
    }

    /* And update the global viewing matrix: */
    if (IritPrsrWasPrspMat)
	MatMultTwo4by4(GlblViewMat, IritPrsrViewMat, IritPrsrPrspMat);
    else
	GEN_COPY(GlblViewMat, IritPrsrViewMat, sizeof(MatrixType));

    /* Prepare data structures to be able to decide on visibility: */
    PrepareViewData(PObjects);

    if (OutFlag) {
	if ((OutFile = fopen(OutFileName, "w")) == NULL) {
	    fprintf(stderr, "Failed to open \"%s\".\n", OutFileName);
	    Poly3dhExit(2);
	}
    }
    else
	OutFile = stdout;

    OutVisibleEdges(OutFile);	       /* Scan all sub-edges output visible. */

    if (OutFile != stdout)
	fclose(OutFile);

    Poly3dhExit(0);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert all surfaces/curves into polylines as follows:	     M
*   Curve is converted to a single polyline with SamplesPerCurve samples.    M
*   Surface is converted into GlblNumOfIsolines curves in each axes, each    M
* handled as Curve above. The original curves and surfaces are then deleted. M
*   This function is a call back function of the irit parser.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForms:  Crvs/Srfs/Trimmed Srfs/Trivariates read from a file by the   M
*               irit parser.					             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Processed freeform geometry. This function simply    M
*                       returns what it gots.                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrProcessFreeForm, conversion                                      M
*****************************************************************************/
IPObjectStruct *IritPrsrProcessFreeForm(IritPrsrFreeFormStruct *FreeForms)
{
    IPObjectStruct *Object;
    IPPolygonStruct *Poly;

    /* Make sure requested format is something reasonable. */
    if (GlblOptimalPolyApprox == 0 && GlblFineNess < 2) {
	GlblFineNess = 2;
	if (GlblMore)
	    fprintf(stderr, "FineNess is less than 2, 2 picked instead.\n");
    }

    for (Object = FreeForms -> CrvObjs;
	 Object != NULL;
	 Object = Object -> Pnext) {
	CagdCrvStruct *Curves, *Curve;

        Curves = Object -> U.Crvs;
        Object -> U.Pl = NULL;
        Object -> ObjType = IP_OBJ_POLY;
        IP_SET_POLYLINE_OBJ(Object);
	for (Curve = Curves; Curve != NULL; Curve = Curve -> Pnext) {
	    Poly = IritCurve2Polylines(Curve, (int) GlblFineNess, FALSE);

	    if (Poly != NULL) {
		IritPrsrGetLastPoly(Poly) -> Pnext = Object -> U.Pl;
		Object -> U.Pl = Poly;
	    }
	}
	CagdCrvFreeList(Curves);
    }

    for (Object = FreeForms -> SrfObjs;
	 Object != NULL;
	 Object = Object -> Pnext) {
	RealType RelativeFineNess;
	CagdSrfStruct *Surfaces, *Surface;

	if ((RelativeFineNess = AttrGetObjectRealAttrib(Object,
                                           "resolution")) > IP_ATTR_BAD_REAL)
	    RelativeFineNess = 1.0;

        Surfaces = Object -> U.Srfs;
        Object -> U.Pl = NULL;
        Object -> ObjType = IP_OBJ_POLY;
        IP_SET_POLYGON_OBJ(Object);
        for (Surface = Surfaces; Surface != NULL; Surface = Surface -> Pnext) {
	    RealType t;

	    if ((t = AttrGetObjectRealAttrib(Object, "u_resolution"))
							    < IP_ATTR_BAD_REAL)
	        AttrSetRealAttrib(&Surface -> Attr, "u_resolution", t);
	    if ((t = AttrGetObjectRealAttrib(Object, "v_resolution"))
							    < IP_ATTR_BAD_REAL)
	        AttrSetRealAttrib(&Surface -> Attr, "v_resolution", t);

	    Poly = IritSurface2Polygons(Surface, GlblFourPerFlat, GlblFineNess,
					FALSE, GlblOptimalPolyApprox);
	    if (Poly != NULL) {
		IritPrsrGetLastPoly(Poly) -> Pnext = Object -> U.Pl;
		Object -> U.Pl = Poly;
	    }
	    CagdSrfFreeList(Surfaces);
	}
    }

    for (Object = FreeForms -> TrimSrfObjs;
	 Object != NULL;
	 Object = Object -> Pnext) {
	RealType RelativeFineNess;
	TrimSrfStruct *TrimSrfs, *TrimSrf;

	if ((RelativeFineNess = AttrGetObjectRealAttrib(Object,
                                           "resolution")) > IP_ATTR_BAD_REAL)
	    RelativeFineNess = 1.0;

        TrimSrfs = Object -> U.TrimSrfs;
        Object -> U.Pl = NULL;
        Object -> ObjType = IP_OBJ_POLY;
        IP_SET_POLYGON_OBJ(Object);
        for (TrimSrf = TrimSrfs; TrimSrf != NULL; TrimSrf = TrimSrf -> Pnext) {
            Poly = IritTrimSrf2Polygons(TrimSrf, GlblFourPerFlat, GlblFineNess,
					FALSE, GlblOptimalPolyApprox);
	    if (Poly != NULL) {
		IritPrsrGetLastPoly(Poly) -> Pnext = Object -> U.Pl;
		Object -> U.Pl = Poly;
	    }
        }
        TrimSrfFreeList(TrimSrfs);
    }

    for (Object = FreeForms -> TrivarObjs;
	 Object != NULL;
	 Object = Object -> Pnext) {
	RealType RelativeFineNess;
	TrivTVStruct *Trivars, *Trivar;

	if ((RelativeFineNess = AttrGetObjectRealAttrib(Object,
                                           "resolution")) > IP_ATTR_BAD_REAL)
	    RelativeFineNess = 1.0;

        Trivars = Object -> U.Trivars;
        Object -> U.Pl = NULL;
        Object -> ObjType = IP_OBJ_POLY;
        IP_SET_POLYGON_OBJ(Object);
        for (Trivar = Trivars; Trivar != NULL; Trivar = Trivar -> Pnext) {
            Poly = IritTrivar2Polygons(Trivar, GlblFourPerFlat, GlblFineNess,
				       FALSE, GlblOptimalPolyApprox);
	    if (Poly != NULL) {
		IritPrsrGetLastPoly(Poly) -> Pnext = Object -> U.Pl;
		Object -> U.Pl = Poly;
	    }
        }
        TrivTVFreeList(Trivars);
    }

    return IritPrsrConcatFreeForm(FreeForms);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Poly3d-h Exit routine.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   ExitCode:    To notify O.S. with result of program.                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   Poly3dhExit                                                              M
*****************************************************************************/
void Poly3dhExit(int ExitCode)
{
    if (!GlblQuiet)
	fprintf(stderr, "\n");

    exit(ExitCode);
}

#ifdef DEBUG

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Dummy function to link at debugging time.                               *
*                                                                            *
* PARAMETERS:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*****************************************************************************/
void DummyLinkCagdDebug(void)
{
    IritPrsrDbg();
}

#endif /* DEBUG */
