/** * Adds a rotation to this transform, in the xy plane, around the origin and * returns it as a new transform. 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 * @return The rotated transform as a copy */ public DiscreteTransform2 withRotation(int quarterTurns) { return new DiscreteTransform2(this.matrix.rotate(Complexd.fromAngleDeg(quarterTurns * 90))); }
private static Quaterniond fromAxesAngles(Vector3d angles) { return Quaterniond.fromAxesAnglesDeg(angles.getX(), -angles.getY(), angles.getZ()); }
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); }
@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()); }
/** * 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()))); }
/** * 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()); }
private static Vector3d toAxesAngles(Quaterniond quaternion) { final Vector3d axesAngles = quaternion.getAxesAnglesDeg(); return new Vector3d(axesAngles.getX(), -axesAngles.getY(), axesAngles.getZ()); }
/** * Adds a rotation to this transform, around an axis, * around the origin and returns it as a new transform. * 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 rotated transform as a copy */ public DiscreteTransform3 withRotation(int quarterTurns, Axis axis) { return new DiscreteTransform3(this.matrix.rotate(Quaterniond.fromAngleDegAxis(quarterTurns * 90, axis.toVector3d()))); }
/** * 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); }
/** * "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()) ); }
/** * Returns a new transform representing a rotation in the xy plane 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 * @return The new rotation transform */ public static DiscreteTransform2 fromRotation(int quarterTurns) { return new DiscreteTransform2(Matrix3d.createRotation(Complexd.fromAngleDeg(quarterTurns * 90))); }
/** * Returns a new transform representing a rotation around an axis, * around a given point. The rotation is given is quarter turns. * The actual rotation is {@code quarterTurns * 90}. The block corner * flag change the point to be the block corner instead of the center. * * @param quarterTurns The number of quarter turns in this rotation * @param axis The axis to rotate around * @param point The point of rotation, as block coordinates * @param blockCorner Whether or not to use the corner of the block * instead of the center * @return The new rotation transform */ public static DiscreteTransform3 fromRotation(int quarterTurns, Axis axis, Vector3i point, boolean blockCorner) { Vector3d pointDouble = point.toDouble(); if (blockCorner) { pointDouble = pointDouble.add(0.5, 0.5, 0.5); } return new DiscreteTransform3(Matrix4d.createTranslation(pointDouble.negate()).rotate(Quaterniond.fromAngleDegAxis(quarterTurns * 90, axis .toVector3d())).translate(pointDouble)); }
/** * Returns a new transform representing a rotation in the xy plane, around a * given point. The rotation is given is quarter turns. The actual rotation * is {@code quarterTurns * 90}. The tile corner flag change the point to be * the tile corner instead of the center. * * @param quarterTurns The number of quarter turns in this rotation * @param point The point of rotation, as tile coordinates * @param tileCorner Whether or not to use the corner of the tile instead of * the center * @return The new rotation transform */ public static DiscreteTransform2 fromRotation(int quarterTurns, Vector2i point, boolean tileCorner) { Vector2d pointDouble = point.toDouble(); if (tileCorner) { pointDouble = pointDouble.add(0.5, 0.5); } return new DiscreteTransform2( Matrix3d.createTranslation(pointDouble.negate()).rotate(Complexd.fromAngleDeg(quarterTurns * 90)).translate(pointDouble)); }
/** * Adds a a rotation to this transform, around an axis, * around a given point, and returns it as a new transform. * The rotation is given is quarter turns. The actual rotation * is {@code quarterTurns * 90}. The block corner flag changes * the point to be the block upper corner instead of the center. * * @param quarterTurns The number of quarter turns in this rotation * @param axis The axis to rotate around * @param point The point of rotation, as block coordinates * @param blockCorner Whether or not to use the corner of the block * instead of the center * @return The rotated transform as a copy */ public DiscreteTransform3 withRotation(int quarterTurns, Axis axis, Vector3i point, boolean blockCorner) { Vector3d pointDouble = point.toDouble(); if (blockCorner) { pointDouble = pointDouble.add(0.5, 0.5, 0.5); } return new DiscreteTransform3( this.matrix.translate(pointDouble.negate()).rotate(Quaterniond.fromAngleDegAxis(quarterTurns * 90, axis.toVector3d())) .translate(pointDouble)); }
/** * Adds a a rotation to this transform, in the xy plane, around a given * point, and returns it as a new transform. The rotation is given is * quarter turns. The actual rotation is {@code quarterTurns * 90}. The tile * corner flag changes the point to be the tile upper corner instead of the * center. * * @param quarterTurns The number of quarter turns in this rotation * @param point The point of rotation, as tile coordinates * @param tileCorner Whether or not to use the corner of the tile instead of * the center * @return The rotated transform as a copy */ public DiscreteTransform2 withRotation(int quarterTurns, Vector2i point, boolean tileCorner) { Vector2d pointDouble = point.toDouble(); if (tileCorner) { pointDouble = pointDouble.add(0.5, 0.5); } return new DiscreteTransform2( this.matrix.translate(pointDouble.negate()).rotate(Complexd.fromAngleDeg(quarterTurns * 90)).translate(pointDouble)); }
Matrix4d.createTranslation(pointDouble.negate()).rotate(Quaterniond.fromAngleDegAxis(halfTurns * 180, axis.toVector3d())).translate( pointDouble));
/** * Returns a new transform representing a rotation in the xy plane, around a * given point. The rotation is given is half turns. The actual rotation is * {@code halfTurns * 180}. The tile corner flags change the point to be the * tile corner or edge instead of the center. When both flags are false, the * center is used. When only one is true the edge on the opposite axis to * the flag is used. When both are true the upper corner is used. * * @param halfTurns The number of half turns in this rotation * @param point The point of rotation, as tile coordinates * @param tileCornerX Whether or not to use the corner of the tile instead * of the center on the x axis * @param tileCornerY Whether or not to use the corner of the tile instead * of the center on the y axis * @return The new rotation transform */ public static DiscreteTransform2 fromRotation(int halfTurns, Vector2i point, boolean tileCornerX, boolean tileCornerY) { Vector2d pointDouble = point.toDouble(); if (tileCornerX) { pointDouble = pointDouble.add(0.5, 0); } if (tileCornerY) { pointDouble = pointDouble.add(0, 0.5); } return new DiscreteTransform2( Matrix3d.createTranslation(pointDouble.negate()).rotate(Complexd.fromAngleDeg(halfTurns * 180)).translate(pointDouble)); }
@Test public void testTransformation() { final Vector3d position = new Vector3d(1, 2, 3); final Quaterniond rotation = Quaterniond.fromAngleDegAxis(90, Vector3d.UNIT_Y); final Vector3d scale = new Vector3d(4, 5, 6); Transform<Extent> transform = new Transform<>(this.mockExtent1, position); assertTransforms(new Vector3d(11, 12, 13), transform, new Vector3d(10, 10, 10)); transform = transform.addScale(scale); assertTransforms(new Vector3d(41, 52, 63), transform, new Vector3d(10, 10, 10)); transform = transform.addRotation(rotation); assertTransforms(new Vector3d(61, 52, -37), transform, new Vector3d(10, 10, 10)); transform = transform.add(transform); assertTransforms(new Vector3d(-158, 254, -354), transform, new Vector3d(10, 10, 10)); }
/** * Adds a a rotation to this transform, in the xy plane, around a given * point, and returns it as a new transform. The rotation is given is half * turns. The actual rotation is {@code halfTurns * 180}. The tile corner * flags change the point to be the tile corner or edge instead of the * center. When both flags are false, the center is used. When only one is * true the edge on the opposite axis to the flag is used. When both are * true the upper corner is used. * * @param halfTurns The number of half turns in this rotation * @param point The point of rotation, as tile coordinates * @param tileCornerX Whether or not to use the corner of the tile instead * of the center on the x axis * @param tileCornerY Whether or not to use the corner of the tile instead * of the center on the y axis * @return The rotated transform as a copy */ public DiscreteTransform2 withRotation(int halfTurns, Vector2i point, boolean tileCornerX, boolean tileCornerY) { Vector2d pointDouble = point.toDouble(); if (tileCornerX) { pointDouble = pointDouble.add(0.5, 0); } if (tileCornerY) { pointDouble = pointDouble.add(0, 0.5); } return new DiscreteTransform2( this.matrix.translate(pointDouble.negate()).rotate(Complexd.fromAngleDeg(halfTurns * 180)).translate(pointDouble)); }