    // Quaternion
    export interface Quaternion {
      x: number;
      y: number;
      z: number;
      w: number;
    }

    /** @class
     *  @subcategory  Math
     * */
    export class Quaternion {
      x: number;
      y: number;
      z: number;
      w: number;
      constructor(x?: number, y?: number, z?: number, w?: number) {
        this.x = x || 0;
        this.y = y || 0;
        this.z = z || 0;
        this.w = w !== undefined ? w : 1;
      }

      set(x: number, y: number, z: number, w: number) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;

        return this;
      }

      copy(q: Quaternion) {
        this.x = q.x;
        this.y = q.y;
        this.z = q.z;
        this.w = q.w;

        return this;
      }

      conjugate() {
        this.x *= -1;
        this.y *= -1;
        this.z *= -1;

        return this;
      }

      inverse() {
        return this.conjugate().normalize();
      }

      length() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
      }

      lengthxyz() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
      }

      normalize() {
        let l = this.length();

        if (l === 0) {
          this.x = 0;
          this.y = 0;
          this.z = 0;
          this.w = 1;
        } else {
          l = 1 / l;

          this.x *= l;
          this.y *= l;
          this.z *= l;
          this.w *= l;
        }

        return this;
      }

      multiply(q: any) {
        return this.multiplyQuaternions(this, q);
      }

      multiplyScalar(s: number) {
        this.x *= s;
        this.y *= s;
        this.z *= s;
        this.w *= s;
        return this;
      }

      multiplyQuaternions(a: Quaternion, b: Quaternion) {
        const qax = a.x;
        const qay = a.y;
        const qaz = a.z;
        const qaw = a.w;
        const qbx = b.x;
        const qby = b.y;
        const qbz = b.z;
        const qbw = b.w;

        this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
        this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
        this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
        this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
        return this;
      }

      sub(q: Quaternion) {
        this.x -= q.x;
        this.y -= q.y;
        this.z -= q.z;
        this.w -= q.w;
        return this;
      }

      clone() {
        return new Quaternion(this.x, this.y, this.z, this.w);
      }

      setFromEuler(e: Quaternion) {
        const c1 = Math.cos(e.x / 2);
        const c2 = Math.cos(e.y / 2);
        const c3 = Math.cos(e.z / 2);
        const s1 = Math.sin(e.x / 2);
        const s2 = Math.sin(e.y / 2);
        const s3 = Math.sin(e.z / 2);

        this.x = s1 * c2 * c3 + c1 * s2 * s3;
        this.y = c1 * s2 * c3 - s1 * c2 * s3;
        this.z = c1 * c2 * s3 + s1 * s2 * c3;
        this.w = c1 * c2 * c3 - s1 * s2 * s3;

        return this;
      }
    }