/*****************************************************************************
* Module to handle viewing of objects in the ViewWindow.		     *
*									     *
* Written by:  Gershon Elber			IBM PC Ver 1.0,	Jan. 1989    *
*****************************************************************************/

#include <stdio.h>
#include <conio.h>
#include <graphics.h>
#include <math.h>
#include <setjmp.h>
#include <string.h>
#include "Program.h"
#include "PostScrp.h"
#include "GenMat.h"
#include "Parser.h"
#include "ViewObjL.h"
#include "ViewObjG.h"
#include "GraphGnG.h"
#include "MouseDrv.h"
#include "Gif_Lib.h"

static FILE *PSFile = NULL;	       /* Used to save PostScript text file. */
static jmp_buf LongJumpBuffer;		      /* Used in control C trapping. */

/* Interactive mode menu set up structure is define below (See ViewObjL.H): */
InteractWindowStruct InteractMenu = {
    { { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.40, RED,   "Rotate" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.72, GREEN, "Translate" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.04, CYAN,  "Scale" },
    },
    { { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.1,  BROWN,  TRUE,  "Screen Coords." },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.22, BLUE,   TRUE,  "Perspectiv" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.3,  BLUE,   FALSE, "Z" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.46, RED,    FALSE, "X" }, /* Rot */
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.54, RED,    FALSE, "Y" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.62, RED,    FALSE, "Z" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.78, GREEN,  FALSE, "X" }, /* Trans */
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.86, GREEN,  FALSE, "Y" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 0.94, GREEN,  FALSE, "Z" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.1,  CYAN,   FALSE, "" },  /* Scale */
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.26, MAGENTA,TRUE,  "Depth cue" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.42, YELLOW, TRUE,  "Save GIF" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.5,  YELLOW, TRUE,  "Save PS" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.62, YELLOW, TRUE,  "Save Matrix" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.7,  YELLOW, TRUE,  "Reset Matrix" },
      { (SW_MIN_X + SW_MAX_X) / 2.0, SW_MAX_Y - 1.85, WHITE,  TRUE,  "Quit" },
    }
};

/* We support depth cues iff DepthCueFlag is TRUE: */
#define MY_DOTTED_LINE  (GlblDepthCue ? DOTTED_LINE : SOLID_LINE)

/*****************************************************************************
*  Routine to interactively display geometric object(s) PObjList on the View *
* window enable rotating/translating/scaling it using the Input Device.      *
*****************************************************************************/
void InteractGeomObject(FileDescription **FD, int NumOfObjects,	char **Objects)
{
    switch (GlblTransformMode) {
	case TRANS_SCREEN:
	    InteractMenu.SubWindows[0].Str = "Screen Coords.";
	    break;
	case TRANS_OBJECT:
	    InteractMenu.SubWindows[0].Str = "Object Coords.";
	    break;
    }

    switch (GlblViewMode) {
	case VIEW_PERSPECTIVE:
	    InteractMenu.SubWindows[1].Str = "Perspective";
	    break;
	case VIEW_ORTHOGRAPHIC:
	    InteractMenu.SubWindows[1].Str = "Orthographic";
	    break;
    }

    if (GlblDepthCue)
	 InteractMenu.SubWindows[10].Str = "Depth Cue";
    else InteractMenu.SubWindows[10].Str = "No Depth Cue";

    InteractDrawMenu();			    /* Draw the transformation menu. */
    /* Get data from input device, interpret it, and display interactively:  */
    InteractHandleInput(FD, NumOfObjects, Objects);
}

/*****************************************************************************
*  Routine to draw the Interactive Menu in the Menu window, using the        *
* InteractiveMenu structure defined above.				     *
*  It is assumed that string not inside of SubWindow will be of length 1.    *
*****************************************************************************/
static void InteractDrawMenu(void)
{
    int i;
    struct textsettingstype oldtext;

    gettextsettings(&oldtext);
    settextjustify(CENTER_TEXT, CENTER_TEXT);	   /* Draw strings centered. */

    GGClearMenuArea();				   /* Clear the menu window. */

    for (i=0; i<INTERACT_NUM_OF_STRINGS; i++) {/* Draw the strings of struct.*/
        GGMySetColor(InteractMenu.Strings[i].Color);
        GGXYPutStr(InteractMenu.Strings[i].X,
		   InteractMenu.Strings[i].Y,
		   InteractMenu.Strings[i].Str);
    }

    for (i=0; i<INTERACT_NUM_OF_SUB_WINDOWS; i++) {/* Draw strct sub windows.*/
        GGMySetColor(InteractMenu.SubWindows[i].Color);
	/* Draw the frame of the SubWindow: */
	GGMyMove(InteractMenu.SubWindows[i].X - INTERACT_SUB_WINDOW_WIDTH,
		 InteractMenu.SubWindows[i].Y - INTERACT_SUB_WINDOW_HEIGHT);
	GGMyDraw(InteractMenu.SubWindows[i].X + INTERACT_SUB_WINDOW_WIDTH,
		 InteractMenu.SubWindows[i].Y - INTERACT_SUB_WINDOW_HEIGHT);
	GGMyDraw(InteractMenu.SubWindows[i].X + INTERACT_SUB_WINDOW_WIDTH,
		 InteractMenu.SubWindows[i].Y + INTERACT_SUB_WINDOW_HEIGHT);
	GGMyDraw(InteractMenu.SubWindows[i].X - INTERACT_SUB_WINDOW_WIDTH,
		 InteractMenu.SubWindows[i].Y + INTERACT_SUB_WINDOW_HEIGHT);
	GGMyDraw(InteractMenu.SubWindows[i].X - INTERACT_SUB_WINDOW_WIDTH,
		 InteractMenu.SubWindows[i].Y - INTERACT_SUB_WINDOW_HEIGHT);

	/* Now the strings inside (and if outside, a middle vertical line): */
	if (InteractMenu.SubWindows[i].TextInside)
	    GGXYPutStr(InteractMenu.SubWindows[i].X,
		       InteractMenu.SubWindows[i].Y,
		       InteractMenu.SubWindows[i].Str);
	else {
	    GGXYPutStr(InteractMenu.SubWindows[i].X -
				INTERACT_SUB_WINDOW_WIDTH - 0.025,
		       InteractMenu.SubWindows[i].Y,
		       InteractMenu.SubWindows[i].Str);
	    GGMyMove(InteractMenu.SubWindows[i].X,
		     InteractMenu.SubWindows[i].Y -
				INTERACT_SUB_WINDOW_HEIGHT);
	    GGMyDraw(InteractMenu.SubWindows[i].X,
		     InteractMenu.SubWindows[i].Y +
				INTERACT_SUB_WINDOW_HEIGHT);
	}
    }

    settextjustify(oldtext.horiz, oldtext.vert);
}

/*****************************************************************************
*  Routine to handle data from the input device (keyboard, mouse etc.) -     *
* clip it against the sub windows of the interactive menu and perform the    *
* required transfomation, by updating the global view matrix object VIEW_MAT *
*  The input data in the Rotation/Translation/Scaling sub windows is used    *
* (horizontal distance from sub window center) to set amount of change.	     *
*****************************************************************************/
static void InteractHandleInput(FileDescription **FD, int NumOfObj,
							char **Objects)
{
    int i, UpdateView;
    double x, y, ChangeFactor;
    char *p, GifFileName[FILE_NAME_LEN];
    static char GifFileCount = '0';
    MatrixType Mat, OrigViewMat, OrigPerspMat;

    /* Save copy of original matrix, so we can recover if reset is requeired.*/
    GEN_COPY(OrigViewMat,  GlblViewMat,  sizeof(MatrixType));
    GEN_COPY(OrigPerspMat, GlblPerspMat, sizeof(MatrixType));
    switch (GlblViewMode) {			 /* Update the current view. */
	case VIEW_ORTHOGRAPHIC:
	    GEN_COPY(CrntViewMat, GlblViewMat, sizeof(MatrixType));
	    break;
	case VIEW_PERSPECTIVE:
	    MultTwo4by4(CrntViewMat, GlblViewMat, GlblPerspMat);
	    break;
    }

    GGClearViewArea();				   /* Clear the view window. */
    ViewGeomObjectList(FD, NumOfObj, Objects); /* Display it for first time. */

    while (TRUE) {
	MouseFlushBuffer();	  /* Flush out any duplicated points picked. */
	GGGetPoint(&x, &y);		/* Get coordinates from pick device. */

	for (i=0; i<INTERACT_NUM_OF_SUB_WINDOWS; i++)/* Test all sub windows.*/
	    if (ABS(InteractMenu.SubWindows[i].Y - y) <
						INTERACT_SUB_WINDOW_HEIGHT &&
	        ABS(InteractMenu.SubWindows[i].X - x) <
						INTERACT_SUB_WINDOW_WIDTH)
		break;		      /* The picked point is in this window! */

	ChangeFactor = (x - InteractMenu.SubWindows[i].X) //* Between -1..1. */
						INTERACT_SUB_WINDOW_WIDTH;
	if (i >= INTERACT_NUM_OF_SUB_WINDOWS) {
	    GGTone(1000, 100);
	    GGTone(1500, 100);
	    GGTone(1000, 100);
	    continue;
	}

	UpdateView = TRUE;
	GenUnitMat(Mat);	            /* No transformation by default! */

	switch (i) {
	    case 0:		       /* Its Coordinate system - toggle it. */
		switch (GlblTransformMode) {
		    case TRANS_SCREEN:
			InteractUpdateMenu("Object Coords.", 0);
			GlblTransformMode = TRANS_OBJECT;
			break;
		    case TRANS_OBJECT:
			InteractUpdateMenu("Screen Coords.", 0);
			GlblTransformMode = TRANS_SCREEN;
			break;
		}
		UpdateView = FALSE;
		break;
	    case 1:		    /* Toggle Perspective/Orthographic view. */
		switch (GlblViewMode) {
		    case VIEW_PERSPECTIVE:
			InteractUpdateMenu("Orthographic", 1);
			GlblViewMode = VIEW_ORTHOGRAPHIC;
			break;
		    case VIEW_ORTHOGRAPHIC:
			InteractUpdateMenu("Perspective", 1);
			GlblViewMode = VIEW_PERSPECTIVE;
			break;
		}
		break;
	    case 2:			  /* Set Perspective Screen Z point. */
		if (GlblViewMode != VIEW_PERSPECTIVE) {
		    GGTone(1000, 100);			   /* Do some noise! */
		    UpdateView = FALSE;
		    break;
		}
		/* Make it between 0.5 and 1.5: */
		ChangeFactor = ChangeFactor / 2.0 + 1.0;
		GlblPerspMat[2][2] *= ChangeFactor;
		GlblPerspMat[2][3] *= ChangeFactor;
		GlblPerspMat[3][2] *= ChangeFactor;
		break;
	    case 3:			   /* Its rotation along the X axis. */
		GenMatRotX1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
		break;
	    case 4:			   /* Its rotation along the Y axis. */
		GenMatRotY1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
		break;
	    case 5:			   /* Its rotation along the Z axis. */
		GenMatRotZ1(DEG2RAD(ChangeFactor * MAX_ROTATE_ANGLE), Mat);
		break;
	    case 6:			/* Its translation along the X axis. */
		GenMatTrans(ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0, 0.0,
									Mat);
		break;
	    case 7:			/* Its translation along the Y axis. */
		GenMatTrans(0.0, ChangeFactor * MAX_TRANSLATE_FACTOR, 0.0,
									Mat);
		break;
	    case 8:			/* Its translation along the Z axis. */
		GenMatTrans(0.0, 0.0, ChangeFactor * MAX_TRANSLATE_FACTOR,
									Mat);
		break;
	    case 9:			      /* Its scaling along all axes. */
		if (ChangeFactor > 0.0)		      /* Make it around 1... */
		     ChangeFactor = ChangeFactor * MAX_SCALE_FACTOR + 1.0;
		else ChangeFactor = 1.0 /
			(-ChangeFactor * MAX_SCALE_FACTOR + 1.0);
		GenMatScale(ChangeFactor, ChangeFactor, ChangeFactor, Mat);
		break;
	    case 10:				     /* Toggle depth cueing. */
		GlblDepthCue = !GlblDepthCue;
		InteractUpdateMenu(GlblDepthCue ? "Depth Cue" : "No Depth Cue",
				   10);
		break;
	    case 11:			    /* Generate GIF file from screen. */
		strcpy(GifFileName, GENERIC_GIF_FILE);
		if ((p = strchr(GifFileName, '#')) != NULL) {
		    *p = GifFileCount;
		    if (GifFileCount++ == '9') GifFileCount = '0';
		}
		if (DumpScreen(GifFileName, GraphDriver, GGGraphMode) != 0) {
		    /* Something went wrong - let the user know about it. */
		    GGTone(400, 300);
		    GGTone(100, 300);
		}
		else GGTone(1000, 100);
		UpdateView = FALSE;
		break;
	    case 12:			    /* Save view as PostScript file. */
		SavePostScript(FD, NumOfObj, Objects);
		UpdateView = FALSE;
		break;
	    case 13:			      /* Save transformation matrix. */
		SaveCurrentMat();
		UpdateView = FALSE;
		break;
	    case 14:			     /* Reset transformation matrix. */
		GEN_COPY(GlblViewMat,  OrigViewMat,  sizeof(MatrixType));
		GEN_COPY(GlblPerspMat, OrigPerspMat, sizeof(MatrixType));
		break;
	    case 15:
		return;						/* Its Quit. */
	    default:
		GGTone(1000, 100);			   /* Do some noise! */
		UpdateView = FALSE;
		break;
	}
	if (UpdateView) {
	    GGClearViewArea();			   /* Clear the view window. */

	    switch (GlblTransformMode) {/* Udpate the global viewing matrix. */
		case TRANS_SCREEN:
		    MultTwo4by4(GlblViewMat, GlblViewMat, Mat);
		    break;
		case TRANS_OBJECT:
		    MultTwo4by4(GlblViewMat, Mat, GlblViewMat);
		    break;
	    }

	    switch (GlblViewMode) {		 /* Update the current view. */
		case VIEW_ORTHOGRAPHIC:
		    GEN_COPY(CrntViewMat, GlblViewMat, sizeof(MatrixType));
		    break;
		case VIEW_PERSPECTIVE:
		    MultTwo4by4(CrntViewMat, GlblViewMat, GlblPerspMat);
		    break;
	    }

	    ViewGeomObjectList(FD, NumOfObj, Objects);	/* And display it... */
	}
    }
}

/*****************************************************************************
*  Routine to update entry Entry with a new string Str.			     *
*****************************************************************************/
static void InteractUpdateMenu(char *Str, int Entry)
{
    struct textsettingstype oldtext;
    struct viewporttype	view;

    gettextsettings(&oldtext);
    settextjustify(CENTER_TEXT, CENTER_TEXT);	   /* Draw strings centered. */

    getviewsettings(&view);
    GGViewPortMenuArea();

    GGMySetColor(BLACK);			    /* Erase the old string. */
    GGXYPutStr(InteractMenu.SubWindows[Entry].X,
	       InteractMenu.SubWindows[Entry].Y,
	       InteractMenu.SubWindows[Entry].Str);

    InteractMenu.SubWindows[Entry].Str = Str;	       /* Update to new one. */

    GGMySetColor(InteractMenu.SubWindows[Entry].Color); /* And draw the new. */
    GGXYPutStr(InteractMenu.SubWindows[Entry].X,
	       InteractMenu.SubWindows[Entry].Y,
	       InteractMenu.SubWindows[Entry].Str);

    settextjustify(oldtext.horiz, oldtext.vert);
    setviewport(view.left, view.top, view.right, view.bottom, view.clip);
}

/*****************************************************************************
* Routine to draw NumOfObjects given in	Objects	from FileDescription FD	     *
* according to view matrix Mat.	If NumOfObjects	== 0 then all the objects    *
* are drawn. If NumEdges != 0 only the first NumEdges edges in each polygon  *
* are drawn. If InterFlag then INTERNAL edges (created by IRIT solid	     *
* modeller) are also drawn.						     *
*****************************************************************************/
static void ViewGeomObjectList(FileDescription **FD, int NumOfObjects,
								char **Objects)
{
    int	i;
    ObjectStruct *PObject;
    struct linesettingstype savetype;

    getlinesettings(&savetype);		      /* Save original line setting. */

    GGMySetColor(RED);			        /* Make default color - RED. */

    if (setjmp(LongJumpBuffer) == 0)	      /* Its the setjmp itself call! */
	if (NumOfObjects > 0)	   /* There was something on command line... */
	    for (i=0; i<NumOfObjects; i++) {
		if ((PObject = SearchObject(FD, *Objects)) ==
							(ObjectStruct *) NULL)
		    printf("Given Object %s not found in data files\n",
								    *Objects);
		else ViewOneObject(PObject);
		Objects++;
	    }
	else {
	    /* Draw all objects not in other object by scanning object trees.*/
	    DrawAllObjects(FD);	    /* and drawing ones with Reference == 0. */
	}

    setlinestyle(savetype.linestyle, savetype.upattern, savetype.thickness);
}

/*****************************************************************************
* Routine to save the current view trans. GlblViewMat to a generic mat file  *
*****************************************************************************/
static void SaveCurrentMat(void)
{
    int	i, j;
    FILE *f;
    char *p, FileName[FILE_NAME_LEN];
    static char FileCount = '0';

    strcpy(FileName, GENERIC_MAT_FILE);
    if ((p = strchr(FileName, '#')) != NULL) {
	*p = FileCount;
	if (FileCount++ == '9') FileCount = '0';
    }
    if ((f = fopen(FileName, "wt")) == NULL) {
	GGTone(700, 200);
	return;
    }

    for	(i=0; i<4; i++)	{
	for (j=0; j<4; j++) fprintf(f, "%12lf ", GlblViewMat[i][j]);
	fprintf(f, "\n");
    }

    if (GlblViewMode == VIEW_PERSPECTIVE) {
	fprintf(f, "\n");
	for (i=0; i<4; i++) {
	    for (j=0; j<4; j++) fprintf(f, "%12lf ", GlblPerspMat[i][j]);
	    fprintf(f, "\n");
	}
    }

    fclose(f);
}

/*****************************************************************************
* Routine to search for	an object in the File descriptions FD. Note that if  *
* an object exists more	than once only the first will be returned. If none   *
* is found then	NULL is	returned, else a pointer to that object	struct.	     *
*****************************************************************************/
static ObjectStruct *SearchObject(FileDescription **FD, char *Object)
{
     BinTree *PBinTree;

     while (*FD) {
	  if ((PBinTree	= GetBinTree(Object, (*FD++) -> ObjectPointer)) !=
						 (BinTree *) NULL)
	  return PBinTree -> Data.PObject;
     }
     return (ObjectStruct *) NULL;
}

/*****************************************************************************
* Routine to draw all the objects not in other objects...		     *
* by scanning all the object trees and drawing the objects with	Reference==0 *
* meaning nobody referred to them yet...				     *
*****************************************************************************/
static void DrawAllObjects(FileDescription **FD)
{
    while (*FD)	VisitObjectTree((*FD++) -> ObjectPointer);
}

/*****************************************************************************
* Routine to draw all the objects not in other objects...		     *
* by scanning all the object in	tree PBinTree and drawing the objects with   *
* Reference==0,	meaning	nobody referred	to them	yet...			     *
*****************************************************************************/
static void VisitObjectTree(BinTree *PBinTree)
{
    if (PBinTree == (BinTree *)	NULL) return;

    VisitObjectTree(PBinTree -> right);

    if (MoreFlag == 1)
	printf("Drawing object %s\n", PBinTree -> Name);
    ViewOneObject(PBinTree -> Data.PObject);

    VisitObjectTree(PBinTree -> left);
}

/*****************************************************************************
* Routine to draw one object Object, using the Matrix transform	Mat.	     *
*****************************************************************************/
static void ViewOneObject(ObjectStruct *PObject)
{
    PolygonStruct *PList = PObject -> PPolygon;

    SetDrawColor(PObject -> Color);	        /* Default color for object. */

    while (PList) {
	ViewOnePolygon(PList);
	PList =	PList -> Pnext;
    }
}

/*****************************************************************************
* Routine to draw one polygon, using the Matrix	transform Mat.		     *
* Note this is the routine that	makes the real drawing...		     *
*****************************************************************************/
static void ViewOnePolygon(PolygonStruct *PPolygon)
{
    int	i, Count, DrawNextEdge, NumOfVertices;
    float MappedNormal[3], PolyNormal[3];
    VertexStruct *PList = PPolygon -> PVertex;

    TestQuitView();

    Count = NumEdges;

    if (PList == NULL) return;

    switch (PPolygon -> PolyType) {
	case POINTLIST:
	    while (PList) {
		MyMoveTo(PList -> Coord);
		MyDrawTo(PList -> Coord);
		PList = PList -> Pnext;
	    }
	    break;
	case POLYLINE:
	    MyMoveTo(PList -> Coord);
	    DrawNextEdge = !PList -> Internal;
	    PList = PList -> Pnext;

	    while (PList) {
		if (DrawNextEdge || InterFlag)
		    MyDrawTo(PList -> Coord);
		else MyMoveTo(PList -> Coord);

		if (DrawVNormalsFlag && PList -> HasNormal) {
		    for (i=0; i<3; i++) MappedNormal[i] =
					PList -> Coord[i] + PList -> Normal[i];
		    MyDrawTo(MappedNormal);
		    MyMoveTo(PList -> Coord);
		}
		DrawNextEdge = !PList -> Internal;
		PList = PList -> Pnext;
	    }
	    break;
	case POLYGON:
	    if (DrawPNormalsFlag && PPolygon->HasPlane)
	    {
		for (i=0; i<3; i++) PolyNormal[i] = PList -> Coord[i];
		NumOfVertices = 1;
	    }

	    MyMoveTo(PList -> Coord);
	    DrawNextEdge = !PList -> Internal;
	    PList = PList -> Pnext;

	    while (PList) {
		if (DrawNextEdge || InterFlag)
		    MyDrawTo(PList -> Coord);
		else MyMoveTo(PList -> Coord);

		if (DrawVNormalsFlag && PList -> HasNormal) {
		    for (i=0; i<3; i++) MappedNormal[i] =
					PList -> Coord[i] + PList -> Normal[i];
		    i = ClosedObject;
		    ClosedObject = FALSE;
		    MyDrawTo(MappedNormal);
		    MyMoveTo(PList -> Coord);
		    ClosedObject = i;
		}

		if (DrawPNormalsFlag && PPolygon->HasPlane)
		{
		    for (i=0; i<3; i++) PolyNormal[i] += PList -> Coord[i];
		    NumOfVertices++;
		}
		/* If -e option specified #Edges to draw. */
		if (!(--Count)) return;
		DrawNextEdge = !PList -> Internal;
		PList = PList -> Pnext;
	    }

	    /* Close polygon by drawing a line to first vertex. */
	    if (DrawNextEdge || InterFlag)
		MyDrawTo(PPolygon -> PVertex -> Coord);

	    if (DrawPNormalsFlag && PPolygon->HasPlane)
	    {
		for (i=0; i<3; i++) PolyNormal[i] /= NumOfVertices;
		MyMoveTo(PolyNormal);
		for (i=0; i<3; i++) PolyNormal[i] += PPolygon->Plane[i];
		i = ClosedObject;
		ClosedObject = FALSE;
		MyDrawTo(PolyNormal);
		ClosedObject = i;
	    }
	    break;
    }
}

static double LastCoord[3];    /* Used to store last point we moved/draw to. */

/*****************************************************************************
* Routine to mave to 3D	point given as Coord[3], using Mat transform.	     *
*****************************************************************************/
static void MyMoveTo(float Coord[3])
{
    MultVecby4by4(LastCoord, Coord, CrntViewMat);   /* Set last point coord. */
}

/*****************************************************************************
* Routine to draw to 3D	point given as Coord[3], using Mat transform, from   *
* the last point we moved to.						     *
*****************************************************************************/
static void MyDrawTo(float Coord[3])
{
    double NewCoord[3], MiddleCoord[3], t;

    MultVecby4by4(NewCoord, Coord, CrntViewMat);    /* Set last point coord. */
    if (ClosedObject && NewCoord[2] < LastCoord[2]) {
	GEN_COPY(LastCoord, NewCoord, 3 * sizeof(double));
	return;
    }

    /* Implementation of simple depth cue - if line is >Z or <Z ... */
    if (LastCoord[2] <= 0.0 && NewCoord[2] <= 0.0) {
	setlinestyle(MY_DOTTED_LINE, 0, NORM_WIDTH);
	GGMyMove(LastCoord[0], LastCoord[1]);
	GGMyDraw(NewCoord[0], NewCoord[1]);			    /* DRAW! */
    }
    else
    if (LastCoord[2] >= 0.0 && NewCoord[2] >= 0.0 ||
	ABS(LastCoord[2] - NewCoord[2]) < EPSILON) {
	setlinestyle(SOLID_LINE, 0, NORM_WIDTH);
	GGMyMove(LastCoord[0], LastCoord[1]);
	GGMyDraw(NewCoord[0], NewCoord[1]);			    /* DRAW! */
    }
    else {				      /* Line intersect Z = 0 plane. */
	t = LastCoord[2] / (LastCoord[2] - NewCoord[2]);
	MiddleCoord[0] = LastCoord[0] * (1.0 - t) + NewCoord[0] * t;
	MiddleCoord[1] = LastCoord[1] * (1.0 - t) + NewCoord[1] * t;
	setlinestyle(SOLID_LINE, 0, NORM_WIDTH);	/* Draw the >Z part: */
	if (LastCoord[2] > 0.0) {
	    GGMyMove(LastCoord[0], LastCoord[1]);
	    GGMyDraw(MiddleCoord[0], MiddleCoord[1]);		    /* DRAW! */
	}
	else {
	    GGMyMove(MiddleCoord[0], MiddleCoord[1]);
	    GGMyDraw(NewCoord[0], NewCoord[1]);			    /* DRAW! */
	}
	setlinestyle(MY_DOTTED_LINE, 0, NORM_WIDTH);	/* Draw the <Z part: */
	if (LastCoord[2] < 0.0) {
	    GGMyMove(LastCoord[0], LastCoord[1]);
	    GGMyDraw(MiddleCoord[0], MiddleCoord[1]);		    /* DRAW! */
	}
	else {
	    GGMyMove(MiddleCoord[0], MiddleCoord[1]);
	    GGMyDraw(NewCoord[0], NewCoord[1]);			    /* DRAW! */
	}
    }

    GEN_COPY(LastCoord, NewCoord, 3 * sizeof(double)); /* Set current point. */
}

/*****************************************************************************
* Routine to set current drawing color to given	RGB values:		     *
* Currently only 8 colors are supported:				     *
*****************************************************************************/
static void SetDrawColor(int Color)
{
    GGMySetColor(Color);
}

/*****************************************************************************
*  Routine to test if quit display event - occured - SPACE was hit on	     *
* keyboard or right button was clicked on mouse.			     *
*****************************************************************************/
static void TestQuitView(void)
{
    int x, y, Buttons;

    if (kbhit() && getch() == ' ')
	longjmp(LongJumpBuffer, 1);			       /* Jump to... */

    if (MouseExists && MouseQueryBuffer()) {
	MouseGetBuffer(&x, &y, &Buttons);
	if (Buttons & 0x02) longjmp(LongJumpBuffer, 1);	       /* Jump to... */
    }
}
