package java3d;

public class Quat4d implements Cloneable {
	private static final double EPS = 1.0e-12;
	public double x;
	public double y;
	public double z;
	public double w;

	public Quat4d clone() {
		Quat4d q;
		try {
			q = (Quat4d) super.clone();
		} catch (CloneNotSupportedException ce) {
			throw new RuntimeException();
		}
		q.set(x, y, z, w);
		return q;
	}

	// コンストラクタ
	public Quat4d() {
		x = 0.0;
		y = 0.0;
		z = 0.0;
		w = 0.0;
	}

	// コンストラクタ
	public Quat4d(double px, double py, double pz, double pw) {
		double mag = 1.0/Math.sqrt(px*px+py*py+pz*pz+pw*pw);
		x = px*mag;
		y = py*mag;
		z = pz*mag;
		w = pw*mag;
	}

	public Quat4d(Quat4d q) {
		x = q.x;
		y = q.y;
		z = q.z;
		w = q.w;
	}

	public void set(AxisAngle4d a) {
		double mag,amag;
		amag = Math.sqrt( a.x*a.x + a.y*a.y + a.z*a.z);
		if( amag < EPS ) {
		w = 0.0;
		x = 0.0;
		y = 0.0;
		z = 0.0;
		} else {
		amag = 1.0/amag;
		mag = Math.sin(a.angle/2.0);
		w = Math.cos(a.angle/2.0);
		x = a.x*amag*mag;
		y = a.y*amag*mag;
		z = a.z*amag*mag;
		}
	}
	
	private void set(double x2, double y2, double z2, double w2) {
		x = x2;
		y = y2;
		z = z2;
		w = w2;
	}

	public void mul(Quat4d q1) {
		double x, y, w;
		w = this.w * q1.w - this.x * q1.x - this.y * q1.y - this.z * q1.z;
		x = this.w * q1.x + q1.w * this.x + this.y * q1.z - this.z * q1.y;
		y = this.w * q1.y + q1.w * this.y - this.x * q1.z + this.z * q1.x;
		this.z = this.w * q1.z + q1.w * this.z + this.x * q1.y - this.y * q1.x;
		this.w = w;
		this.x = x;
		this.y = y;
	}

	public void interpolate(Quat4d q1, double alpha) {
		double dot, s1, s2, om, sinom;

		dot = x * q1.x + y * q1.y + z * q1.z + w * q1.w;

		if (dot < 0) {
			// negate quaternion
			q1.x = -q1.x;
			q1.y = -q1.y;
			q1.z = -q1.z;
			q1.w = -q1.w;
			dot = -dot;
		}

		if ((1.0 - dot) > EPS) {
			om = Math.acos(dot);
			sinom = Math.sin(om);
			s1 = Math.sin((1.0 - alpha) * om) / sinom;
			s2 = Math.sin(alpha * om) / sinom;
		} else {
			s1 = 1.0 - alpha;
			s2 = alpha;
		}

		w = s1 * w + s2 * q1.w;
		x = s1 * x + s2 * q1.x;
		y = s1 * y + s2 * q1.y;
		z = s1 * z + s2 * q1.z;
	}

	public void normalize() {
		double norm;

		norm = (this.x * this.x + this.y * this.y + this.z * this.z + this.w
				* this.w);

		if (norm > 0.0) {
			norm = 1.0 / Math.sqrt(norm);
			this.x *= norm;
			this.y *= norm;
			this.z *= norm;
			this.w *= norm;
		} else {
			this.x = 0.0;
			this.y = 0.0;
			this.z = 0.0;
			this.w = 0.0;
		}
	}
}
