2011 Oct 02

// ================================================================
// Filename:    space.h
// Description: Defines space class for Orbits applications.
//              Implemented as classic Cartesian three space coordinates.
//
//              ASSUMES angles are in radians
//
// Author:      L.R. McFarland
// Created:     12 May 2004
// Revised:     27 May 2009
// Language:    C++
//
//  This file is part of lrm's Orbits software library.
//
//  Orbits 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 3 of the License, or
//  (at your option) any later version.
//
//  Orbits 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 Orbits.  If not, see <http://www.gnu.org/licenses/>.
// ================================================================

#ifndef SPACE_H
#define SPACE_H

#include <cmath>
#include <deque>
#include <exception>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>

namespace Cartesian {

  // catch all SpaceErrors when simulation fails.
  class SpaceError : public std::runtime_error {
  public:
  SpaceError(const std::string& msg) : std::runtime_error(msg) {}
  };

  // in Cartesian namespace
  class DivideZeroError : public SpaceError {
  public:
  DivideZeroError(const std::string&  msg) : SpaceError(msg) {}
  };

  class SpaceRecorderIOError : public SpaceError {
  public:
  SpaceRecorderIOError(const std::string& msg) : SpaceError(msg) {}
  };

  // -----------------------
  // ----- class space -----
  // -----------------------

  class space {
  public:

    // ----- unit vectors -----

    static const space Uo; // zero
    static const space Ux;
    static const space Uy;
    static const space Uz;

    // ----- ctor and dtor -----

    explicit space(double a = 0.0, double b = 0.0, double c = 0.0)
      : m_x(a), m_y(b), m_z(c) {}; // ctors, including default.
    ~space() {}; // destructor. default also ok.

    inline space(const space& a); // copy constructor
    inline space& operator=(const space& rhs); // copy assignment

    // ----- accessors -----

    const double& x() const            {return(m_x);}
    void          x(const double& rhs) {m_x = rhs;}

    const double& y() const            {return(m_y);}
    void          y(const double& rhs) {m_y = rhs;}

    const double& z() const            {return(m_z);}
    void          z(const double& rhs) {m_z = rhs;}

    // ----- bool operators -----

    inline bool operator== (const space& rhs) const;
    inline bool operator!= (const space& rhs) const;

    // ----- assignment operators -----

    inline space& operator+=(const space& rhs);
    inline space& operator-=(const space& rhs);

    inline space& operator*=(const double& rhs); // scale
    inline space& operator/=(const double& rhs) throw (DivideZeroError);

    // ----- other methods -----

    void zero() {x(0.0); y(0.0); z(0.0);};

    inline double magnitude()  const;
    inline double magnitude2() const;

    inline space  normalized() const throw (DivideZeroError);

    void setUsingPolarCoords(double radius, double theta, double phi = M_PI/2);

  private:

    // ----- data members -----

    double m_x, m_y, m_z;

  };

  // ---------------------------------------------------
  // ----- inline implementations of space methods -----
  // ---------------------------------------------------

  // copy constructor
  inline space::space(const space::space& a) {
    m_x = a.x();
    m_y = a.y();
    m_z = a.z();
  };

  // copy assignment
  inline space& space::operator=(const space::space& rhs) {
    if (this == &rhs) return(*this);
    m_x = rhs.x();
    m_y = rhs.y();
    m_z = rhs.z();
    return(*this);
  }

  // ----- bool operators -----

  inline bool space::operator== (const space::space& rhs) const {
    return (x() == rhs.x() && y() == rhs.y() && z() == rhs.z());
  }

  inline bool space::operator!= (const space::space& rhs) const {
    return (!operator==(rhs));
  }

  // ----- assignment operators -----

  inline space& space::operator+=(const space& rhs) {
    m_x += rhs.x();
    m_y += rhs.y();
    m_z += rhs.z();
    return(*this);
  }

  inline space& space::operator-=(const space& rhs) {
    m_x -= rhs.x();
    m_y -= rhs.y();
    m_z -= rhs.z();
    return(*this);
  }

  inline space& space::operator*=(const double& rhs) {
    m_x *= rhs;
    m_y *= rhs;
    m_z *= rhs;
    return(*this);
  }

  inline space& space::operator/=(const double& rhs) throw (DivideZeroError) {
    if (rhs == 0)
      throw DivideZeroError("space::operator/=(const double& rhs) "
			    "attempted divide by zero.");
    m_x /= rhs;
    m_y /= rhs;
    m_z /= rhs;
    return(*this);
  }

  // ----- normalizing -----

  inline double space::magnitude() const {
    return(sqrt(magnitude2()));
  }

  inline double space::magnitude2() const {
    return(m_x*m_x + m_y*m_y + m_z*m_z);
  }

  inline space space::normalized() const throw (DivideZeroError) {
    const double h(magnitude());
    return(space(m_x/h, m_y/h, m_z/h));
  }

  // ---------------------
  // ----- functions -----
  // ---------------------

  // implicit convertion double or int to space commutes double +-* space.
  const space operator+ (const space& lhs, const space& rhs);
  const space operator- (const space& lhs, const space& rhs);

  // explicit double cast to force scale, not dot product of default space ctor.
  const space operator* (const space& lhs, const double& rhs); // scale
  const space operator* (const double& lhs, const space& rhs); // scale

  const space operator/ (const space& lhs, const double& rhs)
    throw (DivideZeroError); // scale
  const space operator/ (const double& lhs, const space& rhs)
    throw (DivideZeroError); // scale

  // vector products
  const double operator* (const space& lhs, const space& rhs); // dot product
  space cross(const space& a, const space& b);  // vector cross product

  // operator<<
  inline std::ostream& operator<< (std::ostream& os, const space& a) {
    os << "<space><x>" << a.x() 
       << "</x><y>" << a.y() 
       << "</y><z>" << a.z() 
       << "</z></space>";
    return os;
  }


  // -------------------------
  // ----- class rotator -----
  // -------------------------

  // supports rotating space vectors about space axies.

  class rotator {
  public:

    // angle conversions
    static double deg2rad(const double& deg) {return(deg*M_PI/180.0);}
    static double rad2deg(const double& rad) {return(rad*180.0/M_PI);}

    rotator(const space& a_axis); // ctor, no default
    ~rotator(); // dtor
    rotator(const rotator& a); // copy ctor
    rotator& operator=(const rotator& rhs); // assignment ctor

    const space& axis() const              {return(m_axis);}
    void         axis(const space& a_axis);

    space rotate(const space& a_heading, const double& a_radians);

  private:

    space                              m_axis; 
    std::vector< std::vector<double> > m_rotation_matrix;

    // for optimization
    bool                               m_is_new_axis;
    double                             m_old_radians;

  };


  // -------------------------------
  // ----- class SpaceRecorder -----
  // -------------------------------

  // implements a simple deque to store three space data.
  // It is intended for use to store and later plotting positions and
  // other three space data.

  class SpaceRecorder {

  public:

    static const unsigned int default_size; /// default size limit for deque

    SpaceRecorder(const unsigned int& a_size_limit=SpaceRecorder::default_size);
   ~SpaceRecorder() {}; // dtor
    SpaceRecorder(const SpaceRecorder& a);   // copy ctor
    SpaceRecorder& operator=(const SpaceRecorder& a); // copy assignment

    const unsigned int& sizeLimit() const       {return(m_size_limit);}
    void                sizeLimit(const int& a) {m_size_limit = a;}

    unsigned int size() const            {return(m_data.size());}
    const space& get(const unsigned int& idx) {return(m_data[idx]);} // TODO test index?

    void push(space a);
    void clear() {m_data.clear();}


    void write2R(const std::string& flnm, bool skip_Uo=true);

  private:

    unsigned int       m_size_limit; /// size limit of position deque

    std::deque<space>  m_data;      /// data deque


  };

} // end namespace Cartesian

#endif

// EOF