// -*- C++ -*-
// $RCSfile: eulerangle.C,v $
// $Revision: 1.5 $
// $Author: langer $
// $Date: 2000/09/06 20:11:30 $


/* This software was produced by NIST, an agency of the U.S. government,
 * and by statute is not subject to copyright in the United States.
 * Recipients of this software assume all responsibilities associated
 * with its operation, modification and maintenance. However, to
 * facilitate maintenance we ask that before distributing modifed
 * versions of this software, you first contact the authors at
 * oof_manager@ctcms.nist.gov. 
 */


/* 3D Euler angle rotation
 *     alpha = polar declination
 *     beta = azimuthal rotation
 *     gamma = axial twist
 * For input, operator>> accepts two formats:
 *    [alpha, beta, gamma]
 * and
 *    beta
 * Any of the numbers can be replaced by the letter 'r', and an
 * angle will be generated with the appropriate random distribution.
 */
 
#include "eulerangle.h"
#include "random.h"
#include "ctype.h"
#include <math.h>
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>   // for finite(), which is sometimes not in math.h
#endif

static const double DEGREES = 180/M_PI;
static const double RADIANS = M_PI/180;

EulerAngle::EulerAngle(const EulerAngle &ea) {
  alpha_ = ea.alpha_;
  beta_ = ea.beta_;
  gamma_ = ea.gamma_;
}

EulerAngle &EulerAngle::operator=(const EulerAngle &ea) {
  if(this != &ea) {
    alpha_ = ea.alpha_;
    beta_ = ea.beta_;
    gamma_ = ea.gamma_;
  }
  return *this;
}

ostream &operator<<(ostream &os, const EulerAngle &ea) {
    if(ea.alpha_ == 0.0 && ea.gamma_ == 0.0)
	os << DEGREES*ea.beta_;
    else
	os << "[" << DEGREES*ea.alpha_ << ", " << DEGREES*ea.beta_ << ", "
	    << DEGREES*ea.gamma_ << "]";
    return os;
}

static void skipspace(istream &is) {
  int ch;
  while(ch = is.get()) {
    if(!isspace(ch)) {
      is.putback(ch);
      return;
    }
  }
}
  
static int israndom(istream &is) {
  skipspace(is);
  int ch = is.get();
  if(ch == 'r') return 1;
  is.putback(ch);
  return 0;
}


istream &operator>>(istream &is, EulerAngle &ea) {
  int ch;
  if((ch = is.get()) != '[') {
    if(isdigit(ch) || ch == '.' || ch == '-') { /* single number */
      is.putback(ch);
      is >> ea.beta_;
      if(!is) return is;
      ea.beta_ *= RADIANS;
      ea.alpha_ = ea.gamma_ = 0.0;
      return is;
    }
    else if(ch == 'r') {	// construct a random angle
      ea.alpha_ = acos(2*rndm() - 1.0);
      ea.beta_ = 2*M_PI*rndm();
      ea.gamma_ = 2*M_PI*rndm();
      return is;
    }
    // not a single number, "r", or start of a triple in square brackets
    is.clear(ios::badbit | is.rdstate());
    return is;
  }
  
  if(israndom(is))
    ea.alpha_ = acos(2*rndm() - 1.0);
  else {
    is >> ea.alpha_;
    if(!is) return is;
    ea.alpha_ *= RADIANS;
  }

  skipspace(is);    
  if((ch = is.get()) != ',') {
    is.clear(ios::badbit | is.rdstate());
    return is;
  }

  if(israndom(is))
    ea.beta_ = 2*M_PI*rndm();
  else {
    is >> ea.beta_;
    if(!is) return is;
    ea.beta_ *= RADIANS;
  }

  skipspace(is);
  if((ch = is.get()) != ',') {
    is.clear(ios::badbit | is.rdstate());
    return is;
  }

  if(israndom(is)) 
    ea.gamma_ = 2*M_PI*rndm();
  else {
    is >> ea.gamma_;
    if(!is) return is;
    ea.gamma_ *= RADIANS;
  }

  skipspace(is);
  if((ch = is.get()) != ']') {
    is.clear(ios::badbit | is.rdstate());
    return is;
  }
  return is;
}

void sincos(const double angle, double &sine, double &cosine) {
    double tn = tan(0.5*angle);
    if(!finite(tn)) {
      sine = sin(angle);
      cosine = cos(angle);
      return;
    }
    double tntn = 1./(1 + tn*tn);
    cosine = (1 - tn*tn)*tntn;
    sine = 2*tn*tntn;
}

// Compute the rotation matrix corresponding to the Euler angle. The
// rotation matrix multiplied by a vector gives the coordinates of the
// vector in a coordinate system that has been rotated by the Euler
// angle.

MV_ColMat_double EulerAngle::rotation() const {

    double cosa, sina, cosb, sinb, cosg, sing;
    sincos(alpha_, sina, cosa);
    sincos(beta_, sinb, cosb);
    sincos(gamma_, sing, cosg);
    
    MV_ColMat_double r(3, 3);
    r(0, 0) =  cosa*cosb*cosg - sinb*sing;
    r(0, 1) =  cosa*sinb*cosg + cosb*sing;
    r(0, 2) = -sina*cosg;
    r(1, 0) = -cosa*cosb*sing - sinb*cosg;
    r(1, 1) = -cosa*sinb*sing + cosb*cosg;
    r(1, 2) =  sina*sing;
    r(2, 0) =  sina*cosb;
    r(2, 1) =  sina*sinb;
    r(2, 2) =  cosa;
    
    return r;
}

int operator==(const EulerAngle &om1, const EulerAngle &om2) {
  if(om1.alpha_ != om2.alpha_) return 0;
  if(om1.beta_ != om2.beta_) return 0;
  if(om1.gamma_ != om2.gamma_) return 0;
  return 1;
}


syntax highlighted by Code2HTML, v. 0.9.1