// Geometry3D.cpp
// Implementation 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 <math.h>
#include <string.h>
#include <iostream.h>
#include "Geometry3D.h"

// Classes are given below in alphabetic order.


// *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  
// Member functions for the DirectedPoint3D class:

// Default constructor:
DirectedPoint3D::DirectedPoint3D()
{
  _d = new Direction3D;
  _p = new Point3D;
}

// General constructor:
DirectedPoint3D::DirectedPoint3D(const Direction3D& d, const Point3D& p)
{
  _d = new Direction3D(d);
  _p = new Point3D(p);
}

// Copy constructor:
DirectedPoint3D::DirectedPoint3D(const DirectedPoint3D& dp)
{
  _d = new Direction3D(*(dp._d));
  _p = new Point3D(*(dp._p));
}

// Overloaded assignment operator:
void DirectedPoint3D::operator =(const DirectedPoint3D& dp)
{
  Direction3D *d = new Direction3D(*(dp._d));
  Point3D *p = new Point3D(*(dp._p));
  delete _d;
  delete _p;
  _d = d;
  _p = p;
}

// Destructor:
DirectedPoint3D::~DirectedPoint3D()
{
  delete _d;
  delete _p;
}

// Accessors:
Direction3D* DirectedPoint3D::getDirection() const
{
  return new Direction3D(*_d);
}

Point3D* DirectedPoint3D::getPoint() const
{
  return new Point3D(*_p);
}

// Mutators:
void DirectedPoint3D::setDirection(const Direction3D& d)
{
  delete _d;
  _d = new Direction3D(d);
}

void DirectedPoint3D::setPoint(const Point3D& p)
{
  delete _p;
  _p = new Point3D(p);
}

// Non-member functions for the DirectedPoint3D class:

Point3D* LinePlaneIntersection(DirectedPoint3D line, DirectedPoint3D plane)
{
  // Returns the point of intersection with the plane.
  // Assumes the line and plane are not parallel.

  Point3D *p;                                            // Return point pointer.

  float t = 0;                                           // Parameter t of p along the input line.
  float sfNumerator = 0;
  float sfDenominator = 0;
  
  float x0 = line.getPoint()->getX();
  float y0 = line.getPoint()->getY();
  float z0 = line.getPoint()->getZ();
  float dx = line.getDirection()->getX();
  float dy = line.getDirection()->getY();
  float dz = line.getDirection()->getZ();

  float Qx = plane.getPoint()->getX();
  float Qy = plane.getPoint()->getY();
  float Qz = plane.getPoint()->getZ();
  float Vx = plane.getDirection()->getX();
  float Vy = plane.getDirection()->getY();
  float Vz = plane.getDirection()->getZ();
  
  // Solve the plane and line equations for parameter t:
  sfNumerator = Vx * (Qx - x0) + Vy * (Qy - y0) + Vz * (Qz - z0);
  sfDenominator = Vx * dx + Vy * dy + Vz * dz;
  
  if (sfDenominator == 0)
  {
    cout << "Error in LinePlaneIntersection: Attempted division by zero." << endl;
    t = 1 / gsfEpsilon;                                   // Ten billion is close enough to infinity.
  }
  else
  {
    t = sfNumerator / sfDenominator;
  }

  cout << "LinePlaneIntersection() sfNumerator = " << sfNumerator << endl;
  cout << "LinePlaneIntersection() sfDenominator = " << sfDenominator << endl;
  cout << "LinePlaneIntersection() t = " << t << endl;
  //x0 *= t;
  //y0 *= t;
  //z0 *= t;
  x0 += dx * t;
  y0 += dy * t;
  z0 += dz * t;

  p = new Point3D(x0, y0, z0);

  return p;
}

DirectedPoint3D* GetPlaneIntersection(DirectedPoint3D plane1, DirectedPoint3D plane2)
{
  // Finds the line of intersection of two planes; assumes planes are not parallel.
  // Input planes are represented as applied vectors; so is the returned line.

  DirectedPoint3D *Line;                        // Return line pointer.
  float dx, dy, dz, x0, y0, z0;                 // Pre-define line variables for compiler
  
  // Pull out the components of plane1:
  float x1 = plane1.getPoint()->getX();
  float y1 = plane1.getPoint()->getY();
  float z1 = plane1.getPoint()->getZ();
  float A1 = plane1.getDirection()->getX();
  float B1 = plane1.getDirection()->getY();
  float C1 = plane1.getDirection()->getZ();
  
  // Pull out the components of plane2:
  float x2 = plane2.getPoint()->getX();
  float y2 = plane2.getPoint()->getY();
  float z2 = plane2.getPoint()->getZ();
  float A2 = plane2.getDirection()->getX();
  float B2 = plane2.getDirection()->getY();
  float C2 = plane2.getDirection()->getZ();
  
  float D1 = -(A1 * x1 + B1 * y1 + C1 * z1);
  float D2 = -(A2 * x2 + B2 * y2 + C2 * z2);
    
  if ((B2 * A1 - B1 * A2) != 0)
  {
    // The line is not parallel to the X-Y plane
    dx = (B1 * C2 - B2 * C1) / (B2 * A1 - B1 * A2);
    x0 = (B1 * D2 - B2 * D1) / (B2 * A1 - B1 * A2);
    dy = (A1 * C2 - A2 * C1) / (A2 * B1 - A1 * B2);
    y0 = (A1 * D2 - A2 * D1) / (A2 * B1 - A1 * B2);
    dz = 1;
    z0 = 0;
  }    
  else
  {
    // Line of intersection is parallel to X-Y plane.
    if ((C2 * A1 - C1 * A2) != 0)
    {
      // The line is not parallel to the X-Z plane
      dx = (C1 * B2 - C2 * B1) / (C2 * A1 - C1 * A2);
      x0 = (C1 * D2 - C2 * D1) / (C2 * A1 - C1 * A2);
      dy = 1;
      y0 = 0;
      dz = (A1 * B2 - A2 * B1) / (A2 * C1 - A1 * C2);
      z0 = (A1 * D2 - A2 * D1) / (A2 * C1 - A1 * C2);    
    }
    else
    {
      // The line is not parallel to the Y-Z plane
      dx = 1;
      x0 = 0;
      if ((C2 * B1 - C1 * B2) != 0)
      {
        dy = (C1 * A2 - C2 * A1) / (C2 * B1 - C1 * B2);
        y0 = (C1 * D2 - C2 * D1) / (C2 * B1 - C1 * B2);
        dz = (B1 * A2 - B2 * A1) / (B2 * C1 - B1 * C2);
        z0 = (B1 * D2 - B2 * D1) / (B2 * C1 - B1 * C2);
      }
      else
      {
        cout << "Error in GetPlaneIntersection: trying to divide by zero." << endl;
      }
    }
  }  

  // Construct the return value:
  Point3D TempPoint(x0, y0, z0);
  Direction3D TempDirection (dx, dy, dz);
  Line = new DirectedPoint3D(TempDirection, TempPoint);  
  
  return Line;
}

// *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  
// Member functions for the Direction3D class:

// Default constructor:
Direction3D::Direction3D()
{
  _x = 1;
  _y = 0;
  _z = 0;
}

// 3-vector constructor:
Direction3D::Direction3D(float x, float y, float z)
// Precondition: at least one input paramter must be nonzero.
// Postcondition: vector components are normalized and initialized.
{
  float length = 0;

  length = x * x + y * y + z * z;

  if (length == 0)
  {
    x = 1;                        // Let it be a default direction vector
  }
  else
  {
    length = (float) sqrt((double) length);
    x /= length;
    y /= length;
    z /= length;
  }

  _x = x;
  _y = y;
  _z = z;
}

// 2-angle constructor:
Direction3D::Direction3D(float alpha, float beta)
{
  _x = (float) (cos(beta) * cos(alpha));
  _y = (float) (cos(beta) * sin(alpha));
  _z = (float) sin(beta);
}

// Copy constructor
Direction3D::Direction3D(const Direction3D& p)
// Postcondition: vector components are initialized to p._x, p._y, and p._z.
{
  _x = p._x;
  _y = p._y;
  _z = p._z;
}

// Accessors:
float Direction3D::getX() const
// Postcondition: vector x component is returned.
{
  return _x;
}

float Direction3D::getY() const
// Postcondition: vector y component is returned.
{
  return _y;
}

float Direction3D::getZ() const
// Postcondition: vector z component is returned.
{
  return _z;
}

float Direction3D::getAzimuth() const                                // Angle about Z axis.
{
  float r = 0;

  if (fabs(1 - _z) < gsfEpsilon)                                     // Straight up or down.
  {
    r = 0;                                                           // Can't have both x and y zero.
  }
  else
  {
    r = (float) atan2(_y, _x);
  }
  return r;
}

float Direction3D::getElevation() const                              // Angle above or below X-Y plane.
{
  float l = (float) sqrt(_x * _x + _y * _y);
  return (float) atan2(_z, l);
}

// Mutators:
void Direction3D::setX(float x)
{
  float length = 0;

  length = x * x + _y * _y + _z * _z;

  if (length == 0)
  {
    x = 1;                        // Let it be a default direction vector
  }
  else
  {
    length = (float) sqrt((double) length);
    x /= length;
    _y /= length;
    _z /= length;
  }
  _x = x;
}

void Direction3D::setY(float y)
{
  float length = 0;

  length = _x * _x + y * y + _z * _z;

  if (length == 0)
  {
    _x = 1;                        // Let it be a default direction vector
  }
  else
  {
    length = (float) sqrt((double) length);
    _x /= length;
    y /= length;
    _z /= length;
  }
  _y = y;
}

void Direction3D::setZ(float z)
{
  float length = 0;

  length = _x * _x + _y * _y + z * z;

  if (length == 0)
  {
    _x = 1;                        // Let it be a default direction vector
  }
  else
  {
    length = (float) sqrt((double) length);
    _x /= length;
    _y /= length;
    z /= length;
  }
  _z = z;
}

void Direction3D::setXYZ(float x, float y, float z)
// Postcondition: vector components are changed to normalized input values.
{
  float length = 0;

  length = x * x + y * y + z * z;

  if (length == 0)
  {
    x = 1;                        // Let it be a default direction vector
  }
  else
  {
    length = (float) sqrt((double) length);
    x /= length;
    y /= length;
    z /= length;
  }

  _x = x;
  _y = y;
  _z = z;
}

void Direction3D::setAzimuth(float alpha)
{
  float beta = getElevation();
  if (fabs(beta - gsfPi / 2) < gsfEpsilon)                     // Straight up.
  {
    // Azimuth has no meaning: do nothing.
  }
  else
  {
    if (fabs(beta + gsfPi / 2) < gsfEpsilon)                   // Straight down.
    {
      // Azimuth has no meaning: do nothing.
    }
    else
    {
      _x = (float) (cos(beta) * cos(alpha));
      _y = (float) (cos(beta) * sin(alpha));
    }
  }
}

void Direction3D::setElevation(float beta)
{
  float alpha = 0;
  float currentBeta = getElevation();

  if (fabs(currentBeta - gsfPi / 2) < gsfEpsilon)             // Straight up.
  {
    _x = 0;
    _y = 0;
    _z = 1;
  }
  else
  {
    if (fabs(currentBeta + gsfPi / 2) < gsfEpsilon)           // Straight down.
    {
      _x = 0;
      _y = 0;
      _z = -1;
    }
    else
    {
      alpha = getAzimuth();
      _x = (float) (cos(beta) * cos(alpha));
      _y = (float) (cos(beta) * sin(alpha));
      _z = (float) sin(beta);
    }
  }
}

float Direction3D::dotProduct(const Direction3D& d)
{
  return _x * d._x + _y * d._y + _z * d._z;
}

float Direction3D::angleBetween(const Direction3D& d)
// Postcondition: the angle between the directions is returned.
{
  return (float) acos(dotProduct(d));
}

Direction3D* Direction3D::crossProduct(const Direction3D& d)
{
  // Return the cross product of two directions.
  Direction3D *rd = NULL;                                        // The return object pointer.
  float dx = 0;
  float dy = 0;
  float dz = 0;

  dx = _y * d._z - _z * d._y;                                    // From Protter-Morrey, p 253.
  dy = _z * d._x - _x * d._z;
  dz = _x * d._y - _y * d._x;

  rd = new Direction3D(dx, dy, dz);

  return rd;
}

bool operator ==(const Direction3D& d1, const Direction3D& d2)
// Postcondition: returns true if the input directions are parallel.
{
  return fabs(d1.getX() - d2.getX()) < gsfEpsilon &&
         fabs(d1.getY() - d2.getY()) < gsfEpsilon &&
         fabs(d1.getZ() - d2.getZ()) < gsfEpsilon;
}


// *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  
// Member functions for the Edge3D class:

// Default constructor:
Edge3D::Edge3D()
{
  _p1 = new Point3D;
  _p2 = new Point3D;
}

// Two-point constructor:
Edge3D::Edge3D(const Point3D& p1, const Point3D& p2)
{
  _p1 = new Point3D(p1);
  _p2 = new Point3D(p2);
}

// Copy constructor:
Edge3D::Edge3D(const Edge3D& e)
{
  _p1 = new Point3D(*(e._p1));
  _p2 = new Point3D(*(e._p2));
}

// Destructor:
Edge3D::~Edge3D()
{
  delete _p1;
  delete _p2;
}

// Overloaded assignment operator:
void Edge3D::operator =(const Edge3D& source)
{
  // Handles the degenerate case p = p
  Point3D *p1 = new Point3D(*(source._p1));
  Point3D *p2 = new Point3D(*(source._p2));
  delete _p1;
  delete _p2;
  _p1 = p1;
  _p2 = p2;
}

Point3D* Edge3D::getP1() const
{
  return new Point3D(*(_p1));
}

Point3D* Edge3D::getP2() const
{
  return new Point3D(*(_p2));
}

void Edge3D::setP1(const Point3D& p)
{
  delete _p1;
  _p1 = new Point3D(p);
}

void Edge3D::setP2(const Point3D& p)
{
  delete _p2;
  _p2 = new Point3D(p);
}


bool Edge3D::equals(const Edge3D& e)
{
  return _p1->equals(*(e._p1)) && _p2->equals(*(e._p2));
}


// *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  
// Member functions for the Point3D class:

// Constructor:
Point3D::Point3D(float x, float y, float z, char* l)
// Postcondition: point coordinates are initialized to x, y, and z, and the label.
{
  _label = new char[strlen(l) + 1];
  strcpy(_label, l);

  _x = x;
  _y = y;
  _z = z;
}

// Copy constructor
Point3D::Point3D(const Point3D& p)
// Postcondition: point coordinates are initialized to p._x, p._y, and p._z, the label is too.
{
  _label = new char[strlen(p._label) + 1];
  strcpy(_label, p._label);

  _x = p._x;
  _y = p._y;
  _z = p._z;
}

// Destructor
Point3D::~Point3D()
{
  delete [] _label;
}

// Accessors:
float Point3D::getX() const
// Postcondition: point x coordinate is returned.
{
  return _x;
}

float Point3D::getY() const
// Postcondition: point y coordinate is returned.
{
  return _y;
}

float Point3D::getZ() const
// Postcondition: point z coordinate is returned.
{
  return _z;
}

char* Point3D::getLabel() const
// Postcondition: the label text string is returned.
{
  return _label;
}


// Mutators:
void Point3D::setX(float x)
// Postcondition: point x coordinate is changed to x.
{
  _x = x;
}

void Point3D::setY(float y)
// Postcondition: point y coordinate is changed to y.
{
  _y = y;
}

void Point3D::setZ(float z)
// Postcondition: point z coordinate is changed to z.
{
  _z = z;
}

void Point3D::setLabel(char* l)
// Postcondition: the label string is rewritten from the input string.
{
  delete [] _label;
  _label = new char[strlen(l) + 1];
  strcpy(_label, l);
}

void Point3D::operator =(const Point3D& source)
// Postcondition: the point is an exact copy of the source point.
{
  char* l = new char[strlen(source._label) + 1];    // Allocate memory for the new label.
  strcpy(l, source._label);                         // Copy the characters to the new label.
  delete [] _label;                                 // Get rid of the old label.
  _label = l;                                       // Assign the pointer to _label.
  _x = source._x;                                   // Copy the coordinates.
  _y = source._y;
  _z = source._z;
}

// Other public member functions:
float Point3D::distance(const Point3D& p)
// Postcondition: The scalar distance between the points is returned.
{
  float deltaX = 0;
  float deltaY = 0;
  float deltaZ = 0;
  float distance = 0;

  deltaX = p._x - _x;                   // Compute the orthogonal distances.
  deltaY = p._y - _y;
  deltaZ = p._z - _z;

  deltaX *= deltaX;                     // Square the orthogonal distances.
  deltaY *= deltaY;
  deltaZ *= deltaZ;

  distance = deltaX + deltaY + deltaZ;  // Sum the squares.
  distance = (float) sqrt((float) distance);

  return distance;
}

bool Point3D::equals(const Point3D& p)
// Postcondition: The return valus is true if the input point has nearly the same coordinates.
{
  return fabs(_x - p._x) < gsfEpsilon &&
         fabs(_y - p._y) < gsfEpsilon &&
         fabs(_z - p._z) < gsfEpsilon;
}

// Nonmember functions for the Point3D class:
float distance(const Point3D& p1, const Point3D& p2)
// Postcondition: The scalar distance to the input point is returned.
{
  float deltaX = 0;
  float deltaY = 0;
  float deltaZ = 0;
  float distance = 0;

  deltaX = p2.getX() - p1.getX();       // Compute the orthogonal distances.
  deltaY = p2.getY() - p1.getY();
  deltaZ = p2.getZ() - p1.getZ();

  deltaX *= deltaX;                     // Square the orthogonal distances.
  deltaY *= deltaY;
  deltaZ *= deltaZ;

  distance = deltaX + deltaY + deltaZ;  // Sum the squares.
  distance = (float) sqrt((float) distance);

  return distance;
}

bool operator ==(const Point3D& p1, const Point3D& p2)
// Postcondition: The return valus is true if the points have nearly the same coordinates.
{
  return fabs(p1.getX() - p2.getX()) < gsfEpsilon &&
         fabs(p1.getY() - p2.getY()) < gsfEpsilon &&
         fabs(p1.getZ() - p2.getZ()) < gsfEpsilon;
}


// *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  
// Member functions for the Polygon3D class:

// Default constructor:
Polygon3D::Polygon3D()
{
  _n = 0;
  _e = NULL;
}

// General constructor:
Polygon3D::Polygon3D(Edge3D **e, int n)
{
  int i = 0;

  _n = n;
  _e = new Edge3D*[_n];

  for (i = 0; i < _n; i++)
  {
    _e[i] = (Edge3D*) new Edge3D(*(e[i]));
  }

}

// Copy constructor:
Polygon3D::Polygon3D(const Polygon3D& p)
{
  int i = 0;

  _n = p._n;
  _e = new Edge3D*[_n];

  for (i = 0; i < _n; i++)
  {
    _e[i] = (Edge3D*) new Edge3D(*(p._e[i]));
  }
}

// Destructor:
Polygon3D::~Polygon3D()
{
  int i = 0;

  for (i = 0; i < _n; i++)
  {
    delete _e[i];                               // Delete the edge.
  }
  delete [] _e;
}

// Accessors:
Edge3D* Polygon3D::getEdge(int n) const
{
  return new Edge3D(*(_e[n]));
}

  // Mutators:
void Polygon3D::setEdge(const Edge3D& e, int n)
// Postcondition: a pointer to a copy of the input edge replaces the nth one in the edge pointer array.
{
  delete _e[n];                                 // No memory leak.
  _e[n] = new Edge3D(e);
}

// Add an edge to the polygon (useful for creating polygons by adding edges to a default polygon):
void Polygon3D::addEdge(const Edge3D& e)
// Postcondition: a pointer to a copy of the input edge is added to the end of the edge array.
{
  int i = 0;

  Edge3D **newArray = new Edge3D*[_n + 1];
  for (i = 0; i < _n; i++)
  {
    newArray[i] = _e[i];
  }
  delete [] _e;                                 // No memory leak.
  _e = newArray;
  _e[_n] = new Edge3D(e);                       // Add the new edge.
  _n++;                                         // Increment the edge counter.
}

// Overloaded assignment operator:
void Polygon3D::operator =(const Polygon3D& source)
// Postcondition: a copy of the source polygon overwrites "this."
{
  int i = 0;

  _n = source._n;
  delete [] _e;
  _e = new Edge3D*[_n];

  for (i = 0; i < _n; i++)
  {
    _e[i] = (Edge3D*) new Edge3D(*(source._e[i]));
  }
}


// *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  
// Member functions for the Polyhedron3D class:

// Default constructor:
Polyhedron3D::Polyhedron3D()
{
  _n = 0;
  _p = NULL;
}

// General constructor:
Polyhedron3D::Polyhedron3D(Polygon3D **p, int n)
{
  int i = 0;

  _n = n;
  _p = new Polygon3D*[_n];

  for (i = 0; i < _n; i++)
  {
    _p[i] = (Polygon3D*) new Polygon3D(*(p[i]));
  }

}

// Copy constructor:
Polyhedron3D::Polyhedron3D(const Polyhedron3D& p)
{
  int i = 0;

  _n = p._n;
  _p = new Polygon3D*[_n];

  for (i = 0; i < _n; i++)
  {
    _p[i] = (Polygon3D*) new Polygon3D(*(p._p[i]));
  }
}

// Destructor:
Polyhedron3D::~Polyhedron3D()
{
  int i = 0;

  for (i = 0; i < _n; i++)
  {
    delete _p[i];                                 // Delete the polygons.
  }
  delete [] _p;
}

// Accessors:
Polygon3D* Polyhedron3D::getPolygon(int n) const
{
  return new Polygon3D(*(_p[n]));
}

  // Mutators:
void Polyhedron3D::setPolygon(const Polygon3D& p, int n)
// Postcondition: a pointer to a copy of the input edge replaces the nth one in the edge pointer array.
{
  delete _p[n];                                    // No memory leak.
  _p[n] = new Polygon3D(p);
}

// Add an edge to the polygon (useful for creating polygons by adding edges to a default polygon):
void Polyhedron3D::addPolygon(const Polygon3D& p)
// Postcondition: a pointer to a copy of the input edge is added to the end of the edge array.
{
  int i = 0;

  Polygon3D **newArray = new Polygon3D*[_n + 1];
  for (i = 0; i < _n; i++)
  {
    newArray[i] = _p[i];
  }
  delete [] _p;                                    // No memory leak.
  _p = newArray;
  _p[_n] = new Polygon3D(p);                       // Add the new polygon.
  _n++;                                            // Increment the polygon counter.
}

// Overloaded assignment operator:
void Polyhedron3D::operator =(const Polyhedron3D& source)
// Postcondition: a copy of the source polygon overwrites "this."
{
  int i = 0;

  _n = source._n;
  delete [] _p;
  _p = new Polygon3D*[_n];

  for (i = 0; i < _n; i++)
  {
    _p[i] = (Polygon3D*) new Polygon3D(*(source._p[i]));
  }
}


// *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  
// Member functions for the Vector3D class:

// Default constructor:
Vector3D::Vector3D()
{
  _d = new Direction3D;                                         // Default direction is 1, 0, 0
  _l = 1;
}

// General constructor:
Vector3D::Vector3D(const Direction3D& d, float l)
{
  _d = new Direction3D(d);                                      // Make a copy the direction.
  _l = l;
}

// Copy constructor:
Vector3D::Vector3D(const Vector3D& v)
{
  _d = new Direction3D(*(v._d));                                // Make a copy of the direction.
  _l = v._l;
}

// Destructor:
Vector3D::~Vector3D()
{
  delete _d;
}

// Accessors:
Direction3D* Vector3D::getDirection() const
{
  return new Direction3D(*_d);
}

float Vector3D::getLength() const
{
  return _l;
}

// Mutators:
void Vector3D::setDirection(const Direction3D& d)
{
  delete _d;
  _d = new Direction3D(d);
}

void Vector3D::setLength(float l)
{
  _l = l;
}

// Overloaded assignment operator:
void Vector3D::operator =(const Vector3D& source)
{
  Direction3D *d = new Direction3D(*(source._d));                  // Make a copy of the direction.
  delete _d;
  _d = d;
  _l = source._l;
}

float Vector3D::dotProduct(const Vector3D& v)
{
  return  _d->dotProduct(*(v._d)) * _l * v._l;
}

float Vector3D::angleBetween(const Vector3D& v)
{
  return _d->angleBetween(*(v._d));
}

