Source: mesh/math/euler.js

import {Quaternion} from './Quaternion';
import {Vector3} from './Vector3';
import {Matrix4} from './Matrix4';
import {_Math} from './Math';

/**
 * Euler
 * @param {*} x
 * @param {*} y
 * @param {*} z
 * @param {*} order
 */
function Euler( x, y, z, order ) {
  this._x = x || 0;
  this._y = y || 0;
  this._z = z || 0;
  this._order = order || Euler.DefaultOrder;
}

Euler.RotationOrders = ['XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX'];

Euler.DefaultOrder = 'XYZ';

Euler.prototype = {

  constructor: Euler,

  isEuler: true,

  get x() {
    return this._x;
  },

  set x( value ) {
    this._x = value;
    this.onChangeCallback();
  },

  get y() {
    return this._y;
  },

  set y( value ) {
    this._y = value;
    this.onChangeCallback();
  },

  get z() {
    return this._z;
  },

  set z( value ) {
    this._z = value;
    this.onChangeCallback();
  },

  get order() {
    return this._order;
  },

  set order( value ) {
    this._order = value;
    this.onChangeCallback();
  },

  set: function( x, y, z, order ) {
    this._x = x;
    this._y = y;
    this._z = z;
    this._order = order || this._order;

    this.onChangeCallback();

    return this;
  },

  clone: function() {
    return new this.constructor( this._x, this._y, this._z, this._order );
  },

  copy: function( euler ) {
    this._x = euler._x;
    this._y = euler._y;
    this._z = euler._z;
    this._order = euler._order;

    this.onChangeCallback();

    return this;
  },

  setFromRotationMatrix: function( m, order, update ) {
    let clamp = _Math.clamp;

    // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

    let te = m.elements;
    let m11 = te[0];
    let m12 = te[4];
    let m13 = te[8];
    let m21 = te[1];
    let m22 = te[5];
    let m23 = te[9];
    let m31 = te[2];
    let m32 = te[6];
    let m33 = te[10];

    order = order || this._order;

    if ( order === 'XYZ' ) {
      this._y = Math.asin( clamp( m13, - 1, 1 ) );

      if ( Math.abs( m13 ) < 0.99999 ) {
        this._x = Math.atan2( - m23, m33 );
        this._z = Math.atan2( - m12, m11 );
      } else {
        this._x = Math.atan2( m32, m22 );
        this._z = 0;
      }
    } else if ( order === 'YXZ' ) {
      this._x = Math.asin( - clamp( m23, - 1, 1 ) );

      if ( Math.abs( m23 ) < 0.99999 ) {
        this._y = Math.atan2( m13, m33 );
        this._z = Math.atan2( m21, m22 );
      } else {
        this._y = Math.atan2( - m31, m11 );
        this._z = 0;
      }
    } else if ( order === 'ZXY' ) {
      this._x = Math.asin( clamp( m32, - 1, 1 ) );

      if ( Math.abs( m32 ) < 0.99999 ) {
        this._y = Math.atan2( - m31, m33 );
        this._z = Math.atan2( - m12, m22 );
      } else {
        this._y = 0;
        this._z = Math.atan2( m21, m11 );
      }
    } else if ( order === 'ZYX' ) {
      this._y = Math.asin( - clamp( m31, - 1, 1 ) );

      if ( Math.abs( m31 ) < 0.99999 ) {
        this._x = Math.atan2( m32, m33 );
        this._z = Math.atan2( m21, m11 );
      } else {
        this._x = 0;
        this._z = Math.atan2( - m12, m22 );
      }
    } else if ( order === 'YZX' ) {
      this._z = Math.asin( clamp( m21, - 1, 1 ) );

      if ( Math.abs( m21 ) < 0.99999 ) {
        this._x = Math.atan2( - m23, m22 );
        this._y = Math.atan2( - m31, m11 );
      } else {
        this._x = 0;
        this._y = Math.atan2( m13, m33 );
      }
    } else if ( order === 'XZY' ) {
      this._z = Math.asin( - clamp( m12, - 1, 1 ) );

      if ( Math.abs( m12 ) < 0.99999 ) {
        this._x = Math.atan2( m32, m22 );
        this._y = Math.atan2( m13, m11 );
      } else {
        this._x = Math.atan2( - m23, m33 );
        this._y = 0;
      }
    } else {
      console.warn( 'Euler: unsupported order: ' + order );
    }

    this._order = order;

    if ( update !== false ) this.onChangeCallback();

    return this;
  },

  setFromQuaternion: function() {
    let matrix;

    return function setFromQuaternion( q, order, update ) {
      if ( matrix === undefined ) matrix = new Matrix4();

      matrix.makeRotationFromQuaternion( q );

      return this.setFromRotationMatrix( matrix, order, update );
    };
  }(),

  setFromVector3: function( v, order ) {
    return this.set( v.x, v.y, v.z, order || this._order );
  },

  reorder: function() {
    // WARNING: this discards revolution information -bhouston

    let q = new Quaternion();

    return function reorder( newOrder ) {
      q.setFromEuler( this );

      return this.setFromQuaternion( q, newOrder );
    };
  }(),

  equals: function( euler ) {
    return euler._x === this._x &&
    euler._y === this._y &&
    euler._z === this._z &&
    euler._order === this._order;
  },

  fromArray: function( array ) {
    this._x = array[0];
    this._y = array[1];
    this._z = array[2];
    if ( array[3] !== undefined ) this._order = array[3];

    this.onChangeCallback();

    return this;
  },

  toArray: function( array, offset ) {
    if ( array === undefined ) array = [];
    if ( offset === undefined ) offset = 0;

    array[offset] = this._x;
    array[offset + 1] = this._y;
    array[offset + 2] = this._z;
    array[offset + 3] = this._order;

    return array;
  },

  toVector3: function( optionalResult ) {
    if ( optionalResult ) {
      return optionalResult.set( this._x, this._y, this._z );
    } else {
      return new Vector3( this._x, this._y, this._z );
    }
  },

  onChange: function( callback ) {
    this.onChangeCallback = callback;

    return this;
  },

  onChangeCallback: function() {},

};


export {Euler};