/** * Creates a new quaternion that is the conjugate <code>[-x, -y, -z, w]</code> of this quaternion. * * @param store * the <code>Quaternion</code> to store the result in. If <code>null</code>, a new one is created. * @return the conjugate to this quaternion. */ @Override public Quaternion conjugate(Quaternion store) { if (store == null) { store = new Quaternion(); } return store.set(-getX(), -getY(), -getZ(), getW()); }
/** * Does a spherical linear interpolation between the given start and end quaternions by the given change amount. * Stores the result locally. * * @param startQuat * @param endQuat * @param changeAmnt * @return this quaternion for chaining. * @throws NullPointerException * if startQuat or endQuat are null. */ public Quaternion slerpLocal(final ReadOnlyQuaternion startQuat, final ReadOnlyQuaternion endQuat, final double changeAmnt) { final Quaternion end = Quaternion.fetchTempInstance().set(endQuat); slerpLocal(startQuat, endQuat, changeAmnt, end); Quaternion.releaseTempInstance(end); return this; }
/** * Calculates the <i>multiplicative inverse</i> <code>Q<sup>-1</sup></code> of this quaternion <code>Q</code> such * that <code>QQ<sup>-1</sup> = [0,0,0,1]</code> (the identity quaternion). Note that for unit quaternions, a * quaternion's inverse is equal to its (far easier to calculate) conjugate. * * @param store * the <code>Quaternion</code> to store the result in. If <code>null</code>, a new one is created. * @see #conjugate(Quaternion) * @return the multiplicative inverse of this quaternion. */ public Quaternion invert(Quaternion store) { if (store == null) { store = new Quaternion(); } final double magnitudeSQ = magnitudeSquared(); conjugate(store); if (Math.abs(1.0 - magnitudeSQ) <= MathUtils.EPSILON) { return store; } else { return store.multiplyLocal(1.0 / magnitudeSQ); } }
final Quaternion kQ0 = Quaternion.fetchTempInstance(), kQ1 = Quaternion.fetchTempInstance(); kQ0.fromAxes(rkBox0._xAxis, rkBox0._yAxis, rkBox0._zAxis); kQ1.fromAxes(rkBox1._xAxis, rkBox1._yAxis, rkBox1._zAxis); if (kQ0.dot(kQ1) < 0.0) { kQ1.multiplyLocal(-1.0); final Quaternion kQ = kQ0.addLocal(kQ1); kQ.normalizeLocal(); final Matrix3 kBoxaxis = kQ.toRotationMatrix(Matrix3.fetchTempInstance()); final Vector3 newXaxis = kBoxaxis.getColumn(0, Vector3.fetchTempInstance()); final Vector3 newYaxis = kBoxaxis.getColumn(1, Vector3.fetchTempInstance()); Quaternion.releaseTempInstance(kQ0); Quaternion.releaseTempInstance(kQ1); Matrix3.releaseTempInstance(kBoxaxis); Vector3.releaseTempInstance(kBoxCenter);
@Test public void testGetSet() { final Quaternion quat1 = new Quaternion(); assertEquals(Quaternion.IDENTITY, quat1); assertTrue(quat1.isIdentity()); quat1.setX(1); assertTrue(quat1.getX() == 1.0); quat1.setX(Double.POSITIVE_INFINITY); assertTrue(quat1.getX() == Double.POSITIVE_INFINITY); quat1.setX(Double.NEGATIVE_INFINITY); assertTrue(quat1.getX() == Double.NEGATIVE_INFINITY); quat1.setY(1); assertTrue(quat1.getY() == 1.0); quat1.setY(Double.POSITIVE_INFINITY); assertTrue(quat1.getY() == Double.POSITIVE_INFINITY); quat1.setY(Double.NEGATIVE_INFINITY); assertTrue(quat1.getY() == Double.NEGATIVE_INFINITY); quat1.setZ(1); assertTrue(quat1.getZ() == 1.0); quat1.setZ(Double.POSITIVE_INFINITY); assertTrue(quat1.getZ() == Double.POSITIVE_INFINITY); quat1.setZ(Double.NEGATIVE_INFINITY); assertTrue(quat1.getZ() == Double.NEGATIVE_INFINITY); quat1.setW(1); assertTrue(quat1.getW() == 1.0); quat1.setW(Double.POSITIVE_INFINITY);
@Test public void testValid() { final Quaternion quat = new Quaternion(); assertTrue(Quaternion.isValid(quat)); quat.set(Double.NaN, 0, 0, 0); assertFalse(Quaternion.isValid(quat)); quat.set(0, Double.NaN, 0, 0); assertFalse(Quaternion.isValid(quat)); quat.set(0, 0, Double.NaN, 0); assertFalse(Quaternion.isValid(quat)); quat.set(0, 0, 0, Double.NaN); assertFalse(Quaternion.isValid(quat)); quat.set(Double.NEGATIVE_INFINITY, 0, 0, 0); assertFalse(Quaternion.isValid(quat)); quat.set(0, Double.NEGATIVE_INFINITY, 0, 0); assertFalse(Quaternion.isValid(quat)); quat.set(0, 0, Double.NEGATIVE_INFINITY, 0); assertFalse(Quaternion.isValid(quat)); quat.set(0, 0, 0, Double.NEGATIVE_INFINITY); assertFalse(Quaternion.isValid(quat)); quat.setIdentity(); assertTrue(Quaternion.isValid(quat)); assertFalse(Quaternion.isValid(null)); assertTrue(quat.strictEquals(quat)); assertFalse(quat.equals(null));
@Test public void testMultiply() { final Quaternion quat1 = new Quaternion(0.5, 1, 2, 3); final Quaternion quat2 = new Quaternion(); assertEquals(new Quaternion(1, 2, 4, 6), quat1.multiply(2, null)); assertEquals(new Quaternion(2, 4, 8, 12), quat1.multiply(4, quat2)); assertEquals(new Quaternion(1, 2, 4, 6), quat1.multiplyLocal(2)); quat1.fromAngleNormalAxis(MathUtils.QUARTER_PI, Vector3.UNIT_Y); quat1.multiply(quat1, quat2); final ReadOnlyVector3 vec = Vector3.UNIT_Z; assertTrue(Math.abs(Vector3.UNIT_X.distance(quat2.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE); quat1.multiplyLocal(quat1.getX(), quat1.getY(), quat1.getZ(), quat1.getW()); assertTrue(Math.abs(Vector3.UNIT_X.distance(quat1.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE); quat2.fromAngleNormalAxis(MathUtils.HALF_PI, Vector3.UNIT_Y); quat1.multiplyLocal(quat2); assertTrue(Math.abs(Vector3.NEG_UNIT_Z.distance(quat1.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE); quat1.multiplyLocal(new Matrix3().applyRotationY(MathUtils.HALF_PI)); assertTrue(Math.abs(Vector3.NEG_UNIT_X.distance(quat1.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE); }
public MoveWidget withXAxis(final ReadOnlyColorRGBA color, final double scale, final double width, final double lengthGap, final double tipGap) { if (_xArrow != null) { _xArrow.removeFromParent(); } _xColor.set(color); _xArrow = new InteractArrow("xMoveArrow", scale, width, lengthGap, tipGap); _xArrow.setDefaultColor(color); final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.UNIT_Y); _xArrow.setRotation(rotate); _handle.attachChild(_xArrow); return this; }
@Test public void testAngleAxis() { final Quaternion quat = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, new Vector3(2, 0, 0)); final Quaternion quat2 = new Quaternion().fromAngleNormalAxis(MathUtils.HALF_PI, new Vector3(1, 0, 0)); assertEquals(quat2, quat); assertTrue(1 - quat.magnitude() <= MathUtils.EPSILON); assertEquals(quat.apply(Vector3.ONE, null), quat2.apply(Vector3.ONE, null)); assertTrue(Math.abs(new Vector3(0, -1, 0).distance(quat.apply(new Vector3(0, 0, 1), null))) <= MathUtils.EPSILON); assertEquals(Quaternion.IDENTITY, new Quaternion(1, 2, 3, 4).fromAngleAxis(MathUtils.HALF_PI, new Vector3(0, 0, 0))); final Vector3 axisStore = new Vector3(); double angle = quat.toAngleAxis(axisStore); assertEquals(quat, new Quaternion().fromAngleAxis(angle, axisStore)); quat.set(0, 0, 0, 0); angle = quat.toAngleAxis(axisStore); assertTrue(0.0 == angle); assertEquals(Vector3.UNIT_X, axisStore); }
@Test public void testRotations() { final double a = MathUtils.QUARTER_PI; final Quaternion quat = new Quaternion().fromRotationMatrix(new Matrix3(// Math.cos(a), -Math.sin(a), 0, // Math.sin(a), Math.cos(a), 0, // 0, 0, 1)); final Vector3 column = quat.getRotationColumn(0, null); assertTrue(Math.abs(new Vector3(Math.cos(a), Math.sin(a), 0).distance(column)) <= MathUtils.EPSILON); quat.getRotationColumn(1, column); assertTrue(Math.abs(new Vector3(-Math.sin(a), Math.sin(a), 0).distance(column)) <= MathUtils.EPSILON); quat.getRotationColumn(2, column); assertTrue(Math.abs(new Vector3(0, 0, 1).distance(column)) <= MathUtils.EPSILON); quat.set(0, 0, 0, 0); assertEquals(Vector3.UNIT_X, quat.getRotationColumn(0, null)); quat.fromEulerAngles(MathUtils.QUARTER_PI, MathUtils.PI, MathUtils.HALF_PI); final Vector3 rotated = new Vector3(1, 1, 1); quat.apply(rotated, rotated); final Quaternion worker = new Quaternion(); worker.applyRotationY(MathUtils.QUARTER_PI); worker.applyRotationZ(MathUtils.PI); worker.applyRotationX(MathUtils.HALF_PI); worker.apply(expected, expected); worker.setIdentity().applyRotationX(MathUtils.QUARTER_PI).apply(expected, expected); worker.setIdentity().applyRotation(MathUtils.QUARTER_PI, 1, 0, 0).apply(rotated, rotated);
final Quaternion q = Quaternion.fetchTempInstance(); q.fromAxes(shadowCam.getLeft(), shadowCam.getUp(), shadowCam.getDirection()); final Vector3 lightSpace = q.invert(null).apply(tmpVec, Vector3.fetchTempInstance()); q.apply(lightSpace, tmpVec); Vector3.releaseTempInstance(lightSpace); Quaternion.releaseTempInstance(q);
/** * Construct a new TransformChannel. * * @param channelName * our name. * @param times * our time offset values. * @param transforms * the transform to set on this channel at each time offset. These are separated into rotation, scale and * translation components. Note that supplying transforms with non-rotational matrices (with built in * shear, scale.) will produce a warning and may not give you the expected result. */ public TransformChannel(final String channelName, final float[] times, final ReadOnlyTransform[] transforms) { super(channelName, times); // Construct our data _rotations = new ReadOnlyQuaternion[transforms.length]; _translations = new ReadOnlyVector3[transforms.length]; _scales = new ReadOnlyVector3[transforms.length]; for (int i = 0; i < transforms.length; i++) { final ReadOnlyTransform transform = transforms[i]; if (!transform.isRotationMatrix()) { TransformChannel.logger.warning("TransformChannel '" + channelName + "' supplied transform with non-rotational matrices. May have unexpected results."); } _rotations[i] = new Quaternion().fromRotationMatrix(transform.getMatrix()).normalizeLocal(); _translations[i] = new Vector3(transform.getTranslation()); _scales[i] = new Vector3(transform.getScale()); } }
@Test public void testSlerp() { final Quaternion quat = new Quaternion(); final Quaternion quat2 = new Quaternion().applyRotationY(MathUtils.HALF_PI); final Quaternion store = quat.slerp(quat2, .5, null); assertTrue(Math.abs(new Vector3(Math.sin(MathUtils.QUARTER_PI), 0, Math.sin(MathUtils.QUARTER_PI)) .distance(store.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE); quat2.setIdentity().applyRotationZ(MathUtils.PI); quat.slerp(quat2, 1.0, store); assertTrue(Math.abs(new Vector3(-1, 0, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE); quat2.setIdentity().applyRotationZ(MathUtils.PI); quat.slerp(quat2, .5, store); assertTrue(Math.abs(new Vector3(0, 1, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE); quat2.setIdentity().applyRotationZ(MathUtils.PI); quat.slerp(quat2, 0, store); assertTrue(Math.abs(new Vector3(1, 0, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE); quat2.setIdentity(); quat.slerp(quat2, 0.25, store); assertTrue(Math.abs(new Vector3(1, 0, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE); quat.setIdentity().applyRotationX(-2 * MathUtils.HALF_PI); quat2.setIdentity().applyRotationX(MathUtils.HALF_PI); quat.slerp(quat2, 0.5, store); assertTrue(Math.abs(new Vector3(0, -Math.sin(MathUtils.QUARTER_PI), Math.sin(MathUtils.QUARTER_PI)) .distance(store.apply(Vector3.UNIT_Y, null))) <= Quaternion.ALLOWED_DEVIANCE);
@Test public void testMatrix3() { double a = MathUtils.HALF_PI; final Quaternion quat = new Quaternion(); quat.fromRotationMatrix( // 1, 0, 0, // 0, Math.cos(a), -Math.sin(a), // 0, Math.sin(a), Math.cos(a)); assertTrue(Math.abs(Vector3.UNIT_Z.distance(quat.apply(Vector3.UNIT_Y, null))) <= MathUtils.EPSILON); final Matrix3 mat = quat.toRotationMatrix((Matrix3) null); assertTrue(Math.abs(quat.apply(Vector3.NEG_ONE, null).distance(mat.applyPost(Vector3.NEG_ONE, null))) <= MathUtils.EPSILON); quat.fromRotationMatrix( // 1, 0, 0, // 0, Math.cos(a), -Math.sin(a), // 0, Math.sin(a), Math.cos(a)); assertTrue(Math.abs(Vector3.NEG_UNIT_Y.distance(quat.apply(Vector3.UNIT_Y, null))) <= MathUtils.EPSILON); quat.toRotationMatrix(mat); assertTrue(Math.abs(quat.apply(Vector3.ONE, null).distance(mat.applyPost(Vector3.ONE, null))) <= MathUtils.EPSILON); quat.set(0, 0, 0, 0); assertEquals(Matrix3.IDENTITY, quat.toRotationMatrix((Matrix3) null)); quat.fromRotationMatrix( // Math.cos(a), 0, Math.sin(a), // 0, 1, 0, // -Math.sin(a), 0, Math.cos(a));
switch (type) { case GeomMesh: { final Quaternion tempQuat = Quaternion.fetchTempInstance(); final ReadOnlyVector3 norm = triModel.getNormal(); if (orient != 0) { tempQuat.fromAngleNormalAxis(orient, norm); tempQuat.apply(triModel.get(x), tempVec3); } else { tempVec3.set(triModel.get(x)); BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + x); Quaternion.releaseTempInstance(tempQuat); break;
@Test public void testInvert() { final Quaternion quat1 = new Quaternion(0, 1, 2, 3); final Quaternion quat2 = quat1.invert(null); assertEquals(Quaternion.IDENTITY, quat1.multiply(quat2, null)); assertEquals(quat1, quat2.invert(new Quaternion())); assertEquals(quat1, quat2.invertLocal()); // normalized version quat1.fromAngleAxis(MathUtils.QUARTER_PI, Vector3.UNIT_Y); quat1.invert(quat2); assertEquals(Quaternion.IDENTITY, quat1.multiply(quat2, null)); assertEquals(quat1, quat2.invert(new Quaternion())); assertEquals(quat1, quat2.invertLocal()); // conjugate check assertEquals(new Quaternion(-1, -2, -3, 4), new Quaternion(1, 2, 3, 4).conjugate(null)); }
@Test public void testLookAt() { final Vector3 direction = new Vector3(-1, 0, 0); final Quaternion quat = new Quaternion().lookAt(direction, Vector3.UNIT_Y); assertTrue(Math.abs(direction.distance(quat.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE); direction.set(1, 1, 1).normalizeLocal(); quat.lookAt(direction, Vector3.UNIT_Y); assertTrue(Math.abs(direction.distance(quat.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE); direction.set(-1, 2, -1).normalizeLocal(); quat.lookAt(direction, Vector3.UNIT_Y); assertTrue(Math.abs(direction.distance(quat.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE); }
@Test public void testFromVectorToVector() { final Quaternion quat = new Quaternion().fromVectorToVector(Vector3.UNIT_Z, Vector3.UNIT_X); assertEquals(new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.UNIT_Y), quat); quat.fromVectorToVector(Vector3.UNIT_Z, Vector3.NEG_UNIT_Z); assertTrue(Math.abs(new Vector3(0, 0, -1).distance(quat.apply(new Vector3(0, 0, 1), null))) <= Quaternion.ALLOWED_DEVIANCE); quat.fromVectorToVector(Vector3.UNIT_X, Vector3.NEG_UNIT_X); assertTrue(Math.abs(new Vector3(-1, 0, 0).distance(quat.apply(new Vector3(1, 0, 0), null))) <= Quaternion.ALLOWED_DEVIANCE); quat.fromVectorToVector(Vector3.UNIT_Y, Vector3.NEG_UNIT_Y); assertTrue(Math.abs(new Vector3(0, -1, 0).distance(quat.apply(new Vector3(0, 1, 0), null))) <= Quaternion.ALLOWED_DEVIANCE); quat.fromVectorToVector(Vector3.ONE, Vector3.NEG_ONE); assertTrue(Math.abs(new Vector3(-1, -1, -1).distance(quat.apply(new Vector3(1, 1, 1), null))) <= Quaternion.ALLOWED_DEVIANCE); quat.fromVectorToVector(Vector3.ZERO, Vector3.ZERO); assertEquals(Quaternion.IDENTITY, quat); }
/** * Interpolates between the given quaternions using the * {@link Quaternion#slerpLocal(ReadOnlyQuaternion, ReadOnlyQuaternion, double)} method. */ @Override protected void interpolate(final ReadOnlyQuaternion from, final ReadOnlyQuaternion to, final double delta, final Spatial caller) { assert (null != from) : "parameter 'from' can not be null"; assert (null != to) : "parameter 'to' can not be null"; assert (null != caller) : "parameter 'caller' can not be null"; final Quaternion tempQuat = Quaternion.fetchTempInstance(); tempQuat.slerpLocal(from, to, delta); if (isLocalRotation()) { caller.setRotation(tempQuat); } else { caller.setWorldRotation(tempQuat); } Quaternion.releaseTempInstance(tempQuat); }
@Override public Quaternion clone() { return new Quaternion(this); }