namespace Minecraft.Server.FourKit.Util; /// /// Represents a mutable vector. Because the components of Vectors are mutable, /// storing Vectors long term may be dangerous if passing code modifies the /// Vector later. If you want to keep around a Vector, it may be wise to call /// in order to get a copy. /// public class Vector { private static readonly double EPSILON = 0.000001; private static readonly Random _random = new(); protected double x; protected double y; protected double z; public Vector() { x = 0; y = 0; z = 0; } /// /// Construct the vector with provided integer components. /// /// X component. /// Y component. /// Z component. public Vector(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } /// /// Construct the vector with provided double components. /// /// X component. /// Y component. /// Z component. public Vector(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } /// /// Construct the vector with provided float components. /// /// X component. /// Y component. /// Z component. public Vector(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } /// /// Adds a vector to this one. /// /// The other vector. /// The same vector. public Vector add(Vector vec) { x += vec.x; y += vec.y; z += vec.z; return this; } /// /// Subtracts a vector from this one. /// /// The other vector. /// The same vector. public Vector subtract(Vector vec) { x -= vec.x; y -= vec.y; z -= vec.z; return this; } /// /// Multiplies the vector by another. /// /// The other vector. /// The same vector. public Vector multiply(Vector vec) { x *= vec.x; y *= vec.y; z *= vec.z; return this; } /// /// Divides the vector by another. /// /// The other vector. /// The same vector. public Vector divide(Vector vec) { x /= vec.x; y /= vec.y; z /= vec.z; return this; } /// /// Copies another vector. /// /// The other vector. /// The same vector. public Vector copy(Vector vec) { x = vec.x; y = vec.y; z = vec.z; return this; } /// /// Gets the magnitude of the vector, defined as sqrt(x^2+y^2+z^2). /// The value of this method is not cached and uses a costly square-root /// function, so do not repeatedly call this method to get the vector's /// magnitude. NaN will be returned if the inner result of the sqrt() /// function overflows, which will be caused if the length is too long. /// /// The magnitude. public double length() { return Math.Sqrt(x * x + y * y + z * z); } /// /// Gets the magnitude of the vector squared. /// /// The magnitude squared. public double lengthSquared() { return x * x + y * y + z * z; } /// /// Get the distance between this vector and another. The value of this /// method is not cached and uses a costly square-root function, so do not /// repeatedly call this method to get the vector's magnitude. NaN will be /// returned if the inner result of the sqrt() function overflows, which /// will be caused if the distance is too long. /// /// The other vector. /// The distance. public double distance(Vector o) { return Math.Sqrt(distanceSquared(o)); } /// /// Get the squared distance between this vector and another. /// /// The other vector. /// The distance squared. public double distanceSquared(Vector o) { double dx = x - o.x; double dy = y - o.y; double dz = z - o.z; return dx * dx + dy * dy + dz * dz; } /// /// Gets the angle between this vector and another in radians. /// /// The other vector. /// Angle in radians. public float angle(Vector other) { double dot = this.dot(other) / (length() * other.length()); return (float)Math.Acos(dot); } /// /// Sets this vector to the midpoint between this vector and another. /// /// The other vector. /// This same vector (now a midpoint). public Vector midpoint(Vector other) { x = (x + other.x) / 2.0; y = (y + other.y) / 2.0; z = (z + other.z) / 2.0; return this; } /// /// Gets a new midpoint vector between this vector and another. /// /// The other vector. /// A new midpoint vector. public Vector getMidpoint(Vector other) { double mx = (x + other.x) / 2.0; double my = (y + other.y) / 2.0; double mz = (z + other.z) / 2.0; return new Vector(mx, my, mz); } /// /// Performs scalar multiplication, multiplying all components with a scalar. /// /// The factor. /// The same vector. public Vector multiply(int m) { x *= m; y *= m; z *= m; return this; } /// /// Performs scalar multiplication, multiplying all components with a scalar. /// /// The factor. /// The same vector. public Vector multiply(double m) { x *= m; y *= m; z *= m; return this; } /// /// Performs scalar multiplication, multiplying all components with a scalar. /// /// The factor. /// The same vector. public Vector multiply(float m) { x *= m; y *= m; z *= m; return this; } /// /// Calculates the dot product of this vector with another. The dot product /// is defined as x1*x2+y1*y2+z1*z2. The returned value is a scalar. /// /// The other vector. /// Dot product. public double dot(Vector other) { return x * other.x + y * other.y + z * other.z; } /// /// Calculates the cross product of this vector with another. /// The cross product is defined as: /// /// x = y1 * z2 - y2 * z1 /// y = z1 * x2 - z2 * x1 /// z = x1 * y2 - x2 * y1 /// /// /// The other vector. /// The same vector. public Vector crossProduct(Vector o) { double newX = y * o.z - o.y * z; double newY = z * o.x - o.z * x; double newZ = x * o.y - o.x * y; x = newX; y = newY; z = newZ; return this; } /// /// Converts this vector to a unit vector (a vector with length of 1). /// /// The same vector. public Vector normalize() { double mag = length(); x /= mag; y /= mag; z /= mag; return this; } /// /// Zero this vector's components. /// /// The same vector. public Vector zero() { x = 0; y = 0; z = 0; return this; } /// /// Returns whether this vector is in an axis-aligned bounding box. /// The minimum and maximum vectors given must be truly the minimum and /// maximum X, Y and Z components. /// /// Minimum vector. /// Maximum vector. /// Whether this vector is in the AABB. public bool isInAABB(Vector min, Vector max) { return x >= min.x && x <= max.x && y >= min.y && y <= max.y && z >= min.z && z <= max.z; } /// /// Returns whether this vector is within a sphere. /// /// Sphere origin. /// Sphere radius. /// Whether this vector is in the sphere. public bool isInSphere(Vector origin, double radius) { return distanceSquared(origin) <= radius * radius; } /// /// Gets the X component. /// /// The X component. public double getX() { return x; } /// /// Gets the floored value of the X component, indicating the block that /// this vector is contained with. /// /// Block X. public int getBlockX() { return (int)Math.Floor(x); } /// /// Gets the Y component. /// /// The Y component. public double getY() { return y; } /// /// Gets the floored value of the Y component, indicating the block that /// this vector is contained with. /// /// Block Y. public int getBlockY() { return (int)Math.Floor(y); } /// /// Gets the Z component. /// /// The Z component. public double getZ() { return z; } /// /// Gets the floored value of the Z component, indicating the block that /// this vector is contained with. /// /// Block Z. public int getBlockZ() { return (int)Math.Floor(z); } /// /// Set the X component. /// /// The new X component. /// This vector. public Vector setX(int x) { this.x = x; return this; } /// /// Set the X component. /// /// The new X component. /// This vector. public Vector setX(double x) { this.x = x; return this; } /// /// Set the X component. /// /// The new X component. /// This vector. public Vector setX(float x) { this.x = x; return this; } /// /// Set the Y component. /// /// The new Y component. /// This vector. public Vector setY(int y) { this.y = y; return this; } /// /// Set the Y component. /// /// The new Y component. /// This vector. public Vector setY(double y) { this.y = y; return this; } /// /// Set the Y component. /// /// The new Y component. /// This vector. public Vector setY(float y) { this.y = y; return this; } /// /// Set the Z component. /// /// The new Z component. /// This vector. public Vector setZ(int z) { this.z = z; return this; } /// /// Set the Z component. /// /// The new Z component. /// This vector. public Vector setZ(double z) { this.z = z; return this; } /// /// Set the Z component. /// /// The new Z component. /// This vector. public Vector setZ(float z) { this.z = z; return this; } /// /// Get a new vector. /// /// A clone of this vector. public Vector clone() { return new Vector(x, y, z); } /// /// Gets a Location version of this vector with yaw and pitch being 0. /// /// The world to link the location to. /// The location. public Location toLocation(World world) { return new Location(world, x, y, z); } /// /// Gets a Location version of this vector. /// /// The world to link the location to. /// The desired yaw. /// The desired pitch. /// The location. public Location toLocation(World world, float yaw, float pitch) { return new Location(world, x, y, z, yaw, pitch); } /// /// Get the threshold used for equals(). /// /// The epsilon. public static double getEpsilon() { return EPSILON; } /// /// Gets the minimum components of two vectors. /// /// The first vector. /// The second vector. /// 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. /// /// The first vector. /// The second vector. /// 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 a random vector with components having a random value between 0 and 1. /// /// A random vector. public static Vector getRandom() { return new Vector(_random.NextDouble(), _random.NextDouble(), _random.NextDouble()); } /// public override bool Equals(object? obj) { if (obj is not Vector other) return false; return Math.Abs(x - other.x) < EPSILON && Math.Abs(y - other.y) < EPSILON && Math.Abs(z - other.z) < EPSILON; } /// public override string ToString() { return $"{x},{y},{z}"; } }