// GeomDemo.cpp
// Demonstration file for the 3D geometry classes.
// Copyright 1999 and 2000 by Rick Wagner, all rights reserved.
//
// Version 1.14.
// Created September 10, 1999, last edited June 23, 2000.

#include <iostream.h>
#include "Geometry3D.h"

// Global constants:
const char* gsVerNum = "1.14";

// Function prototypes:
void menu();                                          // Text based user interface
void testPoint3D();                                   // ATS for the Point3D class
void testDirection3D();                               // ATS for the Direction3D class
void testVector3D();                                  // ATS for the Vector3D class
void testDirectedPoint3D();                           // ATS for the DirectedPoint3D class
void testEdge3D();                                    // ATS for the Edge3D class
void testPolygon3D();                                 // ATS for the Polygon3D class
void testPolyhedron3D();                              // ATS for the Polyhedron3D class
void testLinePlaneIntersection();                     // ATS for the LinePlaneIntersection() class

int main()
{
  char* head1 = "Geometry demonstration program, version ";
  char* head2 = ", Copyright 1999 by USC and\n";
  char* head3 = "Rick Wagner, all rights reserved.\n\n";
  cout << head1 << gsVerNum << head2 << head3;

  menu();

  return 0;
}

void menu()
{
  int choice = 0;

  while(choice != 7)
  {
    cout << "                           Geometry3D Test Menu" << endl << endl
         << "  1. Test the Point3D class." << endl
         << "  2. Test the Direction3D class." << endl
         << "  3. Test the Vector3D class." << endl
         << "  4. Test the Edge3D class." << endl
         << "  5. Test the Polygon3D class." << endl
         << "  6. Test the Polyhedron3D class." << endl
         << "  7. Test the Polyhedron3D class." << endl
         << "  8. Quit." << endl << endl
         << "  Please enter your choice: ";

    cin >> choice;

    switch(choice)
    {
      case 1:
      {
        cout << endl << endl;
        testPoint3D();
        break;
      }
      case 2:
      {
        cout << endl << endl;
        testDirection3D();
        break;
      }
      case 3:
      {
        cout << endl << endl;
        testVector3D();
        break;
      }
      case 4:
      {
        cout << endl << endl;
        testEdge3D();
        break;
      }
      case 5:
      {
        cout << endl << endl;
        testPolygon3D();
        break;
      }
      case 6:
      {
        cout << endl << endl;
        testPolyhedron3D();
        break;
      }
      case 7:
      {
        cout << endl << endl;
        testLinePlaneIntersection();
        break;
      }
      default:
      {
        cout << endl << "Thank you and goodbye. ";
      }
    }
  }
}

void testPoint3D()
{
  Point3D p1;                                         // Test the default constructor.
  Point3D p2(1, 2, 3);                                // Test the 3-parameter constructor.
  Point3D p3(p2);                                     // Test the copy constructor.
  Point3D p4(1, 2, 3, "Point 4");                     // Test the 4-parameter constructor.

  cout << "Point3D Automated Test Sequence:" << endl << endl;

  cout << "The p3 coordinates are: "                  // Test the accessors.
       << p3.getX() << " "
       << p3.getY() << " "
       << p3.getZ() << " " << endl;

  cout << "The distance from p1 to p2 is: "           // Test the member distance() function.
       << p1.distance(p2) << endl;

  cout << "The distance from p1 to p2 is: "           // Test the nonmember distance() function.
       << distance(p1, p2) << endl;

  p3.setX(-1);                                        // Test the mutators.
  p3.setY(-2);
  p3.setZ(-3);
  cout << "The distance from p2 to p3 is: "
       << p2.distance(p3) << endl;                    // Should be twice the distance above.

  cout << "p2 == p4: " << (p2 == p4) << endl;         // Test the == operator.
  cout << "p1 == p2: " << (p1 == p2) << endl;

  cout << "p2.equals(p4): "
       << p2.equals(p4) << endl;                      // Test the member equals() function.
  cout << "p1.equals(p2): "
       << p1.equals(p2) << endl << endl;

  cout << "The label for p4 is: "                     // Test the label accessor.
       << p4.getLabel() << endl;

  p4.setLabel("Point Four");                          // Test the label mutator.
  cout << "The label for p4 has been changed to: "
       << p4.getLabel() << endl << endl;

  p1 = p2;                                            // Test the overloaded assignment operator.
  cout << "The new p1 coordinates are: "
       << p1.getX() << ", " << p1.getY()
       << ", " << p1.getZ() << endl << endl;

}

void testDirection3D()
{
  float angle = 0;
  Direction3D d1;                                      // Test the default constructor.
  Direction3D d2(1, 2, 3);                             // Test the 3-parameter constructor.
  Direction3D d3(d2);                                  // Test the copy constructor.

  cout << endl  << endl << endl << endl;
  cout << "Direction3D Automated Test Sequence:"
       << endl << endl;

  cout << "The d3 coordinates are: "                   // Test the accessors.
       << d3.getX() << " "
       << d3.getY() << " "
       << d3.getZ() << endl << endl;

  d3.setXYZ(1, 0, 0);                                  // Test the mutator.
  d1.setXYZ(0, 0, 1);

  angle = d1.angleBetween(d3);                         // Should be pi / 2.
  angle *= 2;
  cout << "Twice the angle between = "                 // Should be pi.
       << angle << endl;

  d1.setXYZ(1, 0, 1);
  angle = d1.angleBetween(d3);                         // Should be pi / 4.
  angle *= 4;                                          // Should be pi.
  cout << "Four times the angle between = "
       << angle << endl;

  d1.setAzimuth(gsfPi / 2);
  d2.setXYZ(0, 1, 0);
  angle = d1.angleBetween(d2);                         // Should be pi / 4.
  angle *= 4;                                          // Should be pi.
  cout << "Four times the angle between = "
       << angle << endl;
  angle = d1.getElevation();                           // Should be pi / 4.
  angle *= 4;                                          // Should be pi.
  cout << "Four times the angle between = "
       << angle << endl;

  angle = d2.getAzimuth();                             // Should be pi / 2.
  angle *= 2;                                          // Should be pi.
  cout << "Twice the angle between = "
       << angle << endl;

  cout << endl << endl;
}

void testVector3D()
{
  Direction3D d1(1, 2, 3);
  Vector3D v1;
  Vector3D v2(d1, 5);
  Vector3D v3(v2);

  cout << "Vector3D Automated Test Sequence:" << endl << endl;

  cout << "Vector v3 directions are "
       << v3.getDirection()->getX() << ", "
       << v3.getDirection()->getY() << ", "
       << v3.getDirection()->getZ() << endl
       << "and its length is " << v3.getLength()
       << "." << endl;

  v1 = v3;                                             // Test the assignment operator
  cout << "Vector v1 directions are "
       << v3.getDirection()->getX() << ", "
       << v3.getDirection()->getY() << ", "
       << v3.getDirection()->getZ() << endl
       << "and its length is " << v3.getLength()
       << "." << endl;

  cout << endl << endl << endl << endl << endl
       << endl << endl << endl << endl << endl;
}

void testDirectedPoint3D()
{
  Direction3D d1;
  Direction3D d2(0, 0, 5);
  Point3D p1(3, 1, 0, "Point one");
  Point3D p2(1, 5, 0, "Point two");

  DirectedPoint3D dp1;                                 // Test the default constructor.
  DirectedPoint3D dp2(d2, p2);                         // Test the general constructor.
  DirectedPoint3D dp3(dp2);                            // Test the copy constructor.
  DirectedPoint3D dp4 = dp3;                           // Test the assignment operator.

  cout << "DirectedPoint3D Automated Test Sequence:" << endl << endl;


  cout << "The dp4 direction components are: "          // Test the direction accessors.
       << dp4.getDirection()->getX() << " "
       << dp4.getDirection()->getY() << " "
       << dp4.getDirection()->getZ() << " " << endl << endl;

  cout << "The dp4 point coordinates are: "             // Test the direction accessors.
       << dp4.getPoint()->getX() << " "
       << dp4.getPoint()->getY() << " "
       << dp4.getPoint()->getZ() << " " << endl;

  cout << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl;
}

void testEdge3D()
{
  Point3D p1(0, 0, 0, "P1");
  Point3D p2(3, 0, 0, "P2");
  Point3D p3(3, 2, 0, "P3");
  Point3D p4(0, 2, 0, "P4");

  Edge3D e0;                                           // Test the default constructor.
  Edge3D e1(p1, p2);                                   // Test the 2-point constructor.
  Edge3D e2(p2, p3);
  Edge3D e3(e1);                                       // Test the copy constructor.
  Edge3D e4(p3, p4);
  Edge3D e5(p4, p1);

  cout << "Edge Automated Test Sequence:" << endl << endl;

  cout << "Edge 1 has the points " << e1.getP1()->getLabel() << " and " << e1.getP2()->getLabel() << endl;
  cout << "Edge 2 has the points " << e2.getP1()->getLabel() << " and " << e2.getP2()->getLabel() << endl;

  cout << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl;
}

void testPolygon3D()
{
  int i = 0;
  Polygon3D pgon1;
  // construct array of edge pointers
  Edge3D** edges = new Edge3D*[3];
  for(i = 0; i < 3; i++)
  {
    edges[i] = new Edge3D;
  }
  // construct a polygon
  Polygon3D pgon2(edges, 3);
  pgon2.addEdge(*(pgon2.getEdge(1)));
  pgon2.setEdge(*(pgon2.getEdge(1)), 2);
  pgon1 = pgon2;

  // Build some polygons:
  Point3D p1(0, 0, 0, "P1");                           // 8 vertices of a cube.
  Point3D p2(1, 0, 0, "P2");
  Point3D p3(1, 1, 0, "P3");
  Point3D p4(0, 1, 0, "P4");
  Point3D p5(0, 0, 1, "P5");
  Point3D p6(1, 0, 1, "P6");
  Point3D p7(1, 1, 1, "P7");
  Point3D p8(0, 1, 1, "P8");

  Edge3D e1(p1, p2);                                   // 12 edges of the cube.
  Edge3D e2(p2, p3);
  Edge3D e3(p3, p4);
  Edge3D e4(p4, p1);
  Edge3D e5(p5, p6);
  Edge3D e6(p6, p7);
  Edge3D e7(p7, p8);
  Edge3D e8(p8, p5);
  Edge3D e9(p1, p5);
  Edge3D e10(p2, p6);
  Edge3D e11(p3, p7);
  Edge3D e12(p4, p8);

  Polygon3D *pg1 = new Polygon3D();                    // 6 faces of the cube.
  Polygon3D pg2;
  Polygon3D pg3;
  Polygon3D pg4;
  Polygon3D pg5;
  Polygon3D pg6;

  pg1->addEdge(e4);                                    // Back face
  pg1->addEdge(e3);
  pg1->addEdge(e2);
  pg1->addEdge(e1);

  pg2.addEdge(e5);                                     // Front face
  pg2.addEdge(e6);
  pg2.addEdge(e7);
  pg2.addEdge(e8);

  pg3.addEdge(e1);                                     // Bottom face
  pg3.addEdge(e10);
  pg3.addEdge(e5);
  pg3.addEdge(e11);

  pg4.addEdge(e10);                                    // Right face
  pg4.addEdge(e2);
  pg4.addEdge(e9);
  pg4.addEdge(e12);

  pg5.addEdge(e11);                                    // Left face
  pg5.addEdge(e8);
  pg5.addEdge(e12);
  pg5.addEdge(e4);

  pg6.addEdge(e7);                                     // Top face
  pg6.addEdge(e9);
  pg6.addEdge(e3);
  pg6.addEdge(e12);

  delete pg1;
}

void testPolyhedron3D()
{
  // Build some polygons:
  Point3D p1(0, 0, 0, "P1");                           // 8 vertices of a cube.
  Point3D p2(1, 0, 0, "P2");
  Point3D p3(1, 1, 0, "P3");
  Point3D p4(0, 1, 0, "P4");
  Point3D p5(0, 0, 1, "P5");
  Point3D p6(1, 0, 1, "P6");
  Point3D p7(1, 1, 1, "P7");
  Point3D p8(0, 1, 1, "P8");

  Edge3D e1(p1, p2);                                   // 12 edges of the cube.
  Edge3D e2(p2, p3);
  Edge3D e3(p3, p4);
  Edge3D e4(p4, p1);
  Edge3D e5(p5, p6);
  Edge3D e6(p6, p7);
  Edge3D e7(p7, p8);
  Edge3D e8(p8, p5);
  Edge3D e9(p1, p5);
  Edge3D e10(p2, p6);
  Edge3D e11(p3, p7);
  Edge3D e12(p4, p8);

  Polygon3D *pg1 = new Polygon3D();                    // 6 faces of the cube.
  Polygon3D pg2;
  Polygon3D pg3;
  Polygon3D pg4;
  Polygon3D pg5;
  Polygon3D pg6;

  pg1->addEdge(e4);                                    // Back face
  pg1->addEdge(e3);
  pg1->addEdge(e2);
  pg1->addEdge(e1);

  pg2.addEdge(e5);                                     // Front face
  pg2.addEdge(e6);
  pg2.addEdge(e7);
  pg2.addEdge(e8);

  pg3.addEdge(e1);                                     // Bottom face
  pg3.addEdge(e10);
  pg3.addEdge(e5);
  pg3.addEdge(e11);

  pg4.addEdge(e10);                                    // Right face
  pg4.addEdge(e2);
  pg4.addEdge(e9);
  pg4.addEdge(e12);

  pg5.addEdge(e11);                                    // Left face
  pg5.addEdge(e8);
  pg5.addEdge(e12);
  pg5.addEdge(e4);

  pg6.addEdge(e7);                                     // Top face
  pg6.addEdge(e9);
  pg6.addEdge(e3);
  pg6.addEdge(e12);

  // Build the polyhedron:
  Polyhedron3D* ph1 = new Polyhedron3D();

  ph1->addPolygon(*pg1);
  delete pg1;
  ph1->addPolygon(pg2);
  ph1->addPolygon(pg3);
  ph1->addPolygon(pg4);
  ph1->addPolygon(pg5);
  ph1->addPolygon(pg6);

  delete ph1;
}

void testLinePlaneIntersection()
{
  Point3D* pIntersect = NULL;

  // Make a line.
  Direction3D d1(0, 0, 1);
  Point3D p1(0, 0, 0);
  DirectedPoint3D line1(d1, p1);

  // Make a plane.
  Direction3D d2(1, 0, 1);
  Point3D p2(1, 0, 5);
  DirectedPoint3D plane1(d2, p2);

  // Get the intersection.
  pIntersect = LinePlaneIntersection(line1, plane1);
  cout << "x = " << pIntersect->getX() << " y = " << pIntersect->getY() << " z = " << pIntersect->getZ() << endl;

}