/** * Simply returns {@code log(a. inverse() * b)}. * * <p>Useful for {@link #squadTangent(Quaternion, Quaternion, Quaternion)}. * * @param a the first Quaternion * @param b the second Quaternion */ public static final Quaternion lnDif(Quaternion a, Quaternion b) { Quaternion dif = a.inverse(); dif.multiply(b); dif.normalize(); return dif.log(); }
/** * Returns a tangent Quaternion for {@code center}, defined by {@code before} and {@code after} * quaternions. * * @param before the first Quaternion * @param center the second Quaternion * @param after the third Quaternion */ public static final Quaternion squadTangent( Quaternion before, Quaternion center, Quaternion after) { Quaternion l1 = Quaternion.lnDif(center, before); Quaternion l2 = Quaternion.lnDif(center, after); Quaternion e = new Quaternion(); e.x = -0.25f * (l1.x + l2.x); e.y = -0.25f * (l1.y + l2.y); e.z = -0.25f * (l1.z + l2.z); e.w = -0.25f * (l1.w + l2.w); return Quaternion.multiply(center, e.exp()); }
/** * Returns the slerp interpolation of quaternions {@code a} and {@code b}, at time {@code t}. * * <p>{@code t} should range in {@code [0,1]}. Result is a when {@code t=0 } and {@code b} when * {@code t=1}. * * <p>When {@code allowFlip} is true (default) the slerp interpolation will always use the * "shortest path" between the quaternions' orientations, by "flipping" the source Quaternion if * needed (see {@link #negate()}). * * @param a the first Quaternion * @param b the second Quaternion * @param t the t interpolation parameter * @param allowFlip tells whether or not the interpolation allows axis flip */ public static final Quaternion slerp(Quaternion a, Quaternion b, float t, boolean allowFlip) { // Warning: this method should not normalize the Quaternion float cosAngle = Quaternion.dotProduct(a, b); float c1, c2; // Linear interpolation for close orientations if ((1.0 - PApplet.abs(cosAngle)) < 0.01) { c1 = 1.0f - t; c2 = t; } else { // Spherical interpolation float angle = PApplet.acos(PApplet.abs(cosAngle)); float sinAngle = PApplet.sin(angle); c1 = PApplet.sin(angle * (1.0f - t)) / sinAngle; c2 = PApplet.sin(angle * t) / sinAngle; } // Use the shortest path if (allowFlip && (cosAngle < 0.0)) c1 = -c1; return new Quaternion( c1 * a.x + c2 * b.x, c1 * a.y + c2 * b.y, c1 * a.z + c2 * b.z, c1 * a.w + c2 * b.w, false); }
/** * Returns the slerp interpolation of the two quaternions {@code a} and {@code b}, at time {@code * t}, using tangents {@code tgA} and {@code tgB}. * * <p>The resulting Quaternion is "between" {@code a} and {@code b} (result is {@code a} when * {@code t=0} and {@code b} for {@code t=1}). * * <p>Use {@link #squadTangent(Quaternion, Quaternion, Quaternion)} to define the Quaternion * tangents {@code tgA} and {@code tgB}. * * @param a the first Quaternion * @param tgA the first tangent Quaternion * @param tgB the second tangent Quaternion * @param b the second Quaternion * @param t the t interpolation parameter */ public static final Quaternion squad( Quaternion a, Quaternion tgA, Quaternion tgB, Quaternion b, float t) { Quaternion ab = Quaternion.slerp(a, b, t); Quaternion tg = Quaternion.slerp(tgA, tgB, t, false); return Quaternion.slerp(ab, tg, 2.0f * t * (1.0f - t), false); }
/** * Returns the associated inverse rotation processing PMatrix3D. This is simply {@link #matrix()} * of the {@link #inverse()}. * * <p><b>Attention:</b> The result is only valid until the next call to {@link #inverseMatrix()}. * Use it immediately (as in {@code applyMatrix(q.inverseMatrix())}). */ public final PMatrix3D inverseMatrix() { Quaternion tempQuat = new Quaternion(x, y, z, w); tempQuat.invert(); return tempQuat.matrix(); }
/** * Wrapper function that simply calls {@code slerp(a, b, t, true)}. * * <p>See {@link #slerp(Quaternion, Quaternion, float, boolean)} for details. */ public static final Quaternion slerp(Quaternion a, Quaternion b, float t) { return Quaternion.slerp(a, b, t, true); }
/** * Returns the image of {@code v} by the Quaternion {@link #inverse()} rotation. * * <p>{@link #rotate(PVector)} performs an inverse transformation. * * @param v the PVector */ public final PVector inverseRotate(PVector v) { Quaternion tempQuat = new Quaternion(x, y, z, w); tempQuat.invert(); return tempQuat.rotate(v); }
/** * Returns the inverse Quaternion (inverse rotation). * * <p>The result has a negated {@link #axis()} direction and the same {@link #angle()}. * * <p>A composition of a Quaternion and its {@link #inverse()} results in an identity function. * Use {@link #invert()} to actually modify the Quaternion. * * @see #invert() */ public final Quaternion inverse() { Quaternion tempQuat = new Quaternion(this); tempQuat.invert(); return tempQuat; }
/** * Returns the product of Quaternion {@code q1} by the inverse of Quaternion {@code q2} (i.e., * {@code q1 * q2^-1}). The value of both argument quaternions is preserved. * * @param q1 the first Quaternion * @param q2 the second Quaternion */ public static final Quaternion multiplyInverse(Quaternion q1, Quaternion q2) { Quaternion tempQuat = new Quaternion(q2); tempQuat.invert(); return Quaternion.multiply(q1, tempQuat); }
/** * Multiplies this Quaternion by the inverse of Quaternion {@code q1} and places the value into * this Quaternion (i.e., {@code this = this * q^-1}). The value of the argument Quaternion is * preserved. * * @param q1 the other Quaternion */ public final void multiplyInverse(Quaternion q1) { Quaternion tempQuat = new Quaternion(q1); tempQuat.invert(); this.multiply(tempQuat); }
/** * Returns the image of {@code v} by the rotation {@code q1}. Same as {@code q1.rotate(v).} * * @param q1 the Quaternion * @param v the PVector * @see #rotate(PVector) * @see #inverseRotate(PVector) */ public static final PVector multiply(Quaternion q1, PVector v) { return q1.rotate(v); }