/* * WorldEdit, a Minecraft world manipulation toolkit * Copyright (C) sk89q * Copyright (C) WorldEdit team and contributors * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ package com.sk89q.worldedit; /** * * @author sk89q */ public class Vector implements Comparable { public static final Vector ZERO = new Vector(0, 0, 0); public static final Vector UNIT_X = new Vector(1, 0, 0); public static final Vector UNIT_Y = new Vector(0, 1, 0); public static final Vector UNIT_Z = new Vector(0, 0, 1); public static final Vector ONE = new Vector(1, 1, 1); protected final double x, y, z; /** * Construct the Vector object. * * @param x * @param y * @param z */ public Vector(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } /** * Construct the Vector object. * * @param x * @param y * @param z */ public Vector(int x, int y, int z) { this.x = (double) x; this.y = (double) y; this.z = (double) z; } /** * Construct the Vector object. * * @param x * @param y * @param z */ public Vector(float x, float y, float z) { this.x = (double) x; this.y = (double) y; this.z = (double) z; } /** * Construct the Vector object. * * @param pt */ public Vector(Vector pt) { this.x = pt.x; this.y = pt.y; this.z = pt.z; } /** * Construct the Vector object. */ public Vector() { this.x = 0; this.y = 0; this.z = 0; } /** * @return the x */ public double getX() { return x; } /** * @return the x */ public int getBlockX() { return (int) Math.round(x); } /** * Set X. * * @param x * @return new vector */ public Vector setX(double x) { return new Vector(x, y, z); } /** * Set X. * * @param x * @return new vector */ public Vector setX(int x) { return new Vector(x, y, z); } /** * @return the y */ public double getY() { return y; } /** * @return the y */ public int getBlockY() { return (int) Math.round(y); } /** * Set Y. * * @param y * @return new vector */ public Vector setY(double y) { return new Vector(x, y, z); } /** * Set Y. * * @param y * @return new vector */ public Vector setY(int y) { return new Vector(x, y, z); } /** * @return the z */ public double getZ() { return z; } /** * @return the z */ public int getBlockZ() { return (int) Math.round(z); } /** * Set Z. * * @param z * @return new vector */ public Vector setZ(double z) { return new Vector(x, y, z); } /** * Set Z. * * @param z * @return new vector */ public Vector setZ(int z) { return new Vector(x, y, z); } /** * Adds two points. * * @param other * @return New point */ public Vector add(Vector other) { return new Vector(x + other.x, y + other.y, z + other.z); } /** * Adds two points. * * @param x * @param y * @param z * @return New point */ public Vector add(double x, double y, double z) { return new Vector(this.x + x, this.y + y, this.z + z); } /** * Adds two points. * * @param x * @param y * @param z * @return New point */ public Vector add(int x, int y, int z) { return new Vector(this.x + x, this.y + y, this.z + z); } /** * Adds points. * * @param others * @return New point */ public Vector add(Vector... others) { double newX = x, newY = y, newZ = z; for (int i = 0; i < others.length; ++i) { newX += others[i].x; newY += others[i].y; newZ += others[i].z; } return new Vector(newX, newY, newZ); } /** * Subtracts two points. * * @param other * @return New point */ public Vector subtract(Vector other) { return new Vector(x - other.x, y - other.y, z - other.z); } /** * Subtract two points. * * @param x * @param y * @param z * @return New point */ public Vector subtract(double x, double y, double z) { return new Vector(this.x - x, this.y - y, this.z - z); } /** * Subtract two points. * * @param x * @param y * @param z * @return New point */ public Vector subtract(int x, int y, int z) { return new Vector(this.x - x, this.y - y, this.z - z); } /** * Subtract points. * * @param others * @return New point */ public Vector subtract(Vector... others) { double newX = x, newY = y, newZ = z; for (int i = 0; i < others.length; ++i) { newX -= others[i].x; newY -= others[i].y; newZ -= others[i].z; } return new Vector(newX, newY, newZ); } /** * Component-wise multiplication * * @param other * @return New point */ public Vector multiply(Vector other) { return new Vector(x * other.x, y * other.y, z * other.z); } /** * Component-wise multiplication * * @param x * @param y * @param z * @return New point */ public Vector multiply(double x, double y, double z) { return new Vector(this.x * x, this.y * y, this.z * z); } /** * Component-wise multiplication * * @param x * @param y * @param z * @return New point */ public Vector multiply(int x, int y, int z) { return new Vector(this.x * x, this.y * y, this.z * z); } /** * Component-wise multiplication * * @param others * @return New point */ public Vector multiply(Vector... others) { double newX = x, newY = y, newZ = z; for (int i = 0; i < others.length; ++i) { newX *= others[i].x; newY *= others[i].y; newZ *= others[i].z; } return new Vector(newX, newY, newZ); } /** * Scalar multiplication. * * @param n * @return New point */ public Vector multiply(double n) { return new Vector(this.x * n, this.y * n, this.z * n); } /** * Scalar multiplication. * * @param n * @return New point */ public Vector multiply(float n) { return new Vector(this.x * n, this.y * n, this.z * n); } /** * Scalar multiplication. * * @param n * @return New point */ public Vector multiply(int n) { return new Vector(this.x * n, this.y * n, this.z * n); } /** * Component-wise division * * @param other * @return New point */ public Vector divide(Vector other) { return new Vector(x / other.x, y / other.y, z / other.z); } /** * Component-wise division * * @param x * @param y * @param z * @return New point */ public Vector divide(double x, double y, double z) { return new Vector(this.x / x, this.y / y, this.z / z); } /** * Component-wise division * * @param x * @param y * @param z * @return New point */ public Vector divide(int x, int y, int z) { return new Vector(this.x / x, this.y / y, this.z / z); } /** * Scalar division. * * @param n * @return new point */ public Vector divide(int n) { return new Vector(x / n, y / n, z / n); } /** * Scalar division. * * @param n * @return new point */ public Vector divide(double n) { return new Vector(x / n, y / n, z / n); } /** * Scalar division. * * @param n * @return new point */ public Vector divide(float n) { return new Vector(x / n, y / n, z / n); } /** * Get the length of the vector. * * @return length */ public double length() { return Math.sqrt(x * x + y * y + z * z); } /** * Get the length^2 of the vector. * * @return length^2 */ public double lengthSq() { return x * x + y * y + z * z; } /** * Get the distance away from a point. * * @param pt * @return distance */ public double distance(Vector pt) { return Math.sqrt(Math.pow(pt.x - x, 2) + Math.pow(pt.y - y, 2) + Math.pow(pt.z - z, 2)); } /** * Get the distance away from a point, squared. * * @param pt * @return distance */ public double distanceSq(Vector pt) { return Math.pow(pt.x - x, 2) + Math.pow(pt.y - y, 2) + Math.pow(pt.z - z, 2); } /** * Get the normalized vector. * * @return vector */ public Vector normalize() { return divide(length()); } /** * Gets the dot product of this and another vector. * * @param other * @return the dot product of this and the other vector */ public double dot(Vector other) { return x * other.x + y * other.y + z * other.z; } /** * Gets the cross product of this and another vector. * * @param other * @return the cross product of this and the other vector */ public Vector cross(Vector other) { return new Vector( y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x ); } /** * Checks to see if a vector is contained with another. * * @param min * @param max * @return */ public boolean containedWithin(Vector min, Vector max) { return x >= min.x && x <= max.x && y >= min.y && y <= max.y && z >= min.z && z <= max.z; } /** * Checks to see if a vector is contained with another. * * @param min * @param max * @return */ public boolean containedWithinBlock(Vector min, Vector max) { return getBlockX() >= min.getBlockX() && getBlockX() <= max.getBlockX() && getBlockY() >= min.getBlockY() && getBlockY() <= max.getBlockY() && getBlockZ() >= min.getBlockZ() && getBlockZ() <= max.getBlockZ(); } /** * Clamp the Y component. * * @param min * @param max * @return */ public Vector clampY(int min, int max) { return new Vector(x, Math.max(min, Math.min(max, y)), z); } /** * Rounds all components down. * * @return */ public Vector floor() { return new Vector(Math.floor(x), Math.floor(y), Math.floor(z)); } /** * Rounds all components up. * * @return */ public Vector ceil() { return new Vector(Math.ceil(x), Math.ceil(y), Math.ceil(z)); } /** * Rounds all components to the closest integer.
*
* Components < 0.5 are rounded down, otherwise up * * @return */ public Vector round() { return new Vector(Math.floor(x + 0.5), Math.floor(y + 0.5), Math.floor(z + 0.5)); } /** * Returns a vector with the absolute values of the components of this vector. * * @return */ public Vector positive() { return new Vector(Math.abs(x), Math.abs(y), Math.abs(z)); } /** * 2D transformation. * * @param angle in degrees * @param aboutX about which x coordinate to rotate * @param aboutZ about which z coordinate to rotate * @param translateX what to add after rotation * @param translateZ what to add after rotation * @return */ public Vector transform2D(double angle, double aboutX, double aboutZ, double translateX, double translateZ) { angle = Math.toRadians(angle); double x = this.x - aboutX; double z = this.z - aboutZ; double x2 = x * Math.cos(angle) - z * Math.sin(angle); double z2 = x * Math.sin(angle) + z * Math.cos(angle); return new Vector( x2 + aboutX + translateX, y, z2 + aboutZ + translateZ ); } public boolean isCollinearWith(Vector other) { if (x == 0 && y == 0 && z == 0) { // this is a zero vector return true; } final double otherX = other.x; final double otherY = other.y; final double otherZ = other.z; if (otherX == 0 && otherY == 0 && otherZ == 0) { // other is a zero vector return true; } if ((x == 0) != (otherX == 0)) return false; if ((y == 0) != (otherY == 0)) return false; if ((z == 0) != (otherZ == 0)) return false; final double quotientX = otherX / x; if (!Double.isNaN(quotientX)) { return other.equals(multiply(quotientX)); } final double quotientY = otherY / y; if (!Double.isNaN(quotientY)) { return other.equals(multiply(quotientY)); } final double quotientZ = otherZ / z; if (!Double.isNaN(quotientZ)) { return other.equals(multiply(quotientZ)); } throw new RuntimeException("This should not happen"); } /** * Get this vector's pitch as used within the game. * * @return pitch in radians */ public float toPitch() { double x = getX(); double z = getZ(); if (x == 0 && z == 0) { return getY() > 0 ? -90 : 90; } else { double x2 = x * x; double z2 = z * z; double xz = Math.sqrt(x2 + z2); return (float) Math.atan(-getY() / xz); } } /** * Get this vector's yaw as used within the game. * * @return yaw in radians */ public float toYaw() { double x = getX(); double z = getZ(); double t = Math.atan2(-x, z); double _2pi = 2 * Math.PI; return (float) ((t + _2pi) % _2pi); } /** * Get a block point from a point. * * @param x * @param y * @param z * @return point */ public static BlockVector toBlockPoint(double x, double y, double z) { return new BlockVector( Math.floor(x), Math.floor(y), Math.floor(z) ); } /** * Get a block point from a point. * * @return point */ public BlockVector toBlockPoint() { return new BlockVector( Math.floor(x), Math.floor(y), Math.floor(z) ); } /** * Checks if another object is equivalent. * * @param obj * @return whether the other object is equivalent */ @Override public boolean equals(Object obj) { if (!(obj instanceof Vector)) { return false; } Vector other = (Vector) obj; return other.x == this.x && other.y == this.y && other.z == this.z; } @Override public int compareTo(Vector other) { if (y != other.y) return Double.compare(y, other.y); if (z != other.z) return Double.compare(z, other.z); if (x != other.x) return Double.compare(x, other.x); return 0; } /** * Gets the hash code. * * @return hash code */ @Override public int hashCode() { int hash = 7; hash = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); hash = 79 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); hash = 79 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); return hash; } /** * Returns string representation "(x, y, z)". * * @return string */ @Override public String toString() { return "(" + x + ", " + y + ", " + z + ")"; } /** * Gets a BlockVector version. * * @return BlockVector */ public BlockVector toBlockVector() { return new BlockVector(this); } /** * Creates a 2D vector by dropping the Y component from this vector. * * @return Vector2D */ public Vector2D toVector2D() { return new Vector2D(x, z); } /** * Gets the minimum components of two vectors. * * @param v1 * @param v2 * @return minimum */ public static Vector getMinimum(Vector v1, Vector v2) { return new Vector( Math.min(v1.x, v2.x), Math.min(v1.y, v2.y), Math.min(v1.z, v2.z) ); } /** * Gets the maximum components of two vectors. * * @param v1 * @param v2 * @return maximum */ public static Vector getMaximum(Vector v1, Vector v2) { return new Vector( Math.max(v1.x, v2.x), Math.max(v1.y, v2.y), Math.max(v1.z, v2.z) ); } /** * Gets the midpoint of two vectors. * * @param v1 * @param v2 * @return maximum */ public static Vector getMidpoint(Vector v1, Vector v2) { return new Vector( (v1.x + v2.x) / 2, (v1.y + v2.y) / 2, (v1.z + v2.z) / 2 ); } }