2011 Oct 02

// ==================================================================
// Filename:    space.cpp
// Description: Implements the space class
//              Assumes angles are in radians
// Author:      L.R. McFarland
// Created:     4 Feb 1999
// 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/>.
// ==================================================================


#include <space.h>

using namespace std;
using namespace Cartesian;

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

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

const space space::Uo(0,0,0);
const space space::Ux(1,0,0);
const space space::Uy(0,1,0);
const space space::Uz(0,0,1);

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

const space Cartesian::operator+(const space& lhs, const space& rhs) {
  return(space(lhs.x() + rhs.x(), lhs.y() + rhs.y(), lhs.z() + rhs.z()));
}

const space Cartesian::operator-(const space& lhs, const space& rhs) {
  return(space(lhs.x() - rhs.x(), lhs.y() - rhs.y(), lhs.z() - rhs.z()));
}

// scale
const space Cartesian::operator*(const space& lhs, const double& rhs) {
  return(space(lhs.x() * rhs, lhs.y() * rhs, lhs.z() * rhs));
}

const space Cartesian::operator*(const double& lhs, const space& rhs) {
  return(Cartesian::operator*(rhs, lhs));
}

const space Cartesian::operator/(const space& lhs, const double& rhs)
  throw (DivideZeroError) {
  if (rhs == 0)
    throw DivideZeroError("space::operator/() attempted divide by zero.");
  return(space(lhs.x() / rhs, lhs.y() / rhs, lhs.z() / rhs));
}

const space Cartesian::operator/(const double& lhs, const space& rhs)
  throw (DivideZeroError) {
  if (rhs.x() == 0 || rhs.y() == 0 || rhs.z() == 0)
    throw DivideZeroError("space::operator/() attempted divide by zero.");
  return(space(lhs / rhs.x(), lhs / rhs.y(), lhs / rhs.z()));
}

// ----- vector products -----

const double Cartesian::operator*(const space& lhs, const space& rhs) {
  return(lhs.x()*rhs.x() + lhs.y()*rhs.y() + lhs.z()*rhs.z());
}

space Cartesian::cross(const space& a, const space& b) {
  space tmp;
  tmp.x(a.y()*b.z() - a.z()*b.y());
  tmp.y(a.z()*b.x() - a.x()*b.z());
  tmp.z(a.x()*b.y() - a.y()*b.x());
  return(tmp);
}

// ----- set using polar coordinates -----

void space::setUsingPolarCoords(double radius, double theta, double phi) {
  /// ASSUMES: angles in radians
  // theta: angle in the x-y plane
  // phi: polar angle (from the z axis)
  x(radius * sin(phi) * cos(theta));
  y(radius * sin(phi) * sin(theta));
  z(radius * cos(phi));
}

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

rotator::rotator(const space& a_axis) :
  m_axis(a_axis),
  m_rotation_matrix(3, vector<double>(3, 0)),
  m_is_new_axis(true),
  m_old_radians(0.0)
{}

rotator::~rotator() {}

rotator::rotator(const rotator& rhs) :
  m_axis(rhs.axis()),
  m_rotation_matrix(rhs.m_rotation_matrix),
  m_is_new_axis(rhs.m_is_new_axis)
{} // TBD test this

rotator::rotator& rotator::operator=(const rotator::rotator& rhs) {
  if (this == &rhs) return(*this);
  axis(rhs.axis());
  m_rotation_matrix = rhs.m_rotation_matrix;
  m_is_new_axis = rhs.m_is_new_axis;
  return(*this);
} // TBD test this

void rotator::axis(const space& a_axis) {
  if (a_axis != m_axis) {
    m_axis = a_axis;
    m_is_new_axis = true;
  }
}

space rotator::rotate(const space& a_heading, const double& a_radians) {

  if (m_is_new_axis || m_old_radians != a_radians) {

    double c(cos(a_radians));
    double s(sin(a_radians));

    space normal(axis().normalized());

    double t(1-c);

    m_rotation_matrix[0][0] = c + normal.x()*normal.x()*t;
    m_rotation_matrix[1][1] = c + normal.y()*normal.y()*t;
    m_rotation_matrix[2][2] = c + normal.z()*normal.z()*t;

    double t1(normal.x()*normal.y()*t);
    double t2(normal.z()*s);

    m_rotation_matrix[1][0] = t1 + t2;
    m_rotation_matrix[0][1] = t1 - t2;

    t1 = normal.x()*normal.z()*t;
    t2 = normal.y()*s;

    m_rotation_matrix[2][0] = t1 - t2;
    m_rotation_matrix[0][2] = t1 + t2;

    t1 = normal.y()*normal.z()*t;
    t2 = normal.x()*s;

    m_rotation_matrix[2][1] = t1 + t2;
    m_rotation_matrix[1][2] = t1 - t2;

    m_is_new_axis = false;
    m_old_radians = a_radians;

  }

  space tmp(m_rotation_matrix[0][0]*a_heading.x() +
	    m_rotation_matrix[0][1]*a_heading.y() +
	    m_rotation_matrix[0][2]*a_heading.z(),
	    m_rotation_matrix[1][0]*a_heading.x() +
	    m_rotation_matrix[1][1]*a_heading.y() +
	    m_rotation_matrix[1][2]*a_heading.z(),
	    m_rotation_matrix[2][0]*a_heading.x() +
	    m_rotation_matrix[2][1]*a_heading.y() +
	    m_rotation_matrix[2][2]*a_heading.z());

  return(tmp);

}


// =========================
// ===== SpaceRecorder =====
// =========================

const unsigned int SpaceRecorder::default_size(1024);

SpaceRecorder::SpaceRecorder(const unsigned int& a_size_limit) :
  m_size_limit(a_size_limit),
  m_data(deque<space>(m_size_limit))
{}

SpaceRecorder::SpaceRecorder(const SpaceRecorder& a):
  m_size_limit(a.sizeLimit()),
  m_data(a.m_data)
{}

SpaceRecorder& 
SpaceRecorder::operator=(const SpaceRecorder::SpaceRecorder& rhs) {
  if (this == &rhs) return(*this);
  sizeLimit(rhs.sizeLimit());
  m_data = rhs.m_data;
  return(*this);
}

void SpaceRecorder::push(space a) {
  // even up the sizes to just under the limit.
  while (m_data.size() > sizeLimit() - 1)
    m_data.pop_front();
  m_data.push_back(a);
}

// output compatible for R frames <- read.table(flnm)
void SpaceRecorder::write2R(const string& flnm, bool skip_Uo) {

  ofstream ssfile(flnm.c_str());

  if (!ssfile.is_open()) {
    stringstream err;
    err << "Error: unable to open file \"" << flnm << "\"";
    throw SpaceRecorderIOError(err.str());
  }

  ssfile << "# Formated for R frames <- read.table(" << flnm << ")" << endl;
  ssfile << "x y z" << endl;

  for (unsigned int k = 0; k < m_data.size(); ++k) {

    // skip zero points from partially filled buffer.
    if (skip_Uo and m_data[k] == space::Uo)
      continue;

    ssfile << k << " "
	   << m_data[k].x() << " "
	   << m_data[k].y() << " "
	   << m_data[k].z() << endl;
  }

  ssfile.close();

}



// EOF