/****************************************************************************
| ABOUT THIS Header file (threed.h) and object file (threed.o)
| Written by: Michael Main
| Date written: Nov 3, 1994
|
| To use the threed functions follow these steps:
| 1. In your program put this include statement:
|    #include "/home/staff/faculty/main/threed.h"
| 2. Create Xturbo.o and threed.o:
|    g++ -Wall -gstabs -c threed.c
|    g++ -Wall -gstabs -c Xturbo.c -I/usr/local/X11/include
| 3. When you compile your program, include these items:
|    g++ ... Xturbo.o threed.o -L/usr/local/X11/lib -lX11 -lm
|    The ... is your main program and any other .o files that you use.
| An example program which you may compile and run is test3d.c,
| which you may copy from the ~main/1210 or ~main/1300 directory.
|
| WHAT threed PROVIDES:
| This unit provides one new class called Polygon, and thwo fairly
| simple types called Axis and Cartesian. The unit also provides
| nine procedures which are used to control where a "viewer" is viewing the
| Polygons from, and for drawing the view that this viewer sees.
| A description of each data type and procedure follows:
|
| type Axis
|   An enumerated type for the names X, Y, and Z.  These are the names of
|   the three axes in the usual 3-dimensional Cartesian coordinate system.
| type Cartesian
|   A value of this type represents a point in 3-dimensional space.  It is
|   stored as an array of three real numbers.  The index of the array is
|   the Axis data type.  For example, a Cartesian point P could be assigned an
|   X-coordinate of 3.1 by the assignment statement P[X] := 3.1;
| type Polygon
|   This is a class type which represents a polygon in 3-dimensional space.
|   The methods of the class are listed here:
|   Polygon();
|   Polygon(unsigned int RequestedCapacity); 
|     Note: These are the two constructors.  The constructor with no
|     parameter uses a RequestedCapacity of 4.  The constructor should only
|     be called at the time that a Polygon is first declared.
|     Postcondition: The Polygon has been initialized and the other methods
|     may now be used.  The RequestedCapacity is the number of vertices
|     that you are requesting the polygon to hold.  However, sometimes
|     that entire request will not be met, and the polygon might not be
|     able to hold that many vertices.
|     NOTE: After the constructor executes, the "origin" of the Polygon is
|     (0,0,0), which means that any vertices which are later added to
|     the Polygon will be drawn with respect to the location (0,0,0).
|     The fill color of the polygon has been set to MAGENTA, the
|     border color has been set to BLUE, and the Polygon is not hidden.
|  int Full();
|     Precondition: None.
|     Postcondition: The value returned is true if the Polygon is full (so that
|     no more vertices may be added.)  Otherwise the value returned is false.
|  unsigned int ManyVertices();
|  unsigned int Capacity();
|     Precondition: None.
|     Postcondition: The value returned is the number of vertices in the
|     Polygon (for ManyVertices) and the maximum number of vertices (for
|     Capacity).
|  void operator = (Polygon& Other);
|     Precondition: Capacity() >= Other.ManyVertices();
|     Postcondition: The Polygon has been assigned to be the same as Other.
|  void AddVertex(Cartesian Vertex);
|  void AddVertexXYZ(float DataX, float DataY, float DataZ);
|     Precondition: Full() returns false.
|     Postcondition: Another vertex has been added to the Polygon.  In the
|     AddVertex version, the Cartesian value Vertex is the new vertex.  In the
|     AddVertexXYZ version, the X, Y and Z coordinates of the new point are
|     given as the three parameters DataX, DataY and DataZ.
|  void MoveVertex(int VertexNumber, Cartesian Vertex);
|  void MoveVertexXYZ(int VertexNumber, float DataX, float DataY, float DataZ);
|     Precondition: VertexNumber is a positive integer <= the number of
|     vertices in the Polygon.  (So that the first vertex number is 1.)
|     Postcondition: The vertex whose number is VertexNumber has been moved
|     to the indicated location.
|  void SetColor(int Color);
|     Precondition: None.
|     Postcondition: The color for drawing the Polygon has been set.
|     If Color is 0 then the Polygon is drawn in white with a black border.
|     Any other value draws the Polygon in black with a white border.
|     If SetColor is not called, then the polygon is drawn white.
|  void SetOrigin(Cartesian NewOrigin);
|  void SetOriginXYZ(float DataX, float DataY, float DataZ);
|     Precondition: None.
|     Postcondition: The "origin" of the Polygon has been set to the location
|     specified by the parameter to SetOrigin or SetOriginXYZ.  The origin
|     is used to determine where the Polygon's vertices are drawn from.
|     For example, if the origin is (1,0,0) and a vertex is at (2,3,4) --
|     then the vertex will actually be drawn at (3,3,4).  In other words,
|     the origin gets added to each vertex location before the vertex is
|     actually drawn.  The purpose of having an origin is to allow you
|     to quickly move an entire polygon by simply moving the origin.
|  void TranslateOrigin(float DeltaX, float DeltaY, float DeltaZ);
|     Precondition: None.
|     Postcondition: The origin of the Polygon has been moved by the
|     amounts DeltaX, DeltaY, and DeltaZ in the X, Y and Z directions.
|  void Rotate(Axis AxisOfRotation, float Amount);
|     Precondition: None.
|     Postcondition: All the vertices of the polygon have been rotated
|     around the origin of the polygon.  The rotation is around the
|     axis given by AxisOfRotation, and the amount of the rotation is
|     given in Radians by the parameter Amount.
|  void Hide();
|     Precondition: None.
|     Postcondition: The Polygon will no longer be drawn on the screen,
|     until Unhide() is called.  Other member functions, such as
|     AddVertex, etc., may continue to be called to change the Polygon.
|  void Unhide();
|    Precondition: None.
|    Postcondition: The Polygon will once again be drawn on the screen,
|    cancelling the effect of a previous call to Hide().
|  int Hidden();
|    Precondition: None.
|    Postcondition: A true return value indicates that the Polygon is being
|    drawn on the screen; false indicates that the Polygon is not being
|    drawn on the screen.
|  void Remove();
|    Precondition: None.
|    Postcondition: The Polygon has been removed from the screen, and none of
|    the member functions may be called until Revive() is called.
|  void Revive(unsigned int RequestedCapacity = 4);
|    Precondition: The most recent activation of a member function was a call
|    to Remove().
|    Postcondition: The Polygon has been initialized (in the same manner as
|    the constructor works).  All member functions can again be used.
|
| Procedures
|   The unit also provides procedures for controling a "viewer" who is
|   looking at all the Polygons, and for drawing what the viewer sees.
|   All procedures assume a right-handed coordinate system, meaning that
|   if you point your right thumb in the direction of the positive z-axis,
|   then your right fingers curl from the positive x-axis to the positive
|   y-axis.  The procedures are:
|
| void PutIntoGraphicsMode();
|   Precondition: InGraphics returns false.
|   Postcondition: The computer is now has a graphics window and the various
|   graphics routines listed below may be called to draw Polygons.
|   NOTE: Call the InGraphics function to see if PutIntoGraphicsMode
|   failed or succeeded.
| int InGraphics();
|   Precondition: None.
|   Postcondition: The value returned is true if the machine currently
|   has a graphics window to draw in.
| void ShutDownGraphics();
|   Precondition: InGraphics returns true.
|   Postcondition: The machine is no longer has a graphics window to draw in.
| void SetView(float DistanceFromOrigin, float Width, float Height);
|   Precondition: InGraphics returns true.
|   Postcondition: Parameters for viewing the Polygons have been set up.
|   The "viewer" of the Polygons is placed on the negative Y-axis, at a
|   distance of DistanceFromOrigin from the location (0,0,0).  This
|   viewer is looking toward the origin.  The viewer sees through a
|   rectangular window which is centered at the origin, with a width given
|   by the parameter Width, and a height given by the parameter Height.
|   NOTE: If you don't call SetView, then this unit will use the values
|   of DistanceFromOrigin = 100, Width = 15, Height = 15.
| void ZoomView(float Amount);
|   Precondition: InGraphics returns true.
|   Postcondition: The "viewer" who is viewing the Polygons is moved closer
|   or farther from the origin.  Example: Amount=2 "zooms" by a factor of 2,
|   so that the viewer is closer to the origin.  Amount= 0.5 "unzooms"
|   by a factor of 1/2, so that the viewer is farther from the origin.
| void RollView(float Amount)
|   Precondition: InGraphics returns true.
|   Postcondition: The "viewer" who is viewing the Polygons is moved upward
|   over the origin by Amount radians.  Example: Amount = pi/2
|   will move the viewer upward over the origin by 90 degrees.
| void TiltView(float Amount);
|   Precondition: InGraphics returns true.
|   Postcondition: The "viewer" who is viewing the Polygons tilts their
|   head clockwise by Amount radians.
| void SpinView(float Amount);
|   Precondition: InGraphics returns true.
|   Postcondition: The "viewer" who is viewing the Polygons moves in a
|   counterclockwise circle (as viewed from above) around the Z-axis by
|   Amount radians.
| void RefreshScreen();
|   Precondition: InGraphics returns true.
|   Postcondition: The screen is updated to show the current view of all
|   Polygons which have been initialized.  NOTE: Changes that are made to
|   Polygons, and changes which are made to viewers do not take effect

|   until RefreshScreen is called!
|
| These last two procedures have nothing to do with graphics, but they are
| useful in many graphics programs.  A test program which uses these two
| functions is in ~main/1300/testtime.c
| float Ticks();
|   Precondition: None.
|   Postcondition: The first time this function is called, the function will
|   return zero.  Each subsequent call returns the approximate number of
|   seconds which have passed since the previous call.
|   Example:
|     cout << Ticks();  // This will print 0
|     ...               // Then do some computation that takes 1.5 seconds
|     cout << Ticks();  // This will print 1.5
|     ...               // Then do more computation that takes 1.1 seconds
|     cout << Ticks();  // This will print 1.1
| char ReadChar();
|   Precondition: PRIOR TO USING THIS FUNCTION YOU SHOULD MAKE THE CALL:
|                 system("stty raw");
|                 This makes the keyboard send input to the program
|                 immediately, rather than waiting for a carriage return.
|                 Input buffering may be turned back on with
|                 system("stty cooked");
|                 As a side-effect of "stty raw", all newline characters
|                 which are output with \n or with endl will be JUST a
|                 a new line (and no carriage return).  If you also want
|                 a carriage return, then you must output \r.
|                 If you want none of the input to be echoed to the screen,
|                 then give the call: system("stty -echo");
|   Postcondition: If a key is being pressed on the keyboard, then the value
|   of the key is read and returned by the function.  Otherwise the function
|   returns the character value zero.
|   Example:
|     #include <stdlib.h>    // Provides the system function
|     system("stty raw");
|     Next = ReadChar();     // Next is a char variable 
|     if (Next != 0)
|       cout << You just pressed the key: " << Next << "\r" << endl;
|     else
|       cout << No key is being pressed right now.\r" << endl;
****************************************************************************/

/* In general, we do not want to include all these definitions if they have
|  already been included from some other location.  Therefore, we check whether
|  the definitions have previously been defined, and if so then we skip to
|  the end of this file. */
#ifndef THREED_DEFINITIONS
#define THREED_DEFINITIONS
#define FALSE 0
#define TRUE 1

  const
    int MAXVERTICES = 4; // Maximum number of vertices in a polygon.

  enum
    Axis {X, Y, Z};

  typedef
    float Cartesian[3];

  class Polygon {
    unsigned int Size;
    unsigned int MaxSize;
    int FillColor;
    Cartesian Vertices[MAXVERTICES];
    Cartesian Origin;
    unsigned int Location;
    int IsHidden;
    int Changed; // True if polygon has changed since last draw
    /* Drawing data of some sort is declared here. 
    |  This drawing data is valid if Changed = false
    |  and ViewChanged = false too. For now, there is simple drawing data,
    |  which means that Draw and DistanceFromViewer have to often
    |  re-do their entire work each time they are called.  A good project
    |  for students would be to implement this improvement! */
    short QuadPoints[2*MAXVERTICES];
    int Behind;
    float DistanceRecord;
    void Draw();
    float DistanceFromViewer();
    friend void RefreshScreen();
  public:
    Polygon() { Revive(); }
    Polygon(unsigned int RequestedCapacity) { Revive(RequestedCapacity); }
    int Full() const { return (Size == MaxSize); }
    unsigned int ManyVertices() const { return Size; }
    unsigned int  Capacity() const { return MaxSize; }
    void operator = (const Polygon& Other);
    void AddVertex(Cartesian Vertex);
    void AddVertexXYZ(float DataX, float DataY, float DataZ);
    void MoveVertex(int VertexNumber, Cartesian Vertex);
    void MoveVertexXYZ(int VertexNumber, float DataX, float DataY, float DataZ);
    void SetColor(int Color);
    void SetOrigin(Cartesian NewOrigin);
    void SetOriginXYZ(float DataX, float DataY, float DataZ);
    void TranslateOrigin(float DeltaX, float DeltaY, float DeltaZ);
    void Hide() { IsHidden = Changed = TRUE; }
    void Rotate(Axis AxisOfRotation, float Amount);
    void Unhide() { IsHidden = FALSE; Changed = TRUE; }
    int Hidden() const { return IsHidden; }
    void Remove();
    void Revive(unsigned int RequestedCapacity = 4);         
    ~Polygon() { Remove(); }
  };

  void PutIntoGraphicsMode();
  int  InGraphics();
  void ShutDownGraphics();

  void SetView(float DistanceFromOrigin, float Width, float Height);
  void ZoomView(float Amount);
  void RollView(float Amount);
  void TiltView(float Amount);
  void SpinView(float Amount);
  void RefreshScreen();

  float Ticks();
  char  ReadChar();

#endif

