#include <exec/libraries.h>
#include <exec/io.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <devices/console.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <graphics/display.h>
#include <graphics/gfxbase.h>
#include <libraries/asl.h>

#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/asl_protos.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#include <limits.h>

/* Note - using two character <CSI> ESC[.  Hex 9B could be used instead */
#define RESETCON  "\033c"
#define CURSOFF   "\033[0 p"
#define CURSON    "\033[ p"
#define DELCHAR   "\033[P"
#define BACKSPACE "\x08"
#define LINEFEED  "\x0A"

/* SGR (select graphic rendition) */
// \033 is octal for hex 1B wich is ESC
#define COLOR00   "\033[30m"
#define COLOR01   "\033[31m"
#define COLOR02   "\033[32m"
#define COLOR03   "\033[33m"
#define COLOR04   "\033[34m"
#define COLOR05   "\033[35m"
#define COLOR06   "\033[36m"
#define COLOR07   "\033[37m"

#define ITALICS   "\033[3m"
#define BOLD      "\033[1m"
#define UNDERLINE "\033[4m"
#define NORMAL    "\033[0m"

char CursorToColumn0[]    ="\x9B\x3B\x30\x48";
char DeleteLine[]         ="\x9B\x4D";
char CursorForward[]      ="\x9B\x43";
char CursorBackward[]     ="\x9B\x44";
char InsertCharacter[]    ="\x9B\x40";
char CursorNextLine[]     ="\x9B\x45";
char EraseToEndOfLine[]   ="\x9B\x4B";
char CursorHome[]         ="\x9B\x48";
char EraseToEndOfDisplay[]="\x9B\x4A";
char ShiftArrowRight[]    ="\x9B\x20\x40";
char ShiftArrowLeft[]     ="\x9B\x20\x41";
char ShiftArrowUp[]       ="\x9B\x54";
char ShiftArrowDown[]     ="\x9B\x53";
char ArrowUp[]            ="\x9B\x41";
char ArrowDown[]          ="\x9B\x42";
char F1[]                 ="\x9B\x30\x7E";
char F2[]                 ="\x9B\x31\x7E";
char F3[]                 ="\x9B\x32\x7E";
char F4[]                 ="\x9B\x33\x7E";
char F5[]                 ="\x9B\x34\x7E";
char F6[]                 ="\x9B\x35\x7E";
char F7[]                 ="\x9B\x36\x7E";
char F8[]                 ="\x9B\x37\x7E";
char F9[]                 ="\x9B\x38\x7E";
char F10[]                ="\x9B\x39\x7E";
char HELP[]               ="\x9B\x3F\x7E";

#define CLR1   0x000
#define CLR2   0x444  
#define CLR3   0x555
#define CLR4   0x666
#define CLR5   0x777
#define CLR6   0x888
#define CLR7   0x999
#define CLR8   0xaaa
#define CLR9   0xbbb
#define CLR10  0xccc
#define CLR11  0xddd
#define CLR12  0x848
#define CLR13  0x00f
#define CLR14  0xf00
#define CLR15  0x0ff
#define CLR16  0xfff

#define CL1   0xaaa  //background
#define CL2   0x000  //textcolor
#define CL3   0xfff  //window color2
#define CL4   0x9be  //screen & window color1
#define CL5   0x848  //screen color 2
#define CL6   0x00f  //info text
#define CL7   0xf00  //error text
#define CL8   0x0ff  //cursorcolor

static UWORD colortable[] = { CLR1, CLR2, CLR3, CLR4, CLR5, CLR6, CLR7, CLR8,
                               CLR9, CLR10, CLR11, CLR12, CLR13, CLR14, CLR15, CLR16 };
static UWORD colortable2[] = { CL1, CL2, CL3, CL4, CL5, CL6, CL7, CL8 };

#define MAXSEQ 10
#define MAXCMD 80
#define MAXHIS 20
#define MAXLAYERNAME 32

#define DEPTH 3      /* Screen depth (number of bitplanes)                */
#define WIDTH 640
#define COLORS 8
#define SCR1_HEIGHT 362 // both heights together must equal 512 or 400(NTSC)
#define SCR2_HEIGHT 150 // due to dual viewports
#define SCR1_DEPTH 4
#define SCR1_COLORS 16

//requester
#define MYLEFTEDGE 320
#define MYTOPEDGE  362 // equal to SCR1_HEIGHT
#define MYWIDTH    320
#define MYHEIGHT   150 // equal to SCR2_HEIGHT

#define maxcharinline 80 //record length
char windowname[]="Objector V1.1 PAL version - by Toine Beunes - Januari '96";

UBYTE *vers = "$VER: filereq 37.0";

/* our functions */
#define Allocmem(type, n) (type *)malloc((long)n * sizeof(type))
#define Allocmem1(type) (type *)malloc(sizeof(type))
int InitDBuff(struct GfxBase *GfxBase, struct Screen *screen, int depth);
int SwapBitmap(void);
FreeDBuff(struct Screen *screen, int depth, struct RastPort  *rport1, struct RastPort  *rport2);
make_screen(SHORT y, SHORT w, SHORT h, SHORT d, UBYTE colour0, 
            UBYTE colour1, USHORT mode, USHORT type, UBYTE *name);
make_window(SHORT x, SHORT y, SHORT w, SHORT h, UBYTE *name, ULONG flags, ULONG iflags, 
            UBYTE colour0, UBYTE colour1, struct Screen *screen, struct Gadget *gadg);
DrawFigure(struct RastPort *rp, int n, int p);
void cleanexit(UBYTE *,LONG);
void cleanup(void);
BYTE OpenConsole(struct IOStdReq *,struct IOStdReq *, struct Window *);
void CloseConsole(struct IOStdReq *);
void QueueRead(struct IOStdReq *, UBYTE *);
UBYTE ConGetChar(struct MsgPort *, UBYTE *);
LONG ConMayGetChar(struct MsgPort *, UBYTE *);
void ConPuts(struct IOStdReq *, UBYTE *);
void ConWrite(struct IOStdReq *, UBYTE *, LONG);
void ConPutChar(struct IOStdReq *, UBYTE);
void main(int argc, char **argv);
int ReadKeyboard(void);
int GetFileName(char *FileName);
int getline(char *line, int max);
double Determinant(struct Point *P1, struct Point *P2, struct Point *P3);
struct Vector DirectionEdges(struct Point *P1, struct Point *P2);
struct Vector NormaalFaces(struct Point *P1, struct Point *P2, struct Point *P3);
double CosAngle(struct Vector *V1, struct Vector *V2);
int ConvertTTDDD(void);
int LoadTTDDD(char *FileName);
int LoadVS(char *FileName);
int CreateNewFace(void);
int SaveVS(char *FileName);
int SaveDXF(char *FileName);
int FreeNewFace(void);
int FreeLoadedObject(void);
void errmsg(char *errormessage);
void infomsg(char *errormessage);
void normalmsg(char *errormessage);
int Outline3DObject(void);
int Wire3DObject(void);
int Solid3DObject(void);
#define Freemem(p) free(p)
#define huge /* Empty */
#define Nscreen 10

void coeff(double rho, double theta, double phi);
void eyecoord(struct vec3, float *x, float *y, float *z);
double dotproduct(struct vec3, struct vec3);
int triangul(int *pol, int n, struct trianrs *nrs,
             int orienta(int P, int Q, int R));
void set_color(int color);
int XLarge(double xs);
int YLarge(double ys);
int ZLarge(double ze);
double xScreen(int X);
double yScreen(int Y);
double zEye(int Z);   
int min2(int i, int j);
int min3(int i, int j, int k);
void myqsort(struct myTable *a, int n);
int Orientation(int u1, int u2, int v1, int v2);
int orienta(int Pnr, int Qnr, int Rnr);
void complete_infobase(int facenr, int anr, int bnr, int cnr);
void findcolorrange(int i);
int to_pix(unsigned X);
void settruecolor(int i);
void fill_window(int color, int Xmin, int Xmax, int Ymin, int Ymax);
int Display3DObject(void);
void putpix(int X, int Y);                      
void horline(int xleft, int xright, int y);     
void draw_line(int X1, int Y1, int X2, int Y2); 
void fill(int *X, int *Y, int n);
void Drawline(int xP, int yP, int xQ, int yQ);
void putpixel(int X, int Y, int activecolor);
int DisplayObject(void);
int DisplaySignature(void);

UWORD areabuffer[250];
struct AreaInfo areainfo;
PLANEPTR rpAlloc;
struct TmpRas tmpras;
struct RasInfo *DB_rinfo = NULL;     /* Intuition supplied rasinfo     */
struct BitMap  *DB_bmap1 = NULL,     /* Intuition supplied bitmap      */
               *DB_bmap2 = NULL;     /* Second bitmap                  */
struct RastPort *DB_rport     = NULL;  /* rastport     */
struct ViewPort *vport     = NULL;  /* Intuition Viewport              */
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;
struct IOStdReq *writeReq = NULL;    /* I/O request block pointer */
struct MsgPort  *writePort = NULL;   /* replyport for writes      */
struct IOStdReq *readReq = NULL;     /* I/O request block pointer */
struct MsgPort  *readPort = NULL;    /* replyport for reads       */
struct Screen *screen = NULL;        /* Intuition screen          */
struct Screen *screen2 = NULL;
struct Window *window = NULL;        /* Intuition window          */
struct Window *textwindow = NULL;    /* Intuition window          */
struct View *DB_view  = NULL;        /* Intuition View            */
struct NewScreen NewScreen;
struct NewWindow NewWindow;
struct Library *AslBase = NULL;
struct TagItem frtags[] =
{
    ASL_Hail,       (ULONG)"file requester",
    ASL_Height,     MYHEIGHT,
    ASL_Width,      MYWIDTH,
    ASL_LeftEdge,   MYLEFTEDGE,
    ASL_TopEdge,    MYTOPEDGE,
    ASL_OKText,     (ULONG)"Cont",
    ASL_CancelText, (ULONG)"Cancel",
    ASL_Dir,        (ULONG)"RAM:",
    TAG_DONE
};

struct Point {
int used;
int new;
double x;
double y;
double z;
struct Object *layer;
} *Pnt;

struct Vector {
double x;
double y;
double z;
int deter;
} *EdgVec,*FacVec;

struct Edge {
int used;
struct Vector *vector;
struct Point *p1;
struct Point *p2;
struct Edge *newedge;
} *Edg, *VSEdg;

struct Face {
int used;
struct Edge *edge;
struct Face *next;
} *Fac;

struct VSFace {
int pntcnt;
struct Point *p;
struct VSFace *next;
} *VSFac;

struct Object {
char layer[MAXLAYERNAME];
int number;
int points;
int edges;
int faces;
double xmax;
double xmin;
double ymax;
double ymin;
double zmax;
double zmin;
} *Obj;

// faces and points to create
struct myFace {
struct myPoint *p;
struct myFace *nxt;
} *myFac;

struct myInfo {
int pntcnt;
struct Object *layer;
int backface;
struct Vector normal;
float h; 
int Z;
int color;
} *myInf;

struct myTable {
int Z;
int myfacnr;
} *myTab;

struct myPoint {
int new;
double x;
double y;
double z;
} *myPnt;

struct vec3
{  float x;
   float y;
   float z;};

struct vec {
float x;
float y;};

struct trianrs {
int A, B, C;};

struct vec_int {
int X;
int Y;};

struct vertex{
int X;
int Y;
int Z;
int used;
} huge*V;

struct triadata {
struct vec3 normal;
float h; 
int color;};

struct tria {
int Anr; 
int Bnr; 
int Cnr; 
int Z; 
struct triadata huge*ptria;
} huge*triangles, huge*ptriangle;

//console
BOOL OpenedConsole = FALSE;
BOOL FromWb;
UBYTE *displaymem = NULL;     /*  Pointer for writing to BitMap memory.  */
struct IntuiMessage *winmsg;
ULONG signals, conreadsig, windowsig;
LONG lch;
SHORT InControl = 0;
BOOL Done = FALSE;
UBYTE ch, ibuf;
UBYTE obuf[MAXCMD];

char message[MAXCMD];
char inp[MAXCMD];
char his[MAXHIS][MAXCMD];
int  showhis=-1, currenthis=-1, aanthis=-1;
char Sequence[MAXSEQ];
int  seqposition,seq=1;
int  position,lastposition,cmd=1;

//exit
BYTE error;
int exitval = 0;
int problem = 0;
int j;

//GetFileName
char TTDDDFile[MAXCMD];
char VideoFile[MAXCMD];
char DXFFile[MAXCMD];
char ObjectName[MAXCMD]={"DEFAULT"};
struct FileRequester *fr;

//Demo protection
// activated if DEMO is set to 1
int DEMO=0; //1=aan 0=uit
// search for help1 & DemoPoints for help*DemoPoints = maxnumber of objects
int DemoPoints = 50; //multiplied by 2 (help1)
char DemoMessage[MAXCMD]={"ShareWare protection! maximum number off objectpoints 100"};
int help1=2;

//convertTDDD
int  PCount, ECount, TCount, OCount;
FILE *fp;
char *pointer, *pointer2, *pointer3;
int  PntCount = 0;
int  EdgCount = 0;
int  FacCount = 0;
int  PntCount2 = 0;
int  EdgCount2 = 0;
int  FacCount2 = 0;
int  ObjCount = 0;
int  newTotCount = 0; //allocated myfaces (necessary for free)
int  newFacCount = 0; //number of faces left after merging
int  newPntCount = 0; //number of points left after merging (faces, edges, points)
int  newEdgCount = 0; //number of edges not used in faces, left after merging
int  newPuntCount = 0; //number of points not used in faces and edges, left after merging

double  X, Y, Z;
char line[maxcharinline];
char CharECount[] = {"ECount"};
char CharTCount[] = {"TCount"}; 
char CharPCount[] = {"PCount"};
char CharPoint[]  = {"Point"};
char CharEdge[]   = {"Edge"};
char CharConnect[]= {"Connect"};
char CharStarter[]= {"T3DLIB"};
char CharStartVS[]= {"3DG1"};
char CharNAME[]   = {"NAME"};
char CharSpace[]  = {" "};
char CharIs[]     = {"="};
char CharPnt[]     = {"."};

struct Point P1,P2,P3,Q1,Q2,Q3;
struct Point *pP1,*pP2,*pQ1,*pQ2,*pC1;

int c,cc, A,B,C,D ,bit1,bit2,bit3;
double angle;
double deter;
struct Face *pface,*p2face,*p3face,*p4face, *p5face, *p6face, *p7face, *p8face;
struct Face *target;

struct Edge *pedge1, *pedge2, *pedge3, *pedge4, *pedge5, *pedge6;
struct myFace *pmy1, *pmy2, *pmy3, *pmy4;
struct VSFace *vsface1, *vsface2, *vsface3, *vsface4;
//DOUBLEFACE = 1 -> normal operation
//DOUBLEFACE = 2 -> if every face has to have a backface created
int DOUBLEFACE=2;
//faceangle = negative -> no merging
//faceangle = positive -> angle wich determines if two faces are to be merged to one
double faceangle=0.3; 
//edgeangle = negative -> no merging
//edgeangle = positive -> angle wich determines if two faces are to be merged to one
double edgeangle=0.3; 

//hideface
const int LARGE=32000; // Not greater than sqrt(LONG_MAX)
const double PIdiv180 = 0.0174532;
const double BIG = 1.0E30;
double f, zfactor, xsmin, xsmax, ysmin, ysmax, zemin, zemax;
double rcolormin = 1.0E30, rcolormax = -1.0E30, delta;
double xsC, ysC, XLCreal, YLCreal;
struct vec3 huge*vt, lightvector;
int k, hk, XLC, YLC, vertexcount;

//graphics defaults
float	x_center, y_center, r_max, x_min=0, x_max, y_min=0, y_max;
int     X__max, Y__max, ncolors;
float   density;
float   xmin, xmax, ymin, ymax; 
double  rho = 0, theta = 0, phi = 0, realdummy;
int activecolor=1, deltamove=5, displaylines=2;

//commands
char LOADT3D[]   ="LOADT3D";
char LOADVS[]    ="LOADVS";
char MERGE[]     ="MERGE";
char SAVEVS[]    ="SAVEVS";
char SAVEDXF[]   ="SAVEDXF";
char QUIT[]      ="QUIT";
char HULP[]      ="HELP";
char ZERO[]      ="ZERO";
char THETA[]     ="THETA";
char PHI[]       ="PHI";
char FACEANGLE[] ="FACEANGLE";
char EDGEANGLE[] ="EDGEANGLE";
char BACKFACE[]  ="BACKFACE";
char MAXEDGES[]  ="MAXEDGES";
char On[]        ="ON";
char Off[]       ="OFF";
char ABOUT[]     ="ABOUT";

int loadttddd=-1;
int MaxEdges=-1;
int DXFpoints=0, DXFlines=0, DXFlonelines=0, DXFlonepoints, DXFfaces=0;
int VSpoints=0, VSlines=0, VSfaces=0;
int MERGEfaces=0, MERGEinline=0, MERGElines=0, MERGElone=0;

void main(argc, argv)
int argc;
char **argv;
    {
int k=0;
int PntCnt[10];

   FromWb = (argc==0L) ? TRUE : FALSE;

   if(!(IntuitionBase = (struct IntuitionBase *)
                        OpenLibrary("intuition.library", 0)))
   {
      printf("NO INTUITION LIBRARY\n");
      exitval = 1;
      goto EXITING;
   }

   if(!(GfxBase = (struct GfxBase *)
                  OpenLibrary("graphics.library", 0)))
   {
      printf("NO GRAPHICS LIBRARY\n");
      exitval = 2;
      goto EXITING;
   }
   /*** Allocate bitmap & memory for bitplanes ***/
   if(!(DB_bmap1=(struct BitMap *)AllocMem(sizeof(struct BitMap), MEMF_PUBLIC|MEMF_CLEAR))){
      printf("alloc bitmap failed\n");
      problem  = 1;
      goto EXITING;
   }
   InitBitMap(DB_bmap1, SCR1_DEPTH, WIDTH, SCR1_HEIGHT);

   /* We'll use depth planes. */
   for(j=0; j<SCR1_DEPTH; j++)
   {
      if (!(DB_bmap1->Planes[j] =
         (PLANEPTR) AllocRaster(WIDTH, SCR1_HEIGHT)))
      {
         printf("alloc raster failed\n");
         problem  = 1;
         goto EXITING;
      }
   }

   if(!(DB_bmap2=(struct BitMap *)
                 AllocMem(sizeof(struct BitMap), MEMF_PUBLIC|MEMF_CLEAR)))
   {
      printf("alloc bitmap failed\n");
      problem  = 1;
      goto EXITING;
   }
   InitBitMap(DB_bmap2, SCR1_DEPTH, WIDTH, SCR1_HEIGHT);

   for(j=0; j<SCR1_DEPTH; j++)
   {
      if (!(DB_bmap2->Planes[j] =
         (PLANEPTR) AllocRaster(WIDTH, SCR1_HEIGHT)))
      {
         printf("alloc raster failed\n");
         problem  = 1;
         goto EXITING;
      }
   }

   NewScreen.LeftEdge      = 0;
   NewScreen.TopEdge       = 0;
   NewScreen.Width         = WIDTH;
   NewScreen.Height        = SCR1_HEIGHT;
   NewScreen.Depth         = SCR1_DEPTH;
   NewScreen.DetailPen     = 0;
   NewScreen.BlockPen      = 1;
   NewScreen.ViewModes     = HIRES | LACE;
   NewScreen.Type          = CUSTOMSCREEN | CUSTOMBITMAP | SCREENQUIET;
   NewScreen.Font          = NULL;
   NewScreen.DefaultTitle  = "DisplayInfo";
   NewScreen.Gadgets       = NULL;
   NewScreen.CustomBitMap  = DB_bmap1;

   if(!(screen = OpenScreen(&NewScreen))){
      printf("Can't open double buffered screen\n");
      exitval = 1;
      goto EXITING;
   }

   //prepare rastport for filling operations
   rpAlloc=AllocRaster(WIDTH, SCR1_HEIGHT);
   if (rpAlloc==NULL){
      printf("no memory for temporary raster allocation\n");
      exitval = 1;
      goto EXITING;
   }
   screen->RastPort.TmpRas=InitTmpRas(&tmpras,rpAlloc,RASSIZE(WIDTH,SCR1_HEIGHT));

   InitArea(&areainfo, &areabuffer[0], 100);
   screen->RastPort.AreaInfo=&areainfo;
    
   /* Indicate that the rastport is double buffered. */
//   screen->RastPort.Flags = DBUFFER;

   LoadRGB4(&(screen->ViewPort), colortable, SCR1_COLORS);  /* Change colors to those in colortable. */

   /* Get the bitmap, rastport, viewport, view and rasinfo supplied by 
      Intuition from the window, screen and graphics base from the call.
   */
   DB_rport    = &(screen->RastPort);
   SetRast(DB_rport, 0);
   vport       = &(screen->ViewPort);
   DB_view     = GfxBase->ActiView;
   DB_rinfo    = (*vport).RasInfo;

   NewScreen.LeftEdge      = 0;
   NewScreen.TopEdge       = SCR1_HEIGHT;
   NewScreen.Width         = WIDTH;
   NewScreen.Height        = SCR2_HEIGHT;
   NewScreen.Depth         = DEPTH;
   NewScreen.DetailPen     = 3;
   NewScreen.BlockPen      = 4;
   NewScreen.ViewModes     = HIRES|LACE;
   NewScreen.Type          = CUSTOMSCREEN;
   NewScreen.Font          = NULL;
   NewScreen.DefaultTitle  = "TextInfo";
   NewScreen.Gadgets       = NULL;
   NewScreen.CustomBitMap  = NULL;

   if(!(screen2 = OpenScreen(&NewScreen))){
      printf("Can't open screen\n");
      exitval = 1;
      goto EXITING;
   }
   LoadRGB4(&(screen2->ViewPort), colortable2, COLORS);  /* Change colors to those in colortable. */

   NewWindow.LeftEdge      = 0;
   NewWindow.TopEdge       = 0;
   NewWindow.Width         = WIDTH;
   NewWindow.Height        = SCR2_HEIGHT; 
   NewWindow.DetailPen     = 3;
   NewWindow.BlockPen      = 2;
   NewWindow.Title         = windowname;
   NewWindow.Flags         = WINDOWCLOSE|SMART_REFRESH|ACTIVATE;
   NewWindow.IDCMPFlags    = CLOSEWINDOW;
   NewWindow.Type          = CUSTOMSCREEN;
   NewWindow.FirstGadget   = NULL;
   NewWindow.CheckMark     = NULL;
   NewWindow.Screen        = screen2;
   NewWindow.BitMap        = NULL;
   NewWindow.MinWidth      = 0;
   NewWindow.MinHeight     = 0;
   NewWindow.MaxWidth      = 0;
   NewWindow.MaxHeight     = 0;

   if(!(textwindow = OpenWindow(&NewWindow))){
      printf("Can't open window\n");
      exitval = 1;
      goto EXITING;
   } 

    /* Create reply port and io block for writing to console */
    if(!(writePort = CreatePort("RKM.console.write",0)))
         cleanexit("Can't create write port\n",RETURN_FAIL);

    if(!(writeReq = (struct IOStdReq *)
                    CreateExtIO(writePort,(LONG)sizeof(struct IOStdReq))))
         cleanexit("Can't create write request\n",RETURN_FAIL);

    /* Create reply port and io block for reading from console */
    if(!(readPort = CreatePort("RKM.console.read",0)))
         cleanexit("Can't create read port\n",RETURN_FAIL);

    if(!(readReq = (struct IOStdReq *)
                   CreateExtIO(readPort,(LONG)sizeof(struct IOStdReq))))
         cleanexit("Can't create read request\n",RETURN_FAIL);

    /* Now, attach a console to the window */
    if(error = OpenConsole(writeReq,readReq,textwindow))
         cleanexit("Can't open console.device\n",RETURN_FAIL);
    else OpenedConsole = TRUE;

   DB_rinfo->BitMap  = DB_bmap2;
   DB_rport->BitMap = DB_bmap2;
   SetRast(DB_rport, 0);
   MakeScreen(screen);
   RethinkDisplay();

   DB_rinfo->BitMap  = DB_bmap1;
   DB_rport->BitMap = DB_bmap1;
    
   SetRast(DB_rport, 0);
   MakeScreen(screen);
   RethinkDisplay();
   
   SwapBitmap();

   QueueRead(readReq,&ibuf); /* send the first console read request */

   conreadsig = 1 << readPort->mp_SigBit;
   windowsig = 1 << textwindow->UserPort->mp_SigBit;

//-------------------------------------------------------------------------
// User code
//-------------------------------------------------------------------------
DisplaySignature();

readcommand:

ReadKeyboard();
if (Done) goto cleanitup;

if (cmd==1){
   strupr(inp);

   if (strncmp(LOADT3D,inp,strlen(LOADT3D))==NULL){

      if ((pointer = strstr(inp,CharSpace))!=NULL){
         for (j=0; j<MAXCMD; j++){TTDDDFile[j]='\0';}
         strncpy(TTDDDFile,++pointer,strlen(pointer));
         k=LoadTTDDD(TTDDDFile);
      } else k=1;

      if (k!=NULL && k!=7 && !GetFileName(TTDDDFile))k=LoadTTDDD(TTDDDFile);
 
      switch(k){
         case 0:if (displaylines>=0){
                   infomsg("Displaying");
                   theta=270; phi=270;
                   DisplayObject();
                }
                break;
         case 1:errmsg("can't open file"); break;
         case 2:errmsg("not a T3DLIB file"); 
                errmsg("TIP: use READWRITE command from T3DLIB.lha to convert imagine file to ascii format");
                break;
         case 3:errmsg("out of memory"); break;
         case 4:errmsg("Number of points more then specified"); break;
         case 5:errmsg("Number of edges more then specified"); break;
         case 6:errmsg("Number of connections more then specified"); break;
         case 7:errmsg("If you want to convert larger objects please register,");
                errmsg("and may I state again, the C-source will be included.");
                errmsg("greetings,  Toine Beunes."); 
       }

   } else if (strncmp(LOADVS,inp,strlen(LOADVS))==NULL){

      if ((pointer = strstr(inp,CharSpace))!=NULL){
         for (j=0; j<MAXCMD; j++){VideoFile[j]='\0';}
         strncpy(VideoFile,++pointer,strlen(pointer));
         k=LoadVS(VideoFile);
      } else k=1;

      if (k!=NULL && k!=7 && !GetFileName(VideoFile))k=LoadVS(VideoFile);
 
      switch(k){
         case 0:if (displaylines>=0){
                   infomsg("Displaying");
                   theta=270; phi=270;
                   DisplayObject();
                }
                break;
         case 1:errmsg("can't open file"); break;
         case 2:errmsg("not a VideoScape file"); break;
         case 3:errmsg("out of memory"); break;
         case 4:errmsg("number of points differs from number specified"); break;
         case 5:errmsg("Empty object"); break;
         case 7:errmsg("If you want to convert larger objects please register,");
                errmsg("and may I state again, the C-source will be included.");
                errmsg("greetings,  Toine Beunes."); 
       }

   } else if (strncmp(SAVEVS,inp,strlen(SAVEVS))==NULL && loadttddd==1){
      if ((pointer = strstr(inp,"-PO"))!=NULL)VSpoints=1;
      else VSpoints=-1;
      if ((pointer = strstr(inp,"-LO"))!=NULL)VSlines=1;
      else VSlines=-1;
      if ((pointer = strstr(inp,"-F"))!=NULL)VSfaces=1;
      else VSfaces=-1;

      if (VSpoints==-1 && VSlines==-1 && VSfaces==-1){
         errmsg("at least one of the flags [-f][-lo][-po] must be set");
         goto NotSaveVS;
      }

      pointer2=strstr(inp,CharSpace);
      if (pointer2!=NULL && (pointer = strstr(++pointer2,CharSpace))!=NULL){
         for (j=0; j<MAXCMD; j++){VideoFile[j]='\0';}
         strncpy(VideoFile,++pointer,strlen(pointer));
         k=SaveVS(VideoFile);
      } else k=1;

      if (k!=NULL && !GetFileName(VideoFile))k=SaveVS(VideoFile);

      switch(k){
         case 1:errmsg("can't create file"); break;
      }

NotSaveVS:;

   } else if (strncmp(SAVEDXF,inp,strlen(SAVEDXF))==NULL && loadttddd==1){

      if ((pointer = strstr(inp,"-P"))!=NULL)DXFpoints=1;
      else DXFpoints=-1;
      if ((pointer = strstr(inp,"-L"))!=NULL)DXFlines=1;
      else DXFlines=-1;
      if ((pointer = strstr(inp,"-F"))!=NULL)DXFfaces=1;
      else DXFfaces=-1;
      if ((pointer = strstr(inp,"-LO"))!=NULL){DXFlonelines=1; DXFlines=-1;}
      else DXFlonelines=-1;
      if ((pointer = strstr(inp,"-PO"))!=NULL){DXFlonepoints=1; DXFpoints=-1;}
      else DXFlonepoints=-1;

      if (DXFpoints==-1 && DXFlines==-1 && DXFfaces==-1 && DXFlonelines==-1 && DXFlonepoints==-1){
         errmsg("at least one of the flags [-f][-l][-p][-lo][-po] must be set");
         goto NotSaveDXF;
      }

      pointer2=strstr(inp,CharSpace);
      if (pointer2!=NULL && (pointer = strstr(++pointer2,CharSpace))!=NULL){
         for (j=0; j<MAXCMD; j++){DXFFile[j]='\0';}
         strncpy(DXFFile,++pointer,strlen(pointer));
         k=SaveDXF(DXFFile);
      } else k=1;

      if (k!=NULL && !GetFileName(DXFFile))k=SaveDXF(DXFFile);

      switch(k){
         case 1:errmsg("can't create file"); break;
         case 2:errmsg("Maximum number of edges in a face is 4 [use command MAXEDGES 4]"); break;
      }
NotSaveDXF:;
         
   } else if ((pointer = strstr(inp,THETA))!=NULL) {
      if ((pointer2 = strstr(pointer,CharSpace))!=NULL) {
         realdummy = atof(++pointer2);
         if (realdummy>=0 && realdummy <=360){
            theta=realdummy;
            DisplayObject();
         } else errmsg("degrees [0-360] allowed");
      } else errmsg("syntax: THETA <degrees>");     

   } else if ((pointer = strstr(inp,MAXEDGES))!=NULL) {
      if ((pointer2 = strstr(pointer,CharSpace))!=NULL) {
         c = atoi(++pointer2);
         if (c>0 && c<3) errmsg("number of edges [3->] allowed, negative is NO maximum");
         else MaxEdges=c;
      } else errmsg("syntax: MAXEDGES <number>");     
  
   } else if ((pointer = strstr(inp,FACEANGLE))!=NULL) {
      if ((pointer2 = strstr(pointer,CharSpace))!=NULL) {
         realdummy = atof(++pointer2);
         if (realdummy<1){
            faceangle=realdummy;
         } else errmsg("merge angle [0-1) allowed, negative value is no merging");
      } else errmsg("syntax: FACEANGLE <degrees>");     

   } else if ((pointer = strstr(inp,EDGEANGLE))!=NULL) {
      if ((pointer2 = strstr(pointer,CharSpace))!=NULL) {
         realdummy = atof(++pointer2);
         if (realdummy<1){
            edgeangle=realdummy;
         } else errmsg("merge angle [0-1) allowed, negative value is no merging");
      } else errmsg("syntax: EDGEANGLE <degrees>");     

   } else if (strncmp(MERGE,inp,strlen(MERGE))==NULL && loadttddd==1){

      if ((pointer = strstr(inp,"-F"))!=NULL)MERGEfaces=1;
      else MERGEfaces=-1;
      if ((pointer = strstr(inp,"-FL"))!=NULL)MERGEinline=1;
      else MERGEinline=-1;
      if ((pointer = strstr(inp,"-L"))!=NULL)MERGElines=1;
      else MERGElines=-1;
      if ((pointer = strstr(inp,"-O"))!=NULL)MERGElone=1;
      else MERGElone=-1;

      if (MERGEfaces==-1 && MERGElines==-1 && MERGElone==-1){
         errmsg("at least one of the flags [-f][-l][-o][-fl] must be set");
         goto NotMerge;
      }

      if (ConvertTTDDD())errmsg("out of memory");
      else {
          if (displaylines>=0){
             infomsg("Displaying");
             DisplayObject();
          }
      }

NotMerge:;

   } else if ((pointer = strstr(inp,PHI))!=NULL) {
      if ((pointer2 = strstr(pointer,CharSpace))!=NULL) {
         realdummy = atof(++pointer2);
         if (realdummy>=0 && realdummy <=360){
            phi=realdummy;
            DisplayObject();
         } else errmsg("degrees [0-360] allowed");
      } else errmsg("syntax: PHI <degrees>");      

   } else if ((pointer = strstr(inp,BACKFACE))!=NULL) {
      if ((pointer2 = strstr(pointer,CharSpace))!=NULL) { 
         if ((pointer = strstr(pointer2,On))!=NULL){
            DOUBLEFACE=2;
            FreeNewFace();
            CreateNewFace();
         } else if ((pointer = strstr(pointer2,Off))!=NULL) {
            DOUBLEFACE=1;
         } else errmsg("ON or OFF allowed");
      } else errmsg("syntax: BACKFACE ON|OFF");      

   } else if (strcmp(ZERO,inp)==NULL){
      theta=0;
      phi=0;
      DisplayObject();
   } else if (strcmp(ABOUT,inp)==NULL){
      DisplaySignature();
   
   } else if (strcmp(HULP,inp)==NULL){

      sprintf(obuf,"%s%s",CursorHome,EraseToEndOfDisplay);
      ConPuts(writeReq,obuf);
      ConPuts(writeReq,"F1 = display on/off  F2,F3,F4,F5 = solid-wire,solid,wire,box \n");
      ConPuts(writeReq,"F6,F7,F8 = statistics      F9,F10 = -,+ rotate angle\n");
      ConPuts(writeReq,"Shift UP/DOWN/LEFT/RIGHT = rotate object\n");
      ConPuts(writeReq,"PHI <angle> = hor. angle   THETA <angle> = vert. angle  ZERO = angles 0\n");
      ConPuts(writeReq,"LOADT3D [<TTDDD file>] = loads Imagine ASCII object file\n");
      ConPuts(writeReq,"LOADVS [<Videoscape file>] = loads VideoScape ASCII object file\n");
      ConPuts(writeReq,"MERGE [-f][-fl][-l][-o]= merges triangles\n");

      infomsg("\npress return to continue");
      ReadKeyboard();
      sprintf(obuf,"%s%s",CursorHome,EraseToEndOfDisplay);
      ConPuts(writeReq,obuf);

      ConPuts(writeReq,"FACEANGLE <degrees>    = angle wich determines the merging of polygons\n");
      ConPuts(writeReq,"EDGEANGLE <degrees>    = angle wich determines the merging of edges\n");
      ConPuts(writeReq,"BACKFACE ON|OFF        = creates a backface polygon for every polygon\n");
      ConPuts(writeReq,"MAXEDGES <number>      = max number of edges for a polygon\n");
      ConPuts(writeReq,"SAVEVS [-f][-lo][-po] [filename] = saves Videoscape ASCII object file\n");
      ConPuts(writeReq,"SAVEDXF [-f][-l][-p][-lo][-po] [filename] = saves Auto-CAD ASCII object file\n");
      ConPuts(writeReq,"ABOUT = program info  QUIT = leave program\n");

   } else if (strcmp(QUIT,inp)==NULL){
      goto cleanitup;
   } else ConPuts(writeReq,"\a");

   for (j=0; j<MAXCMD; j++) {inp[j]='\0';}
  
} else if (seq==1) {

  if (strncmp(&ShiftArrowLeft[1],Sequence,2)==NULL){
     theta = (theta-deltamove>0) ? theta-deltamove : 360+theta-deltamove;
     DisplayObject();
  } else if (strncmp(&ShiftArrowRight[1],Sequence,2)==NULL){
     theta = (theta+deltamove<360) ? theta+deltamove : theta+deltamove-360;
     DisplayObject();
  } else if (strncmp(&ShiftArrowUp[1],Sequence,1)==NULL){
     phi = (phi-deltamove>0) ? phi-deltamove : 360+phi-deltamove;
     DisplayObject();
  } else if (strncmp(&ShiftArrowDown[1],Sequence,1)==NULL){
     phi = (phi+deltamove<360) ? phi+deltamove : phi+deltamove-360;
     DisplayObject();
  } else if (strncmp(&ArrowUp[1],Sequence,1)==NULL){
  } else if (strncmp(&ArrowDown[1],Sequence,1)==NULL){
  } else if (strncmp(&F1[1],Sequence,2)==NULL){
     if (displaylines==-1) {
        displaylines=2;
        set_color(15);
        Move(DB_rport,WIDTH/2-16*10,SCR1_HEIGHT/2);
        Text(DB_rport,"D I S P L A Y  A C T I V A T E D",32);
        infomsg("Display activated");
      } else {   
        displaylines=-1;
        set_color(15);
        Move(DB_rport,WIDTH/2-18*10,SCR1_HEIGHT/2);
        Text(DB_rport,"D I S P L A Y  D E A C T I V A T E D",36);
        infomsg("Display deactivated");
      }   
      SwapBitmap();
      DisplayObject();
  } else if (strncmp(&F2[1],Sequence,2)==NULL){
         displaylines=0;
         infomsg("Displaymode SOLID-WIRE");
         DisplayObject();
  } else if (strncmp(&F3[1],Sequence,2)==NULL){
         displaylines=1;
         infomsg("Displaymode SOLID");
         DisplayObject();
  } else if (strncmp(&F4[1],Sequence,2)==NULL){
         displaylines=2;
         infomsg("Displaymode WIRE");
         DisplayObject();
  } else if (strncmp(&F5[1],Sequence,2)==NULL){
         displaylines=3;
         infomsg("Displaymode BOX");
         DisplayObject();
  } else if (strncmp(&F6[1],Sequence,2)==NULL){
      sprintf(obuf,"%s%s",CursorHome,EraseToEndOfDisplay);
      ConPuts(writeReq,obuf);
      cc=0;
      for (c=0;c<ObjCount;c++){
         cc++;
         if (cc>10){
            cc=0;
            infomsg("\npress return to continue");
            ReadKeyboard();
            sprintf(obuf,"%s%s",CursorHome,EraseToEndOfDisplay);
            ConPuts(writeReq,obuf);
         }

         sprintf(obuf,"Object:");
         ConPuts(writeReq,obuf);
         if (Obj[c].number%4 == 0) sprintf(obuf,"%s",COLOR04);
         if (Obj[c].number%4 == 1) sprintf(obuf,"%s",COLOR05);
         if (Obj[c].number%4 == 2) sprintf(obuf,"%s",COLOR06);
         if (Obj[c].number%4 == 3) sprintf(obuf,"%s",COLOR07);
         ConPuts(writeReq,obuf);
         sprintf(obuf," %20s",Obj[c].layer);
         ConPuts(writeReq,obuf);
         sprintf(obuf,"%s",COLOR01);
         ConPuts(writeReq,obuf);
         sprintf(message,"  points: %6d  edges: %6d  faces: %6d\n",
                 Obj[c].points,Obj[c].edges,Obj[c].faces);
         ConPuts(writeReq,message);
      }

  } else if (strncmp(&F7[1],Sequence,2)==NULL){
      PntCnt[0]=PntCnt[1]=PntCnt[2]=PntCnt[3]=PntCnt[4]=PntCnt[5]=PntCnt[6]=PntCnt[7]=PntCnt[8]=PntCnt[9]=0;
      for (c=0;c<FacCount;c++){
          if (Fac[c].used==1){
             cc=0;
             p3face=&Fac[c];
countfac:
             cc++;
             if (p3face->next!=NULL){
                p4face=p3face->next;
                p3face=p4face;
                goto countfac;
             }
             if (cc<=11)PntCnt[cc-3]++;
             else PntCnt[9]++;                  
         }
      }
      sprintf(obuf,"%s%s",CursorHome,EraseToEndOfDisplay);
      ConPuts(writeReq,obuf);
      normalmsg("                         Number of faces");
      for (c=0;c<9;c++){
          sprintf(message,"Number of points [%2d] :  %d",c+3,PntCnt[c]); normalmsg(message);
      }
      sprintf(message,"Number of points [%2d+]:  %d",c+3,PntCnt[9]);
      normalmsg(message);

 } else if (strncmp(&F8[1],Sequence,2)==NULL){
      sprintf(obuf,"%s%s",CursorHome,EraseToEndOfDisplay);
      ConPuts(writeReq,obuf);
      normalmsg("parameter values");
      normalmsg("----------------");
      sprintf(message,"MAXEDGES   : %d",MaxEdges); normalmsg(message);
      if (DOUBLEFACE==2)
      sprintf(message,"BACKFACE   : ON");
      else sprintf(message,"BACKFACE   : OFF");
      normalmsg(message);
      sprintf(message,"FACEANGLE  : %f degrees",faceangle); normalmsg(message);
      sprintf(message,"EDGEANGLE  : %f degrees",edgeangle); normalmsg(message);

  } else if (strncmp(&F9[1],Sequence,2)==NULL){
     deltamove = (deltamove>1) ? --deltamove : 360;
     sprintf(message,"angle for movement = %d",deltamove);
     infomsg(message);
  } else if (strncmp(&F10[1],Sequence,2)==NULL){
     deltamove = (deltamove<360) ? ++deltamove : 1;
     sprintf(message,"angle for movement = %d",deltamove);
     infomsg(message);
  } 

}

goto readcommand;

//-------------------------------------------------------------------------
// End User code
//-------------------------------------------------------------------------
cleanitup:

    if (loadttddd==1) {FreeLoadedObject(); FreeNewFace();}

    /* We always have an outstanding queued read request
     * so we must abort it if it hasn't completed,
     * and we must remove it.
     */
    if(!(CheckIO((struct IORequest *)readReq)))  AbortIO((struct IORequest *)readReq);
    WaitIO((struct IORequest *)readReq);     /* clear it from our replyport */

EXITING:

   cleanup();

   exit(exitval);
}

int DisplayObject(void){
 if (loadttddd==1){
   if (displaylines>=0){
      if (newPntCount>0){
         k=Display3DObject();
         if(k==1)errmsg("Object point O and a vertex on different sides of viewpoint E");
         else if(k==2)errmsg("Incorrect input file");
         else if(k==3)errmsg("not enough memory");
         else if(k==4)errmsg("Programming error: maxnpoly");
         else if(k==5)errmsg("Programming error: totnrtria");
         SwapBitmap();
      } else errmsg("Sorry, nothing to display");
   } 
 } else return(1);
 return(0);
}

void errmsg(char *errormessage){

sprintf(obuf,"%s%s%s\n",COLOR06,BOLD,errormessage);
ConPuts(writeReq,obuf);
ConPuts(writeReq,NORMAL);

}

void infomsg(char *errormessage){

sprintf(obuf,"%s%s%s\n",COLOR05,ITALICS,errormessage);
ConPuts(writeReq,obuf);
ConPuts(writeReq,NORMAL);

}
void normalmsg(char *errormessage){
sprintf(obuf,"%s\n",errormessage);
ConPuts(writeReq,obuf);
}

int GetFileName(char *FileName){
    
    int faulty;
 
    faulty=1;
    if (AslBase = OpenLibrary("asl.library", 37L))
    {
        if (fr = (struct FileRequester *)AllocAslRequest(ASL_FileRequest, frtags)){
            if (AslRequestTags(fr, 
                               ASL_Window, (struct window *)textwindow,
                               TAG_DONE)){
                strcpy(FileName,fr->rf_Dir);
                AddPart(FileName,fr->rf_File,80);
                faulty=0;
            }
            FreeAslRequest(fr);
        } else errmsg("can't open file requester");
        CloseLibrary(AslBase);
    } else errmsg("can't open file requester");
    return(faulty);
}

SwapBitmap(){

   MakeScreen(screen);
   RethinkDisplay();

   if (DB_rinfo->BitMap == DB_bmap2){
      DB_rinfo->BitMap  = DB_bmap1;
      DB_rport->BitMap = DB_bmap1;
   }
   else {
      DB_rinfo->BitMap  = DB_bmap2;
      DB_rport->BitMap = DB_bmap2;
   }
   SetRast(DB_rport,0);
   return(0);
}

ReadKeyboard(){

int j;
 
nxtcommand:

    if (seq==1){
       seq=0;
       seqposition=0;
    }
    if (cmd==1){
       position=0;
       lastposition=0;
       cmd=0;
    }

readnextchar:
        /* A character, or an IDCMP msg, or both could wake us up */
        signals = Wait(conreadsig|windowsig);

        /* If a console signal was received, get the character */
        if (signals && conreadsig){
            if((lch = ConMayGetChar(readPort,&ibuf)) != -1){
                ch = lch;
                /* Show hex and ascii (if printable) for char we got.
                 * If you want to parse received control sequences, such as
                 * function or Help keys, you would buffer control sequences
                 * as you receive them, starting to buffer whenever you
                 * receive 0x9B (or 0x1B[ for user-typed sequences) and
                 * ending when you receive a valid terminating character
                 * for the type of control sequence you are receiving.
                 * For CSI sequences, valid terminating characters
                 * are generally 0x40 through 0x7E.
                 * In our example, InControl has the following values:
                 * 0 = no, 1 = have 0x1B, 2 = have 0x9B OR 0x1B and [,
                 * 3 = now inside control sequence, -1 = normal end esc,
                 * -2 = non-CSI(no [) 0x1B end esc
                 * NOTE - a more complex parser is required to recognize
                 *  other types of control sequences.
                 */

                /* 0x1B ESC not followed by '[', is not CSI seq */
                if (InControl==1){
                    if (ch=='[') InControl = 2;
                    else InControl = -2;
                }

                if ((ch==0x9B)||(ch==0x1B)){
                    // start control sequence
                    InControl = (ch==0x1B) ? 1 : 2;
                }
                if (InControl==3){
                   if (seqposition<MAXSEQ){
                      Sequence[seqposition++]=ch;
                   } else InControl = -2; 
               }

                /* We'll show value of this char we received */
                if (InControl!=2 && InControl!=1 && InControl!=3 &&
                     (((ch >= 0x1F)&&(ch <= 0x7E))||(ch >= 0xA0))){
                     if (position<MAXCMD){
                        if (position<lastposition) {
                           ConPuts(writeReq,InsertCharacter);
                           for (j=lastposition;j>=position;j--){
                               inp[j+1]=inp[j];
                           }
                        } 
                        inp[position++]=ch;
                        lastposition++;
                        sprintf(obuf,"%c",ch);
                     } else sprintf(obuf,"\a");
                     ConPuts(writeReq,obuf);

                } else if (InControl!=2 && InControl!=1 && InControl!=3){ 
                   switch (ch){
                      case 0x0D:
                         if (position<MAXCMD){
                            inp[position++]='\0';
                            lastposition=position; 
                            cmd=1; 
                            sprintf(obuf,"%s%s",EraseToEndOfLine,CursorNextLine);
                            if (currenthis+1==MAXHIS)currenthis=0;
                            else {currenthis++; if(aanthis+1<MAXHIS)aanthis++;}
                            showhis=currenthis;
                            for (j=0; j<MAXCMD; j++) {his[currenthis][j]=inp[j];}
                         } else sprintf(obuf,"\a");
                         break;
                      case 0x08: 
                         if (position!=0){
                            for (j=position-1;j<=lastposition;j++){
                               inp[j]=inp[j+1];
                            }
                            sprintf(obuf,"%s%s",BACKSPACE,DELCHAR);
                            lastposition--;
                            position--;
                         } else sprintf(obuf,"\a");
                         break;
                      case 0x7F:
                         if (position<lastposition){ 
	                            for (j=position;j<=lastposition;j++){
                               inp[j]=inp[j+1];
                            }
                            sprintf(obuf,DELCHAR); /* delete the character */
                            lastposition--;
                         } else sprintf(obuf,"\a");
                         break;
                      default: 
                         sprintf(obuf,"\a"); break; 
                   }
                   ConPuts(writeReq,obuf);
                }

                /* Valid ESC sequence terminator ends an ESC seq */
                if ((InControl==3)&&((ch >= 0x40) && (ch <= 0x7E))){
                   if (strncmp(&CursorForward[1],Sequence,1)==NULL && position<lastposition){
                      position++;
                      sprintf(obuf,"%s",CursorForward);
                      ConPuts(writeReq,obuf);
                   // arrow LEFT
                   } else if (strncmp(&CursorBackward[1],Sequence,1)==NULL && position!=0){
                      position--;
                      sprintf(obuf,"%s",CursorBackward);
                      ConPuts(writeReq,obuf);
                   // arrow UP
                   } else if (strncmp(&ArrowUp[1],Sequence,1)==NULL && showhis!=-1){
                      for (j=0;j<strlen(inp);j++){
                          sprintf(obuf,"%s%s",BACKSPACE,DELCHAR);
                          ConPuts(writeReq,obuf);
                      }
                      for (j=0; j<MAXCMD; j++) {inp[j]=his[showhis][j];}
                      showhis--;
                      showhis = (showhis<0) ? aanthis : showhis;
                      sprintf(obuf,"%s",inp);
                      ConPuts(writeReq,obuf);
                      position=strlen(inp);
                      lastposition=position;
                   } else if (strncmp(&ArrowDown[1],Sequence,1)==NULL && showhis!=-1){
                      for (j=0;j<strlen(inp);j++){
                          sprintf(obuf,"%s%s",BACKSPACE,DELCHAR);
                          ConPuts(writeReq,obuf);
                      }
                      showhis++;
                      showhis = (showhis>aanthis) ? 0 : showhis;
                      for (j=0; j<MAXCMD; j++) {inp[j]=his[showhis][j];}
                      sprintf(obuf,"%s",inp);
                      ConPuts(writeReq,obuf);
                      position=strlen(inp);
                      lastposition=position;
                   } else if (strncmp(&HELP[1],Sequence,2)==NULL){
                      position=0;
                      lastposition=0;
                      inp[position++]='H';inp[position++]='E';inp[position++]='L';inp[position++]='P';
                      inp[position++]='\0';
                      lastposition=position; 
                      ConPuts(writeReq,inp);
                      cmd=1; 
                      sprintf(obuf,"%s%s",EraseToEndOfLine,CursorNextLine);
                      if (currenthis+1==MAXHIS)currenthis=0;
                      else {currenthis++; if(aanthis+1<MAXHIS)aanthis++;}
                      showhis=currenthis;
                      for (j=0; j<MAXCMD; j++) {his[currenthis][j]=inp[j];}

                   } else if (strncmp(&F1[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F2[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F3[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F4[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F5[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F6[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F7[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F8[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F9[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&F10[1],Sequence,2)==NULL)seq=1;
                   else if (strncmp(&ShiftArrowRight[1]  ,Sequence,2)==NULL)seq=1;
                   else if (strncmp(&ShiftArrowLeft[1]   ,Sequence,2)==NULL)seq=1;
                   else if (strncmp(&ShiftArrowUp[1]     ,Sequence,1)==NULL)seq=1;
                   else if (strncmp(&ShiftArrowDown[1]   ,Sequence,1)==NULL)seq=1;
                   else if (strncmp(&ArrowUp[1]          ,Sequence,1)==NULL)seq=1;
                   else if (strncmp(&ArrowDown[1]        ,Sequence,1)==NULL)seq=1;
                   else ConPuts(writeReq,"\a");
                   
                   InControl = -1;
                   if (seq==0) for (j=0; j<MAXSEQ; j++) {Sequence[j]='\0';}
                }
                if (InControl==2) InControl = 3;

                /* ESC sequence finished (-1 if OK, -2 if bogus) */
                if (InControl < 0){
                    seqposition=0;
                    InControl = 0;
                }
            }
        }

        /* If IDCMP messages received, handle them */
        if (signals && windowsig)
            {
            /* We have to ReplyMsg these when done with them */
            while (winmsg = (struct IntuiMessage *)GetMsg(textwindow->UserPort)){
                switch(winmsg->Class){
                    case CLOSEWINDOW:
                      Done = TRUE;
                      break;
                    default:
                      break;
                }
            ReplyMsg((struct Message *)winmsg);
            }
        }
   if (!Done && cmd==0 && seq==0)goto readnextchar;
   return(0);
}

void cleanexit(UBYTE *s,LONG n)
    {
    if(*s & (!FromWb)) printf(s);
    cleanup();
    exit(n);
    }

void cleanup()
    {
    if (rpAlloc!=NULL)FreeRaster(rpAlloc, WIDTH, SCR1_HEIGHT);
    if (OpenedConsole)CloseConsole(writeReq);
    if (readReq)DeleteExtIO((struct IORequest *)readReq);
    if (readPort)DeletePort(readPort);
    if (writeReq)DeleteExtIO((struct IORequest *)writeReq);
    if (writePort)DeletePort(writePort);
    if (textwindow)CloseWindow(textwindow);
    if (screen){
       DB_rinfo->BitMap  = DB_bmap1;
       DB_rport->BitMap = DB_bmap1;
       MakeScreen(screen);
       RethinkDisplay();
       CloseScreen(screen);
    }
    if(screen2)CloseScreen(screen2);
    if(DB_bmap1) 
    {
       for(j=0;j<SCR1_DEPTH;j++)
       {
          if(DB_bmap1->Planes[j])
             FreeRaster(DB_bmap1->Planes[j], WIDTH, SCR1_HEIGHT);
       }
       FreeMem(DB_bmap1, sizeof (struct BitMap));
    }
    if(DB_bmap2) 
    {
       for(j=0;j<SCR1_DEPTH;j++)
       {
          if(DB_bmap2->Planes[j])
             FreeRaster(DB_bmap2->Planes[j], WIDTH, SCR1_HEIGHT);
       }
       FreeMem(DB_bmap2, sizeof (struct BitMap));
    }
    if(GfxBase)       CloseLibrary((struct Library *)GfxBase);
    if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);

    }

/* Attach console device to an open Intuition window.
 * This function returns a value of 0 if the console
 * device opened correctly and a nonzero value (the error
 * returned from OpenDevice) if there was an error.
 */
BYTE OpenConsole(writereq, readreq, window)
struct IOStdReq *writereq;
struct IOStdReq *readreq;
struct Window *window;
    {
    BYTE error;

    writereq->io_Data = (APTR) window;
    writereq->io_Length = sizeof(struct Window);
    error = OpenDevice("console.device", 0, (struct IORequest *)writereq, 0);
    readreq->io_Device = writereq->io_Device; /* clone required parts */
    readreq->io_Unit   = writereq->io_Unit;
    return(error);
    }

void CloseConsole(struct IOStdReq *writereq)
    {
    CloseDevice((struct IORequest *)writereq);
    }

/* Output a single character to a specified console
 */
void ConPutChar(struct IOStdReq *writereq, UBYTE character)
    {
    writereq->io_Command = CMD_WRITE;
    writereq->io_Data = (APTR)&character;
    writereq->io_Length = 1;
    DoIO((struct IORequest *)writereq);
    /* command works because DoIO blocks until command is done
     * (otherwise ptr to the character could become invalid)
     */
    }

/* Output a stream of known length to a console
 */
void ConWrite(struct IOStdReq *writereq, UBYTE *string, LONG length)
    {
    writereq->io_Command = CMD_WRITE;
    writereq->io_Data = (APTR)string;
    writereq->io_Length = length;
    DoIO((struct IORequest *)writereq);
    /* command works because DoIO blocks until command is done
     * (otherwise ptr to string could become invalid in the meantime)
     */
    }

/* Output a NULL-terminated string of characters to a console
 */
void ConPuts(struct IOStdReq *writereq,UBYTE *string)
    {
    writereq->io_Command = CMD_WRITE;
    writereq->io_Data = (APTR)string;
    writereq->io_Length = -1;  /* means print till terminating null */
    DoIO((struct IORequest *)writereq);
    }

/* Queue up a read request to console, passing it pointer
 * to a buffer into which it can read the character
 */
void QueueRead(struct IOStdReq *readreq, UBYTE *whereto)
   {
   readreq->io_Command = CMD_READ;
   readreq->io_Data = (APTR)whereto;
   readreq->io_Length = 1;
   SendIO((struct IORequest *)readreq);
   }

/* Check if a character has been received.
 * If none, return -1
 */
LONG ConMayGetChar(struct MsgPort *msgport, UBYTE *whereto)
    {
    register temp;
    struct IOStdReq *readreq;

    if (!(readreq = (struct IOStdReq *)GetMsg(msgport))) return(-1);
    temp = *whereto;                /* get the character */
    QueueRead(readreq,whereto);     /* then re-use the request block */
    return(temp);
    }

/* Wait for a character
 */
UBYTE ConGetChar(struct MsgPort *msgport, UBYTE *whereto)
    {
    register temp;
    struct IOStdReq *readreq;

    WaitPort(msgport);
    readreq = (struct IOStdReq *)GetMsg(msgport);
    temp = *whereto;               /* get the character */
    QueueRead(readreq,whereto);    /* then re-use the request block*/
    return((UBYTE)temp);
    }

int SaveVS(char *FileName){

int numberprintpoints=0;

// returncode 1 = can't create file

      //print file with new object

      if ((fp = fopen(FileName, "w")) == NULL) {return 1;}

      ConPuts(writeReq,COLOR05);
      ConPuts(writeReq,ITALICS);
      sprintf(message,"Saving file %s",FileName);
      ConPuts(writeReq,message);

      fprintf(fp,"3DG1\n");

      //determine number of used points for this print selection
      for (c=0; c<newPntCount; c++){
          myPnt[c].new=-1;
      }
      for (c=0; c<newPuntCount+newEdgCount+newFacCount; c++){
          if (myInf[c].pntcnt==1 && VSpoints==1) myFac[c].p->new=1;
          if (myInf[c].pntcnt==2 && VSlines==1){
             myFac[c].p->new=1;
             myFac[c].nxt->p->new=1;
          }
          if (myInf[c].pntcnt>=3 && VSfaces==1){
             pmy1=&myFac[c];
nextpoint9:
             pmy1->p->new=1;
             if (pmy1->nxt!=NULL) {
                pmy2=pmy1->nxt;
                pmy1=pmy2;
                goto nextpoint9;
             }
          }

      }
      numberprintpoints=0;
      for (c=0; c<newPntCount; c++){
          if (myPnt[c].new==1)myPnt[c].new=numberprintpoints++;
      }

      //print videoscape file
      fprintf(fp,"%d\n",numberprintpoints);

      for (c=0; c<newPntCount;c++){
          if (myPnt[c].new>=0)fprintf(fp,"%.4f %.4f %.4f\n",myPnt[c].x,myPnt[c].y,myPnt[c].z);
      }    

      for (c=0; c<newPuntCount+newEdgCount+newFacCount*DOUBLEFACE;c++){

          if (myInf[c].pntcnt==1 && VSpoints!=1) goto notprint;
          if (myInf[c].pntcnt==2 && VSlines !=1) goto notprint;
          if (myInf[c].pntcnt>=3 && VSfaces !=1) goto notprint;

          fprintf(fp,"%d ",myInf[c].pntcnt);

          //print consequtive points
          pmy1=&myFac[c];
printnextpoint:
          fprintf(fp,"%d ",pmy1->p->new);
          if (pmy1->nxt!=NULL) {
             pmy2=pmy1->nxt;
             pmy1=pmy2;
             goto printnextpoint;
          }
          fprintf(fp,"%d\n",myInf[c].layer->number);
notprint:;          
      }    
      fclose(fp);

      for (c=0; c<newPntCount; c++){
          myPnt[c].new=c;
      }

      ConPuts(writeReq,CursorNextLine);
      ConPuts(writeReq,NORMAL);
      return(0);
}

int SaveDXF(char *FileName){

int PCnt;

// returncode 1 = can't create file
// returncode 2 = Maximum number of edges in a face is 4 (use command MAXEDGES 4)

//print file with new object

      //if faces are to be printed check the maximum edges of one polygon wich is 4
      if (DXFfaces==1){
         for (c=newPuntCount+newEdgCount; c<newPuntCount+newEdgCount+newFacCount;c++){
             if (myInf[c].pntcnt>4)return(2);
          }
      }

      if ((fp = fopen(FileName, "w")) == NULL) {return 1;}

      ConPuts(writeReq,COLOR05);
      ConPuts(writeReq,ITALICS);
      sprintf(message,"Saving file %s",FileName);
      ConPuts(writeReq,message);

      //Header
      fprintf(fp,"0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n256\n");
      for (c=0;c<ObjCount;c++){
          fprintf(fp,"0\nLAYER\n2\n%s\n70\n64\n62\n1\n6\nCONTINUOUS\n",Obj[c].layer);
      }
      fprintf(fp,"0\nENDTAB\n");
      fprintf(fp,"0\nENDSEC\n");

      //begin entities
      fprintf(fp,"0\nSECTION\n2\nENTITIES\n0\n");

      //Points
      if (DXFpoints==1){
         for (c=0; c<PntCount; c++){
             if (Pnt[c].used>=1){ 
                fprintf(fp,"POINT\n8\n%s\n62\n4\n10\n%.8f\n20\n%.8f\n30\n%.8f\n0\n",
                        Pnt[c].layer,Pnt[c].x,Pnt[c].y,Pnt[c].z);
             }
         }    
      }

      //Points
      if (DXFlonepoints==1){
         for (c=0; c<PntCount; c++){
             if (Pnt[c].used==1){ 
                fprintf(fp,"POINT\n8\n%s\n62\n4\n10\n%.8f\n20\n%.8f\n30\n%.8f\n0\n",
                        Pnt[c].layer,Pnt[c].x,Pnt[c].y,Pnt[c].z);
             }
         }    
      }

      //Faces
      if (DXFfaces==1){
         for (c=newPuntCount+newEdgCount; c<newPuntCount+newEdgCount+newFacCount*DOUBLEFACE;c++){

          //print consequtive points
          pmy1=&myFac[c];
          PCnt=0;
printnextpoint:
          switch(PCnt){
          case 0:
             fprintf(fp,"3DFACE\n8\n%s\n62\n4\n10\n%.8f\n20\n%.8f\n30\n%.8f\n",
                        myInf[c].layer,pmy1->p->x,pmy1->p->y,pmy1->p->z);
             PCnt++;
             break;
          case 1:
             fprintf(fp,"11\n%.8f\n21\n%.8f\n31\n%.8f\n",
                        pmy1->p->x,pmy1->p->y,pmy1->p->z);
             PCnt++;
             break;
          case 2:
             fprintf(fp,"12\n%.8f\n22\n%.8f\n32\n%.8f\n",
                        pmy1->p->x,pmy1->p->y,pmy1->p->z);
             PCnt++;
             if (myInf[c].pntcnt==4)break;
          case 3:
             fprintf(fp,"13\n%.8f\n23\n%.8f\n33\n%.8f\n0\n",
                        pmy1->p->x,pmy1->p->y,pmy1->p->z);
             PCnt++;
             break;
          }

          if (pmy1->nxt!=NULL) {
             pmy2=pmy1->nxt;
             pmy1=pmy2;
             goto printnextpoint;
          }
         }
      }

      //Lines
      if (DXFlines==1){
         for (c=0; c<EdgCount; c++){
            if (Edg[c].used>=1){
               fprintf(fp,"LINE\n8\n%s\n62\n4\n10\n%.8f\n20\n%.8f\n30\n%.8f\n11\n%.8f\n21\n%.8f\n31\n%.8f\n0\n",           
                          Edg[c].p1->layer,Edg[c].p1->x,Edg[c].p1->y,Edg[c].p1->z,Edg[c].p2->x,Edg[c].p2->y,Edg[c].p2->z);
            }
         }
      } else if (DXFlonelines==1) {
         for (c=0; c<EdgCount; c++){
            if (Edg[c].used==1){
               fprintf(fp,"LINE\n8\n%s\n62\n4\n10\n%.8f\n20\n%.8f\n30\n%.8f\n11\n%.8f\n21\n%.8f\n31\n%.8f\n0\n",           
                          Edg[c].p1->layer,Edg[c].p1->x,Edg[c].p1->y,Edg[c].p1->z,Edg[c].p2->x,Edg[c].p2->y,Edg[c].p2->z);
            }
         }
      }

      fprintf(fp,"ENDSEC\n0\nEOF\n");
      fclose(fp);

      ConPuts(writeReq,CursorNextLine);
      ConPuts(writeReq,NORMAL);
      return(0);
}

int FreeLoadedObject(void){

     //free allocated edges still in faces
     if (FacCount>0){
        for (c=0; c<FacCount; c++){
            Fac[c].used=0;
            p3face=Fac[c].next;
freealledges:
            p5face=p3face->next;
            free(p3face);
            p3face=p5face;
            if (p3face!=NULL)goto freealledges;
        }
        //free original t3d data arrays
        free(Fac); free(FacVec);
     }
     if (EdgCount>0){free(Edg); free(EdgVec);}
     if (PntCount>0)free(Pnt);

     return(0);
}

FreeNewFace(){

      //free all allocated new faces     
      if (newTotCount>0){
         for (c=0; c<newTotCount; c++){
             pmy1=myFac[c].nxt;
             if (pmy1!=NULL){
freemyface:
                pmy2=pmy1->nxt;
                free(pmy1);
                pmy1=pmy2;
                if (pmy1!=NULL)goto freemyface;
             }
         }               
         free(myFac);
         free(myInf);
      }
      //free all allocated newpoints
      if (newPntCount>0)free(myPnt);

      return(0);
}

int LoadTTDDD(char *FileName){

char LayerName[MAXLAYERNAME]={"DEFAULT"};

int EdgeJump,PointJump;

// returncodes 1=can't open file
//             2=not a T3DLIB file
//             3=out of memory
//             4=Number of points more then specified
//             5=Number of edges more then specified
//             6=Number of connections more then specified
//             7=Demo number off points exceeded

//begin code
if ((fp = fopen(FileName, "r")) == NULL) return(1);
else {
      if (loadttddd==1) {FreeNewFace(); FreeLoadedObject();}

      infomsg("Initializing");

      OCount=0;
      PCount=0;
      ECount=0;
      TCount=0;
      PntCount=0;    
      EdgCount=0;
      FacCount=0;
      ObjCount=0;
      PointJump=0;
      EdgeJump=0;

      getline(line, maxcharinline);
      if ((pointer = strstr(line,CharStarter))==NULL){fclose(fp); return(2);}
      
      while(feof(fp)==NULL){

         getline(line, maxcharinline);
          
         //Read object name
         if ((pointer = strstr(line,CharNAME))!=NULL) {
            OCount++;
            continue;
         }      

         //Read PCount and allocate points
         if ((pointer = strstr(line,CharPCount))!=NULL) {
            pointer2 = strstr(pointer,CharSpace);
            PCount = PCount + atoi(pointer2);
            continue;
         }      

         //Read ECount and allocate edges
         if ((pointer = strstr(line,CharECount))!=NULL) {
            pointer2 = strstr(pointer,CharSpace);
            ECount = ECount + atoi(pointer2);
            continue;
         }      

         //Read TCount and allocate connections
         if ((pointer = strstr(line,CharTCount))!=NULL) {
            pointer2 = strstr(pointer,CharSpace);
            TCount = TCount + atoi(pointer2);
            continue;
         }      
      }
      if (DEMO==1 && PCount>DemoPoints*help1){infomsg(DemoMessage); return(7);}
      
      if (PCount>0){
         Pnt = Allocmem(struct Point, PCount);
         if (Pnt == NULL){fclose(fp);return 3;}
      }

      if (ECount>0){
         Edg = Allocmem(struct Edge, ECount);
         if (Edg == NULL) {fclose(fp); return 3;}
         EdgVec = Allocmem(struct Vector, ECount);
         if (EdgVec == NULL) {fclose(fp); return 3;}
      }

      //destination faces never more than source triangles
      if (TCount>0){
         Fac = Allocmem(struct Face, TCount);
         if (Fac == NULL) {fclose(fp); return 3;}
         FacVec = Allocmem(struct Vector, TCount);
         if (FacVec == NULL) {fclose(fp); return 3;}
      }

      if (OCount>0){
         Obj = Allocmem(struct Object, OCount);
         if (Obj == NULL) {fclose(fp); return 3;}
      }
      
      rewind(fp);

      while(feof(fp)==NULL){
         
         getline(line, maxcharinline);

         //Read object name
         if ((pointer = strstr(line,CharNAME))!=NULL) {
            PointJump=PntCount;
            EdgeJump=EdgCount;
            pointer2 = strstr(pointer,CharSpace);
            for (j=0; j<MAXLAYERNAME; j++){LayerName[j]='\0';}
            strncpy(LayerName,++pointer2,strlen(pointer2)-1);
            if (OCount>1)sprintf(message,"Loading group object %s",LayerName);
            else sprintf(message,"Loading object %s",LayerName);
            infomsg(message);
            c=0;
            for (j=0; j<MAXLAYERNAME; j++){
               if (LayerName[j]!='"' && LayerName[j]!='.')Obj[ObjCount].layer[c++]=LayerName[j];
            }
            Obj[ObjCount].number=ObjCount;
            Obj[ObjCount].points=0;
            Obj[ObjCount].edges=0;
            Obj[ObjCount].faces=0;
            Obj[ObjCount].xmin=1.0E30;
            Obj[ObjCount].xmax=-1.0E30;
            Obj[ObjCount].ymin=1.0E30;
            Obj[ObjCount].ymax=-1.0E30;
            Obj[ObjCount].zmin=1.0E30;
            Obj[ObjCount].zmax=-1.0E30;

            ObjCount++;
            continue;
         }      
       
         //Lees points
         if ((pointer = strstr(line,CharPoint))!=NULL) {
            pointer2 = strpbrk(pointer,CharIs);
            X=atof(++pointer2);
            pointer = strpbrk(pointer2,CharIs);
            Y=atof(++pointer); 
            pointer2 = strpbrk(pointer,CharIs);
            Z=atof(++pointer2);
            if(PntCount > PCount-1){fclose(fp); return 4;}
            Pnt[PntCount].used = 1;
            Pnt[PntCount].new = PntCount;
            Pnt[PntCount].x = X;
            Pnt[PntCount].y = Y;
            Pnt[PntCount].z = Z;
            Pnt[PntCount].layer = &Obj[ObjCount-1];
            if (Obj[ObjCount-1].xmin>X)Obj[ObjCount-1].xmin=X;
            if (Obj[ObjCount-1].xmax<X)Obj[ObjCount-1].xmax=X;
            if (Obj[ObjCount-1].ymin>Y)Obj[ObjCount-1].ymin=Y;
            if (Obj[ObjCount-1].ymax<Y)Obj[ObjCount-1].ymax=Y;
            if (Obj[ObjCount-1].zmin>Z)Obj[ObjCount-1].zmin=Z;
            if (Obj[ObjCount-1].zmax<Z)Obj[ObjCount-1].zmax=Z;
	    PntCount++;
            
            Obj[ObjCount-1].points++;
            continue;
         }      

         //Lees edges
         if ((pointer = strstr(line,CharEdge))!=NULL) {
            pointer2 = strpbrk(pointer,CharSpace);
            A = (int)atoi(++pointer2)+PointJump;
            pointer = strpbrk(pointer2,CharSpace);
            B = (int)atoi(++pointer)+PointJump;
            if(EdgCount > ECount-1){fclose(fp); return 5;}
            Edg[EdgCount].used = 1;
            Edg[EdgCount].vector = &EdgVec[EdgCount];
            Edg[EdgCount].p1 = &Pnt[A];
            Edg[EdgCount].p2 = &Pnt[B];
            Pnt[A].used=3;
            Pnt[B].used=3;
            Edg[EdgCount].newedge = NULL; //used in edgemerging process

            //calculate direction edge
            P1.x=Pnt[A].x;
            P1.y=Pnt[A].y;
            P1.z=Pnt[A].z;
            P2.x=Pnt[B].x;
            P2.y=Pnt[B].y;
            P2.z=Pnt[B].z;
            EdgVec[EdgCount]=DirectionEdges(&P1, &P2);

            EdgCount++;
            Obj[ObjCount-1].edges++;
	    continue;
         }      
         
         //read connections
         if ((pointer = strstr(line,CharConnect))!=NULL) {
            pointer2 = strpbrk(pointer,CharSpace);
            A = (int)atoi(++pointer2)+EdgeJump;
            pointer = strpbrk(pointer2,CharSpace);
            B = (int)atoi(++pointer)+EdgeJump;
            pointer2 = strpbrk(pointer,CharSpace);
            C = (int)atoi(++pointer2)+EdgeJump;

            //Counterclockwise edge connection
            if (Edg[A].p1==Edg[B].p1||
                Edg[A].p1==Edg[B].p2) { 
             P1.x = Edg[A].p1->x; 
             P1.y = Edg[A].p1->y; 
             P1.z = Edg[A].p1->z; 
            } else {
             P1.x = Edg[A].p2->x; 
             P1.y = Edg[A].p2->y; 
             P1.z = Edg[A].p2->z;
            }
            if (Edg[B].p1==Edg[C].p1||
                Edg[B].p1==Edg[C].p2) { 
             P2.x = Edg[B].p1->x; 
             P2.y = Edg[B].p1->y; 
             P2.z = Edg[B].p1->z; 
            } else {
             P2.x = Edg[B].p2->x; 
             P2.y = Edg[B].p2->y; 
             P2.z = Edg[B].p2->z;
            }
            if (Edg[C].p1==Edg[A].p1||
                Edg[C].p1==Edg[A].p2) { 
             P3.x = Edg[C].p1->x; 
             P3.y = Edg[C].p1->y; 
             P3.z = Edg[C].p1->z; 
            } else {
             P3.x = Edg[C].p2->x; 
             P3.y = Edg[C].p2->y; 
             P3.z = Edg[C].p2->z;
            }
            deter = Determinant(&P1, &P2, &P3);
            if (deter < 0) {
               //herschik van links naar rechts
               D=A; A=C; C=D; //B=B
            }

            if(FacCount > TCount-1) {fclose(fp); return 6;}

            //create Fac
            pface=Allocmem1(struct Face);
            if (pface == NULL) {fclose(fp); return 3;}
            Fac[FacCount].used = 1;
            Fac[FacCount].edge = &Edg[A]; //first edge in face
            Edg[A].used=2;
            Edg[A].p1->used=2;
            Edg[A].p2->used=2;
	    Fac[FacCount].next = pface;

            p2face=Allocmem1(struct Face);
            if (p2face == NULL) {fclose(fp); return 3;}
            pface->used = 1;
            pface->edge = &Edg[B]; //second edge in face
            Edg[B].used=2;
            Edg[B].p1->used=2;
            Edg[B].p2->used=2;
	    pface->next = p2face;

            p2face->used = 1;
            p2face->edge = &Edg[C];
            Edg[C].used=2;
            Edg[C].p1->used=2;
            Edg[C].p2->used=2;
            p2face->next = NULL; //third and last edge in face

            //determine normaal face
            if (Edg[A].p1==Edg[B].p1||
                Edg[A].p1==Edg[B].p2) { 
               P1.x = Edg[A].p1->x; 
               P1.y = Edg[A].p1->y; 
               P1.z = Edg[A].p1->z; 
            } else {
               P1.x = Edg[A].p2->x; 
               P1.y = Edg[A].p2->y; 
               P1.z = Edg[A].p2->z;
            }
            if (Edg[B].p1==Edg[C].p1||
                Edg[B].p1==Edg[C].p2) { 
               P2.x = Edg[B].p1->x; 
               P2.y = Edg[B].p1->y; 
               P2.z = Edg[B].p1->z; 
            } else {
               P2.x = Edg[B].p2->x; 
               P2.y = Edg[B].p2->y; 
               P2.z = Edg[B].p2->z;
            }
            if (Edg[C].p1==Edg[A].p1||
                Edg[C].p1==Edg[A].p2) { 
               P3.x = Edg[C].p1->x; 
               P3.y = Edg[C].p1->y; 
               P3.z = Edg[C].p1->z; 
            } else {
               P3.x = Edg[C].p2->x; 
               P3.y = Edg[C].p2->y; 
               P3.z = Edg[C].p2->z;
            }
            deter = Determinant(&P1, &P2, &P3);
            FacVec[FacCount].deter=deter;
            FacVec[FacCount]=NormaalFaces(&P1,&P2,&P3);

            Obj[ObjCount-1].faces++;
	    FacCount++;
	    continue;
         }      
      }
     //end reading T3D file
     fclose(fp);

     CreateNewFace();
     loadttddd=1;

     return(0);
     }
}      

int LoadVS(char *FileName){
// returncodes 1=can't open file
//             2=not a VideoScape file
//             3=out of memory
//             4=number of points differs from number specified
//             5=empty object
//             7=demo number off points exceeded

char LayerName[MAXLAYERNAME]={"DEFAULT"};
int VSFacCount,VSEdgCount,s,q;


//begin code
if ((fp = fopen(FileName, "r")) == NULL) return(1);
else {
     if (loadttddd==1) {FreeNewFace(); FreeLoadedObject();}

     OCount=1;
     PCount=0;
     ECount=0;
     TCount=0;
     VSFacCount=0;
     VSEdgCount=0;
     PntCount=0;    
     EdgCount=0;
     FacCount=0;
     ObjCount=0;

     //read header
     getline(line, maxcharinline);
     if ((pointer = strstr(line,CharStartVS))==NULL){fclose(fp); return(2);}

     for (j=0; j<MAXLAYERNAME; j++){LayerName[j]='\0';}
     pointer2 = line;
     strncpy(LayerName,pointer2,strlen(pointer2)-1);
     sprintf(message,"Loading object %s",LayerName);
     infomsg(message);

     //read number of points
     getline(line, maxcharinline);

     q=atoi(line);
     if (DEMO==1 && q>DemoPoints*help1){infomsg(DemoMessage); return(7);}

     while(feof(fp)==NULL){
        getline(line, maxcharinline);
        if ((pointer = strstr(line,CharPnt))!=NULL) {
            PCount++;
        } else TCount++;
     } 
     if (q!=PCount) return(4);

      //allocate Object, points, faces

     if (PCount>0){
        Pnt = Allocmem(struct Point, PCount);
        if (Pnt == NULL){fclose(fp);return 3;}
     } else return(5);

     Obj = Allocmem(struct Object, OCount);
     if (Obj == NULL){fclose(fp);return 3;}

     if (TCount>0){
        VSFac = Allocmem(struct VSFace, TCount);
        if (VSFac == NULL){fclose(fp);return 3;}
     }

     //initialise object

     c=0;
     for (j=0; j<MAXLAYERNAME; j++){
         if (LayerName[j]!='"' && LayerName[j]!='.')Obj[ObjCount].layer[c++]=LayerName[j];
     }
     Obj[ObjCount].number=ObjCount;
     Obj[ObjCount].points=0;
     Obj[ObjCount].edges=0;
     Obj[ObjCount].faces=0;
     Obj[ObjCount].xmin=1.0E30;
     Obj[ObjCount].xmax=-1.0E30;
     Obj[ObjCount].ymin=1.0E30;
     Obj[ObjCount].ymax=-1.0E30;
     Obj[ObjCount].zmin=1.0E30;
     Obj[ObjCount].zmax=-1.0E30;
     ObjCount++;

     rewind(fp);

     getline(line, maxcharinline); //Header
     getline(line, maxcharinline); //Number of points

     TCount=0;
     while(feof(fp)==NULL){
         
         getline(line, maxcharinline);
         
         if ((pointer = strstr(line,CharPnt))!=NULL) {
            pointer2=line;
            X=atof(pointer2);
            pointer = strpbrk(pointer2,CharSpace);
            Y=atof(++pointer); 
            pointer2 = strpbrk(pointer,CharSpace);
            Z=atof(++pointer2);
            Pnt[PntCount].used = 1;
            Pnt[PntCount].new = PntCount;
            Pnt[PntCount].x = X;
            Pnt[PntCount].y = Y;
            Pnt[PntCount].z = Z;
            Pnt[PntCount].layer = &Obj[ObjCount-1];
            if (Obj[ObjCount-1].xmin>X)Obj[ObjCount-1].xmin=X;
            if (Obj[ObjCount-1].xmax<X)Obj[ObjCount-1].xmax=X;
            if (Obj[ObjCount-1].ymin>Y)Obj[ObjCount-1].ymin=Y;
            if (Obj[ObjCount-1].ymax<Y)Obj[ObjCount-1].ymax=Y;
            if (Obj[ObjCount-1].zmin>Z)Obj[ObjCount-1].zmin=Z;
            if (Obj[ObjCount-1].zmax<Z)Obj[ObjCount-1].zmax=Z;
	    PntCount++;
            
            Obj[ObjCount-1].points++;
            continue;
         } else {
            pointer=line;
            VSFac[VSFacCount].pntcnt = c = atoi(pointer);
            
            if (c==2)ECount++;
            else if (c>=3) {ECount=ECount+c; TCount++;}

            vsface1=&VSFac[VSFacCount];
            pointer2=line;
            c=-1;
nextpointinnewface:
            pointer  = strpbrk(pointer2,CharSpace);
            pointer3 = strpbrk(++pointer,CharSpace); //color code detector
            if (pointer3!=NULL){
               if (c!=-1){
                  vsface2 = Allocmem1(struct VSFace);
                  if (vsface2 == NULL) {return 3;}
                  vsface1->next=vsface2;
                  vsface1=vsface2;
               }
               c = atoi(pointer);
               vsface1->p=&Pnt[c];
               vsface1->next=NULL;
               pointer2=pointer;
               goto nextpointinnewface;
            }
            VSFacCount++;   
         }
     }

     //end reading T3D file
     fclose(fp);

     sprintf(message,"Creating edges");
     infomsg(message);

     //allocate edges 
     if (ECount>0){

        VSEdg = Allocmem(struct Edge, ECount);
        if (VSEdg == NULL) {fclose(fp); return 3;}
       
        //copy edges to VSEdg
        VSEdgCount=0;
        for (c=0; c<VSFacCount; c++){
            if (VSFac[c].pntcnt>=2){
               vsface1=&VSFac[c];
nextvsedge:
               if (vsface1->next!=NULL){
                  A=vsface1->p->new;
                  vsface2=vsface1->next;
                  vsface1=vsface2;
                  B=vsface1->p->new;

                  VSEdg[VSEdgCount].used = 1;
                  VSEdg[VSEdgCount].p1 = &Pnt[A];
                  VSEdg[VSEdgCount].p2 = &Pnt[B];
                  VSEdgCount++;
 
                  goto nextvsedge;
               } 
               if (VSFac[c].pntcnt!=2) {
                  A=vsface1->p->new;
                  B=VSFac[c].p->new;

                  VSEdg[VSEdgCount].used = 1;
                  VSEdg[VSEdgCount].p1 = &Pnt[A];
                  VSEdg[VSEdgCount].p2 = &Pnt[B];
                  VSEdgCount++;
               }
            }
        }

        if (ECount>100){
           sprintf(message,"please wait");
           infomsg(message);
        }

        ECount=VSEdgCount;
        if (ECount>10){
           s=0;
           q=10;
next10step:
           for (c=s; c<q; c++){
               if (VSEdg[c].used==1){
                  for (cc=s+c+1; cc<q; cc++){
                      if (VSEdg[cc].used==1){
                         if (((VSEdg[c].p1==VSEdg[cc].p1 && VSEdg[c].p2==VSEdg[cc].p2)||
                             (VSEdg[c].p1==VSEdg[cc].p2 && VSEdg[c].p2==VSEdg[cc].p1))){
                             VSEdg[cc].used=0;
                         }
                      }
                  }
               }
           }
           if (q+10<ECount){s=q; q=q+10; goto next10step;}
        }
        if (ECount>100){
           s=0;
           q=100;
next100step:
           for (c=s; c<q; c++){
               if (VSEdg[c].used==1){
                  for (cc=s+c+1; cc<q; cc++){
                      if (VSEdg[cc].used==1){
                         if (((VSEdg[c].p1==VSEdg[cc].p1 && VSEdg[c].p2==VSEdg[cc].p2)||
                             (VSEdg[c].p1==VSEdg[cc].p2 && VSEdg[c].p2==VSEdg[cc].p1))){
                             VSEdg[cc].used=0;
                         }
                      }
                  }
               }
           }
           if (q+100<ECount){s=q; q=q+100; goto next100step;}
        }

        for (c=0; c<VSEdgCount; c++){
            if (VSEdg[c].used==1){
               for (cc=c+1; cc<VSEdgCount; cc++){
                   if (VSEdg[cc].used==1){
                      if (((VSEdg[c].p1==VSEdg[cc].p1 && VSEdg[c].p2==VSEdg[cc].p2)||
                          (VSEdg[c].p1==VSEdg[cc].p2 && VSEdg[c].p2==VSEdg[cc].p1))){
                          VSEdg[cc].used=0;
                      }
                   }
               }
            } else ECount--;
        }
     }         

     //allocate used edges
     if (ECount>0){

        Edg = Allocmem(struct Edge, ECount);
        if (Edg == NULL) {fclose(fp); return 3;}

        EdgVec = Allocmem(struct Vector, ECount);
        if (EdgVec == NULL) {fclose(fp); return 3;}

        //write edges
        EdgCount=0;
        for (c=0; c<VSEdgCount; c++){
            if (VSEdg[c].used==1){
               A = VSEdg[c].p1->new;
               B = VSEdg[c].p2->new;
               Edg[EdgCount].used = 1;
               Edg[EdgCount].vector = &EdgVec[EdgCount];
               Edg[EdgCount].p1 = &Pnt[A];
               Edg[EdgCount].p2 = &Pnt[B];
               Pnt[A].used=3;
               Pnt[B].used=3;
               Edg[EdgCount].newedge = NULL; //used in edgemerging process

               //calculate direction edge
               P1.x=Pnt[A].x;
               P1.y=Pnt[A].y;
               P1.z=Pnt[A].z;
               P2.x=Pnt[B].x;
               P2.y=Pnt[B].y;
               P2.z=Pnt[B].z;
               EdgVec[EdgCount]=DirectionEdges(&P1, &P2);

               EdgCount++;
               Obj[ObjCount-1].edges++;
            }
        }
     }
     if (VSEdgCount>0) free(VSEdg);

     //allocate faces
     sprintf(message,"Creating faces");
     infomsg(message);

     if (TCount>0){
        Fac = Allocmem(struct Face, TCount);
        if (Fac == NULL){fclose(fp);return 3;}

        FacVec = Allocmem(struct Vector, TCount);
        if (FacVec == NULL) {fclose(fp); return 3;}

        //write faces
        FacCount=0;
        for (c=0; c<VSFacCount; c++){
            pface=&Fac[FacCount];
            pface->used=0;
            if (VSFac[c].pntcnt>=3){
 
               vsface1=&VSFac[c];
               P1.x=vsface1->p->x;
               P1.y=vsface1->p->y;
               P1.z=vsface1->p->z;
               vsface2=vsface1->next;
               P2.x=vsface2->p->x;
               P2.y=vsface2->p->y;
               P2.z=vsface2->p->z;
               vsface1=vsface2->next;
pointsonlinenextpoint:
               P3.x=vsface1->p->x;
               P3.y=vsface1->p->y;
               P3.z=vsface1->p->z;
               deter = Determinant(&P1, &P2, &P3);
               if (deter == 0) {
                  if (vsface1->next!=NULL){
                     vsface2=vsface1->next;
                     vsface1=vsface2;
                     goto pointsonlinenextpoint;
                  }
               }
               FacVec[FacCount].deter=deter;
               FacVec[FacCount]=NormaalFaces(&P1,&P2,&P3);

               vsface1=&VSFac[c];
nextvsedge2:
               if (vsface1->next!=NULL){
                  A=vsface1->p->new;
                  vsface2=vsface1->next;
                  vsface1=vsface2;
                  B=vsface1->p->new;

                  for (cc=0; cc<EdgCount; cc++){
                      if ((Edg[cc].p1->new==A && Edg[cc].p2->new==B)||
                          (Edg[cc].p1->new==B && Edg[cc].p2->new==A)){
                          if (pface->used==1){
                             p2face= Allocmem1(struct Face);
                             if (p2face == NULL) {return 3;}
                             pface->next=p2face;
                             pface=p2face;
                          }
                          pface->used=1;
                          pface->edge=&Edg[cc];
                          pface->next=NULL;
                          Edg[cc].used=2;
                          Edg[cc].p1->used=2;
                          Edg[cc].p2->used=2;
                      }
                  }
                  goto nextvsedge2;
   
               } else {
                  A=vsface1->p->new;
                  B=VSFac[c].p->new; 

                  for (cc=0; cc<EdgCount; cc++){
                      if ((Edg[cc].p1->new==A && Edg[cc].p2->new==B)||
                          (Edg[cc].p1->new==B && Edg[cc].p2->new==A)){

                          p2face= Allocmem1(struct Face);
                          if (p2face == NULL) {return 3;}
                          pface->next=p2face;
                          pface=p2face;
 
                          pface->used=1;
                          pface->edge=&Edg[cc];
                          pface->next=NULL;
                          Edg[cc].used=2;
                          Edg[cc].p1->used=2;
                          Edg[cc].p2->used=2;
                      }
                  }
                  Obj[ObjCount-1].faces++;
                  FacCount++;
               }
            }
        }
     }

     if (VSFacCount>0){
        //free allocated VSFaces
        for (c=0; c<VSFacCount; c++){
            vsface1=VSFac[c].next;
freeVSFace:
            vsface2=vsface1->next;
            free(vsface1);
            vsface1=vsface2;
            if (vsface1!=NULL) goto freeVSFace;
        }
        free(VSFac);
     }

     CreateNewFace();
     loadttddd=1;

     return(0);

     }

}      

int ConvertTTDDD(void){

int linemerge,facemerge;
int NEdges,q;

// returncodes 3=out of memory
      
if (faceangle<0 || MERGEfaces==-1) goto endmergeface;

      //generate merged faces
      ConPuts(writeReq,COLOR05);
      ConPuts(writeReq,ITALICS);
      if (MERGEinline!=-1) sprintf(message,"Merging lines & faces: %3d %s",0,"%");
      else sprintf(message,"Merging faces: %3d %s",0,"%");
      ConPuts(writeReq,message);
      
allpasses:
      c=-1;
onemorepass:
      c++;
      if (c<FacCount) {

         sprintf(message,"%s%s%s%s%s",BACKSPACE,BACKSPACE,BACKSPACE,BACKSPACE,BACKSPACE);
         ConPuts(writeReq,message);
         cc=(int)( ((double)100*c)/FacCount+0.5 );
         sprintf(message,"%3d %s",cc,"%");
         ConPuts(writeReq,message);

allpasses2:
         cc=-1;
onemorepass2:
         cc++;
         linemerge=0;
         facemerge=0;
         if (cc<FacCount) {

            if (c != cc 
                && Fac[c].used == 1 
                && Fac[cc].used == 1 
                && ((FacVec[c].deter>0 && FacVec[cc].deter>0)
                    ||(FacVec[c].deter<0 && FacVec[cc].deter<0))
                && Fac[c].edge->p1->layer == Fac[cc].edge->p1->layer){

               pface=&Fac[c];
               p2face=&Fac[cc];
edgecompare:
               if (pface->edge==p2face->edge) {         

                  //common edge found, check if polygons in same face
                  angle=CosAngle(&FacVec[c],&FacVec[cc]);

                  if (faceangle>0 &&  sin(acos(angle))<=sin(faceangle*(acos((double)-1)/(double)180))){

                     //merge two polygons to target polygon
                     facemerge=1;
                     p3face=p6face=pface;

                     //necessary to start face on *target
	             target=Allocmem1(struct Face);
                     if (target == NULL) {return 3;}
                     target->used=p3face->used;
                     target->edge=p3face->edge;
	             target->next=NULL;

                     p4face=target;

		     bit1=0;bit2=0;bit3=0;	
nextedge:
                     if (p3face->next!=NULL && p3face->next!=p6face){
                        
                        p5face=p3face; //next from source polygon
                        p3face=p5face->next;
edgebegin:
                        p5face=Allocmem1(struct Face); //create new target polygon
                        if (p5face == NULL) {return 3;}

                        p4face->next=p5face;
                        p4face=p5face;

                        p4face->used=p3face->used;
                        p4face->edge=p3face->edge;
                        p4face->next=NULL;
                        goto nextedge;

                     } else if (&Fac[c]!=pface && bit1!=1) {

                        bit1=1;
                        p3face=&Fac[c]; 
                        goto edgebegin;
                         
                     } else if (bit2!=1) {

                        bit2=1;
                        p3face=p6face=p2face;
                        goto edgebegin;

                     } else if (&Fac[cc]!=p2face && bit3!=1) {

                        bit3=1;
                        p3face=&Fac[cc]; 
                        goto edgebegin;

                     } 
                    
	             //remove two occurences of the same edge in the target polygon
                     p3face=target;
                     p4face=target;
nextdouble:          
                     if(p3face!=p4face && p3face->edge==p4face->edge) {
                       if (p3face->edge->used==2)p3face->edge->used=3;
                       p3face->used=0; 		              
                       p4face->used=0;
                     }
                     p5face=p4face->next;
                     p4face=p5face;
                     if (p4face!=NULL) goto nextdouble;

                     p5face=p3face->next;
                     p3face=p5face;
                     if (p3face!=NULL) {
                        p4face=target;
                        goto nextdouble;
                     }

                     //start merging edges
                     if (edgeangle<0 || MERGEinline==-1) goto endmergeedge; 

                     p3face=target;
                     goto beginmergeedge;
nextmergeedge:                     
                     if (p3face->next==NULL) goto endmergeedge;
                     p5face=p3face->next;
                     p3face=p5face;
beginmergeedge:
                     if (p3face->used==0 || p3face->edge->used==0 || p3face->edge->used==3)
                        goto nextmergeedge;
                     
                     p4face=p3face->next;
                     if (p4face==NULL) p4face=target;
nextmergeedge2:                     
                     if (p4face->used==0 || p4face->edge->used==0 || p4face->edge->used==3){
                        if (p4face->next==NULL) {
                           p4face=target;
                           goto nextmergeedge2;
                        }
                        p5face=p4face->next;
                        p4face=p5face;
                        goto nextmergeedge2;
                     } 

                     angle=CosAngle(p3face->edge->vector,p4face->edge->vector);

                     if (edgeangle>0 && sin(acos(angle))<=sin(edgeangle*(acos((double)-1)/(double)180))){

                        if (p3face->edge->newedge==NULL) {
                           pP1=p3face->edge->p1;
                           pP2=p3face->edge->p2;
                        } else {
                           pP1=p3face->edge->newedge->p1;
                           pP2=p3face->edge->newedge->p2;
                        }
                        if (p4face->edge->newedge==NULL) {
                           pQ1=p4face->edge->p1;
                           pQ2=p4face->edge->p2;
                        } else {
                           pQ1=p4face->edge->newedge->p1;
                           pQ2=p4face->edge->newedge->p2;
                        }

			//determine common point
                        if (pP1 == pQ1 || pP1 == pQ2) pC1=pP1;
                        else pC1=pP2;

                        //determine if this common point is a crosspoint 
                        for (q=0; q<EdgCount; q++){
                            if (&Edg[q]!=p3face->edge && 
                                &Edg[q]!=p4face->edge &&
                                pP1->layer==Edg[q].p1->layer &&
                                Edg[q].used==2 &&
                                ( (Edg[q].newedge==NULL && (Edg[q].p1==pC1 || Edg[q].p2==pC1)) ||
                                  (Edg[q].newedge!=NULL && 
                                         (Edg[q].newedge->p1==pC1 || Edg[q].newedge->p2==pC1)) )
                            ) goto crosspointdetected2;
                        }

                        //merge edges
                        linemerge=1;

                        if (p3face->edge->newedge==NULL){
                           pedge1 = (struct Edge *) malloc(sizeof(struct Edge)); //create newedge
                           if (pedge1 == NULL) {return 3;}
                           pedge1->used=1;
                           pedge1->p1=NULL;
                           pedge1->p2=NULL;
                           pedge1->newedge=NULL;
                           p3face->edge->newedge=pedge1;
                        }

                        //temporarely disable edge
                        p4face->edge->used=3;
 
                        if (pP1 != pQ1 && pP1 != pQ2) p3face->edge->newedge->p1=pP1;
                        else p3face->edge->newedge->p1=pP2;
                        if (pQ1 != pP1 && pQ1 != pP2) p3face->edge->newedge->p2=pQ1;
                        else p3face->edge->newedge->p2=pQ2;

                        goto nextmergeedge;
crosspointdetected2:;
                     }

                     goto nextmergeedge;
endmergeedge:

                     //check if the polygon has more than MaxEdge edges
                     if (MaxEdges>0) {
                        p3face=target;
                        NEdges=0;
NextEdgeCount:                        
                        if (p3face->used==1 && p3face->edge->used==2) NEdges++;
                        if (p3face->next!=NULL){
                           p4face=p3face->next;
                           p3face=p4face;
                           goto NextEdgeCount;
                        }
                        if (NEdges>MaxEdges) {
                           linemerge=0;
                           facemerge=0;
                           goto NoMerging;
                        }
		     }

                     //free memory source polygon
                     Fac[c].used=0;
                     p3face=Fac[c].next;
freecedges:
                     p5face=p3face->next;
                     free(p3face);
                     p3face=p5face;
                     if (p3face!=NULL)goto freecedges;

                     //this polygon is not used
                     Fac[cc].used=0; 
                     
                     //replace source polygon 1 with target polygon
                     p3face=target;
                     p4face=&Fac[c];
nextcopyedge:
                     if (p3face->used==0) {
                        //set used=0 for all edges and points NOT USED in merged polygons
                        //set used=2 for all edges and points USED in merged polygons
                        //(used=1 are edges and points that are not part of a polygon)
                        p3face->edge->used=0;
			p3face->edge->p1->used=0;	
			p3face->edge->p2->used=0;	

                        p5face=p3face->next;
                        if (p5face==NULL) goto edgescopied;
                        p3face=p5face;
                        goto nextcopyedge;
	             }
copyusededges:                     
                     p4face->used=1;
                     p4face->edge=p3face->edge;

                     if (p3face->edge->newedge!=NULL){
			p4face->edge->p1->used=0;	
			p4face->edge->p2->used=0;
                        p3face->edge->p1=p3face->edge->newedge->p1;
                        p3face->edge->p2=p3face->edge->newedge->p2;
                        free(p3face->edge->newedge);
                        p3face->edge->newedge=NULL;
			p3face->edge->p1->used=2;	
			p3face->edge->p2->used=2;
                     }
                     p4face->next=NULL;

next2copyedge:
                     p5face=p3face->next;
                     if (p5face==NULL)goto edgescopied;
                     p3face=p5face;
                     if (p3face->used==0) {
                        //set used=0 for all edges and points NOT USED in merged polygons
                        //set used=2 for all edges and points USED in merged polygons
                        //(used=1 are edges and points that are not part of a polygon)
                        p3face->edge->used=0;
			p3face->edge->p1->used=0;	
			p3face->edge->p2->used=0;	
                        goto next2copyedge;
                     }

                     p5face=Allocmem1(struct Face); //create new source polygon
                     if (p5face == NULL) {return 3;}
                     p4face->next=p5face;
                     p4face=p5face;

                     goto copyusededges;
edgescopied:

NoMerging:
                     //free memory target polygon		 
                     p3face=target;
freetargetedges:
                     p5face=p3face->next;

                     if (facemerge==0 && p3face->edge->used==3)
                        p3face->edge->used=2;
                     else if (facemerge==1 && p3face->edge->used==3)
                        p3face->edge->used=0;

                     if (p3face->edge->newedge!=NULL){
                        free(p3face->edge->newedge);
                        p3face->edge->newedge=NULL;
                     }

                     free(p3face);
                     p3face=p5face;
                     if (p3face!=NULL)goto freetargetedges;
                     
                     goto comparenextfaces;
                  }
               }

               // next edge Fac[cc]
               if (p2face->next!=NULL) {
                  p3face=p2face->next;
                  p2face=p3face;
                  goto edgecompare;
               }

               // next edge Fac[c]
               if (pface->next!=NULL) {
                  p3face=pface->next;
                  pface=p3face;
                  p2face=&Fac[cc];
                  goto edgecompare;
               }    
            }
comparenextfaces:;
            //if a linemerge occurred it is possible that a former merge wich was rejected
            //by the maxedges maximum is now possible, we have to start the face merging process
            //anew!
         } else goto endpass2;
         if (linemerge==1) {
            goto allpasses;
         } else goto onemorepass2;
endpass2:;
      } else goto endpass;
      goto onemorepass;
endpass:; 

      //mark all used points in faces
      for (c=0; c<FacCount; c++){
         if (Fac[c].used==1) {
            pface=&Fac[c];
nextnewedge1:
            pedge1=pface->edge;
            if (pedge1->used==2){
               //mark all used points in faces
               pedge1->p1->used=2;
               pedge1->p2->used=2;
            }
            if (pface->next!=NULL){
               p2face=pface->next;
               pface=p2face;
               goto nextnewedge1;
            } 
         } else Fac[c].used=0;
      }

      //mark all used points in edges not used in faces
      for (c=0; c<EdgCount; c++){
          if (Edg[c].used==1){
             if (Edg[c].p1->used!=2)Edg[c].p1->used=3;
             if (Edg[c].p2->used!=2)Edg[c].p2->used=3;
          }
      }

      ConPuts(writeReq,CursorNextLine);

endmergeface:

      //merge edges
      if (MERGElone==-1 && MERGElines==-1) goto nolinemerge;

      ConPuts(writeReq,COLOR05);
      ConPuts(writeReq,ITALICS);
      sprintf(message,"Merging edges: %3d %s",0,"%");
      ConPuts(writeReq,message);

      if (edgeangle>=0){
         for (c=0; c<EdgCount; c++){

             sprintf(message,"%s%s%s%s%s",BACKSPACE,BACKSPACE,BACKSPACE,BACKSPACE,BACKSPACE);
             ConPuts(writeReq,message);
             cc=(int)( ((double)100*c)/EdgCount+0.5 );
             sprintf(message,"%3d %s",cc,"%");
             ConPuts(writeReq,message);

             for (cc=0; cc<EdgCount; cc++){
                 if (c!=cc && 
                     Edg[c].p1->layer==Edg[cc].p1->layer &&
                     ((MERGElone==1 && Edg[c].used==1 && Edg[cc].used==1) ||
                      (MERGElines==1 && Edg[c].used==2 && Edg[cc].used==2)) &&
                     (Edg[c].p1==Edg[cc].p1 ||
                      Edg[c].p1==Edg[cc].p2 ||
                      Edg[c].p2==Edg[cc].p1 ||
                      Edg[c].p2==Edg[cc].p2)){

                     angle=CosAngle(Edg[c].vector,Edg[cc].vector);

                     if (edgeangle>0 && sin(acos(angle))<=sin(edgeangle*(acos((double)-1)/(double)180))){
 
			//search for common point
                        if (Edg[c].p1 == Edg[cc].p1 || Edg[c].p1 == Edg[cc].p2) pP1=Edg[c].p1;
                        else pP1=Edg[c].p2;

                        //search for a crosspoint these may not be merged
                        for (q=0; q<EdgCount; q++){
                            if (q!=c && 
                                q!=cc &&
                                Edg[c].p1->layer==Edg[q].p1->layer &&
                                ((MERGElone==1 && Edg[q].used==1 && Edg[c].used==1 && Edg[cc].used==1) ||
                                 (MERGElines==1 && Edg[q].used==2 && Edg[c].used==2 && Edg[cc].used==2)) &&
                                (Edg[q].p1==pP1 || Edg[q].p2==pP1)) goto crosspointdetected;
                        }

                        pP1->used=0;
                        
                        if (Edg[c].p1 != Edg[cc].p1 && Edg[c].p1 != Edg[cc].p2) pP1=Edg[c].p1;
                        else pP1=Edg[c].p2;
                        if (Edg[cc].p1 != Edg[c].p1 && Edg[cc].p1 != Edg[c].p2) pP2=Edg[cc].p1;
                        else pP2=Edg[cc].p2;
                       
                        Edg[cc].used=0;
                        Edg[c].p1=pP1;
                        Edg[c].p2=pP2;
crosspointdetected:;
                     }
                 }
             }
         }  
      }

      ConPuts(writeReq,CursorNextLine);

nolinemerge:

      //Remove unused edges from faces
      if (MERGElines==1 || MERGEinline==1){
         for (c=0; c<FacCount; c++){
             if (Fac[c].used==1){
                pface=&Fac[c];
filteredges:
                if (pface->edge->used==2){
                   p6face=pface;
                   if (pface->next!=NULL){
                      p2face=pface->next;
                      pface=p2face;
                      goto filteredges;
                   }
                } else {
                   p4face=pface;
filteredges5:
                   if (p4face->next!=NULL){
                      p5face=p4face->next;
                      p4face=p5face;
                      if (p4face->edge->used==2){
                         pedge1=pface->edge;
                         pface->edge=p4face->edge;
                         p4face->edge=pedge1;
                         goto filteredges;
                      }
                      goto filteredges5;
                   } else {
		     p3face=p6face->next;
                     p6face->next=NULL;
freewhatsleft:
                     p4face=p3face->next;
                     free(p3face);
                     if (p4face!=NULL)p3face=p4face;
                     else goto endfilter;
                     goto freewhatsleft;
                   }			
                }
             }
endfilter:;

         }
      }

      //Define new numbers for the total amount of points,edges and faces in the objects
      for (c=0; c<ObjCount; c++){
          Obj[c].points=0;
          Obj[c].edges=0;
          Obj[c].faces=0;
      }
      for (c=0; c<FacCount; c++){
          if(Fac[c].used!=0)Fac[c].edge->p1->layer->faces++;
      }
      for (c=0; c<EdgCount; c++){
          if(Edg[c].used!=0)Edg[c].p1->layer->edges++;
      }	
      for (c=0; c<PntCount; c++){
          if(Pnt[c].used!=0)Pnt[c].layer->points++;
      }
      PntCount2=0;
      EdgCount2=0;
      FacCount2=0;
      //Define total number of points,edges,faces after merging
      for (c=0; c<ObjCount; c++){
	  PntCount2=PntCount2+Obj[c].points;
	  EdgCount2=EdgCount2+Obj[c].edges;
	  FacCount2=FacCount2+Obj[c].faces;
      }

      FreeNewFace();
      CreateNewFace();

      ConPuts(writeReq,NORMAL);
      sprintf(message,"Source object: %6d points %6d edges %6d faces",
                     PntCount,EdgCount,FacCount);
      infomsg(message);
      sprintf(message,"Target object: %6d points %6d edges %6d faces",
                     PntCount2,EdgCount2,FacCount2);
      infomsg(message);
      return(0);
}

int CreateNewFace(void){

int tel;

      //renumber points used (some points can become obsolete in the algorithm)
      cc=0;
      for (c=0; c<PntCount;c++){
          if (Pnt[c].used!=0){
             Pnt[c].new=cc; 
             cc++;
          }
      }    
      newPntCount=cc;
     
      //create myPoints were Pnt[c].new => index myPnt
      if (newPntCount!=0){
         myPnt = Allocmem(struct myPoint, newPntCount);
         if (myPnt == NULL){return 3;}

         //copy Pnt.used!=0 -> myPnt
         for (c=0; c<PntCount;c++){
            if (Pnt[c].used!=0){
               myPnt[Pnt[c].new].x=Pnt[c].x;
               myPnt[Pnt[c].new].y=Pnt[c].y;
               myPnt[Pnt[c].new].z=Pnt[c].z;
               myPnt[Pnt[c].new].new=Pnt[c].new;
            }
         }
      }

      newEdgCount=0;
      for (c=0; c<EdgCount;c++){
          if (Edg[c].used==1)newEdgCount++;
      }
      newPuntCount=0;
      for (c=0; c<PntCount;c++){
          if (Pnt[c].used==1)newPuntCount++;
      }
      newFacCount=0;
      for (c=0; c<FacCount; c++){
         p5face=&Fac[c];
         if (p5face->used==1)newFacCount++;
      }

      //allocate number of faces to create in new format
      newTotCount=newEdgCount+newPuntCount+DOUBLEFACE*newFacCount;
      if (newTotCount!=0){
         myFac = Allocmem(struct myFace, newTotCount);
         if (myFac == NULL) {return 3;}
         myInf = Allocmem(struct myInfo, newTotCount);
         if (myInf == NULL) {return 3;}

         //create myPunt
         if (newPuntCount!=0){
            //copy Pnt.used==1
            cc=-1;
            for (c=0; c<PntCount;c++){
                if (Pnt[c].used==1){
                   cc++;
                   myFac[cc].p=&myPnt[Pnt[c].new];
                   myFac[cc].nxt=NULL;
                   myInf[cc].pntcnt=1;
                   myInf[cc].layer=Pnt[c].layer;
                }
            }
         }

         //create myEdg 
         if (newEdgCount!=0){
            //copy Edg.used==1 -> myEdg
            cc=newPuntCount;
            for (c=0; c<EdgCount;c++){
               if (Edg[c].used==1){
                  myFac[cc].p=&myPnt[Edg[c].p1->new];
                  myInf[cc].layer=Edg[c].p1->layer;
                  myInf[cc].pntcnt=2;
                  pmy1 = Allocmem1(struct myFace);
                  if (pmy1 == NULL) {return 3;}
                  myFac[cc].nxt=pmy1;
                  pmy1->p=&myPnt[Edg[c].p2->new];
                  pmy1->nxt=NULL;
                  cc++;
               }
            }
         }

         //fill newfaces with consecutive points from source edges
         cc=newPuntCount+newEdgCount;
         for (c=0; c<FacCount; c++){
            if (Fac[c].used==1) {
               tel=0;
               pmy1=&myFac[cc];
               pface=&Fac[c];
               p2face=pface->next;
nextnewedge:
               pedge1=pface->edge;
               pedge2=p2face->edge;
               if (pedge1->p1==pedge2->p1||
                  pedge1->p1==pedge2->p2) { 
                  tel++;
                  pmy1->p=&myPnt[pedge1->p1->new];
                  pmy1->nxt=NULL;   
               } else {
                  tel++;
                  pmy1->p=&myPnt[pedge1->p2->new];
                  pmy1->nxt=NULL; 
               }
               if (p2face!=&Fac[c]){
                  pface=p2face;
                  p2face=pface->next;
                  if (p2face==NULL){
                     //compare last edge to first edge
                     p2face=&Fac[c];
                  } 
                  pmy2 = Allocmem1(struct myFace);
                  if (pmy2 == NULL) {printf("out of memory\n"); return 2;}
                  pmy1->nxt=pmy2;
                  pmy1=pmy2;
                  goto nextnewedge;
               }
            myInf[cc].pntcnt=tel;
            myInf[cc].layer=Fac[c].edge->p1->layer;
            cc++;
            }
         }

         //if DOUBLEFACE=2 then create for every face a corresponding backface
         if (DOUBLEFACE==2){
            for (cc=newPuntCount+newEdgCount+newFacCount; cc<newTotCount; cc++){
               pmy1=&myFac[cc-newFacCount];
               pmy2=&myFac[cc];
               myInf[cc].layer =myInf[cc-newFacCount].layer;
               myInf[cc].pntcnt=myInf[cc-newFacCount].pntcnt;
               pmy3=NULL;
searchlast:
               if (pmy1->nxt!=pmy3){
                  pmy4=pmy1->nxt;
                  pmy1=pmy4;
                  goto searchlast;
               }
               if (pmy3!=NULL){
                  pmy4 = Allocmem1(struct myFace);
                  if (pmy4 == NULL) {return 3;}
                  pmy2->nxt=pmy4;
                  pmy2=pmy4; 
               }
               pmy2->p=pmy1->p;
               pmy2->nxt=NULL;
               if (pmy1!=&myFac[cc-newFacCount]){
                  pmy3=pmy1;
                  pmy1=&myFac[cc-newFacCount];
                  goto searchlast;
               }
            }         
         }
      }
}

int getline(char *line, int max)
{
   if (fgets(line, max, fp) == NULL)
      return 0;
   else 
      return (int) strlen(line);
}

double Determinant(struct Point *P1, struct Point *P2, struct Point *P3)
{
double detP3X, detP3Y, detP3Z, value;
detP3X =      (P3->x+1)*((P1->y+1)*(P2->z+1)-(P2->y+1)*(P1->z+1));
detP3Y = -1 * (P3->y+1)*((P1->x+1)*(P2->z+1)-(P2->x+1)*(P1->z+1));
detP3Z =      (P3->z+1)*((P1->x+1)*(P2->y+1)-(P2->x+1)*(P1->y+1));
value  = detP3X + detP3Y + detP3Z;
return value;
}

struct Vector DirectionEdges(struct Point *P1, struct Point *P2){
struct Vector Vec1;
Vec1.x = P2->x - P1->x;
Vec1.y = P2->y - P1->y;
Vec1.z = P2->z - P1->z;
return Vec1;
}

double CosAngle(struct Vector *V1, struct Vector *V2)
{
double V1V2, absV1, absV2, out;
V1V2 = V1->x*V2->x+V1->y*V2->y+V1->z*V2->z;
absV1= sqrt((V1->x*V1->x)+(V1->y*V1->y)+(V1->z*V1->z));
absV2= sqrt((V2->x*V2->x)+(V2->y*V2->y)+(V2->z*V2->z));
out = (V1V2!=0) ? V1V2/(absV1*absV2) : 0;
return out;
}


struct Vector NormaalFaces(struct Point *P1, struct Point *P2, struct Point *P3)
{
struct Vector Vec1;
struct Point R1, R2;
//richtingsvectoren R1 R2 vlak 1
R1.x = P2->x - P1->x;
R1.y = P2->y - P1->y;
R1.z = P2->z - P1->z;
R2.x = P3->x - P1->x;
R2.y = P3->y - P1->y;
R2.z = P3->z - P1->z;
//normaalvector vlak 1
Vec1.x = R1.y * R2.z - R1.z * R2.y;
Vec1.y = R1.z * R2.x - R1.x * R2.z;
Vec1.z = R1.x * R2.y - R1.y * R2.x;
return Vec1;
}

int Display3DObject(){

// return = 1 Object point O and a vertex on different sides of viewpoint E
// return = 2 Incorrect input file;
// return = 3 not enough memory;
// return = 4 Programming error: maxnpoly;
// return = 5 Programming error: totnrtria;

if (displaylines==0 || displaylines==1)c=Solid3DObject();
else if (displaylines==2)c=Wire3DObject();
else if (displaylines==3)c=Outline3DObject();

return(c);
}

int Outline3DObject(){

   int	j,i;                           
   int maxvertnr=0;
   int k1, k2, XLmax, YLmax;
   int nvertex;
   double fx, fy, xs, ys, xe, ye, ze;
   struct myPoint P[8];
   double xsrange, ysrange;
   double xmin=1.0E30, xmax=-1.0E30, ymin=1.0E30, ymax=-1.0E30, zmin=1.0E30, zmax=-1.0E30;
   struct vec3 Objpoint, Pnew;

   //need to be done only once

   ncolors = 10;
   X__max = x_max = WIDTH;
   Y__max = y_max = SCR1_HEIGHT-3;

   density = X__max / (x_max - x_min);   
   y_max = y_min + Y__max/density;
   x_center = 0.5 * (x_min + x_max);
   y_center = 0.5 * (y_min + y_max);
   r_max = (x_center < y_center ? x_center : y_center);

   // Initialiseer object

   for ( j=0; j<ObjCount; j++)
   {
      if ((int)(Obj[j].xmin*100) < xmin) xmin = (int)(Obj[j].xmin*100);
      if ((int)(Obj[j].xmax*100) > xmax) xmax = (int)(Obj[j].xmax*100);
      if ((int)(Obj[j].ymin*-100) < ymin) ymin = (int)(Obj[j].ymin*-100);
      if ((int)(Obj[j].ymax*-100) > ymax) ymax = (int)(Obj[j].ymax*-100);
      if ((int)(Obj[j].zmin*100) < zmin) zmin = (int)(Obj[j].zmin*100);
      if ((int)(Obj[j].zmax*100) > zmax) zmax = (int)(Obj[j].zmax*100);
   }

   Objpoint.x = .5*(xmin+xmax);
   Objpoint.y = .5*(ymin+ymax);
   Objpoint.z = .5*(zmin+zmax);

   // spherical coordinates rho, theta, phi of;
   // viewpoint E (phi = angle between z-axis and OE)

   rho = xmax - xmin;
   if (ymax - ymin > rho) rho = ymax - ymin;
   if (zmax - zmin > rho) rho = zmax - zmin;
   rho *= 3;

   lightvector.x = 1;
   lightvector.y = -1;
   lightvector.z = 0;
 
   //start display run

   maxvertnr = ObjCount*8-1; 
   nvertex = maxvertnr + 1;

   vt = Allocmem(struct vec3, nvertex);
   if (!vt) return(3);
   for (i=0; i<nvertex; i++) vt[i].z = -1e6; // Not in use

   coeff(rho, theta*PIdiv180, phi*PIdiv180); // calculate perspective matrix constants

   // Read vertices:
   xsmin=ysmin=zemin=BIG;
   xsmax=ysmax=zemax=-BIG;

   vertexcount=0;

   for ( c=0; c<ObjCount; c++){

      P[0].x=Obj[c].xmin; P[0].y=Obj[c].ymax; P[0].z=Obj[c].zmax;
      P[1].x=Obj[c].xmin; P[1].y=Obj[c].ymin; P[1].z=Obj[c].zmax;
      P[2].x=Obj[c].xmax; P[2].y=Obj[c].ymin; P[2].z=Obj[c].zmax;
      P[3].x=Obj[c].xmax; P[3].y=Obj[c].ymax; P[3].z=Obj[c].zmax;
      P[4].x=Obj[c].xmin; P[4].y=Obj[c].ymax; P[4].z=Obj[c].zmin;
      P[5].x=Obj[c].xmin; P[5].y=Obj[c].ymin; P[5].z=Obj[c].zmin;
      P[6].x=Obj[c].xmax; P[6].y=Obj[c].ymin; P[6].z=Obj[c].zmin;
      P[7].x=Obj[c].xmax; P[7].y=Obj[c].ymax; P[7].z=Obj[c].zmin;

      for (j=0; j<8; j++){

          Pnew.x = (int)(P[j].x*100) - Objpoint.x;
          Pnew.y = (int)(P[j].y*-100) - Objpoint.y;
          Pnew.z = (int)(P[j].z*100) - Objpoint.z;

          //calculate transformed coordinates
          eyecoord(Pnew, &xe, &ye, &ze);
          if (ze < 0) return(1);

          xs = xe/ze;  ys = ye/ze;
          if (xs < xsmin) xsmin = xs; 
          if (xs > xsmax) xsmax = xs;
          if (ys < ysmin) ysmin = ys; 
          if (ys > ysmax) ysmax = ys;
          if (ze < zemin) zemin = ze; 
          if (ze > zemax) zemax = ze;
          vt[vertexcount].x = xe;
          vt[vertexcount].y = ye; 
          vt[vertexcount].z = ze;

          vertexcount++;
      }
   }

   if (xsmin == BIG) return(2);

   // Compute screen constants:
   xsrange = xsmax - xsmin;
   if (xsmax==xsmin) xsrange=1; 
   ysrange = ysmax - ysmin;
   if (ysmax==ysmin) ysrange=1; 
   xsC = 0.5 * (xsmin + xsmax);
   ysC = 0.5 * (ysmin + ysmax);
   k1 = LARGE/(X__max+1); 
   k2 = LARGE/(Y__max+1);
   k = min2(k1, k2); 
   hk = k/2;			  // k = 50, hk = 25 with VGA
   XLmax = k * (X__max+1);
   YLmax = k * (Y__max+1);
   // Pixel coordinates: Xpix = to_pix(X) and Ypix = to_pix(Y)
   XLC = XLmax/2;
   YLC = YLmax/2;
   XLCreal = XLC + 0.5;
   YLCreal = YLC + 0.5;
   fx = XLmax/xsrange; 
   fy = YLmax/ysrange;
   f = 0.95 * (fx < fy ? fx : fy);
   zfactor = LARGE/(zemax - zemin);

   //integer points 
   V = Allocmem(struct vertex, nvertex);
   if (V == NULL) return(3);

   // Initialize vertex array:
   for (i=0; i<nvertex; i++)
   {  
      if (vt[i].z < -1e5)
      {  V[i].used = 0; continue; // V[i] not in use
      }  
      V[i].used = 1;
      xs = vt[i].x / vt[i].z; //normaliseren naar Z
      ys = vt[i].y / vt[i].z;
      V[i].X = XLarge(xs);    // large integer caculation
      V[i].Y = YLarge(ys);
      V[i].Z = ZLarge(vt[i].z);
   }

   free(vt);

   //don't display backfaces in wireframe mode
   cc=0;
   for (c=0; c<ObjCount; c++){
       set_color(Obj[c].number%4+11);
       Move(DB_rport,to_pix(V[cc].X),to_pix(V[cc].Y));
       Draw(DB_rport,to_pix(V[cc+1].X),to_pix(V[cc+1].Y));
       Draw(DB_rport,to_pix(V[cc+2].X),to_pix(V[cc+2].Y));
       Draw(DB_rport,to_pix(V[cc+3].X),to_pix(V[cc+3].Y));
       Draw(DB_rport,to_pix(V[cc].X),to_pix(V[cc].Y));

       Move(DB_rport,to_pix(V[cc].X),to_pix(V[cc].Y));
       Draw(DB_rport,to_pix(V[cc+4].X),to_pix(V[cc+4].Y));

       Move(DB_rport,to_pix(V[cc+1].X),to_pix(V[cc+1].Y));
       Draw(DB_rport,to_pix(V[cc+5].X),to_pix(V[cc+5].Y));

       Move(DB_rport,to_pix(V[cc+2].X),to_pix(V[cc+2].Y));
       Draw(DB_rport,to_pix(V[cc+6].X),to_pix(V[cc+6].Y));

       Move(DB_rport,to_pix(V[cc+3].X),to_pix(V[cc+3].Y));
       Draw(DB_rport,to_pix(V[cc+7].X),to_pix(V[cc+7].Y));

       Move(DB_rport,to_pix(V[cc+4].X),to_pix(V[cc+4].Y));
       Draw(DB_rport,to_pix(V[cc+5].X),to_pix(V[cc+5].Y));
       Draw(DB_rport,to_pix(V[cc+6].X),to_pix(V[cc+6].Y));
       Draw(DB_rport,to_pix(V[cc+7].X),to_pix(V[cc+7].Y));
       Draw(DB_rport,to_pix(V[cc+4].X),to_pix(V[cc+4].Y));

       cc=cc+8;
   }

   free(V);

   return(0);
}

int Wire3DObject(){

   int	j,i;                           
   int maxvertnr=0;
   int k1, k2, XLmax, YLmax;
   int nvertex;
   double fx, fy, xs, ys, xe, ye, ze;
   double xsrange, ysrange;
   double xmin=1.0E30, xmax=-1.0E30, ymin=1.0E30, ymax=-1.0E30, zmin=1.0E30, zmax=-1.0E30;
   struct vec3 Objpoint, Pnew;

   //need to be done only once

   ncolors = 10;
   X__max = x_max = WIDTH;
   Y__max = y_max = SCR1_HEIGHT-3;

   density = X__max / (x_max - x_min);   
   y_max = y_min + Y__max/density;
   x_center = 0.5 * (x_min + x_max);
   y_center = 0.5 * (y_min + y_max);
   r_max = (x_center < y_center ? x_center : y_center);

   // Initialiseer object

   maxvertnr = newPntCount-1; 
   for ( j=0; j<ObjCount; j++)
   {
      if ((int)(Obj[j].xmin*100) < xmin) xmin = (int)(Obj[j].xmin*100);
      if ((int)(Obj[j].xmax*100) > xmax) xmax = (int)(Obj[j].xmax*100);
      if ((int)(Obj[j].ymin*-100) < ymin) ymin = (int)(Obj[j].ymin*-100);
      if ((int)(Obj[j].ymax*-100) > ymax) ymax = (int)(Obj[j].ymax*-100);
      if ((int)(Obj[j].zmin*100) < zmin) zmin = (int)(Obj[j].zmin*100);
      if ((int)(Obj[j].zmax*100) > zmax) zmax = (int)(Obj[j].zmax*100);
   }

   Objpoint.x = .5*(xmin+xmax);
   Objpoint.y = .5*(ymin+ymax);
   Objpoint.z = .5*(zmin+zmax);

   // spherical coordinates rho, theta, phi of;
   // viewpoint E (phi = angle between z-axis and OE)

   rho = xmax - xmin;
   if (ymax - ymin > rho) rho = ymax - ymin;
   if (zmax - zmin > rho) rho = zmax - zmin;
   rho *= 3;

   lightvector.x = 1;
   lightvector.y = -1;
   lightvector.z = 0;
 
   //start display run

   nvertex = maxvertnr + 1;
   vt = Allocmem(struct vec3, nvertex);
   if (!vt) return(3);
   for (i=0; i<nvertex; i++) vt[i].z = -1e6; // Not in use

   coeff(rho, theta*PIdiv180, phi*PIdiv180); // calculate perspective matrix constants

   // Read vertices:
   xsmin=ysmin=zemin=BIG;
   xsmax=ysmax=zemax=-BIG;

   for ( j=0; j<newPntCount; j++){

      vertexcount++;

      Pnew.x = (int)(myPnt[j].x*100) - Objpoint.x;
      Pnew.y = (int)(myPnt[j].y*-100) - Objpoint.y;
      Pnew.z = (int)(myPnt[j].z*100) - Objpoint.z;

      //calculate transformed coordinates
      eyecoord(Pnew, &xe, &ye, &ze);
      if (ze < 0) return(1);

      xs = xe/ze;  ys = ye/ze;
      if (xs < xsmin) xsmin = xs; 
      if (xs > xsmax) xsmax = xs;
      if (ys < ysmin) ysmin = ys; 
      if (ys > ysmax) ysmax = ys;
      if (ze < zemin) zemin = ze; 
      if (ze > zemax) zemax = ze;
      vt[j].x = xe;
      vt[j].y = ye; 
      vt[j].z = ze;
   }

   if (xsmin == BIG) return(2);

   // Compute screen constants:
   xsrange = xsmax - xsmin;
   if (xsmax==xsmin) xsrange=1; 
   ysrange = ysmax - ysmin;
   if (ysmax==ysmin) ysrange=1; 
   xsC = 0.5 * (xsmin + xsmax);
   ysC = 0.5 * (ysmin + ysmax);
   k1 = LARGE/(X__max+1); 
   k2 = LARGE/(Y__max+1);
   k = min2(k1, k2); 
   hk = k/2;			  // k = 50, hk = 25 with VGA
   XLmax = k * (X__max+1);
   YLmax = k * (Y__max+1);
   // Pixel coordinates: Xpix = to_pix(X) and Ypix = to_pix(Y)
   XLC = XLmax/2;
   YLC = YLmax/2;
   XLCreal = XLC + 0.5;
   YLCreal = YLC + 0.5;
   fx = XLmax/xsrange; 
   fy = YLmax/ysrange;
   f = 0.95 * (fx < fy ? fx : fy);
   zfactor = LARGE/(zemax - zemin);

   //integer points 
   V = Allocmem(struct vertex, nvertex);
   if (V == NULL) return(3);

   // Initialize vertex array:
   for (i=0; i<nvertex; i++)
   {  
      if (vt[i].z < -1e5)
      {  V[i].used = 0; continue; // V[i] not in use
      }  
      V[i].used = 1;
      xs = vt[i].x / vt[i].z; //normaliseren naar Z
      ys = vt[i].y / vt[i].z;
      V[i].X = XLarge(xs);    // large integer caculation
      V[i].Y = YLarge(ys);
      V[i].Z = ZLarge(vt[i].z);
   }

   free(vt);

   //don't display backfaces in wireframe mode
   for (c=newPuntCount+newEdgCount; c<newPuntCount+newEdgCount+newFacCount; c++){
       pmy1=&myFac[c];
       set_color(myInf[c].layer->number%4+11);
       Move(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
       goto getnextpointinface;
drawnextpointinface:
       Draw(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
getnextpointinface:
       if (pmy1->nxt!=NULL){
          pmy2=pmy1->nxt;
          pmy1=pmy2;
          goto drawnextpointinface;
       }
       //draw last line 
       Draw(DB_rport,to_pix(V[myFac[c].p->new].X),to_pix(V[myFac[c].p->new].Y));
   }

   //draw lone edges
   for (c=newPuntCount; c<newPuntCount+newEdgCount; c++){
       set_color(myInf[c].layer->number%4+11);
       Move(DB_rport,to_pix(V[myFac[c].p->new].X),to_pix(V[myFac[c].p->new].Y));
       Draw(DB_rport,to_pix(V[myFac[c].nxt->p->new].X),to_pix(V[myFac[c].nxt->p->new].Y));
   }

   //draw points of edges and faces
   set_color(15);
   for (c=newPuntCount; c<newPuntCount+newEdgCount+newFacCount; c++){
       pmy1=&myFac[c];
drawnextpointinface2:
       WritePixel(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
       if (pmy1->nxt!=NULL){
          pmy2=pmy1->nxt;
          pmy1=pmy2;
          goto drawnextpointinface2;
       }
   }

   //draw lone points
   for (c=0; c<newPuntCount; c++){
       set_color(myInf[c].layer->number%4+11);
       WritePixel(DB_rport,to_pix(V[myFac[c].p->new].X),to_pix(V[myFac[c].p->new].Y));
   } 

   free(V);

   return(0);
}

int Solid3DObject(){

   int pnt1,pnt2,pnt3;

   int	j,i,kk;                           
   int maxvertnr=0;
   int k1, k2, XLmax, YLmax;
   int nvertex;
   double fx, fy, xs, ys, xe, ye, ze;
   double xsrange, ysrange;
   double xmin=1.0E30, xmax=-1.0E30, ymin=1.0E30, ymax=-1.0E30, zmin=1.0E30, zmax=-1.0E30;
   struct vec3 Objpoint, Pnew;

   //need to be done only once

   ncolors = 10;
   X__max = x_max = WIDTH;
   Y__max = y_max = SCR1_HEIGHT-3;

   density = X__max / (x_max - x_min);   
   y_max = y_min + Y__max/density;
   x_center = 0.5 * (x_min + x_max);
   y_center = 0.5 * (y_min + y_max);
   r_max = (x_center < y_center ? x_center : y_center);

   // Initialiseer object

   maxvertnr = newPntCount-1; 
   for ( j=0; j<ObjCount; j++)
   {
      if ((int)(Obj[j].xmin*100) < xmin) xmin = (int)(Obj[j].xmin*100);
      if ((int)(Obj[j].xmax*100) > xmax) xmax = (int)(Obj[j].xmax*100);
      if ((int)(Obj[j].ymin*-100) < ymin) ymin = (int)(Obj[j].ymin*-100);
      if ((int)(Obj[j].ymax*-100) > ymax) ymax = (int)(Obj[j].ymax*-100);
      if ((int)(Obj[j].zmin*100) < zmin) zmin = (int)(Obj[j].zmin*100);
      if ((int)(Obj[j].zmax*100) > zmax) zmax = (int)(Obj[j].zmax*100);
   }

   Objpoint.x = .5*(xmin+xmax);
   Objpoint.y = .5*(ymin+ymax);
   Objpoint.z = .5*(zmin+zmax);

   // spherical coordinates rho, theta, phi of;
   // viewpoint E (phi = angle between z-axis and OE)

   rho = xmax - xmin;
   if (ymax - ymin > rho) rho = ymax - ymin;
   if (zmax - zmin > rho) rho = zmax - zmin;
   rho *= 3;

   lightvector.x = 1;
   lightvector.y = -1;
   lightvector.z = 0;
 
   //start display run

   nvertex = maxvertnr + 1;
   vt = Allocmem(struct vec3, nvertex);
   if (!vt) return(3);
   for (i=0; i<nvertex; i++) vt[i].z = -1e6; // Not in use

   coeff(rho, theta*PIdiv180, phi*PIdiv180); // calculate perspective matrix constants

   // Read vertices:
   xsmin=ysmin=zemin=BIG;
   xsmax=ysmax=zemax=-BIG;

   for ( j=0; j<newPntCount; j++){

      vertexcount++;

      Pnew.x = (int)(myPnt[j].x*100) - Objpoint.x;
      Pnew.y = (int)(myPnt[j].y*-100) - Objpoint.y;
      Pnew.z = (int)(myPnt[j].z*100) - Objpoint.z;

      //calculate transformed coordinates
      eyecoord(Pnew, &xe, &ye, &ze);
      if (ze < 0) return(1);

      xs = xe/ze;  ys = ye/ze;
      if (xs < xsmin) xsmin = xs; 
      if (xs > xsmax) xsmax = xs;
      if (ys < ysmin) ysmin = ys; 
      if (ys > ysmax) ysmax = ys;
      if (ze < zemin) zemin = ze; 
      if (ze > zemax) zemax = ze;
      vt[j].x = xe;
      vt[j].y = ye; 
      vt[j].z = ze;
   }

   if (xsmin == BIG) return(2);

   // Compute screen constants:
   xsrange = xsmax - xsmin;
   if (xsmax==xsmin) xsrange=1; 
   ysrange = ysmax - ysmin;
   if (ysmax==ysmin) ysrange=1; 
   xsC = 0.5 * (xsmin + xsmax);
   ysC = 0.5 * (ysmin + ysmax);
   k1 = LARGE/(X__max+1); 
   k2 = LARGE/(Y__max+1);
   k = min2(k1, k2); 
   hk = k/2;			  // k = 50, hk = 25 with VGA
   XLmax = k * (X__max+1);
   YLmax = k * (Y__max+1);
   // Pixel coordinates: Xpix = to_pix(X) and Ypix = to_pix(Y)
   XLC = XLmax/2;
   YLC = YLmax/2;
   XLCreal = XLC + 0.5;
   YLCreal = YLC + 0.5;
   fx = XLmax/xsrange; 
   fy = YLmax/ysrange;
   f = 0.95 * (fx < fy ? fx : fy);
   zfactor = (zemax-zemin==0) ? LARGE : LARGE/(zemax - zemin);

   //integer points 
   V = Allocmem(struct vertex, nvertex);
   if (V == NULL) return(3);

   // Initialize vertex array:
   for (i=0; i<nvertex; i++)
   {  
      if (vt[i].z < -1e5)
      {  V[i].used = 0; continue; // V[i] not in use
      }  
      V[i].used = 1;
      if (vt[i].z==0)vt[i].z=1e-5;
      xs = vt[i].x / vt[i].z; //normaliseren naar Z
      ys = vt[i].y / vt[i].z;
      V[i].X = XLarge(xs);    // large integer caculation
      V[i].Y = YLarge(ys);
      V[i].Z = ZLarge(vt[i].z);
   }

   free(vt);

   kk=0;
   for (c=newPuntCount+newEdgCount; c<newPuntCount+newEdgCount+newFacCount*DOUBLEFACE; c++){
       pmy1=&myFac[c];
       pnt1=pmy1->p->new;
       pmy2=pmy1->nxt;
       pnt2=pmy2->p->new;
       pmy1=pmy2->nxt;
nexttry:
       pnt3=pmy1->p->new;
       cc = orienta(pnt1, pnt2, pnt3);
       //determinant 0 -> 3 selected points on same line
       if (cc==0){
          if (pmy1->nxt!=NULL){
             pmy2=pmy1->nxt;
             pmy1=pmy2;
             goto nexttry;
          }
       }
       if (cc<=0)myInf[c].backface=1;
       else {
          kk++;
          myInf[c].backface=0;   
          complete_infobase(c, pnt1, pnt2, pnt3);
          findcolorrange(c);
       }
   }

   delta = 0.999 * (ncolors - 1)/(rcolormax - rcolormin + 0.001);

   if (newPuntCount+newEdgCount+kk==0)goto nothingtodisplay;

   myTab = Allocmem(struct myTable, newPuntCount+newEdgCount+kk);
   if (myTab == NULL) return(3);

   for (c=0; c<newPuntCount; c++){
       myInf[c].Z=V[myFac[c].p->new].Z;
       myTab[c].Z=myInf[c].Z;
       myTab[c].myfacnr=c;
   }
   for (c=newPuntCount; c<newPuntCount+newEdgCount; c++){
       myInf[c].Z=(V[myFac[c].p->new].Z+V[myFac[c].nxt->p->new].Z)/2;
       myTab[c].Z=myInf[c].Z;
       myTab[c].myfacnr=c;
   }
   cc=newPuntCount+newEdgCount;
   for (c=newPuntCount+newEdgCount; c<newPuntCount+newEdgCount+newFacCount*DOUBLEFACE; c++){
       if (myInf[c].backface==0){

          settruecolor(c);

          myTab[cc].Z=myInf[c].Z;
          myTab[cc].myfacnr=c;
          cc++;
       }
   }

   myqsort(myTab, cc);

   for (c=cc-1; c>=0; c--){

       kk=myTab[c].myfacnr;

       if (myInf[kk].pntcnt>=3){
 
          //draw faces
          pmy1=&myFac[kk];
          set_color(myInf[kk].color+1);
          AreaMove(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
          goto getnextpointinface2;
drawnextpointinface3:
          AreaDraw(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
getnextpointinface2:
          if (pmy1->nxt!=NULL){
             pmy2=pmy1->nxt;
             pmy1=pmy2;
             goto drawnextpointinface3;
          }
          //draw last line 
          AreaDraw(DB_rport,to_pix(V[myFac[kk].p->new].X),to_pix(V[myFac[kk].p->new].Y));
          AreaEnd(DB_rport);

          if (displaylines==0){
             //draw lines on faces
             pmy1=&myFac[kk];
             set_color(myInf[kk].layer->number%4+11);
             Move(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
             goto getnextpointinface3;
drawnextpointinface4:
             Draw(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
getnextpointinface3:
             if (pmy1->nxt!=NULL){
                pmy2=pmy1->nxt;
                pmy1=pmy2;
                goto drawnextpointinface4;
             }
             //draw last line 
             Draw(DB_rport,to_pix(V[myFac[kk].p->new].X),to_pix(V[myFac[kk].p->new].Y));

             //draw points on faces
             pmy1=&myFac[kk];
             set_color(15);
drawnextpointinface5:
             WritePixel(DB_rport,to_pix(V[pmy1->p->new].X),to_pix(V[pmy1->p->new].Y));
             if (pmy1->nxt!=NULL){
                pmy2=pmy1->nxt;
                pmy1=pmy2;
                goto drawnextpointinface5;
             }
          }

      } else if (myInf[kk].pntcnt==2){

          //draw lone edges
          set_color(myInf[kk].layer->number%4+11);
          Move(DB_rport,to_pix(V[myFac[kk].p->new].X),to_pix(V[myFac[kk].p->new].Y));
          Draw(DB_rport,to_pix(V[myFac[kk].nxt->p->new].X),to_pix(V[myFac[kk].nxt->p->new].Y));
          set_color(15);
          WritePixel(DB_rport,to_pix(V[myFac[kk].p->new].X),to_pix(V[myFac[kk].p->new].Y));
          WritePixel(DB_rport,to_pix(V[myFac[kk].nxt->p->new].X),to_pix(V[myFac[kk].nxt->p->new].Y));

      } else if (myInf[kk].pntcnt==1){

          //draw lone points
          set_color(myInf[kk].layer->number%4+11);
          WritePixel(DB_rport,to_pix(V[myFac[kk].p->new].X),to_pix(V[myFac[kk].p->new].Y));
      } 
       
   }
 
   free(myTab);

nothingtodisplay:

   free(V);

   return(0);
}

void putpixel(int X, int Y, int activecolor)
{
UBYTE bitnumber, CurrentValue;
int depth, Offset;

Offset = Y*WIDTH/8+(X/8);
bitnumber = (X > 7) ? X % 8 : X;

  for (depth=0; depth<SCR1_DEPTH; depth++){
    if (DB_rinfo->BitMap==DB_bmap1) displaymem = DB_bmap1->Planes[depth]+Offset;
    else displaymem = DB_bmap2->Planes[depth]+Offset;

    CurrentValue = *displaymem;
    if ( (activecolor & (1 << depth)) != 0) {
       switch (bitnumber) {
       case 7: *displaymem = CurrentValue | 1; break;	//%0000 0001
       case 6: *displaymem = CurrentValue | 2; break;	//%0000 0010	
       case 5: *displaymem = CurrentValue | 4; break;	//%0000 0100
       case 4: *displaymem = CurrentValue | 8; break;	//%0000 1000
       case 3: *displaymem = CurrentValue | 16; break;	//%0001 0000		
       case 2: *displaymem = CurrentValue | 32; break;	//%0010 0000
       case 1: *displaymem = CurrentValue | 64; break;	//%0100 0000	
       case 0: *displaymem = CurrentValue | 128; break;	//%1000 0000
       }
    } else {
       switch (bitnumber) {
       case 7: *displaymem = CurrentValue & 254; break;	//%1111 1110
       case 6: *displaymem = CurrentValue & 253; break;	//%1111 1101
       case 5: *displaymem = CurrentValue & 251; break;	//%1111 1011
       case 4: *displaymem = CurrentValue & 247; break;	//%1111 0111
       case 3: *displaymem = CurrentValue & 239; break;	//%1110 1111 
       case 2: *displaymem = CurrentValue & 223; break;	//%1101 1111
       case 1: *displaymem = CurrentValue & 191; break;	//%1011 1111
       case 0: *displaymem = CurrentValue & 127; break;	//%0111 1111
       }        
    }
  }
}

static int code(float x, float y)
{  return ((x<xmin)<<3) | ((x>xmax)<<2) |
          ((y<ymin)<<1) | (y>ymax);
}

int triangul(int *pol, int n, struct trianrs *nrs,
             int orienta(int P, int Q, int R)){
/*
      Triangulation of a polygon with successive vertex numbers
      pol[0],..., pol[n-1], in counter-clockwise order.
      With three given vertex numbers P, Q, R, function
      orienta must determine their orientation:
      Negative = clockwise
      Zero     = collinear
      Positive = counter-clockwise
      If triangulation is possible, the resulting triangles are
      successively stored in array 'nrs'. Triangle j has vertex
      numbers nrs[j].A, nrs[j].B, nrs[j].C.
      Memory space for array 'nrs' must be supplied by the caller.
      Return value: the number of triangles found, or
                    -1 if no proper polygon or vertices clockwise.
                    -2: not enough memory.
*/
   int *ptr, q, qA, qB, qC, r=-1, // -1 used as 'NULL'
        i, i1, i2, j, l, m, ok, ortB, *ort, polconvex=1,
        A, B, C, P, collinear;
    if (n < 3) return -1; // No polygon
    if (n == 3)
    {  nrs[0].A = pol[0]; nrs[0].B = pol[1]; nrs[0].C = pol[2];
       return 1;         // Only one triangle
    }
    ort = calloc(n,sizeof *ort); // new int[n];  ort[i] = 1 if vertex i is convex
    if (!ort) return -2;
    do
    {  collinear = 0;
       for (i=0; i<n; i++)
       {  i1 = (i < n - 1 ? i + 1 : 0);
          i2 = (i1 < n - 1 ? i1 + 1 : 0);
          ort[i1] = orienta(pol[i], pol[i1], pol[i2]);
          if (ort[i1] == 0)
          {  collinear = 1;
             for (j=i1; j<n-1; j++) pol[j] = pol[j+1];
             n--; break;
          }
          if (ort[i1] < 1) polconvex = 0;
       }
    }  while (collinear);
    if (n < 3) return -1;
    if (polconvex)  // Use diagonals through vertex 0:
    {  for (j=0; j<n-2; j++)
       {  nrs[j].A = pol[0];
          nrs[j].B = pol[j+1];
          nrs[j].C = pol[j+2];
       }
       free(ort); return n-2;
    }
    ptr = calloc(n,sizeof *ptr); // new int[n]; ptr[i] is i's successor in linked list
    if (!ptr) return -2;
    // Set up a circular linked list of vertex numbers:
    for (i=1; i<n; i++) ptr[i-1] = i;
    ptr[n-1] = 0;
    q = 0; qA = ptr[q]; qB = ptr[qA]; qC = ptr[qB];
    j = 0;     // j triangles stored so far
    for (m=n; m>2; m--)  // m remaining nodes in circular list.
    {  for (l=0; l<m; l++)
       {  // Try triangle ABC:
          ortB = ort[qB]; ok = 0;
          // B is a candidate if it is convex:
          if (ortB > 0)
          {  A = pol[qA]; B = pol[qB]; C = pol[qC];
             ok = 1; r = ptr[qC];
             while (r != qA && ok)
             {  P = pol[r]; // ABC counter-clockwise:
                ok = P == A || P == B || P == C ||
                orienta(A, B, P) < 0 ||
                orienta(B, C, P) < 0 ||
                orienta(C, A, P) < 0;
                r = ptr[r];
             }
             // ok means: P coinciding with A, B, or C
             //           or outside ABC
             if (ok)
             {  nrs[j].A = pol[qA];
                nrs[j].B = pol[qB];
                nrs[j++].C = pol[qC];
             }
          }
          if (ok || ortB == 0)
          {  // Cut off triangle ABC from polygon:
             ptr[qA] = qC; qB = qC; qC = ptr[qC];
             if (ort[qA] < 1)
                ort[qA] = orienta(pol[q], pol[qA], pol[qB]);
             if (ort[qB] < 1)
                ort[qB] = orienta(pol[qA], pol[qB], pol[qC]);
             while (ort[qA] == 0 && m > 2)
             {  ptr[q] = qB; qA = qB;
                qB = qC; qC = ptr[qC]; m--;
             }
             while (ort[qB] == 0 && m > 2)
             {  ptr[qA] = qC; qB = qC; qC = ptr[qC]; m--;
             }
             break;
          }
          q = qA; qA = qB; qB = qC; qC = ptr[qC];
       }
    }
    free(ptr); return j; // j triangles
}

static double v11, v12, v13, v21, v22, v23, v32, v33, v43;
void coeff(double rho, double theta, double phi)
{  double costh, sinth, cosph, sinph;
   // Angles in radians: 
   costh = cos(theta); sinth = sin(theta);
   cosph = cos(phi); sinph = sin(phi);
   v11 = -sinth; v12 = -cosph*costh; v13 = -sinph*costh;
   v21 = costh;  v22 = -cosph*sinth; v23 = -sinph*sinth;
                 v32 = sinph;        v33 = -cosph;
                                     v43 = rho;
}

void eyecoord(struct vec3 pw, float *x, float *y, float *z)
{
   *x = v11 * pw.x + v21 * pw.y;
   *y = v12 * pw.x + v22 * pw.y + v32 * pw.z;
   *z = v13 * pw.x + v23 * pw.y + v33 * pw.z + v43;
}

void perspective(struct vec3 p, float *px, float *py)
{  float x,y,z;
   eyecoord(p, &x, &y, &z);
   if (z < -0.000005 || z > 0.000005)
      {
      *px = x/z;
      *py = y/z;
      }
}

double dotproduct(struct vec3 a, struct vec3 b)
{  return a.x * b.x + a.y * b.y + a.z * b.z;
}

int XLarge(double xs) {return (int)(XLCreal + f * (xs - xsC));}
int YLarge(double ys) {return (int)(YLCreal + f * (ys - ysC));}
int ZLarge(double ze) {return (int)((ze-zemin)*zfactor + 0.5);}
double xScreen(int X) {return xsC + (X - XLC)/f;} 
double yScreen(int Y) {return ysC + (Y - YLC)/f;}
double zEye(int Z)    {return Z/zfactor + zemin;}
int min2(int i, int j) {return i < j ? i : j;}
int min3(int i, int j, int k) {return min2(i, min2(j, k));}

void myqsort(struct myTable *a, int n)
{  int ii, jj;
   struct myTable x, w;
   do {
      ii=0; jj=n-1;
      x = a[jj/2];
      do {
         while (a[ii].Z < x.Z) ii++;
         while (a[jj].Z > x.Z) jj--;
         if (ii < jj) {
            w = a[ii]; 
            a[ii] = a[jj]; 
            a[jj] = w;
         } 
      } while (++ii <= --jj);

      if (ii == jj + 3) {--ii; ++jj;}

      if (jj+1 < n-ii) {
         if (jj > 0) myqsort(a, jj+1);
         a += ii; n -= ii;
      }  else { 
         if (ii < n-1) myqsort(a+ii, n-ii);
         n = jj + 1;
      }
   }  while (n > 1);
}

int Orientation(int u1, int u2, int v1, int v2)
{  long det = (long)u1 * v2 - (long)u2 * v1;
   return det < -250 ? -1 : det > 250;
}

int orienta(int Pnr, int Qnr, int Rnr)
{  int O;
O =  Orientation(V[Qnr].X - V[Pnr].X, V[Qnr].Y - V[Pnr].Y,
                      V[Rnr].X - V[Pnr].X, V[Rnr].Y - V[Pnr].Y);
return O;
}

void complete_infobase(int facenr, int anr, int bnr, int cnr)
{  int ZA, ZB, ZC;
   unsigned Zmin, Zmax;
   double nx, ny, nz, ux, uy, uz, vx, vy, vz, factor, h,
      Ax, Ay, Az, Bx, By, Bz, Cx, Cy, Cz;

   ZA = V[anr].Z; ZB = V[bnr].Z; ZC = V[cnr].Z;
   Az = zEye(ZA); Bz = zEye(ZB); Cz = zEye(ZC);
   Ax = xScreen(V[anr].X) * Az; Ay = yScreen(V[anr].Y) * Az;
   Bx = xScreen(V[bnr].X) * Bz; By = yScreen(V[bnr].Y) * Bz;
   Cx = xScreen(V[cnr].X) * Cz; Cy = yScreen(V[cnr].Y) * Cz;
   // Computation of vector product normvec = (B-A) x (C-A):
   ux = Bx - Ax; uy = By - Ay; uz = Bz - Az;
   vx = Cx - Ax; vy = Cy - Ay; vz = Cz - Az;
   nx = uy * vz - uz * vy;
   ny = uz * vx - ux * vz;
   nz = ux * vy - uy * vx;
   //  The equation of the plane is nx . x + ny . y + nz . z = h.
   h = nx * Ax + ny * Ay + nz * Az;
   factor= (nx==0 && ny==0 && nz==0) ? 1e5 : 1/sqrt(nx * nx + ny * ny + nz * nz);

   pmy1=&myFac[facenr];
   Zmax=V[pmy1->p->new].Z;
   Zmin=V[pmy1->p->new].Z;
   goto nextmyfac;
Zrangenext:
   if (V[pmy1->p->new].Z>Zmax)Zmax=V[pmy1->p->new].Z;
   if (V[pmy1->p->new].Z<Zmin)Zmin=V[pmy1->p->new].Z;
nextmyfac:
   if (pmy1->nxt!=NULL){
      pmy2=pmy1->nxt;
      pmy1=pmy2;
      goto Zrangenext;
   }

   myInf[facenr].normal.x = nx * factor;
   myInf[facenr].normal.y = ny * factor;
   myInf[facenr].normal.z = nz * factor;
   myInf[facenr].h        = h * factor;
   myInf[facenr].Z        = (Zmin + Zmax)/2;
}

void findcolorrange(int i)
{  float rcolor;
   struct vec3 norm;
   norm.x = myInf[i].normal.x;
   norm.y = myInf[i].normal.y;
   norm.z = myInf[i].normal.z;
   rcolor = dotproduct(norm, lightvector);
   if (rcolor < rcolormin) rcolormin = rcolor;
   if (rcolor > rcolormax) rcolormax = rcolor;
}

int to_pix(unsigned X)  // Rounding and converting
{  return (int)(X + hk)/k;
}

void set_color(int color)
{  
SetAPen(DB_rport,color);
// activecolor=kleur; // used for CPU version
}

void settruecolor(int i)
{  struct vec3 normal;
   int color;
   float rcolor;
   normal.x = myInf[i].normal.x;
   normal.y = myInf[i].normal.y;
   normal.z = myInf[i].normal.z;
   rcolor = dotproduct(normal, lightvector);
   color = 1 + (rcolor - rcolormin) * delta;
   if (color < 0) printf("Negative color code");
   if (color >= ncolors) printf("Color code too large"); // (in case of a programming error)
   myInf[i].color = color;
}

void fill_window(int i, int Xmin, int Xmax, int Ymin, int Ymax)
{  // Fill entire window with color determined by normal vector
   // of triangle i
   set_color(triangles[i].ptria->color);
   Xmin /= k; Xmax /= k;
   Ymin /= k; Ymax /= k;
   RectFill(DB_rport,Xmin,Ymin,Xmax,Ymax);	
}

void swap(int *x, int *y)
{  int t;
   t  = *x;
   *x = *y; 
   *y = t;
}

int intersectvertical(struct vec_int *A, struct vec_int *B, int X,
   int Ymin, int Ymax)
   // Does line segment AB have points in common with the
   // vertical line segment {(X, Ymin), (X, Ymax)}?
{  int XA=A->X, YA=A->Y, XB=B->X, YB=B->Y;
   long dX, dY, YdX;
   if (XA < X && XB < X || XA > X && XB > X) return 0;
   if (XA == X && YA >= Ymin && YA <= Ymax ||
       XB == X && YB >= Ymin && YB <= Ymax) return 1;
   if (XA == XB)
      return XA == X &&
      ((long)(YA - Ymax) * (YB - Ymax) < 0 ||
       (long)(YA - Ymin) * (YB - Ymin) < 0);
   if (XA > XB) {swap(&XA, &XB); swap(&YA, &YB);}
   dX = XB - XA; dY = YB - YA;
   YdX = YA * dX + (X - XA) * dY;
   return YdX >= Ymin * dX  &&  YdX <= Ymax * dX;
}

int intersecthorizontal(struct vec_int *A, struct vec_int *B, int Y,
   int Xmin, int Xmax)
   // Does line segment AB have points in common with the
   // horizontal line segment {(Xmin, Y), (Xmax, Y)}?
{  int XA=A->X, YA=A->Y, XB=B->X, YB=B->Y;
   long dX, dY, XdY;
   if (YA < Y && YB < Y || YA > Y && YB > Y) return 0;
   if (YA == Y && XA >= Xmin && XA <= Xmax ||
       YB == Y && XB >= Xmin && XB <= Xmax) return 1;
   if (YA == YB)
      return YA == Y &&
      ((long)(XA - Xmax) * (XB - Xmax) < 0 ||
       (long)(XA - Xmin) * (XB - Xmin) < 0);
   if (YA > YB) {swap(&XA, &XB); swap(&YA, &YB);}
   dX = XB - XA; dY = YB - YA;
   XdY = XA * dY + (Y - YA) * dX;
   return XdY >= Xmin * dY  &&  XdY <= Xmax * dY;
}

void putpix(int X, int Y)
{	
putpixel(X, Y__max - Y, activecolor);
}

void draw_line(int X1, int Y1, int X2, int Y2)
{  
   Drawline(X1, Y__max - Y1, X2, Y__max - Y2);
}

void horline(int xleft, int xright, int y)
{
   Drawline(xleft, Y__max-y, xright, Y__max-y);
}

void Drawline(int xP, int yP, int xQ, int yQ)
{  int x=xP, y=yP, D=0, dx=xQ-xP, dy=yQ-yP, c, M,
       xinc=1, yinc=1;
   if (dx < 0){xinc = -1; dx = -dx;}
   if (dy < 0){yinc = -1; dy = -dy;}
// new code
//   if (dy = 0 && dx > 8) BlitHorLine(xP,yP,dx);
// einde new code
   if (dy < dx)
   {  c = 2 * dx; M = 2 * dy;
      while (x != xQ)
      {  putpix(x, y);
	 x += xinc; D += M;
	 if (D > dx){y += yinc; D -= c;}
      }
   }  else
   {  c = 2 * dy; M = 2 * dx;
      while (y != yQ)
      {  putpix(x, y);
	 y += yinc; D += M;
	 if (D > dy){x += xinc; D -= c;}
      }
   }
}

static void checkmem(void *p)
{  if (p == NULL) printf("Not enough memory in 'fill'");
}

void fill(int *X, int *Y, int n)
{  int x, y, i, ymin=10000, ymax=0, j, ny, i1, xP, yP, xQ, yQ,
       temp, dx, dy, m, dyQ, E, xleft, xright;
   typedef struct element 
   {  int xP, yQ, dx, dy, E; 
      struct element *next;
   }  *eltptr;
   eltptr *table, p, start, end, p0, q;

   x = X[n-1]; y = Y[n-1];
   for (i=0; i<n; i++)
   { 
      draw_line(x, y, X[i], Y[i]);
      x = X[i]; y = Y[i];
      if (y < ymin) ymin = y;
      if (y > ymax) ymax = y;
   }
   ny = ymax - ymin + 1;
   table = calloc(ny, sizeof(eltptr)); 
   checkmem(table);
   for (j=0; j<ny; j++) table[j] = NULL;
   for (i=0; i<n; i++)
   {  i1 = i + 1; 
      if (i1 == n) i1 = 0; // i1 is i's successor
      xP = X[i]; yP = Y[i];
      xQ = X[i1]; yQ = Y[i1];
      if (yP == yQ) continue;
      if (yQ < yP)
      {  temp = xP; xP = xQ; xQ = temp;
         temp = yP; yP = yQ; yQ = temp;
      }
      p = malloc(sizeof(struct element)); checkmem(p);
      p->xP = xP; p->dx = xQ - xP;
      p->yQ = yQ; p->dy = yQ - yP;
      j = yP - ymin;
      p->next = table[j]; table[j] = p;
   }

   start = end = malloc(sizeof(struct element)); // Sentinel
   checkmem(start);
   for (j=0; j<ny; j++)
   {  y = ymin+j; // Build or update active edge list:
      p = start;
      while (p != end)
      {  if (p->yQ == y)
         {  // Delete list element *p:
            if ((q = p->next) == end) end = p; else *p = *q;
            free(q);
         }  else // Update list element *p:
         {  if ((dx = p->dx) != 0)
            {  x = p->xP;
               dy = p->dy;
               E = p->E;
               m = dx/dy; // Integer division!
               dyQ = 2 * dy;
               x += m; E += 2 * dx - m * dyQ;
               if (E > dy || E < -dy)
               {  if (dx > 0) {x++; E -= dyQ;}
                         else {x--; E += dyQ;}
               }
               p->xP = x;
               p->E = E;
            }
            p = p->next;
         }
      } 
      // End of updating the elements (if any) of active list
      // Edges may now be added to active edge list:
      p = table[j];
      while (p != NULL)
      {  x = end->xP = p->xP; yQ = p->yQ;
         dx = p->dx; dy = p->dy; q = start;
         while (q->xP < x ||
                q->xP == x && q != end && 
                (long)q->dx * dy < (long)dx * q->dy) 
                   q = q->next;
         p0 = p; p = p->next;
         if (q == end) end = p0; else *p0 = *q;
         q->xP = x; q->yQ = yQ;
         q->dx = dx; q->dy = dy;
         q->E = 0; q->next = p0;
      }
      // Draw line segments:
      for (p=start; p!=end; p=p->next)
      {   xleft = p->xP + 1; 
          p = p->next;
          xright = p->xP;// - 1 @@
          if (xleft <= xright)
          {
  	  horline(xleft, xright, y);
          } 
      }
   }
   p = start;
   while (p != end)
   {  p0 = p; p = p->next; free(p0);
   }

   free(start); free(table); //tabel was delete[] 
}

int DisplaySignature(void){

int wi=155;
int he=128;
int i,j;
int value;
char ASCIIHEX;
char piccy[]=
"00000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000223222222332211122222222111111111122121122222221111112333333333333343332"
"23333323333333334455455545455555555555555545444444444444444444444444444444444444"
"444778757777665432354444333322222344444442455544433222225888888998999aa8aa778877"
"777788778889ddddeededddddddddeeeeedeeeeeededdddddddddddddddddddddcdcdccccdeccc77"
"76565545666654443332222222223333333432333332222222148788989887778888977877777778"
"877779bcdddddddddddddddddddddeddedddddddddddddddddddddddcccccccccccccdbcc5664222"
"12666666311112212221112223333333112222222222213877666655555455676676777767877777"
"bccddddddddddddddddddddddddddddddddddddddddddcdccccdccccccccccccdbcc543122112656"
"655211112211111222222333333112234455554222444444545555444467787777767878877bccdd"
"ddddddddddddddddddddddddddddddddddddccdcdcddcdcdcccccccccccdbcc54211111366666521"
"11332111111111122233433667777776552213444434434454434bbbbbbaaa88988888bcdddddddd"
"ddddddddddddddddddddddddddddddcdddddcccdccccccccccccccdbcc3201000036666551111432"
"111111111234333334777777766544333333444555554435bbbcccccbbaaaa999cddddddccccaabc"
"dddddddddddddddddddddddddddcdccdcccccccccccccccccdabc000001104566654111253112311"
"1122144544422467777765437433444444443444435bbbcccccbaaa99999bccccccccb866679accd"
"dddddddddddddddddddddcdccdcdccccccccccccccccdabb00000000466766411123211144221341"
"26566632323443321117543333333444454445bcbccccbbaa99999acbb9aaccb96666666789ccddd"
"ddddddddddddddcccccdcccccccccccccccccccdabb0020000001233420111121135554335423555"
"540112221111127642444455556665566cccccccbaa99b997cdcaaa9cbca7876766666ccccdddddd"
"ddddddddddddddcdcdccccccccccccccccdabb024300000000000000001014455545544324444121"
"1221111226555555566658865546bccccccba9aaca98cdbaaaabcec8ca7975456bbccddddddddddd"
"ddddddddddcdcccccccccccccccccdabb04530100000000000000011234445732331212213111111"
"11153434224478889986776ccccccca98aaca98cdbbaabbdec8cc9ca8646cbcddddddddddddddddd"
"ddddccccccccccccccccccccdabb0773333251001122222211123223342211111111111022231054"
"11304569899a986876bdccccba979aca98cdbba9bbddb8dc9dbada6bccdddddddddddddddddddddd"
"cccccccccccccccccccdabb045221123210001111111111111121122224457666522432105431424"
"769899aa85876adccccb9769aba88ddaababccca8cc9cbada6bbcdddddddddddddddddddddddccdc"
"ccccccccccccccdabb00000000111111111121111111122222787988999762353110431130256979"
"9b975876accccbb9759aca88ddbabb9abc96a98caada7bbcddddedddddddddddddddcdcccccccccc"
"ccccccccccabb00011011111111111111111122232222797887987763132110442140466889ab965"
"976adcccba9659aba88ddbabaabcc96766879c96bccdddddddddddddddddddddddcccccccccccccc"
"ccccdabb0111111111112112111121112223212279798884576225311034213125587999865876ac"
"ccbba8658aca89ddbab9abcc88b86866767bbcddddddddddddddddddddddcddccccccccccccccccd"
"abb12211211111121111111111122232222797997955863131110341130365879b98658769ccbbba"
"8658ab989ddbbb9bccd79cb9c88a67bccdddddddddddddddddddddcdcdcccccccccccccccccabb11"
"11121111111111111121112123222279799795576225311035213136477ab98658768cbbbba7648a"
"ca8adcbbaabbcc79cbadaad87bbcddddddddddddddddddddcdcdcccccccccccccbccccabb1121121"
"111111111111111112122221279889795576214211034012025477bb98559768cba9875548aba7ad"
"cbbaabccc69cbadabd96bcddddededddddddddddddddcdcdcccccccccccccbbccabb112112111111"
"2111111121111112211279789795576315211035213136478baa8558768cb321121179b97adcab9b"
"bccd69caacaad97bcdddddddddddddddddddddddddccdcccccccccccccccabb11111211111111111"
"111111111112112797897a5576326211035012115379ba98448768ba211121079b96adcab9bccdc6"
"7b89c9bd87cccdddddddddddddddddddddddccccdccccccccccccccabb1111111111111111111111"
"11111221227878a79557630421102621223746a9765359768ba211121079b96adcaaabcccc566668"
"79c77bcdddddeddddddddddddddddddcdcccccccccccccccccabb011111111111111111111111111"
"22112797897a667631631102611211637a4223248667ca221021079b96adcaaacccdd59b67856767"
"cccddddddeddddddddddddddddcccdcccccccccccccccabb11111111111111111111211122122121"
"7978979998740412102511212657a4222558767ca232121079b96adcaabcccdb5ac9ac79a67bcddd"
"ededdddddddddddddddddddcccccccccccccccccabb1111111111111111111111212113122279789"
"89998741531102621221637a4222449767ca111121179a96bdcaacccdd95acabd9ad87ccdddddddd"
"deddddddddddddddddcccccccccccccbcccabb011111111111111111111111211212227978989997"
"740511101511211547a4223459767ba111121079a86bcb9abcccc65acabdabd87ccdddddddeddddd"
"dddddddddddddcccccccccccccbccdabb01111111111111111121111121121222798898899874053"
"2101621222746a4244459767aa222232245544445567889855acabc9bd87ccdddddedddddddddddd"
"dddddddcdccccccccccccccccabb1111111111111111111111112112122279889899887405221015"
"1111153697744558767b91103333344444345655556557b7ac9bd87ccddddeeedddddddddddddddd"
"dddccdcccccccccbccccabc011011111111111111211111211212127988988988650422100512122"
"756a7944559667aa2113333344433346a69a56755666869b67ccddddddddddddddddddddddddddcc"
"cccccccccccbccdbbb011111111111111111111111112222227888988a9874053210161112163797"
"744759667a92113333334444346b8ab89c79a67856658ccdddddddddddddddddddddddddcccccccc"
"cccccbbcccbbb0111111111111111111111111123211279889889987404121005111216469874575"
"9767a92113334344444346b9bcaac9ac8ac8ab58ccddddedddddddddddddddddddcccccccccccccc"
"cbbccabb011111111111111111211111112221127988998a8875054210052212164698834658768a"
"82113333343444346b8bc9acabc8bd8cd78ccddddeddddddddddddddddcccccccccccccccbbbbbbb"
"abb111111111111111111211111112221227988998a8975042210040212264688845759768982213"
"334334443347b8bc9acabc8bd9cd89ccddddddddccccccccccbcbcbbbbbbbcbbbbbbcbbbbbbaab01"
"1111111111111111111121112321127988988a8976053210043334555567845758669a8211333334"
"4434346868c7ac9ac9bc9bc9abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbba991111111"
"11111111111111121112321227988997a8a75042210043455565445655667668a811133333334444"
"56678889aaaaaabaaabbbbbbbbbbbbbbbbbbbbbbbbbcbbbbcbcbbbbbbbbcbbbbba99111111111111"
"111111211121112321117988997a8a760432112345565554333344566689801133333347889999aa"
"aaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcbbbccbcbbbbbbbbbca9911111111111111111"
"1111121111211117988997a99751533234456555443322222345679812133333369999999aaaaaaa"
"aaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcbccbcbbcbbbbbca990111111111111111111111"
"21113321217998a97a8a76033333445455433222122233456882113333337999999aaaaaaaaaaaaa"
"bbabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbabbbbaaaabbbbbbbca99011111111111111111111121111"
"211126998997a8976043344434555543212122323345774213333338999999aaaaaaaaaaaaaaaaaa"
"aaaaaaaa998888888888887777878888888888bbbbbbca9911111111111111111121112111232121"
"6998997a89761233344334455443221123333356774133333389999999a999999988888887777777"
"777777777777777777777777777777787bbbbbccaa9111111111111111111211121112322216998a"
"97a89863333343333322333322112343345677333333389999877777777777777777777777767666"
"6666666665555555555555554587abbbbbca99111111111111111111111121112221216998997a99"
"75223324343322223333221134433567875433349999967777666666666565555555555555555555"
"55555555555555555543587bbbbccca991111111111111211112111211223122159989a7a9953223"
"22444432233433333323554346777654434989986865555555555555555555555555555555555545"
"555554444444432687bbbbbcca9a1011111111111111112111211122112149989a8a983223221444"
"43445555444444345531567876443599998797655555454444444444444444444444444555555545"
"5554444433688bbbbcccaa91111101121131112112111211123212249989a89a5222222134344566"
"66665544554665235778765459999779985444344444455555555555555555555555655555666565"
"55544688bbcccccaa91111111111575221212111211122112139989a898222222122434566677776"
"65555566643357786546999977999444445555555565565555555556555555666666667666566544"
"788bbbcccca9a1111111112698322112111211133212139999b88412222121143456777777776656"
"677764346676656999977998445555566666667676765555566555556666666666666665544688bb"
"ccccb9aa1111111113798411112111211122212129989a9522222221123345677767877765667778"
"6444666656999978998445555566666666555555555556555555567666676666665544789bbccccb"
"9aa1011111114899421112111211111112129989a832122211112333466656888877667787886545"
"65657999978996455555555556666555555555556555555566666666666665544788bbccccbaaa11"
"11111115898321212111211111121128998a62222111112334435577889998766778889755566556"
"999968995455555555555555555555555555555555556666666666666554889bbccccbaaa1010111"
"127896222112111211111112118998a51111111112345566789999aa987777778886566554589996"
"8995455555555555555555555656565555555557677776666666544889bbccccbaaa111111114799"
"42221121212111111121289989311111111223456688899aaab99776776788765655457999699955"
"5555656665666666666656556556566656666666666666655488abccccdbaaa10111111579932221"
"12112211112112128998921110111112455677899aaaaba987666667977445554899969994555556"
"666656666666666666666676677666666776777776655889bcccccbaaa1011111268993211223456"
"644322122118998823300111123455667899abbbbba9876666677752555479996998556666666666"
"666666666766665665555555555566666666688788acccccdbaaa1111111378a9323578998999989"
"8322218999745431111135555667899abbcccbb97766565776234547999699978855555555666666"
"6777878888877777877778888887777789778accccccaaaa1111111479a84789aaa9989999aa5222"
"1899875554111135555555556789abccccb976655568522454899869a97898988899999999999998"
"89999989888899998999999999997688bcccccdbaaa11111126799889aa999876778887332116887"
"7555322135555444555556789abcccb97655468641454799879a88899a99999999999999a9aa999a"
"aaaba9aaaaaba9aaaaaaa997688abcccccbaa91112213679989a9988876555555323211633265543"
"222455555567787656679abcccba97546965334479977aa988888899999999999999999999a99aa9"
"99a999a99aaa9aba998798bcccccdaaa9212222368999aaa97677887776652221164446753322345"
"5555666789877679abbbabbb97568566244999779a888999aaa99998888899998999999989999989"
"89999898989997688bccccccbaa91111124799aaaaa988999a99999a877779997674332245566555"
"55567777779aaa99888aa88846753699978aa8889aaaaa9988878888999a9a9aaaaaaa9aaaaaaaaa"
"a99999997688cccccccaaa9556666689aaabba9aaaa8899989aa9999999866543334567765444356"
"76777899a976656788747884799968aa88998888999999999aa99999a9aa99aa999aa9ab99a97779"
"9997788bccccccaaa9777777689aaaaa99aaaa767776789999998986654423456778765655986666"
"9aa9877665567526898899968a968abbbaaa99999999ba99999999998899998888999a9676999976"
"88bccccccaaa977777668aa9aa99abaa97445545678985443566644234567788777779877669cb98"
"8889875531579aa99967aa58abccccba9999999ba999999aaaaaaa9aaaa9aaaaa998899997788bcc"
"ccccaaa9233334699a99a9abaa985333333467895333366654334567789987788897779aa7767889"
"976203689a779679a67abbaacccb9999999999a9aaaaaaaab9aabaabb99a99999aa97788bcccccba"
"aa922223478aa999abb99873333333355663333346655444566789a99999887779a9865566787621"
"2468757a878a879bb8bbaabb9999999999999999889999999999999aaaaaaa98789cccccccaaa922"
"333579aa9999aa986443433333333333333146654445667899aaaaa987789a998755566763223555"
"58aa689958ab9a89abba9889899999999999999999999999999aaaa9997789cccccccaaa93333357"
"8aa999999a6444455444444433443310344444566789aabbba977779999778675567323335457ab6"
"689679bb7689abb988899999999999989999999999999999999997789cccccccaaa933334689aa98"
"9a9a945678898444444444444101244445567899abbba9877899998788877774433464579a648876"
"89b8579abcc7888888888888888888888888889999999988788accccccbaa993334579aaa9899aaa"
"99999aaa844444444443112334445567899abbba876789999a988888963443585578a73677669aa6"
"89abcd9878888888888788888888888888899999887688acccccccaaa93345689baa9899aabaaa99"
"99aa85544444442113334445567899abba8676789999aaa99a99544335975689844679669a789abd"
"db978778888999788999887788888888899887688accccccbaaa9335679aaa98999abba999979999"
"855444443212343445566789aaaa9677789a999abbaaa99454436887579943468976769aacddc878"
"777788988788888888888888988997887678accccccbaaa9346789aaa9899aaaba88885788998555"
"44432033344455667899999866678aba99bbbbaa98554446988679944346996579abcddc98877888"
"8778888778887777789888888887688accccccbaaa945689aaaa899aabaaa7776556789a75544431"
"1434344567688999888664479aa99abbbaa976444468887686444458a9689abcddda889999999999"
"98999899999999999998987688accccccbaa995679aaba9899aaa999766554557898555553023434"
"44566779999888876567868aaaaaaa97644436788855454444797789abcddd989999899a99899a99"
"89888a999aa9aaa987788bccccccaaa996689abb9999aaa999755555555579965555404444445567"
"779a967888888778789a9aaa99744444677885555566667889abcddddb8888889988899888989989"
"9899998999887688bccccccaaaa8679aaaa999aaaa986555555555469995555424444444567779aa"
"7556789a9999999aaa9985444446778766678999aa99aabcddddda98989999999988889888888999"
"99999998788bbcccccbaa98789aaaa999aaa9986555555555457997765544444444456779aa98766"
"67899999989999854444446777667899aabbbbaabbcdddddc9999889999999999999999999999999"
"997788bccccccaaa99789aaa999aaaa9998666555555556899985544544344456778999888888777"
"78889999986778665777667899aabcbbccbbccccdeddbaa9999aa9aa99aaa9aa9989888889988778"
"8bccccccaaa98789aa9999aaa9999986666666556789998654455444445677899998899999877779"
"9998899abba9887678999aabccbcdccccdddeeeca99999988899998888999999889999987788bbcc"
"cccaaa99999a999899999999998666676567788876644555544445667788999989999a99aaaa9989"
"aabcccbba978999aabbccbcdddddddeeeedca999999999999998999999999a999987788bccccccaa"
"a9899aa9998999999999997678886778876544445555434455666788999999999999aaa9899a9abb"
"bcbbaaa9999aabbbbbcdcddddeeeeedcaaaaaa9aaaaba99aaaaaa999a999987788bbcccccaaa9899"
"a99998999999a999999898977787655344555555344455567788999999999999a98898667899aaaa"
"a999999aabaaaabbcdddeeeeedcba9aa99988889999988999999888887788cbcccccaa9999999999"
"99aaaaaaaaa999999887887654445555555434455556777889999999999988853333467889998888"
"8899aa999abbcccdeeeeddb988999999999999988999888887776789bbcccccaaa9999999999abab"
"aaaaba9aa99998889854444556665554444555667778999988889987732222222357788876777778"
"77789aabccddeeeedc999999999999999999999999988776788bbcccccaaa999999999abbbbbbaaa"
"baaaa9998889c7544556666665433455556777899998888887322222222114767766656666666778"
"9aabccdeeeedca77777777777777777788888777765788bbbbcccaaa987889999abbccbaaaaaaaa9"
"889a999ac75556666666554344555567789999887787411111111110567675455455555566789abc"
"cddddedca77777777777777777888888888888889bbcccccaaa997888889aaabbaaaaa9988888acb"
"999ac8666777776655334455566788898877774211111111111566776666677777766789aabccddd"
"dddb8888888777777777777777777777778abbbcccc9aa9978888999aaaa99999889a9889cba99ab"
"ca677777777655434455566778888776422221111111115667777667777788767789abbcddddedca"
"9aaaaaaaaaaaaaabbabbaaaaaaaaaabccccccbaaa998888899999999989999abb999acbbaaacdc87"
"7777777655434455556667776632211111111111166667766666666777667899abbccdddddb99aaa"
"abaaabbbbbbbbbbbbcccccccccccccccbaaa9978889888888888aaaaaabba899bccbaabcdda78888"
"77765544344555566665222111111111111166666766667777778776789aabccdddddca9aaaaabbb"
"bbbbbbbbbcbcbbcccccccccccccbaaa997878787777789aaaaa9abbb89abcccbaaacddb888888776"
"6554444455556632111111111111010666666666666677777867889abbccddddca99aaaba9aaaabb"
"bbbbbbbbccccccccccccccbaaa9877777777889aa9aaaaaabcca8abbcdcbaaccddb9988887776655"
"555555558711111111111000016566666666777777788767899abbcccdddc999aaaa9aaaa9bbbbbb"
"bbabbbcccccccccccbaaa9877778889989aaaaaaaaabccc9abbccdcbbbdbbbca9988877777766666"
"6668c421111111110000066666667777778888888777899abccccddcb999aaaaaaaaaaaaaaabaabb"
"bbbbbcccccbbbaaa9977888889989aaaaabbbaccccc9bcddddbbccabacda9988888877777777679c"
"a12111000110000045555566666667777788767889abbcccddcb999aaaaaaaaabbbbaabbbbbbbbbb"
"bbbbbbbaaaa9877888899999aaaaabbcbbcccd9bdddcdccdcabcbcda99988888878787768bbd6111"
"00111100000133344444445555566666677899abbbccddca88999999aaaaaabaabbbbbbbbbcccccc"
"ccbaaa9878998899999bbbbbccccccdddbadcdddcdcbbccccdb9999899888888767aabdb42111110"
"100000010112222223333334444567789abbccccdcb97777888888899888999999aaaaaaaabbbaaa"
"998789989aaaaabbcccccccccdddd9ccdcddddccccdddda999999999987668accddba72221111111"
"11100111222222223333333567889abccccdcca97666665666666666767777788888999999999898"
"9999aabbbbcccccddccddddda9cddddeedcccddcdb99999999998757aacdaccac864432111110100"
"1012233223333333334677899bbcccddcba8666666665666666655555555555555667787799aaaab"
"bbbbbccccccddddddddbaddddddedddddddeca99999999878acdcb988cabbbaaa842111110111132"
"33233333333333567889abbcccdccb9766666666665666665555544334444455666699abbbbbccbc"
"cdccdddddddddddccdddddeeeededdded9999888767ccc965899bbacccbbbb976511011122333333"
"333333333467789abbbcccdcba8666666666666666665545324333334444445aabbbcbcccccccddd"
"ddddddddddddcdddddeeeeeeeeeeb9888878bdddcb658aabb9ccccccbbb9b9211111223333333333"
"33323567889abbcccdcca9767777666766666666545235566666678777aabbccbccccccdcddddddd"
"dddddddcdddddeeeeeeeeeeb988769deedcdd959baccabccccccbcccba2111122333333333332222"
"467789aabbcccccba877777777777666666554156666777779888babbcccccccccddcddddddddddd"
"ddddddeeeeeeeeeeeec77768ddeeddceb78abdcaaccccdddddccb811112233333332211111136678"
"99abbccccccba87888888888888777654256666677789888babccccccccccddddddddddddddedddd"
"eeedeeeeeeeeed7766cdeeeddcec99abdcbacccccdddddccb8201223332211101101112567899aab"
"bcccccba9799aaaaa99988877653333344445556666abbccccccccccddddddddddddddeddddeeeee"
"eeeeeeeed765addeedddddcbaacdccbccccddeeeddcbca301111110000011122234677899aabbccc"
"cbba889abcbba9998887653333333343444445bbccccccccccccdddddddddddddeedddeeeeeeeeee"
"eeee859deeeeddddedcabdddcbbccdddeeedddadca20000000122333333334667899aabbcccccba9"
"79abcbbaa998876654444444444554555bbcccccccccccdddddddddddddeeeeddeeeeeeeeeeeeee7"
"7deeeeeeedeeddbdddddcbcdddddeeeddaddda4122344444445455555567899aabbbccccbba88988"
"8887776666554454445455555556babccccccccccddddddddeddeeeeeedeeeeeeeeeeeeeee8beeee"
"eeeedeeedcddeedcbcddddeeeeddbcddcb986666555555555655667889aaabbcbccbba97b9777776"
"66666655555555555666788babcccccccccddddddddddddeeeeeeeeeeeeeeeeeeeeeeaeeeeeeeeee"
"eeddddeeeedbcdddeeeeeddbddddccba9877767777677776677899aaabbbccbba988b87777777666"
"666666666667788999bbbcccccccccdddddddddeeeeeeeeeeeeeeeeeeeeeeeedceeeeeeeeeeeeddd"
"deeeedccdddeeeeeeedeeeddccbbbb8777777777887677889aababbcccba9878ba88888777777766"
"6677889999876babcccccccccdddddddddeddeeddedeeeeeeeeeeeeeeecdeeeeeeeeeeeeddeeeeee"
"dcddddeeeeeeeeeeeeddcbccba7356566667796677899aabbbbcbbba9879cbbaaaaaaaaaaaabba99"
"99876667bbbccccccccddddddddddeedeeeeeeeeeeeeeeeeeeeeeceeeeeeeeeeeeedeeeeeeedccdd"
"deeeeeeeeeeeeeddcccbca5677655555a96678899aaabbbbbba9877ccbbabaababbbbbcca9976667"
"766babccccccdcddddddddddedeeeeeeeeeeeeeeeeeeeeedceeeeeeeeedeeedeeeeeeedcddddeeee"
"eeeeeeeedddcbcdca56698666679b7777899aaabbbbbbaa977bd87677777788889a76567776666ba"
"bbcccccdcdddddddddddeeeeeeeeeeeeeeeeeeeeeeddeeeeeeeeedeeeeeeeeeeedccdeeeeeeeeeee"
"eeddddcddddc7659b877669ac777899aaabbbbbbba987ada7777766666666667777666778aabbbcc"
"ccccddddddddddeeeeeeeeeeeeeeeeeeeeeeecdeeeeeeeeedeeeeeeeeeeedccddeeeeeeeeeeeeddd"
"deeecdcba69ccca7589bb878899aaabbbbbba9978cb9999998998889887776788888a9bbbbcccccc"
"ddddddddededeeeeeeeeeeeeeeeeeeeeceeeeeeeeeddeeeeeeeeeeedccddeeeeeeeeeeeeddeeeedc"
"ddcc8acccb859aacc88899aaababbbbaa987cb999999a9a9aaa999888999888aabbacccccdcddddd"
"dddddddeeeeeeeeeeeeeeeeeeedbeeeeeeeeeddeeeeeeeeeeddccdeeeeeeeeeeeeedeeeeecbddcc9"
"cddcca69aabcca8999aaabbbbbaa988cca9999999999a87776799988889acbacccccdccddddddddd"
"dddeeeeeeeeeeeeeeeeeedbeeeeeeeedddeeeeeeeeeeddccdeeeeeeeeeeeeddeeeedbcdddbaddbbc"
"b79abbbcda999aaaaabbbba997bba999999999aa87776799998889abbabbccccccccdddddddddeee"
"eeeeeeeeeeeeeeeebbeeeeeeeeddeeeeeeeeeeddcccdeeeeeeeeeeeeeeeeeedaceddacdd9ccb69ab"
"bbccdb99aaaabbbbbaa98bca9999999999aa88877999988879bbabbbccccccddddddddddeeeeeeee"
"eeeeeeeeeedaceeeeeeeeddeeeeeeeeeedcccdeeeeeeeeeeeeeedeeedcadeeccdedbdcb69bbbcccd"
"dcaaabbbbbbbaa98bbba999999999aaaaa98999988859baabbccccccccdddddddddeeeeeeeeeeeee"
"eeeeedaceeeeeeedddeeeeeeeeeedccdddeeeeeeeeeeeeddeeedcbdedcdeeccdcb79abbccccddcbb"
"babbbbbaaa8bbb9999999999999999999998887aa9abbcccccccccdddddddeeeeeeeeeeeeeeeeeee"
"c9deeeeeeedddeeeeeeeeedcccddeeeeeeeeeeeeeddeeddbbdddeeedbcddc99aabcccccdddbbbbbb"
"bbbaa9cbba9999999999999999999988889999aabbbbbbbbbbbcccccccdddddddddddeedddca9cdd"
"eeedcccdddeeeeddcbbbbcdcddcddcdddddcccdccc9abccddcbacccb989aaabbbbccccbaaaaaa999"
"9baa9988898989889898899888770000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000000000"
"00000000000000";

for (j=0;j<he;j++){
   for (i=0;i<wi;i++){
      ASCIIHEX=piccy[j*wi+i];
      stch_i(&ASCIIHEX,&value);
      if (value==1||value==2) value=0;
      else if (value==3) value=1;
      else if (value>=4 && value<14) value=value-3;
      else if (value==14) value=15;
      putpixel(i,j+10,value);      
   }
}

set_color(14);
Move(DB_rport,160,15);  Text(DB_rport,"name      : A.J.H. Beunes (Toine)       ",40);
Move(DB_rport,160,30);  Text(DB_rport,"address   : Mekelweg 15                 ",40);
Move(DB_rport,160,45);  Text(DB_rport,"            2629 JB Delft               ",40);
Move(DB_rport,160,60);  Text(DB_rport,"            HOLLAND                     ",40);
Move(DB_rport,160,75);  Text(DB_rport,"telephone : (0)15 - 78 7109             ",40);
Move(DB_rport,160,90);  Text(DB_rport,"E-mail    : BEUNES@IRI.TUDELFT.NL       ",40);
Move(DB_rport,160,120); Text(DB_rport,"FreeWare  : OBJECTOR V1.1               ",40);
Move(DB_rport,160,135); Text(DB_rport,"Donations : send $25 US to above address",40);
set_color(13);
Move(DB_rport,160,150); Text(DB_rport,"Source Code Included ! (for personal use only)",46);
set_color(14);
Move(DB_rport,0,180); Text(DB_rport,"Special thanks to Erwin Zwart         - for his ideas and Beta-testing",70);
Move(DB_rport,0,195); Text(DB_rport,"                  Jennifer Mackintosh - for her unconditional support ",70);
SwapBitmap();
return(0);
}