/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.utils.coords;
import com.jcwhatever.nucleus.providers.math.FastMath;
import com.jcwhatever.nucleus.storage.IDataNode;
import com.jcwhatever.nucleus.storage.serialize.DeserializeException;
import com.jcwhatever.nucleus.storage.serialize.IDataNodeSerializable;
import com.jcwhatever.nucleus.utils.PreCon;
import com.jcwhatever.nucleus.utils.file.IByteReader;
import com.jcwhatever.nucleus.utils.file.IByteSerializable;
import com.jcwhatever.nucleus.utils.file.IByteWriter;
import com.jcwhatever.nucleus.utils.nms.INmsEntityHandler;
import com.jcwhatever.nucleus.utils.nms.NmsUtils;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import java.io.IOException;
/**
* Implementation of {@link IVector3D}.
*/
public class Vector3D extends Vector2D implements IVector3D, IDataNodeSerializable, IByteSerializable {
private static final Vector ENTITY_VELOCITY = new Vector();
protected double _y;
private transient AsBukkitVector _asVector;
/**
* Constructor.
*
* <p>Initialize all coordinates to 0.</p>
*/
public Vector3D() {}
/**
* Constructor.
*
* @param x The initial X value.
* @param y The initial Y value.
* @param z The initial Z value.
*/
public Vector3D(double x, double y, double z) {
super(x, z);
_y = y;
}
/**
* Constructor.
*
* @param coords Instance to copy coordinate values from.
*/
public Vector3D(ICoords3D coords) {
super(coords);
_y = coords.getY();
}
/**
* Constructor.
*
* @param coords Instance to copy coordinate values from.
*/
public Vector3D(ICoords3Di coords) {
super(coords);
_y = coords.getY();
}
/**
* Constructor.
*
* @param coords Instance to copy coordinate values from.
*/
public Vector3D(ICoords2D coords) {
super(coords);
}
/**
* Constructor.
*
* @param vector The Bukkit vector to copy coordinate values from.
*/
public Vector3D(Vector vector) {
super(vector);
_y = vector.getY();
}
/**
* Constructor.
*
* @param location The Bukkit location to copy coordinate values from.
*/
public Vector3D(Location location) {
super(location.getX(), location.getZ());
_y = location.getY();
}
/**
* Constructor.
*
* @param entity The entity to copy velocity values from.
*/
public Vector3D(Entity entity) {
copyFrom3D(entity);
}
@Override
public double getY() {
return _y;
}
@Override
public int getFloorY() {
return (int)Math.floor(_y);
}
@Override
public Vector3D setY(double y) {
_y = y;
onChange();
return this;
}
@Override
public Vector3D set3D(double x, double y, double z) {
_x = x;
_y = y;
_z = z;
onChange();
return this;
}
@Override
public Vector3D copyFrom3D(Entity entity) {
PreCon.notNull(entity);
INmsEntityHandler handler = NmsUtils.getEntityHandler();
if (handler != null) {
handler.getVelocity(entity, this);
}
else {
copyFrom3D(entity.getVelocity());
}
return this;
}
@Override
public Vector3D copyFrom3D(ICoords2D coords) {
PreCon.notNull(coords);
_y = coords instanceof ICoords3D
? ((ICoords3D) coords).getY()
: 0;
super.copyFrom2D(coords);
onChange();
return this;
}
@Override
public Vector3D copyFrom3D(ICoords2Di coords) {
PreCon.notNull(coords);
_y = coords instanceof ICoords3Di
? ((ICoords3Di) coords).getY()
: 0;
super.copyFrom2D(coords);
onChange();
return this;
}
@Override
public Vector3D copyFrom3D(Vector vector) {
PreCon.notNull(vector);
_y = vector.getY();
super.copyFrom2D(vector);
onChange();
return this;
}
@Override
public Vector3D copyFrom3D(Location location) {
PreCon.notNull(location);
_y = location.getY();
super.copyFrom2D(location);
onChange();
return this;
}
@Override
public Vector3D copyTo3D(Entity entity) {
PreCon.notNull(entity);
copyTo3D(ENTITY_VELOCITY);
entity.setVelocity(ENTITY_VELOCITY);
return this;
}
@Override
public Vector3D copyTo3D(Vector vector) {
PreCon.notNull(vector);
vector.setX(_x);
vector.setY(_y);
vector.setZ(_z);
return this;
}
@Override
public Vector3D copyTo3D(Location location) {
PreCon.notNull(location);
location.setX(_x);
location.setY(_y);
location.setZ(_z);
return this;
}
@Override
public Vector3D copyFrom2D(ICoords2D coords) {
super.copyFrom2D(coords);
onChange();
return this;
}
@Override
public Vector3D copyFrom2D(ICoords2Di coords) {
super.copyFrom2D(coords);
onChange();
return this;
}
@Override
public Vector3D copyFrom2D(Vector vector) {
super.copyFrom2D(vector);
onChange();
return this;
}
@Override
public Vector3D copyFrom2D(Location location) {
super.copyFrom2D(location);
onChange();
return this;
}
@Override
public Vector3D copyTo2D(Vector vector) {
super.copyTo2D(vector);
onChange();
return this;
}
@Override
public Vector3D copyTo2D(Location location) {
super.copyTo2D(location);
onChange();
return this;
}
@Override
public Vector3D add3D(ICoords2D vector) {
PreCon.notNull(vector);
if (vector instanceof ICoords3D) {
_y += ((ICoords3D) vector).getY();
}
add2D(vector);
return this;
}
@Override
public IVector3D add3D(ICoords2Di vector) {
PreCon.notNull(vector);
if (vector instanceof ICoords3Di) {
_y += ((ICoords3Di) vector).getY();
}
add2D(vector);
return this;
}
@Override
public Vector3D add3D(Location vector) {
PreCon.notNull(vector);
_x += vector.getX();
_y += vector.getY();
_z += vector.getZ();
onChange();
return this;
}
@Override
public Vector3D add3D(double scalar) {
_y += scalar;
super.add2D(scalar);
onChange();
return this;
}
@Override
public Vector3D addY(double value) {
_y += value;
onChange();
return this;
}
@Override
public Vector3D add3DMax(double scalar, double value) {
_x = Math.max(_x + scalar, value);
_y = Math.max(_y + scalar, value);
_z = Math.max(_z + scalar, value);
onChange();
return this;
}
@Override
public Vector3D add3DMin(double scalar, double value) {
_x = Math.min(_x + scalar, value);
_y = Math.min(_y + scalar, value);
_z = Math.min(_z + scalar, value);
onChange();
return this;
}
@Override
public Vector3D subtract3D(ICoords2D vector) {
PreCon.notNull(vector);
if (vector instanceof ICoords3D) {
_y -= ((ICoords3D) vector).getY();
}
super.subtract2D(vector);
onChange();
return this;
}
@Override
public Vector3D subtract3D(ICoords2Di vector) {
PreCon.notNull(vector);
if (vector instanceof ICoords3Di) {
_y -= ((ICoords3Di) vector).getY();
}
super.subtract2D(vector);
onChange();
return this;
}
@Override
public Vector3D subtract3D(Location vector) {
PreCon.notNull(vector);
_x -= vector.getX();
_y -= vector.getY();
_z -= vector.getZ();
onChange();
return this;
}
@Override
public Vector3D subtract3D(double scalar) {
_y -= scalar;
super.subtract2D(scalar);
onChange();
return this;
}
@Override
public Vector3D subtract3DMax(double scalar, double value) {
_x = Math.max(_x - scalar, value);
_y = Math.max(_y - scalar, value);
_z = Math.max(_z - scalar, value);
onChange();
return this;
}
@Override
public Vector3D subtract3DMin(double scalar, double value) {
_x = Math.min(_x - scalar, value);
_y = Math.min(_y - scalar, value);
_z = Math.min(_z - scalar, value);
onChange();
return this;
}
@Override
public Vector3D subtractY(double value) {
_y -= value;
onChange();
return this;
}
@Override
public Vector3D multiply3D(ICoords2D vector) {
PreCon.notNull(vector);
if (vector instanceof ICoords3D) {
_y *= ((ICoords3D) vector).getY();
}
else {
_y = 0;
}
_x *= vector.getX();
_z *= vector.getZ();
onChange();
return this;
}
@Override
public Vector3D multiply3D(ICoords2Di vector) {
PreCon.notNull(vector);
if (vector instanceof ICoords3Di) {
_y *= ((ICoords3Di) vector).getY();
}
else {
_y = 0;
}
_x *= vector.getX();
_z *= vector.getZ();
onChange();
return this;
}
@Override
public Vector3D multiply3D(Location vector) {
PreCon.notNull(vector);
_x *= vector.getX();
_y *= vector.getY();
_z *= vector.getZ();
onChange();
return this;
}
@Override
public Vector3D multiply3D(double scalar) {
_y *= scalar;
super.multiply2D(scalar);
onChange();
return this;
}
@Override
public Vector3D multiply3DMax(double scalar, double value) {
_x = Math.max(_x * scalar, value);
_y = Math.max(_y * scalar, value);
_z = Math.max(_z * scalar, value);
onChange();
return this;
}
@Override
public Vector3D multiply3DMin(double scalar, double value) {
_x = Math.min(_x * scalar, value);
_y = Math.min(_y * scalar, value);
_z = Math.min(_z * scalar, value);
onChange();
return this;
}
@Override
public Vector2D multiplyY(double value) {
_y *= value;
onChange();
return this;
}
@Override
public Vector3D average3D(ICoords2D vector) {
PreCon.notNull(vector);
double otherY = vector instanceof ICoords3D
? ((ICoords3D) vector).getY()
: 0;
_x = (_x + vector.getX()) * 0.5D;
_y = (_y + otherY) * 0.5D;
_z = (_z + vector.getZ()) * 0.5D;
onChange();
return null;
}
@Override
public Vector3D average3D(ICoords2Di vector) {
PreCon.notNull(vector);
double otherY = vector instanceof ICoords3Di
? ((ICoords3Di) vector).getY()
: 0;
_x = (_x + vector.getX()) * 0.5D;
_y = (_y + otherY) * 0.5D;
_z = (_z + vector.getZ()) * 0.5D;
onChange();
return null;
}
@Override
public Vector3D reverse3D() {
_x *= -1;
_y *= -1;
_z *= -1;
onChange();
return this;
}
@Override
public Vector3D cross(ICoords3D vector) {
PreCon.notNull(vector);
_x = _y * vector.getZ() - vector.getY() * _z;
_y = _z * vector.getX() - vector.getZ() * _x;
_z = _x * vector.getY() - vector.getX() * _y;
onChange();
return this;
}
@Override
public Vector3D cross(ICoords3Di vector) {
PreCon.notNull(vector);
_x = _y * vector.getZ() - vector.getY() * _z;
_y = _z * vector.getX() - vector.getZ() * _x;
_z = _x * vector.getY() - vector.getX() * _y;
onChange();
return this;
}
@Override
public Vector3D setX(double x) {
super.setX(x);
onChange();
return this;
}
@Override
public Vector3D setZ(double z) {
super.setZ(z);
onChange();
return this;
}
@Override
public Vector3D set2D(double x, double z) {
super.set2D(x, z);
onChange();
return this;
}
@Override
public Vector3D add2D(ICoords2D vector) {
super.add2D(vector);
onChange();
return this;
}
@Override
public Vector3D add2D(ICoords2Di vector) {
super.add2D(vector);
onChange();
return this;
}
@Override
public Vector3D add2D(double value) {
super.add2D(value);
onChange();
return this;
}
@Override
public Vector3D add2DMax(double scalar, double value) {
super.add2DMax(scalar, value);
onChange();
return this;
}
@Override
public Vector3D add2DMin(double scalar, double value) {
super.add2DMin(scalar, value);
onChange();
return this;
}
@Override
public Vector3D addX(double value) {
super.addX(value);
onChange();
return this;
}
@Override
public Vector3D addZ(double value) {
super.addZ(value);
onChange();
return this;
}
@Override
public Vector3D subtract2D(ICoords2D vector) {
super.subtract2D(vector);
onChange();
return this;
}
@Override
public Vector3D subtract2D(ICoords2Di vector) {
super.subtract2D(vector);
onChange();
return this;
}
@Override
public Vector3D subtract2D(double scalar) {
super.subtract2D(scalar);
onChange();
return this;
}
@Override
public Vector3D subtract2DMax(double scalar, double value) {
super.subtract2DMax(scalar, value);
onChange();
return this;
}
@Override
public Vector3D subtract2DMin(double scalar, double value) {
super.subtract2DMin(scalar, value);
onChange();
return this;
}
@Override
public Vector3D subtractX(double value) {
super.subtractX(value);
onChange();
return this;
}
@Override
public Vector3D subtractZ(double value) {
super.subtractZ(value);
onChange();
return this;
}
@Override
public Vector3D multiply2D(ICoords2D vector) {
super.multiply2D(vector);
onChange();
return this;
}
@Override
public Vector3D multiply2D(ICoords2Di vector) {
super.multiply2D(vector);
onChange();
return this;
}
@Override
public Vector3D multiply2D(double scalar) {
super.multiply2D(scalar);
onChange();
return this;
}
@Override
public Vector3D multiply2DMax(double scalar, double value) {
super.multiply2DMax(scalar, value);
onChange();
return this;
}
@Override
public Vector3D multiply2DMin(double scalar, double value) {
super.multiply2DMin(scalar, value);
onChange();
return this;
}
@Override
public Vector3D multiplyX(double value) {
super.multiplyX(value);
onChange();
return this;
}
@Override
public Vector3D multiplyZ(double value) {
super.multiplyZ(value);
onChange();
return this;
}
@Override
public Vector3D average2D(ICoords2D vector) {
super.average2D(vector);
onChange();
return this;
}
@Override
public Vector3D average2D(ICoords2Di vector) {
super.average2D(vector);
onChange();
return this;
}
@Override
public Vector3D reverse2D() {
super.reverse2D();
onChange();
return this;
}
@Override
public Vector3D normalize() {
double magnitude = getMagnitude3D();
if (magnitude < 0.0D || magnitude > 0.0D) {
_x /= magnitude;
_z /= magnitude;
_y /= magnitude;
onChange();
}
return this;
}
@Override
public Vector3D reset() {
_x = _y = _z = 0;
onChange();
return this;
}
@Override
public Vector3D abs() {
_y = Math.abs(_y);
super.abs();
onChange();
return this;
}
@Override
public double getMagnitude3D() {
return FastMath.sqrt(_x * _x + _y * _y + _z * _z);
}
@Override
public double getMagnitudeSquared3D() {
return _x * _x + _y * _y + _z * _z;
}
@Override
public double getDot3D(ICoords3D vector) {
PreCon.notNull(vector);
return _x * vector.getX() + _y * vector.getY() + _z + vector.getZ();
}
@Override
public double getDot3D(ICoords3Di vector) {
PreCon.notNull(vector);
return _x * vector.getX() + _y * vector.getY() + _z + vector.getZ();
}
@Override
public double getDistance3D(ICoords2D vector) {
PreCon.notNull(vector);
return FastMath.sqrt(getDistanceSquared3D(vector));
}
@Override
public double getDistance3D(ICoords2Di vector) {
PreCon.notNull(vector);
return FastMath.sqrt(getDistanceSquared3D(vector));
}
@Override
public double getDistance3D(Location vector) {
PreCon.notNull(vector);
return FastMath.sqrt(getDistanceSquared3D(vector));
}
@Override
public double getDistanceSquared3D(ICoords2D vector) {
PreCon.notNull(vector);
double deltaY = _y - (vector instanceof ICoords3D ? ((ICoords3D) vector).getY() : _y);
double deltaX = _x - vector.getX();
double deltaZ = _z - vector.getZ();
return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
}
@Override
public double getDistanceSquared3D(ICoords2Di vector) {
PreCon.notNull(vector);
double deltaY = _y - (vector instanceof ICoords3Di ? ((ICoords3Di) vector).getY() : _y);
double deltaX = _x - vector.getX();
double deltaZ = _z - vector.getZ();
return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
}
@Override
public double getDistanceSquared3D(Location vector) {
PreCon.notNull(vector);
double deltaX = _x - vector.getX();
double deltaY = _y - vector.getY();
double deltaZ = _z - vector.getZ();
return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
}
@Override
public Vector asBukkitVector() {
if (_asVector == null)
_asVector = new AsBukkitVector(_x, _y, _z);
return _asVector;
}
@Override
public Vector getBukkitVector() {
Vector result = super.getBukkitVector();
result.setY(_y);
return result;
}
@Override
public Vector getBukkitVector(Vector output) {
PreCon.notNull(output);
super.getBukkitVector(output);
output.setY(_y);
return output;
}
@Override
public void serialize(IByteWriter writer) throws IOException {
writer.write(_x);
writer.write(_y);
writer.write(_z);
}
@Override
public void deserialize(IByteReader reader) throws IOException {
_x = reader.getDouble();
_y = reader.getDouble();
_z = reader.getDouble();
onChange();
}
@Override
public void serialize(IDataNode dataNode) {
dataNode.set("x", _x);
dataNode.set("y", _y);
dataNode.set("z", _z);
}
@Override
public void deserialize(IDataNode dataNode) throws DeserializeException {
_x = dataNode.getDouble("x");
_y = dataNode.getDouble("y");
_z = dataNode.getDouble("z");
onChange();
}
@Override
public String toString() {
return getClass().getSimpleName() + " { x:" + getX() + ",y:" + getY() + ", z:" + getZ() + '}';
}
@Override
public int hashCode() {
return (int)(_x * 100) ^ (int)(_y * 100) ^ (int)(_z * 100);
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof Vector3D) {
Vector3D other = (Vector3D)obj;
return Double.compare(other.getX(), getX()) == 0
&& Double.compare(other.getY(), getY()) == 0
&& Double.compare(other.getZ(), getZ()) == 0;
}
return false;
}
private void onChange() {
if (_asVector != null) {
_asVector.update();
}
}
class AsBukkitVector extends Vector {
AsBukkitVector(double x, double y, double z) {
super(x, y, z);
}
@Override
public Vector add(Vector vec) {
this.x = _x += vec.getX();
this.y = _y += vec.getY();
this.z = _z += vec.getZ();
return this;
}
@Override
public Vector subtract(Vector vec) {
this.x = _x -= vec.getX();
this.y = _y -= vec.getY();
this.z = _z -= vec.getZ();
return this;
}
@Override
public Vector multiply(Vector vec) {
this.x = _x *= vec.getX();
this.y = _y *= vec.getY();
this.z = _z *= vec.getZ();
return this;
}
@Override
public Vector divide(Vector vec) {
this.x = _x /= vec.getX();
this.y = _y /= vec.getY();
this.z = _z /= vec.getZ();
return this;
}
@Override
public Vector copy(Vector vec) {
this.x = _x = vec.getX();
this.y = _y = vec.getY();
this.z = _z = vec.getZ();
return this;
}
@Override
public double length() {
return getMagnitude3D();
}
@Override
public double distance(Vector o) {
return FastMath.sqrt(distanceSquared(o));
}
@Override
public double distanceSquared(Vector o) {
double deltaX = _x - o.getX();
double deltaY = _y - o.getY();
double deltaZ = _z - o.getZ();
return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
}
@Override
public Vector midpoint(Vector other) {
this.x = _x = (_x + other.getX()) * 0.5D;
this.y = _y = (_y + other.getY()) * 0.5D;
this.z = _z = (_z + other.getZ()) * 0.5D;
return this;
}
@Override
public Vector multiply(int m) {
Vector3D.this.multiply3D(m);
return this;
}
@Override
public Vector multiply(double m) {
Vector3D.this.multiply3D(m);
return this;
}
@Override
public Vector multiply(float m) {
Vector3D.this.multiply3D(m);
return this;
}
@Override
public double dot(Vector other) {
return _x * other.getX() + _y * other.getY() + _z * other.getZ();
}
@Override
public Vector crossProduct(Vector o) {
this.x = _x = _y * o.getZ() - o.getY() * _z;
this.x = _y = _z * o.getX() - o.getZ() * _x;
this.x = _z = _x * o.getY() - o.getX() * _y;
return this;
}
@Override
public Vector normalize() {
Vector3D.this.normalize();
return this;
}
@Override
public Vector zero() {
this.x = _x = 0.0D;
this.y = _y = 0.0D;
this.z = _z = 0.0D;
return this;
}
@Override
public double getX() {
return _x;
}
@Override
public int getBlockX() {
return Vector3D.this.getFloorX();
}
@Override
public double getY() {
return _y;
}
@Override
public int getBlockY() {
return Vector3D.this.getFloorY();
}
@Override
public double getZ() {
return _z;
}
@Override
public int getBlockZ() {
return Vector3D.this.getFloorZ();
}
@Override
public Vector setX(int x) {
this.x = _x = x;
return this;
}
@Override
public Vector setX(double x) {
this.x = _x = x;
return this;
}
@Override
public Vector setX(float x) {
this.x = _x = x;
return this;
}
@Override
public Vector setY(int y) {
this.y = _y = y;
return this;
}
@Override
public Vector setY(double y) {
this.y = _y = y;
return this;
}
@Override
public Vector setY(float y) {
this.y = _y = y;
return this;
}
@Override
public Vector setZ(int z) {
this.z = _z = z;
return this;
}
@Override
public Vector setZ(double z) {
this.z = _z = z;
return this;
}
@Override
public Vector setZ(float z) {
this.z = _z = z;
return this;
}
@Override
public Vector clone() {
return new Vector(_x, _y, _z);
}
void update() {
this.x = _x;
this.y = _y;
this.z = _z;
}
}
}