com.crowsofwar.gorecore.util.Vector.java Source code

Java tutorial

Introduction

Here is the source code for com.crowsofwar.gorecore.util.Vector.java

Source

/* 
  This file is part of AvatarMod.
    
  AvatarMod is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
      
  AvatarMod 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
  GNU General Public License for more details.
      
  You should have received a copy of the GNU General Public License
  along with AvatarMod. If not, see <http://www.gnu.org/licenses/>.
*/

package com.crowsofwar.gorecore.util;

import static java.lang.Math.*;

import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;

/**
 * A mutable 3-dimensional vector using doubles.
 * 
 * @author CrowsOfWar
 */
public class Vector {

    /**
     * The zero vector.
     */
    public static final Vector ZERO = new Vector();

    public static final Vector UP = new Vector(0, 1, 0);
    public static final Vector DOWN = new Vector(0, -1, 0);
    public static final Vector EAST = new Vector(1, 0, 0);
    public static final Vector WEST = new Vector(-1, 0, 0);
    public static final Vector NORTH = new Vector(0, 0, -1);
    public static final Vector SOUTH = new Vector(0, 0, 1);

    public static final Vector[] DIRECTION_VECTORS = { UP, DOWN, EAST, WEST, NORTH, SOUTH };

    private double cachedMagnitude;
    private double x, y, z;

    /**
     * Creates a new vector at the origin point.
     */
    public Vector() {

    }

    /**
     * Creates using the coordinates (x, y, z).
     * 
     * @param x
     *            X-position of the new vector
     * @param y
     *            Y-position of the new vector
     * @param z
     *            Z-position of the new vector
     */
    public Vector(double x, double y, double z) {
        set(x, y, z);
    }

    /**
     * Creates a copy of the given vector.
     * 
     * @param vec
     *            Vector to copy
     */
    public Vector(Vector vec) {
        set(vec);
        this.cachedMagnitude = vec.cachedMagnitude;
    }

    /**
     * Creates a copy of the given Minecraft vector.
     * 
     * @param vec
     *            Vector to copy
     */
    public Vector(Vec3d vec) {
        this(vec.xCoord, vec.yCoord, vec.zCoord);
    }

    /**
     * Creates a vector from the feet position of the given entity.
     * 
     * @param entity
     *            The entity to use
     */
    public Vector(Entity entity) {
        this(entity.posX, entity.posY, entity.posZ);
        // if (entity instanceof EntityPlayer && entity.worldObj.isRemote)
        // setY(y - 1.62);
    }

    /**
     * Creates a vector from the coordinates defined by blockPos.
     * 
     * @param blockPos
     *            The vanilla blockPos
     */
    public Vector(BlockPos blockPos) {
        this(blockPos.getX(), blockPos.getY(), blockPos.getZ());
    }

    public Vector(Vec3i vec) {
        this(vec.getX(), vec.getY(), vec.getZ());
    }

    public Vector(EnumFacing facing) {
        this(facing.getDirectionVec());
    }

    /**
     * Get the x-coordinate of this vector.
     */
    public double x() {
        return x;
    }

    /**
     * Set the x-coordinate of this vector. Returns <code>this</code> for method
     * chaining.
     * 
     * @param x
     *            X-coordinate
     */
    public Vector setX(double x) {
        this.x = x;
        recalcMagnitude();
        return this;
    }

    /**
     * Get the y-coordinate of this vector.
     */
    public double y() {
        return y;
    }

    /**
     * Set the y-coordinate of this vector. Returns <code>this</code> for method
     * chaining.
     * 
     * @param y
     *            Y-coordinate
     */
    public Vector setY(double y) {
        this.y = y;
        recalcMagnitude();
        return this;
    }

    /**
     * Get the z-coordinate of this vector.
     */
    public double z() {
        return z;
    }

    /**
     * Set the z-coordinate of this vector. Returns <code>this</code> for method
     * chaining.
     * 
     * @param z
     *            Z-coordinate
     */
    public Vector setZ(double z) {
        this.z = z;
        recalcMagnitude();
        return this;
    }

    /**
     * Set this vector to the vector defined by (x, y, z).
     * 
     * @param x
     *            X-coordinate to set to
     * @param y
     *            Y-coordinate to set to
     * @param z
     *            Z-coordinate to set to
     * @return this
     */
    public Vector set(double x, double y, double z) {
        setX(x);
        setY(y);
        setZ(z);
        return this;
    }

    /**
     * Set this vector to the given vector.
     * 
     * @param vec
     *            Vector to set to
     * @return this
     */
    public Vector set(Vector vec) {
        set(vec.x(), vec.y(), vec.z());
        return this;
    }

    /**
     * Returns a new vector with the same coordinates as this one.
     */
    public Vector copy() {
        return new Vector(this);
    }

    /**
     * Add the given vector to this vector.
     * 
     * @param vec
     *            The vector to add
     * @return this
     */
    public Vector add(Vector vec) {
        return add(vec.x(), vec.y(), vec.z());
    }

    /**
     * Add the given vector defined by (x, y, z) to this vector.
     * 
     * @param x
     *            X-coordinate to add
     * @param y
     *            Y-coordinate to add
     * @param z
     *            Z-coordinate to add
     * @return this
     */
    public Vector add(double x, double y, double z) {
        return set(this.x() + x, this.y() + y, this.z() + z);
    }

    /**
     * Creates a new vector from the sum of this vector and the given vector.
     * 
     * @param vec
     *            Vector for sum
     */
    public Vector plus(Vector vec) {
        return plus(vec.x(), vec.y(), vec.z());
    }

    /**
     * Creates a new vector from the sub of this vector and the vector defined
     * by (x, y, z).
     * 
     * @param x
     *            X-coordinate of other vector
     * @param y
     *            Y-coordinate of other vector
     * @param z
     *            Z-coordinate of other vector
     */
    public Vector plus(double x, double y, double z) {
        return new Vector(this).add(x, y, z);
    }

    /**
     * Subtract the given vector from this vector.
     * 
     * @param vec
     *            The reduction vector
     * @return this
     */
    public Vector subtract(Vector vec) {
        return subtract(vec.x(), vec.y(), vec.z());
    }

    /**
     * Subtract the given vector defined by (x, y, z) from this vector.
     * 
     * @param x
     *            X-coordinate to subtract
     * @param y
     *            Y-coordinate to subtract
     * @param z
     *            Z-coordinate to subtract
     * @return this
     */
    public Vector subtract(double x, double y, double z) {
        return set(this.x() - x, this.y() - y, this.z() - z);
    }

    /**
     * Creates a new vector from this vector minus the given vector.
     * 
     * @param vec
     *            Other vector
     */
    public Vector minus(Vector vec) {
        return minus(vec.x(), vec.y(), vec.z());
    }

    /**
     * Creates a new vector from this vector minus the vector defined by
     * (x,y,z).
     * 
     * @param x
     *            X-coordinate to subtract
     * @param y
     *            Y-coordinate to subtract
     * @param z
     *            Z-coordinate to subtract
     */
    public Vector minus(double x, double y, double z) {
        return new Vector(this).subtract(x, y, z);
    }

    /**
     * Multiply this vector by the given scalar, and returns the result.
     * Modifies the original vector.
     * 
     * @param scalar
     *            The scalar to multiply this vector by
     * @return this
     */
    public Vector mul(double scalar) {
        return set(x() * scalar, y() * scalar, z() * scalar);
    }

    /**
     * Creates a new vector from this vector times the scalar.
     * 
     * @param scalar
     *            The scalar to multiply the new vector by
     */
    public Vector times(double scalar) {
        return new Vector(this).mul(scalar);
    }

    /**
     * Divide this vector by the given scalar, and returns the result. Modifies
     * the original vector.
     * 
     * @param scalar
     *            The scalar to divide this vector by
     * @return this
     */
    public Vector divide(double scalar) {
        return set(x() / scalar, y() / scalar, z() / scalar);
    }

    /**
     * Creates a new vector based on this vector divided by the other vector.
     * 
     * @param scalar
     *            The scalar to divide the new vector by
     */
    public Vector dividedBy(double scalar) {
        return new Vector(this).divide(scalar);
    }

    /**
     * Get the length of this vector.
     * <p>
     * The result is cached since square-root is a performance-heavy operation.
     */
    public double magnitude() {
        if (cachedMagnitude == -1) {
            cachedMagnitude = Math.sqrt(sqrMagnitude());
        }
        return cachedMagnitude;
    }

    /**
     * Get the square magnitude of this vector.
     */
    public double sqrMagnitude() {
        return x() * x() + y() * y() + z() * z();
    }

    /**
     * Mark cachedMagnitude so it needs to be recalculated.
     */
    private void recalcMagnitude() {
        cachedMagnitude = -1;
    }

    /**
     * Normalizes this vector so that it has a length of 1.
     * 
     * @return this
     */
    public Vector normalize() {
        return divide(magnitude());
    }

    /**
     * Get the square distance from the given vector.
     * 
     * @param vec
     *            The other vector
     */
    public double sqrDist(Vector vec) {
        return sqrDist(vec.x(), vec.y(), vec.z());
    }

    /**
     * Get the square distance from the vector defined by (x, y, z).
     * 
     * @param x
     *            The x-position of the other vector
     * @param y
     *            The y-position of the other vector
     * @param z
     *            The z-position of the other vector
     */
    public double sqrDist(double x, double y, double z) {
        return (this.x() - x) * (this.x() - x) + (this.y() - y) * (this.y() - y) + (this.z() - z) * (this.z() - z);
    }

    /**
     * Get the distance from the given vector.
     * 
     * @param vec
     *            The other vector
     */
    public double dist(Vector vec) {
        return Math.sqrt(sqrDist(vec));
    }

    /**
     * Get the distance from the vector defined by (x, y, z).
     * 
     * @param x
     *            The x-position of the other vector
     * @param y
     *            The y-position of the other vector
     * @param z
     *            The z-position of the other vector
     */
    public double dist(double x, double y, double z) {
        return Math.sqrt(sqrDist(x, y, z));
    }

    /**
     * Get the dot product with the given vector.
     * 
     * @param vec
     *            The other vector
     */
    public double dot(Vector vec) {
        return dot(vec.x(), vec.y(), vec.z());
    }

    /**
     * Get the dot product with the vector defined by (x, y, z).
     * 
     * @param x
     *            X-coordinate of the other vector
     * @param y
     *            Y-coordinate of the other vector
     * @param z
     *            Z-coordinate of the other vector
     */
    public double dot(double x, double y, double z) {
        return this.x() * x + this.y() * y + this.z() * z;
    }

    /**
     * Returns the cross product of the given vector. This creates a new vector.
     * 
     * @param vec
     *            The vector to cross with
     */
    public Vector cross(Vector vec) {
        return cross(vec.x(), vec.y(), vec.z());
    }

    /**
     * Returns the cross product with the vector defined by (x, y, z). This
     * creates a new vector.
     * 
     * @param x
     *            X-coordinate of other vector
     * @param y
     *            Y-coordinate of other vector
     * @param z
     *            Z-coordinate of other vector
     */
    public Vector cross(double x, double y, double z) {
        return new Vector(this.y() * z - this.z() * y, this.z() * x - this.x() * z, this.x() * y - this.y() * x);
    }

    /**
     * Returns the angle between the other vector, in radians. (result is ranged
     * 0-PI).
     * 
     * @param vec
     *            Other vector
     */
    public double angle(Vector vec) {
        double dot = dot(vec);
        return Math.acos(dot / (this.magnitude() * vec.magnitude()));
    }

    /**
     * Returns this vector reflected across the given normal. Does not modify
     * this vector or the normal.
     * 
     * @param normal
     *            Must be normalized
     */
    public Vector reflect(Vector normal) {
        if (normal.sqrMagnitude() != 1)
            throw new IllegalArgumentException("Normal vector must be normalized");
        return this.minus(normal.times(2).times(this.dot(normal)));
    }

    /**
     * <strong>Assuming</strong> this vector represents spherical coordinates
     * (in radians), returns a unit vector in Cartesian space which has the
     * rotations of this vector.
     * <p>
     * Does not modify this vector.
     * 
     * @see #toRectangular(Vector)
     */
    public Vector toRectangular() {
        return Vector.toRectangular(this);
    }

    /**
     * <strong>Assuming</strong> this vector represents rectangular coordinates,
     * returns the rotations (in radians) for this vector.
     * <p>
     * Does not modify this vector.
     */
    public Vector toSpherical() {
        return Vector.getRotationTo(Vector.ZERO, this);
    }

    /**
     * Returns a minecraft vector with the same coordinates as this vector.
     */
    public Vec3d toMinecraft() {
        return new Vec3d(x(), y(), z());
    }

    /**
     * Returns an integer vector of this vector by rounding each component.
     */
    public VectorI round() {
        return new VectorI((int) Math.round(x()), (int) Math.round(y()), (int) Math.round(z()));
    }

    /**
     * Returns an integer vector of this vector by casting each component to an
     * integer.
     */
    public VectorI cast() {
        return new VectorI((int) x(), (int) y(), (int) z());
    }

    /**
     * Returns a BlockPos with the same coordinates as this vector.
     */
    public BlockPos toBlockPos() {
        return cast().toBlockPos();
    }

    public Vec3i toMinecraftInteger() {
        return new Vec3i(x(), y(), z());
    }

    /**
     * Writes this vector to the packet byte buffer.
     * 
     * @param buf
     *            Buffer to write to
     */
    public void toBytes(ByteBuf buf) {
        buf.writeDouble(x());
        buf.writeDouble(y());
        buf.writeDouble(z());
    }

    @Override
    public String toString() {
        return "(" + x() + ", " + y() + ", " + z() + ")";
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (obj instanceof Vector) {
            Vector vec = (Vector) obj;
            return this.x() == vec.x() && this.y() == vec.y() && this.z() == vec.z();
        } else {
            return false;
        }
    }

    /**
     * Reflects the vector across the given normal. Returns a new vector.
     * 
     * @see #reflect(Vector)
     */
    public static Vector reflect(Vector vec, Vector normal) {
        return vec.reflect(normal);
    }

    /**
     * Returns the euler angles from position 1 to position 2.
     * <p>
     * The returned vector has Y for yaw, and X for pitch. Measurements are in
     * radians.
     * 
     * @param pos1
     *            Where we are
     * @param pos2
     *            Where to look at
     */
    public static Vector getRotationTo(Vector pos1, Vector pos2) {
        Vector diff = pos2.minus(pos1);
        diff.normalize();
        double x = diff.x();
        double y = diff.y();
        double z = diff.z();
        double d0 = x;
        double d1 = y;
        double d2 = z;
        double d3 = (double) MathHelper.sqrt_double(d0 * d0 + d2 * d2);
        double rotY = Math.atan2(d2, d0) - Math.PI / 2;
        double rotX = -Math.atan2(d1, d3);
        double rotZ = 0;
        return new Vector(rotX, rotY, rotZ);
    }

    /**
     * Gets the position of the entity
     */
    public static Vector getEntityPos(Entity entity) {
        return new Vector(entity);
    }

    /**
     * Gets the position of the entity, but adjusted so ypos is the eyepos
     */
    public static Vector getEyePos(Entity entity) {
        Vector pos = getEntityPos(entity);
        pos.setY(pos.y() + entity.getEyeHeight());
        return pos;
    }

    /**
     * Get velocity of the entity in m/s.
     * 
     * @see #getVelocityTpS(Entity)
     */
    public static Vector getVelocityMpS(Entity entity) {
        return new Vector(entity.motionX / 20, entity.motionY / 20, entity.motionZ / 20);
    }

    /**
     * Get velocity of the entity in ticks/s.
     * 
     * @see #getVelocityMpS(Entity)
     */
    public static Vector getVelocityTpS(Entity entity) {
        return new Vector(entity.motionX, entity.motionY, entity.motionZ);
    }

    /**
     * Get the pitch to lob a projectile in radians. Example: pitch to target
     * can be used in {@link #toRectangular(double, double)}
     * 
     * @param v
     *            Force of the projectile, going FORWARDS
     * @param g
     *            Gravity constant
     * @param x
     *            Horizontal distance to target
     * @param y
     *            Vertical distance to target
     */
    public static double getProjectileAngle(double v, double g, double x, double y) {
        return -Math.atan2((v * v + Math.sqrt(v * v * v * v - g * (g * x * x + 2 * y * v * v))), g * x);
    }

    /**
     * Create a rectangular vector from the entity's rotations. This can be used
     * to determine the coordinates the entity is looking at (without raytrace).
     * 
     * @param entity
     *            The entity to use
     */
    public static Vector getLookRectangular(Entity entity) {
        return toRectangular(toRadians(entity.rotationYaw), toRadians(entity.rotationPitch));
    }

    /**
     * Create a rotation vector from the entity's rotations. This is a euler and
     * is in radians.
     * 
     * @see #getEuler(double, double)
     */
    public static Vector getLookRotations(Entity entity) {
        return getEuler(toRadians(entity.rotationYaw), toRadians(entity.rotationPitch));
    }

    /**
     * Gets a vector representing rotations for the given yaw/pitch. Parameters
     * should be in radians.
     */
    public static Vector getEuler(double yaw, double pitch) {
        return new Vector(pitch, yaw, 0);
    }

    /**
     * Converts a rotation vector into a rectangular (Cartesian) vector. Euler
     * must be in radians.
     * 
     * @see #toRectangular(double, double)
     * @see #getEuler(double, double)
     */
    public static Vector toRectangular(Vector euler) {
        return new Vector(-sin(euler.y()) * cos(euler.x()), -sin(euler.x()), cos(euler.y()) * cos(euler.x()));
    }

    /**
     * Converts the given rotations into a rectangular (Cartesian) vector.
     * Parameters must be in radians.
     * 
     * @see #toRectangular(Vector)
     */
    public static Vector toRectangular(double yaw, double pitch) {
        return new Vector(-sin(yaw) * cos(pitch), -sin(pitch), cos(yaw) * cos(pitch));
    }

    /**
     * Creates a new vector from the packet information in the byte buffer.
     * Vectors should be encoded using the non-static {@link #toBytes(ByteBuf)
     * toBytes}.
     * 
     * @param buf
     *            Buffer to read from
     * 
     * @see #toBytes(ByteBuf)
     */
    public static Vector fromBytes(ByteBuf buf) {
        return new Vector(buf.readDouble(), buf.readDouble(), buf.readDouble());
    }

}