//  Vamos Automotive Simulator
//  Copyright (C) 2001--2002 Sam Varner
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#ifndef _THREE_VECTOR_H_
#define _THREE_VECTOR_H_

#include <iostream>
#include <cmath>

// A three-element vector.  Useful for representing physical quantites
// like position, veocity or force.

namespace Vamos_Geometry
{
  class Three_Vector
  {
    // The components of the vector.
    double m_vec [3];

  public:
    Three_Vector ();
    Three_Vector (double x, double y, double z);
    // Initialize with a C-style vector.
    Three_Vector (double [3]);
    // Copy constructor.
    inline Three_Vector (const Three_Vector& vec);
    // Copy assignment.
    Three_Vector& operator = (const Three_Vector& vec);

    // Subscript operators.
    double& operator [] (int index) { return m_vec [index]; }
    const double& operator [] (int index) const { return m_vec [index]; }

    // Set each element to zero.
    void zero ();
    // Return the magnitude (absolute value).
    inline double abs () const;
    // Return the dot product with VEC.
    double dot (const Three_Vector& vec) const;
    // Return the cross product with VEC.
    Three_Vector cross (const Three_Vector& vec) const;
    // Return the projection onto VEC.
    Three_Vector project (const Three_Vector& vec) const;
    Three_Vector back_project (const Three_Vector& vec) const;

    // Return the pependicular distance between vectors parallel to
    // this one passing through the given points.
    double perp_distance (const Three_Vector& point1,
                          const Three_Vector& point2) const;

    // The unit vector that points along this vector.
    Three_Vector unit () const;
    // The component of this vector projected onto another vector.
    double component (const Three_Vector& vec) const;
    // The angle between this vector and another.
    double angle (const Three_Vector& vec) const;

    // These operators must be members of the class because they modify
    // class members.
    Three_Vector& operator += (const Three_Vector& vec);
    Three_Vector& operator -= (const Three_Vector& vec);
    Three_Vector& operator *= (double factor);
    Three_Vector& operator /= (double factor);
    
    static const Three_Vector X;
    static const Three_Vector Y;
    static const Three_Vector Z;
  };

  // Copy constructor, needed so that elements of the vector are copied rather
  // than the pointer to the element array.
  Three_Vector::Three_Vector (const Three_Vector& vec)
  {
    m_vec [0] = vec [0];
    m_vec [1] = vec [1];
    m_vec [2] = vec [2];
  }

  // Return the magnitude.
  double
  Three_Vector::abs () const
  {
    return sqrt (m_vec [0] * m_vec [0] 
                 + m_vec [1] * m_vec [1] 
                 + m_vec [2] * m_vec [2]);
  }

  // Addition operator.
  const Three_Vector operator + (const Three_Vector& vec1, 
                                 const Three_Vector& vec2);
  // Subtraction operator.
  const Three_Vector operator - (const Three_Vector& vec1, 
                                 const Three_Vector& vec2);
  // Negation operator.
  const Three_Vector operator - (const Three_Vector& vec);
  // Scalar multiplication operators.
  const Three_Vector operator * (const Three_Vector& vec1, double factor);
  const Three_Vector operator * (double factor, const Three_Vector& vec1);
  // Scalar division operators.
  const Three_Vector operator / (const Three_Vector& vec1, double factor);
  const Three_Vector operator / (double factor, const Three_Vector& vec1);

  bool operator == (const Three_Vector& vec1, const Three_Vector& vec2);

  // Stream operators.
  std::istream& operator >> (std::istream& is, Three_Vector& vec);
  std::ostream& operator << (std::ostream& os, const Three_Vector& vec);
}

#endif
