/** * Returns a new transform representing a rotation around an * axis around the origin. The rotation is given is quarter turns. * The actual rotation is {@code quarterTurns * 90}. * The rotation is around the block center, not the corner. * * @param quarterTurns The number of quarter turns in this rotation * @param axis The axis to rotate around * @return The new rotation transform */ public static DiscreteTransform3 fromRotation(int quarterTurns, Axis axis) { return new DiscreteTransform3(Matrix4d.createRotation(Quaterniond.fromAngleDegAxis(quarterTurns * 90, axis.toVector3d()))); }
@Test public void testRotation() { final Vector3d rotation1 = new Vector3d(20, 40, 60); final Quaterniond rotationQuat1 = Quaterniond.fromAxesAnglesDeg(rotation1.getX(), -rotation1.getY(), rotation1.getZ()); final Vector3d rotation2 = new Vector3d(45, 135, 225); final Quaterniond rotationQuat2 = Quaterniond.fromAxesAnglesDeg(rotation2.getX(), -rotation2.getY(), rotation2.getZ()); final Quaterniond rotationQuat1Plus2 = rotationQuat2.mul(rotationQuat1); final Vector3d axesAnglesDeg = rotationQuat1Plus2.getAxesAnglesDeg(); final Vector3d rotation1Plus2 = new Vector3d(axesAnglesDeg.getX(), -axesAnglesDeg.getY(), axesAnglesDeg.getZ()); Transform<Extent> transform = new Transform<>(this.mockExtent1, Vector3d.ZERO, rotation1); assertEquals(rotation1, transform.getRotation()); assertEquals(rotationQuat1, transform.getRotationAsQuaternion()); Assert.assertEquals(rotation1.getX(), transform.getPitch(), EPSILON); Assert.assertEquals(rotation1.getY(), transform.getYaw(), EPSILON); Assert.assertEquals(rotation1.getZ(), transform.getRoll(), EPSILON); transform = transform.addRotation(rotation2); assertEquals(rotationQuat1Plus2, transform.getRotationAsQuaternion()); assertEquals(rotation1Plus2, transform.getRotation()); }
private void assertEquals(Quaterniond expected, Quaterniond actual) { // This works because we're only dealing with unit quaternions // See: https://gamedev.stackexchange.com/questions/75072/how-can-i-compare-two-quaternions-for-logical-equality Assert.assertEquals(1, Math.abs(expected.dot(actual)), EPSILON); }
/** * Gets all the entities that are in the line of sight of the given entity, * up to a given distance. This ignores occluders like blocks or other * entities. That is to say, the returned entities might not actually be * visible. Only the entities that pass the filter test are added. * * @param looker The looking entity * @param distance The distance of the ray (from the start) * @param filter The filter test * @return The intersecting entities in no particular order, with the * associated intersection point and normal */ default Set<EntityHit> getIntersectingEntities(Entity looker, double distance, Predicate<EntityHit> filter) { checkNotNull(looker, "looker"); final Vector3d rotation = looker.getRotation(); final Vector3d direction = Quaterniond.fromAxesAnglesDeg(rotation.getX(), -rotation.getY(), rotation.getZ()).getDirection(); final Optional<EyeLocationProperty> data = looker.getProperty(EyeLocationProperty.class); final Vector3d start = data.map(EyeLocationProperty::getValue).orElse(looker.getLocation().getPosition()); return getIntersectingEntities(start, direction, distance, filter); }
/** * Adds a rotation to this transform. * * <p>Quaternions are objectively better than the Euler angles preferred by * Minecraft. This is the preferred method when dealing with rotation * additions. This is for compatibility with the flow-math library.</p> * * <p>Returns the results as a new copy.</p> * * @param rotation The rotation to add * @return A new transform */ public Transform<E> addRotation(Quaterniond rotation) { checkNotNull(rotation, "rotation"); return new Transform<>(getExtent(), getPosition(), toAxesAngles(rotation.mul(getRotationAsQuaternion())), getScale()); }
/** * Creates a new quaternion from the double angles in degrees around the x, y and z axes. * * @param pitch The rotation around x * @param yaw The rotation around y * @param roll The rotation around z * @return The quaternion defined by the rotations around the axes */ public static Quaterniond fromAxesAnglesDeg(double pitch, double yaw, double roll) { return Quaterniond.fromAngleDegAxis(yaw, Vector3d.UNIT_Y). mul(Quaterniond.fromAngleDegAxis(pitch, Vector3d.UNIT_X)). mul(Quaterniond.fromAngleDegAxis(roll, Vector3d.UNIT_Z)); }
/** * Interpolates a quaternion between two others using spherical linear interpolation. * * @param a The first quaternion * @param b The second quaternion * @param percent The percent for the interpolation, between 0 and 1 inclusively * @return The interpolated quaternion */ public static Quaterniond slerp(Quaterniond a, Quaterniond b, double percent) { final double inverted; double cosineTheta = a.dot(b); if (cosineTheta < 0) { cosineTheta = -cosineTheta; inverted = -1; } else { inverted = 1; } if (1 - cosineTheta < GenericMath.DBL_EPSILON) { return a.mul(1 - percent).add(b.mul(percent * inverted)); } final double theta = (double) TrigMath.acos(cosineTheta); final double sineTheta = TrigMath.sin(theta); final double coefficient1 = TrigMath.sin((1 - percent) * theta) / sineTheta; final double coefficient2 = TrigMath.sin(percent * theta) / sineTheta * inverted; return a.mul(coefficient1).add(b.mul(coefficient2)); }
private static Quaterniond fromAxesAngles(Vector3d angles) { return Quaterniond.fromAxesAnglesDeg(angles.getX(), -angles.getY(), angles.getZ()); }
/** * Interpolates a quaternion between two others using linear interpolation. * * @param a The first quaternion * @param b The second quaternion * @param percent The percent for the interpolation, between 0 and 1 inclusively * @return The interpolated quaternion */ public static Quaterniond lerp(Quaterniond a, Quaterniond b, double percent) { return a.mul(1 - percent).add(b.mul(percent)); }
/** * Creates a new quaternion from the double angles in radians around the x, y and z axes. * * @param pitch The rotation around x * @param yaw The rotation around y * @param roll The rotation around z * @return The quaternion defined by the rotations around the axes */ public static Quaterniond fromAxesAnglesRad(double pitch, double yaw, double roll) { return Quaterniond.fromAngleRadAxis(yaw, Vector3d.UNIT_Y). mul(Quaterniond.fromAngleRadAxis(pitch, Vector3d.UNIT_X)). mul(Quaterniond.fromAngleRadAxis(roll, Vector3d.UNIT_Z)); }
/** * Subtracts the double components of another quaternion from this one. * * @param x The x (imaginary) component of the quaternion to subtract * @param y The y (imaginary) component of the quaternion to subtract * @param z The z (imaginary) component of the quaternion to subtract * @param w The w (real) component of the quaternion to subtract * @return A new quaternion, which is the difference of both */ public Quaterniond sub(double x, double y, double z, double w) { return new Quaterniond(this.x - x, this.y - y, this.z - z, this.w - w); }
private static Vector3d toAxesAngles(Quaterniond quaternion) { final Vector3d axesAngles = quaternion.getAxesAnglesDeg(); return new Vector3d(axesAngles.getX(), -axesAngles.getY(), axesAngles.getZ()); }
/** * Normalizes this quaternion. * * @return A new quaternion of unit length */ @Override public Quaterniond normalize() { final double length = length(); if (Math.abs(length) < GenericMath.DBL_EPSILON) { throw new ArithmeticException("Cannot normalize the zero quaternion"); } return new Quaterniond(x / length, y / length, z / length, w / length); }
/** * "Adds" another transform to this one. This is equivalent to adding the * translation, rotation and scale individually. * * <p>Returns the results as a new copy.</p> * * @param other The transform to add * @return A new transform */ public Transform<E> add(Transform<E> other) { checkNotNull(other, "other"); return new Transform<>( getExtent(), getPosition().add(other.getPosition()), toAxesAngles(other.getRotationAsQuaternion().mul(getRotationAsQuaternion())), getScale().mul(other.getScale()) ); }
/** * Initializes a block ray builder for the entity's eye. * If the eye location isn't defined for the entity, the * regular location is used. This sets both the starting * point and direction. * * @param entity The entity * @return A new block ray builder */ public static BlockRayBuilder<World> from(Entity entity) { checkNotNull(entity, "entity"); final Vector3d rotation = entity.getRotation(); final Vector3d direction = Quaterniond.fromAxesAnglesDeg(rotation.getX(), -rotation.getY(), rotation.getZ()).getDirection(); final Location<World> location = entity.getLocation(); final Optional<EyeLocationProperty> data = entity.getProperty(EyeLocationProperty.class); final Vector3d position = data.map(EyeLocationProperty::getValue).orElse(location.getPosition()); return from(location.getExtent(), position).direction(direction); }
/** * Creates a new quaternion from the double angles in degrees around the x, y and z axes. * * @param pitch The rotation around x * @param yaw The rotation around y * @param roll The rotation around z * @return The quaternion defined by the rotations around the axes */ public static Quaterniond fromAxesAnglesDeg(double pitch, double yaw, double roll) { return Quaterniond.fromAngleDegAxis(yaw, Vector3d.UNIT_Y). mul(Quaterniond.fromAngleDegAxis(pitch, Vector3d.UNIT_X)). mul(Quaterniond.fromAngleDegAxis(roll, Vector3d.UNIT_Z)); }
/** * Interpolates a quaternion between two others using spherical linear interpolation. * * @param a The first quaternion * @param b The second quaternion * @param percent The percent for the interpolation, between 0 and 1 inclusively * @return The interpolated quaternion */ public static Quaterniond slerp(Quaterniond a, Quaterniond b, double percent) { final double inverted; double cosineTheta = a.dot(b); if (cosineTheta < 0) { cosineTheta = -cosineTheta; inverted = -1; } else { inverted = 1; } if (1 - cosineTheta < GenericMath.DBL_EPSILON) { return a.mul(1 - percent).add(b.mul(percent * inverted)); } final double theta = (double) TrigMath.acos(cosineTheta); final double sineTheta = TrigMath.sin(theta); final double coefficient1 = TrigMath.sin((1 - percent) * theta) / sineTheta; final double coefficient2 = TrigMath.sin(percent * theta) / sineTheta * inverted; return a.mul(coefficient1).add(b.mul(coefficient2)); }
/** * Creates a new quaternion from the float angles in degrees around the x, y and z axes. * * @param pitch The rotation around x * @param yaw The rotation around y * @param roll The rotation around z * @return The quaternion defined by the rotations around the axes */ public static Quaterniond fromAxesAnglesDeg(float pitch, float yaw, float roll) { return fromAxesAnglesDeg((double) pitch, (double) yaw, (double) roll); }
/** * Interpolates a quaternion between two others using linear interpolation. * * @param a The first quaternion * @param b The second quaternion * @param percent The percent for the interpolation, between 0 and 1 inclusively * @return The interpolated quaternion */ public static Quaterniond lerp(Quaterniond a, Quaterniond b, double percent) { return a.mul(1 - percent).add(b.mul(percent)); }
/** * Creates a new quaternion from the double angles in radians around the x, y and z axes. * * @param pitch The rotation around x * @param yaw The rotation around y * @param roll The rotation around z * @return The quaternion defined by the rotations around the axes */ public static Quaterniond fromAxesAnglesRad(double pitch, double yaw, double roll) { return Quaterniond.fromAngleRadAxis(yaw, Vector3d.UNIT_Y). mul(Quaterniond.fromAngleRadAxis(pitch, Vector3d.UNIT_X)). mul(Quaterniond.fromAngleRadAxis(roll, Vector3d.UNIT_Z)); }