const char * UsageLines [] = {
       "Usage: xyzproject (camera) (target) (width) (height) (magnification)",
       "Reads corners of polygons in x,y,z coordinates, from standard input.",
       "Writes a ppm image of specified width and height to standard output.",
       "Each nonblank input line must begin with colors r g b followed",
       "by x,y,z for each corner (at least 3 corners to have any effect).",
       "The order is front-to-back, meaning a polygon will cover",
       "any polygon given in a later line.",
       "The x,y,z position of the viewer (camera) and the target are given",
       "in the command line.",
       "z is the up direction.",
       "The camera cannot point straight up or straight down.",
       "xyzproject ignores anything after # to end-of-line.",
       "January 4, 2015.  Newest is at gopher -p users/julianbr sdf.org",
       };
const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] );


struct Polygon {
       int r, g, b;
       struct Corner {
               long int x, y, z;
               struct Corner * next;
               } * Corners;
       struct Polygon * next;
       };

#include <stdlib.h>
#include <stdio.h>


#define LengthTarget 15
#define BackgroundR 200
#define BackgroundG 200
#define BackgroundB 200


int ReadPolygon (struct Polygon * * PolygonPtr, int * EndOfInputPtr)
       {
       struct Polygon * Polygon;
       struct Corner * * CornerPtr, * Corners, * Corner;
       int ok, c, IsNegative;
       int r, g, b, x, y, z, Found, FoundB, FoundZ;

       ok = 1;
       c = getchar ();
       while (c == ' ')
               c = getchar ();
       Found = 0;
       Polygon = NULL;
       if (c != EOF && c != '\n' && c != '#') {
               Found = 1;
               PolygonPtr [0] = malloc (sizeof (PolygonPtr [0] [0] ) );
               Polygon = PolygonPtr [0];
               if (ok && Polygon == NULL) {
                       fprintf (stderr, "***xyzproject: Not enough memory");
                       ok = 0;
                       }
               }
       IsNegative = 0;
       if (c == '+')
               c = getchar ();
       else if (c == '-') {
               IsNegative = 1;
               c = getchar ();
               }
       r = 0;
       while (c >= '0' && c <= '9') {
               r = 10*r + (c - '0');
               c = getchar ();
               }
       if (IsNegative)
               r = - r;
       while (c == ' ')
               c = getchar ();
       IsNegative = 0;
       if (c == '+')
               c = getchar ();
       else if (c == '-') {
               IsNegative = 1;
               c = getchar ();
               }
       g = 0;
       while (c >= '0' && c <= '9') {
               g = 10*g + (c - '0');
               c = getchar ();
               }
       if (IsNegative)
               g = - g;
       while (c == ' ')
               c = getchar ();
       IsNegative = 0;
       if (c == '+')
               c = getchar ();
       else if (c == '-') {
               IsNegative = 1;
               c = getchar ();
               }
       FoundB = 0;
       b = 0;
       while (c >= '0' && c <= '9') {
               FoundB = 1;
               b = 10*b + (c - '0');
               c = getchar ();
               }
       if (IsNegative)
               b = - b;
       if (ok && Found && !FoundB) {
               fprintf (stderr, "***xyzproject: r g b not found");
               ok = 0;
               }
       Corners = NULL;
       CornerPtr = & Corners;
       while (c == ' ')
               c = getchar ();
       while (c == '+' || c == '-' || (c >= '0' && c <= '9') ) {
               CornerPtr [0] = malloc (sizeof (CornerPtr [0] [0] ) );
               Corner = CornerPtr [0];
               if (ok && CornerPtr [0] == NULL) {
                       fprintf (stderr, "***xyzproject: Not");
                       fprintf (stderr, " enough memory");
                       ok = 0;
                       }
               IsNegative = 0;
               if (c == '+')
                       c = getchar ();
               else if (c == '-') {
                       IsNegative = 1;
                       c = getchar ();
                       }
               x = 0;
               while (c >= '0' && c <= '9') {
                       x = 10*x + (c - '0');
                       c = getchar ();
                       }
               if (IsNegative)
                       x = - x;
               if (c == ',')
                       c = getchar ();
               IsNegative = 0;
               if (c == '+')
                       c = getchar ();
               else if (c == '-') {
                       IsNegative = 1;
                       c = getchar ();
                       }
               y = 0;
               while (c >= '0' && c <= '9') {
                       y = 10*y + (c - '0');
                       c = getchar ();
                       }
               if (IsNegative)
                       y = - y;
               if (c == ',')
                       c = getchar ();
               IsNegative = 0;
               if (c == '+')
                       c = getchar ();
               else if (c == '-') {
                       IsNegative = 1;
                       c = getchar ();
                       }
               FoundZ = 0;
               z = 0;
               while (c >= '0' && c <= '9') {
                       FoundZ = 1;
                       z = 10*z + (c - '0');
                       c = getchar ();
                       }
               if (IsNegative)
                       z = - z;
               if (ok && !FoundZ) {
                       fprintf (stderr, "***xyzproject: Didn't find x,y,z");
                       ok = 0;
                       }
               while (c == ' ')
                       c = getchar ();
               if (Corner != NULL) {
                       Corner->x = x;
                       Corner->y = y;
                       Corner->z = z;
                       Corner->next = NULL;
                       CornerPtr = & Corner->next;
                       }
               }
       if (ok && c != EOF && c != '\n' && c != '#') {
               fprintf (stderr, "***xyzproject: Found improper");
               fprintf (stderr, " '%c'", c);
               ok = 0;
               }
       while (c != EOF && c != '\n')
               c = getchar ();
       if (Polygon != NULL) {
               Polygon->r = r;
               Polygon->g = g;
               Polygon->b = b;
               Polygon->Corners = Corners;
               Polygon->next = NULL;
               }
       PolygonPtr [0] = Polygon;
       if (c != '\n')
               EndOfInputPtr [0] = 1;
       return ok;
       }


void Translate (long int * xPtr, long int * yPtr, long int * zPtr,
               int CameraX, int CameraY, int CameraZ)
       {
       xPtr [0] -= CameraX;
       yPtr [0] -= CameraY;
       zPtr [0] -= CameraZ;
       }


void Rotate (long int * xPtr, long int * yPtr, long int * zPtr,
               int RotateCosNumer, int RotateSinNumer, int RotateDenom)
       {
       int x, y, z;

       x = xPtr [0];
       y = yPtr [0];
       z = zPtr [0];
       xPtr [0] = x*RotateCosNumer - y*RotateSinNumer;
       yPtr [0] = x*RotateSinNumer + y*RotateCosNumer;
       zPtr [0] = z*RotateDenom;
       }


void Tilt (long int * xPtr, long int * yPtr, long int * zPtr,
               int TiltCosNumer, int TiltSinNumer, int TiltDenom)
       {
       int x, y, z;

       x = xPtr [0];
       y = yPtr [0];
       z = zPtr [0];
       xPtr [0] = x*TiltDenom;
       yPtr [0] = y*TiltCosNumer - z*TiltSinNumer;
       zPtr [0] = y*TiltSinNumer + z*TiltCosNumer;
       }


void Magnify (long int * xPtr, long int * yPtr, long int * zPtr,
               int magnification)
       {
       int scale;

       scale = 1;
       if (scale < yPtr [0]/LengthTarget)
               scale = (yPtr [0] + LengthTarget/2)/LengthTarget;
       xPtr [0] = (magnification*xPtr [0] + scale/2)/scale;
       yPtr [0] = (yPtr [0] + scale/2)/ scale;
       zPtr [0] = (magnification*zPtr [0] + scale/2)/scale;
       }



int ReadPolygons (struct Polygon * * PolygonsPtr)
       {
       struct Polygon * * PolygonPtr, * Polygon;
       int EndOfInput, LineNum, ok;

       PolygonPtr = PolygonsPtr;
       ok = 1;
       LineNum = 0;
       EndOfInput = 0 ;
       while (!EndOfInput) {
               LineNum++;
               if (!ReadPolygon (& Polygon, & EndOfInput) ) {
                       fprintf (stderr, " in line %d.\n", LineNum);
                       ok = 0;
                       }
               if (Polygon != NULL) {
                       PolygonPtr [0] = Polygon;
                       PolygonPtr = & Polygon->next;
                       }
               }
       return ok;
       }


int SetCamera (
               int * RotateCosNumerPtr,
               int * RotateSinNumerPtr,
               int * RotateDenomPtr,
               int * TiltCosNumerPtr,
               int * TiltSinNumerPtr,
               int * TiltDenomPtr,
               int x,
               int y,
               int z)
       {
       int RotateDenom, RotateDenom2;
       int TiltDenom, TiltDenom2;
       int TiltCosNumer, TiltCosNumer2, scale;
       int ScaledX, ScaledY, ScaledZ;

       if (x == 0 && y == 0) {
               fprintf (stderr, "***xyzproject: Camera and target must");
               fprintf (stderr, " not have same x and y.\n");
               return 0;
               }
       scale = 1;
       if (scale < x/LengthTarget)
               scale = x/LengthTarget;
       if (scale < - x/LengthTarget)
               scale = - x/LengthTarget;
       if (scale < y/LengthTarget)
               scale = y/LengthTarget;
       if (scale < - y/LengthTarget)
               scale = - y/LengthTarget;
       if (scale < z/LengthTarget)
               scale = z/LengthTarget;
       if (scale < - z/LengthTarget)
               scale = - z/LengthTarget;
       if (x > 0)
               ScaledX = (scale/2 + x)/scale;
       else
               ScaledX = - (scale/2 - x)/scale;
       if (y > 0)
               ScaledY = (scale/2 + y)/scale;
       else
               ScaledY = - (scale/2 - y)/scale;
       if (z > 0)
               ScaledZ = (scale/2 + z)/scale;
       else
               ScaledZ = - (scale/2 - z)/scale;
       RotateCosNumerPtr [0] = ScaledY;
       RotateSinNumerPtr [0] = ScaledX;
       RotateDenom2 = ScaledX*ScaledX + ScaledY*ScaledY;
       RotateDenom = 0;
       while (RotateDenom*(RotateDenom + 1)
                       < RotateDenom2)
               RotateDenom++;
       RotateDenomPtr [0] = RotateDenom;
       TiltCosNumer2 = ScaledX*ScaledX + ScaledY*ScaledY;
       TiltCosNumer = 0;
       while (TiltCosNumer*(TiltCosNumer + 1) < TiltCosNumer2)
               TiltCosNumer++;
       TiltCosNumerPtr [0] = TiltCosNumer;
       TiltSinNumerPtr [0] = - ScaledZ;
       TiltDenom2
               = ScaledX*ScaledX + ScaledY*ScaledY + ScaledZ*ScaledZ;
       TiltDenom = 0;
       while (TiltDenom*(TiltDenom + 1)
                       < TiltDenom2)
               TiltDenom++;
       TiltDenomPtr [0] = TiltDenom;
       return 1;
       }


void OrientPolygons (struct Polygon * Polygons,
               int CameraX, int CameraY, int CameraZ,
               int RotateCosNumer, int RotateSinNumer, int RotateDenom,
               int TiltCosNumer, int TiltSinNumer, int TiltDenom,
               int magnification)
       {
       struct Polygon * Polygon;
       struct Corner * Corner;

       Polygon = Polygons;
       while (Polygon != NULL) {
               Corner = Polygon->Corners;
               while (Corner != NULL) {
                       Translate (& Corner->x, & Corner->y, & Corner->z,
                                       CameraX, CameraY, CameraZ);
                       Rotate (& Corner->x, & Corner->y, & Corner->z,
                               RotateCosNumer, RotateSinNumer, RotateDenom);
                       Tilt (& Corner->x, & Corner->y, & Corner->z,
                               TiltCosNumer, TiltSinNumer, TiltDenom);
                       Magnify (& Corner->x, & Corner->y, & Corner->z,
                                       magnification);
                       Corner = Corner->next;
                       }
               Polygon = Polygon->next;
               }
       }


void DrawPolygons (struct Polygon * Polygons, int width, int height)
       {
       struct Polygon * Polygon;
       struct Corner * Corner;
       long int x1, y1, z1, x2, y2, z2, x, y, z;
       int i, j, NumCrossings;

       printf ("P6\n");
       printf ("%d %d\n", width, height);
       printf ("255\n");
       y = 1;
       for (i = 0; i < height; i++) {
               z = height/2 - i;
               for (j = 0; j < width; j++) {
                       x = j - width/2;
                       NumCrossings = 0;
                       Polygon = Polygons;
                       while (Polygon != NULL && NumCrossings == 0) {
                               Corner = Polygon->Corners;
                               while (Corner != NULL) {
                                       x1 = Corner->x;
                                       y1 = Corner->y;
                                       z1 = Corner->z;
                                       if (Corner->next == NULL) {
                                               x2 = Polygon->Corners->x;
                                               y2 = Polygon->Corners->y;
                                               z2 = Polygon->Corners->z;
                                               }
                                       else {
                                               x2 = Corner->next->x;
                                               y2 = Corner->next->y;
                                               z2 = Corner->next->z;
                                               }
                                       if (y*y1*z2 > y1*y2*z && y1*y2*z >= y*y2*z1) {
                                               if (x*(y1*z2 - y2*z1) >= y*(x1*z2 - x2*z1) + z*(x2*y1 - x1*y2) )
                                                       NumCrossings++;
                                               }
                                       else if (y*y2*z1 > y1*y2*z && y1*y2*z >= y*y1*z2) {
                                               if (x*(y2*z1 - y1*z2) >= y*(x2*z1 - x1*z2) + z*(x1*y2 - x2*y1) )
                                                       NumCrossings--;
                                               }
                                       Corner = Corner->next;
                                       }
                               if (NumCrossings != 0) {
                                       putchar (Polygon->r);
                                       putchar (Polygon->g);
                                       putchar (Polygon->b);
                                       }
                               Polygon = Polygon->next;
                               }
                       if (NumCrossings == 0) {
                               putchar (BackgroundR);
                               putchar (BackgroundG);
                               putchar (BackgroundB);
                               }
                       }
               }
       }


void WritePolygons (struct Polygon * Polygons)
       {
       struct Polygon * Polygon;
       struct Corner * Corner;

       Polygon = Polygons;
       while (Polygon != NULL) {
               printf ("%d %d %d", Polygon->r, Polygon->g, Polygon->b);
               Corner = Polygon->Corners;
               while (Corner != NULL) {
                       printf (" %ld,%ld,%ld", Corner->x, Corner->y, Corner->z);
                       Corner = Corner->next;
                       }
               printf ("\n");
               Polygon = Polygon->next;
               }
       }


void ClosePolygons (struct Polygon * Polygons)
       {
       struct Polygon * Polygon, * NextPolygon;
       struct Corner * Corner, * NextCorner;

       Polygon = Polygons;
       while (Polygon != NULL) {
               NextPolygon = Polygon->next;
               Corner = Polygon->Corners;
               while (Corner != NULL) {
                       NextCorner = Corner->next;
                       free (Corner);
                       Corner = NextCorner;
                       }
               free (Polygon);
               Polygon = NextPolygon;
               }
       }

int main (int argc, char * argv [] )
       {
       struct Polygon * Polygons;
       int CameraX, CameraY, CameraZ, TargetX, TargetY, TargetZ;
       int RotateCosNumer, RotateSinNumer, RotateDenom;
       int TiltCosNumer, TiltSinNumer, TiltDenom;
       int i, width, height, magnification, ok;
       char c;

       if (argc < 2) {
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               }
       else if (argc == 6) {
               ok = 1;
               if (sscanf (argv [1], "%d,%d,%d%c",
                               & CameraX, & CameraY, & CameraZ, & c) != 3) {
                       fprintf (stderr, "***xyztoad: expecting x,y,z");
                       fprintf (stderr, " camera position, found");
                       fprintf (stderr, " \"%s\".\n", argv [1] );
                       ok = 0;
                       }
               if (sscanf (argv [2], "%d,%d,%d%c",
                               & TargetX, & TargetY, & TargetZ, & c) != 3) {
                       fprintf (stderr, "***xyztoad: expecting x,y,z");
                       fprintf (stderr, " target position, found");
                       fprintf (stderr, " \"%s\".\n", argv [2] );
                       ok = 0;
                       }
               if (sscanf (argv [3], "%d%c", & width, & c) != 1
                               && magnification > 0) {
                       fprintf (stderr, "***xyzproject: expecting");
                       fprintf (stderr, " width, found");
                       fprintf (stderr, " \"%s\".\n", argv [3] );
                       ok = 0;
                       }
               if (sscanf (argv [4], "%d%c", & height, & c) != 1
                               && magnification > 0) {
                       fprintf (stderr, "***xyzproject: expecting");
                       fprintf (stderr, " height, found");
                       fprintf (stderr, " \"%s\".\n", argv [3] );
                       ok = 0;
                       }
               if (sscanf (argv [5], "%d%c", & magnification, & c) != 1
                               && magnification > 0) {
                       fprintf (stderr, "***xyzproject: expecting");
                       fprintf (stderr, " magnification, found");
                       fprintf (stderr, " \"%s\".\n", argv [3] );
                       ok = 0;
                       }
               if (ok) {
                       Polygons = NULL;
                       if (ReadPolygons (& Polygons) ) {
                               if (SetCamera (
                                               & RotateCosNumer, & RotateSinNumer,
                                                       & RotateDenom,
                                               & TiltCosNumer, & TiltSinNumer,
                                                       & TiltDenom,
                                               TargetX - CameraX,
                                               TargetY - CameraY,
                                               TargetZ - CameraZ) ) {
                                       OrientPolygons (Polygons,
                                                       CameraX, CameraY, CameraZ,
                                                       RotateCosNumer, RotateSinNumer,
                                                               RotateDenom,
                                                       TiltCosNumer, TiltSinNumer,
                                                               TiltDenom,
                                                       magnification);
                                       DrawPolygons (Polygons, width, height);
                                       /* WritePolygons (Polygons); */
                                       }
                               else
                                       WritePolygons (Polygons);
                               }
                       ClosePolygons (Polygons);
                       }
               }
       else {
               fprintf (stderr, "Usage: xyzproject");
               fprintf (stderr, " (camera) (target) (width) (height) (magnification)\n");
               }
       return 0;
       }