All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ode4j.math.DQuaternion Maven / Gradle / Ivy

/*************************************************************************
 *                                                                       *
 * Open Dynamics Engine 4J, Copyright (C) 2009-2023 Tilmann Zaeschke     *
 * All rights reserved.  Email: [email protected]   Web: www.ode4j.org        *
 *                                                                       *
 * This library is free software; you can redistribute it and/or         *
 * modify it under the terms of EITHER:                                  *
 *   (1) The GNU Lesser General Public License as published by the Free  *
 *       Software Foundation; either version 2.1 of the License, or (at  *
 *       your option) any later version. The text of the GNU Lesser      *
 *       General Public License is included with this library in the     *
 *       file LICENSE.TXT.                                               *
 *   (2) The BSD-style license that is included with this library in     *
 *       the file ODE4J-LICENSE-BSD.TXT.                                 *
 *                                                                       *
 * This library 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 files    *
 * LICENSE.TXT and ODE4J-LICENSE-BSD.TXT for more details.               *
 *                                                                       *
 *************************************************************************/
package org.ode4j.math;


import static org.ode4j.ode.internal.Common.M_PI;

/**
 * A quaternion consists of four numbers, [w, x, y, z].
 * They are used top represent rigid body orientations.
 *
 * @author Tilmann Zaeschke
 */
public class DQuaternion implements DQuaternionC {

	private double w;
	private double x;
	private double y;
	private double z;
	public static final int LEN = 4;

	public static final DQuaternionC ZERO = new DQuaternion();
	public static final DQuaternionC IDENTITY = new DQuaternion(1, 0, 0, 0);

	public DQuaternion() {
		// Nothing
	}

	public DQuaternion(double w, double x, double y, double z) {
		this();
		set(w, x, y, z);
	}

	public DQuaternion(DQuaternionC x) {
		this();
		set(x);
	}

	@Override
	public String toString() {
		return "DQuaternion[ " +
				get0() + ", " +
				get1() + ", " +
				get2() + ", " +
				get3() + " ]";
	}

	public DQuaternion set(double w, double x, double y, double z) {
		this.w = w;
		this.x = x;
		this.y = y;
		this.z = z;
		return this;
	}

	public DQuaternion set(DQuaternionC q) {
		w = q.get0();
		x = q.get1();
		y = q.get2();
		z = q.get3();
		return this;
	}

	public DQuaternion scale(double d) {
		w *= d;
		x *= d;
		y *= d;
		z *= d;
		return this;
	}

	public DQuaternion add(DQuaternion q) {
		w += q.get0();
		x += q.get1();
		y += q.get2();
		z += q.get3();
		return this;
	}

	@Override
	public double get0() {
		return w;
	}

	@Override
	public double get1() {
		return x;
	}

	@Override
	public double get2() {
		return y;
	}

	@Override
	public double get3() {
		return z;
	}

	public int dim() {
		return LEN;
	}

	/**
	 * Sets w of [w, x, y, z].
	 * @param w w
	 * @return This quaternion.
	 */
	public DQuaternion set0(double w) {
		this.w = w;
		return this;
	}

	/**
	 * Sets x of [w, x, y, z].
	 * @param x x
	 * @return This quaternion.
	 */
	public DQuaternion set1(double x) {
		this.x = x;
		return this;
	}

	/**
	 * Sets y of [w, x, y, z].
	 * @param y y
	 * @return This quaternion.
	 */
	public DQuaternion set2(double y) {
		this.y = y;
		return this;
	}

	/**
	 * Sets z of [w, x, y, z].
	 * @param z z
	 * @return This quaternion.
	 */
	public DQuaternion set3(double z) {
		this.z = z;
		return this;
	}

	@Override
	public boolean isEq(DQuaternion q, double epsilon) {
		return Math.abs(w - q.w) <= epsilon
				&& Math.abs(x - q.x) <= epsilon
				&& Math.abs(y - q.y) <= epsilon
				&& Math.abs(z - q.z) <= epsilon;
	}

	@Override
	@Deprecated // float is generally not comparable. To be removed in 0.6.0. TODO deprecated
	public boolean isEq(DQuaternion q) {
		return w == q.w && x == q.x && y == q.y && z == q.z;
	}

	/**
	 * Do not use. This can be slow, use isEq() instead.
	 * @param obj object
	 * @return true if equal
	 * @deprecated
	 */
	@Override
	@Deprecated // float is generally not comparable. To be removed in 0.6.0. TODO deprecated
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (obj == null) return false;
		if (!(obj instanceof DQuaternion)) return false;
		return isEq((DQuaternion)obj);
	}

	@Override
	@Deprecated // float is generally not comparable. To be removed in 0.6.0. TODO deprecated
	public int hashCode() {
		int h = 0;
		h |= Double.doubleToRawLongBits(w);
		h <<= 6;
		h |= Double.doubleToRawLongBits(x);
		h <<= 6;
		h |= Double.doubleToRawLongBits(y);
		h <<= 6;
		h |= Double.doubleToRawLongBits(z);
		h <<= 6;
		return h;
	}

	@Override
	public DQuaternion copy() {
		return new DQuaternion(this);
	}

	public DQuaternion add(double dw, double dx, double dy, double dz) {
		w += dw;
		x += dx;
		y += dy;
		z += dz;
		return this;
	}

	public final DQuaternion sum(DQuaternion q1, DQuaternion q2, double d2) {
		w = q1.get0() + q2.get0() * d2;
		x = q1.get1() + q2.get1() * d2;
		y = q1.get2() + q2.get2() * d2;
		z = q1.get3() + q2.get3() * d2;
		return this;
	}

	/**
	 * Set a vector/matrix at position i to a specific value.
	 * @param i position
	 * @param d value
	 * @return This quaternion.
	 */
	public final DQuaternion set(int i, double d) {
		switch (i) {
			case 0: w = d; break;
			case 1: x = d; break;
			case 2: y = d; break;
			case 3: z = d; break;
			default:
				throw new ArrayIndexOutOfBoundsException("i=" + i);
		}
		return this;
	}

	public final DQuaternion setZero() {
		set(0, 0, 0, 0);
		return this;
	}

	public final DQuaternion scale(int i, double l) {
		switch (i) {
			case 0: w *= l; break;
			case 1: x *= l; break;
			case 2: y *= l; break;
			case 3: z *= l; break;
			default:
				throw new ArrayIndexOutOfBoundsException("i=" + i);
		}
		return this;
	}

	public final double lengthSquared() {
		return w * w + x * x + y * y + z * z;
	}

	@Override
	public final double get(int i) {
		switch (i) {
			case 0: return w;
			case 1: return x;
			case 2: return y;
			case 3: return z;
			default:
				throw new ArrayIndexOutOfBoundsException("i=" + i);
		}
	}

	public final double length() {
		return Math.sqrt(lengthSquared());
	}

	/**
	 * this may be called for vectors `a' with extremely small magnitude, for
	 * example the result of a cross product on two nearly perpendicular vectors.
	 * we must be robust to these small vectors. to prevent numerical error,
	 * first find the component a[i] with the largest magnitude and then scale
	 * all the components by 1/a[i]. then we can compute the length of `a' and
	 * scale the components by 1/l. this has been verified to work with vectors
	 * containing the smallest representable numbers.
	 * @return 'false' if normnalization failed (returns unit vector)
	 */
	public final boolean safeNormalize4 () {
		double d = Math.abs(get0());
//		for (int i = 1; i < v.length; i++) {
//			if (Math.abs(v[i]) > d) {
//				d = Math.abs(v[i]);
//			}
//		}
		if (Math.abs(get1()) > d) d = Math.abs(get1());
		if (Math.abs(get2()) > d) d = Math.abs(get2());
		if (Math.abs(get3()) > d) d = Math.abs(get3());

		if (d <= Double.MIN_NORMAL) {
			set(1, 0, 0, 0);
			return false;
		}

		scale(1/d);
//		for (int i = 0; i < v.length; i++) {
//			v[i] /= d;
//		}

//		double sum = 0;
//		for (double d2: v) {
//			sum += d2*d2;
//		}

		double l = 1./length();//Math.sqrt(sum);
//		for (int i = 0; i < v.length; i++) {
//			v[i] *= l;
//		}
		scale(l);
		return true;
	}

	/**
	 * this may be called for vectors `a' with extremely small magnitude, for
	 * example the result of a cross product on two nearly perpendicular vectors.
	 * we must be robust to these small vectors. to prevent numerical error,
	 * first find the component a[i] with the largest magnitude and then scale
	 * all the components by 1/a[i]. then we can compute the length of `a' and
	 * scale the components by 1/l. this has been verified to work with vectors
	 * containing the smallest representable numbers.
	 * @return This quaternion.
	 */
	public DQuaternion normalize() {
		if (!safeNormalize4()) throw new IllegalStateException(
				"Normalization failed: " + this);
		return this;
	}

	public DQuaternion setIdentity() {
		set(1, 0, 0, 0);
		return this;
	}

	public boolean isZero() {
		return w == 0 && x == 0 && y == 0 && z == 0;
	}

	/**
	 * @param roll  radians roll
	 * @param pitch radians pitch
	 * @param yaw   radians yaw
	 * @return quaternion
	 */
	@Deprecated // TODO deprecated, to be removed in 0.6.0. Consider using DRotation.dRFromEulerAngles
	public static DQuaternion fromEuler(double roll, double pitch, double yaw) {
		double cr = Math.cos(roll * 0.5);
		double sr = Math.sin(roll * 0.5);
		double cp = Math.cos(pitch * 0.5);
		double sp = Math.sin(pitch * 0.5);
		double cy = Math.cos(yaw * 0.5);
		double sy = Math.sin(yaw * 0.5);

		DQuaternion q = new DQuaternion();
		q.w = cr * cp * cy + sr * sp * sy;
		q.x = sr * cp * cy - cr * sp * sy;
		q.y = cr * sp * cy + sr * cp * sy;
		q.z = cr * cp * sy - sr * sp * cy;
		return q;
	}

	/**
	 * @param angles roll, pitch and yaw as radians.
	 * @return Quaternion
	 */
	@Deprecated // TODO deprecated, to be removed in 0.6.0. Consider using DRotation.dRFromEulerAngles
	public static DQuaternion fromEuler(DVector3C angles) {
		return fromEuler(angles.get0(), angles.get1(), angles.get2());
	}

	/**
	 * @param roll  roll degrees
	 * @param pitch pitch degrees
	 * @param yaw   yaw degrees
	 * @return quaternion
	 */
	@Deprecated // TODO deprecated, to be removed in 0.6.0. Consider using DRotation.dRFromEulerAngles
	public static DQuaternion fromEulerDegrees(double roll, double pitch, double yaw) {
		return fromEuler(Math.toRadians(roll), Math.toRadians(pitch), Math.toRadians(yaw));
	}

	/**
	 * @param angles roll, pitch and yaw as degrees, i.e. 0..360.
	 * @return Quaternion
	 */
	@Deprecated // TODO deprecated, to be removed in 0.6.0. Consider using DRotation.dRFromEulerAngles
	public static DQuaternion fromEulerDegrees(DVector3C angles) {
		return fromEulerDegrees(angles.get0(), angles.get1(), angles.get2());
	}

	@Override
	@Deprecated // TODO deprecated, to be removed in 0.6.0. Consider using DRotation.dRFromEulerAngles
	public DVector3 toEuler() {
		DVector3 angles = new DVector3();
		DQuaternion q = new DQuaternion(this);
		q.safeNormalize4();

		// roll (x-axis rotation)
		double sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
		double cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
		angles.set0(Math.atan2(sinr_cosp, cosr_cosp));

		// pitch (y-axis rotation)
		double sinp = Math.sqrt(1 + 2 * (q.w * q.y - q.x * q.z));
		double cosp = Math.sqrt(1 - 2 * (q.w * q.y - q.x * q.z));
		angles.set1(2 * Math.atan2(sinp, cosp) - M_PI / 2);

		// yaw (z-axis rotation)
		double siny_cosp = 2 * (q.w * q.z + q.x * q.y);
		double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
		angles.set2(Math.atan2(siny_cosp, cosy_cosp));

		return angles;
	}

	@Override
	@Deprecated // TODO deprecated, to be removed in 0.6.0. Consider using DRotation.dRFromEulerAngles
	public DVector3 toEulerDegrees() {
		return toEuler().eqToDegrees();
	}

	/**
	 * Calculates the inverse of the quaternion.
	 * @return the inverted quaternion
	 */
	public DQuaternion eqInverse() {
		double norm = lengthSquared();
		if (norm > 0.0) {
			double invNorm = 1.0 / norm;
			w *= invNorm;
			x *= -invNorm;
			y *= -invNorm;
			z *= -invNorm;
		}
		return this;
	}

	/**
	 * Calculates the inverse of the quaternion and returns it as a new quaternion.
	 * @return an inverted quaternion
	 */
	@Override
	public DQuaternion reInverse() {
		DQuaternion ret = new DQuaternion(this);
		ret.eqInverse();
		return ret;
	}

	/**
	 * Return the 'dot' product of two quaternions.
	 * r = a0*b0 + a1*b1 + a2*b2 + a3*b3;
	 * @param b b
	 * @return (this) * b
	 */
	@Override
	public double dot(DQuaternionC b) {
		return get0()*b.get0() + get1()*b.get1() + get2()*b.get2() + get3()*b.get3();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy