/*
* 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 org.bukkit.Location;
import org.bukkit.util.Vector;
import java.io.IOException;
/**
* Implementation of {@link IVector2D}.
*/
public class Vector2D implements IVector2D, IDataNodeSerializable, IByteSerializable {
protected double _x;
protected double _z;
/**
* Constructor.
*
* <p>Initialize all coordinates to 0.</p>
*/
public Vector2D() {}
/**
* Constructor.
*
* @param x The X value.
* @param z The Z value.
*/
public Vector2D(double x, double z) {
_x = x;
_z = z;
}
/**
* Constructor.
*
* @param coords2D The coordinates to copy from.
*/
public Vector2D(ICoords2D coords2D) {
this(coords2D.getX(), coords2D.getZ());
}
/**
* Constructor.
*
* @param coords2D The coordinates to copy from.
*/
public Vector2D(ICoords2Di coords2D) {
this(coords2D.getX(), coords2D.getZ());
}
/**
* Constructor.
*
* @param vector The Bukkit vector to copy from.
*/
public Vector2D(Vector vector) {
this(vector.getX(), vector.getZ());
}
@Override
public double getX() {
return _x;
}
@Override
public double getZ() {
return _z;
}
@Override
public int getFloorX() {
return (int)Math.floor(_x);
}
@Override
public int getFloorZ() {
return (int)Math.floor(_z);
}
@Override
public Vector2D setX(double x) {
_x = x;
return this;
}
@Override
public Vector2D setZ(double z) {
_z = z;
return this;
}
@Override
public Vector2D set2D(double x, double z) {
_x = x;
_z = z;
return this;
}
@Override
public Vector2D copyFrom2D(ICoords2D coords) {
PreCon.notNull(coords);
_x = coords.getX();
_z = coords.getZ();
return this;
}
@Override
public Vector2D copyFrom2D(ICoords2Di coords) {
PreCon.notNull(coords);
_x = coords.getX();
_z = coords.getZ();
return this;
}
@Override
public Vector2D copyFrom2D(Vector vector) {
PreCon.notNull(vector);
_x = vector.getX();
_z = vector.getZ();
return this;
}
@Override
public Vector2D copyFrom2D(Location location) {
PreCon.notNull(location);
_x = location.getX();
_z = location.getZ();
return this;
}
@Override
public Vector2D copyTo2D(Vector vector) {
PreCon.notNull(vector);
vector.setX(_x);
vector.setZ(_z);
return this;
}
@Override
public Vector2D copyTo2D(Location location) {
PreCon.notNull(location);
location.setX(_x);
location.setZ(_z);
return this;
}
@Override
public Vector2D add2D(ICoords2D vector) {
PreCon.notNull(vector);
_x += vector.getX();
_z += vector.getZ();
return this;
}
@Override
public Vector2D add2D(ICoords2Di vector) {
PreCon.notNull(vector);
_x += vector.getX();
_z += vector.getZ();
return this;
}
@Override
public Vector2D add2D(double value) {
_x += value;
_z += value;
return this;
}
@Override
public Vector2D add2DMax(double scalar, double value) {
_x = Math.max(_x * scalar, value);
_z = Math.max(_z * scalar, value);
return this;
}
@Override
public Vector2D add2DMin(double scalar, double value) {
_x = Math.min(_x * scalar, value);
_z = Math.min(_z * scalar, value);
return this;
}
@Override
public Vector2D addX(double value) {
_x += value;
return this;
}
@Override
public Vector2D addZ(double value) {
_z += value;
return this;
}
@Override
public Vector2D subtract2D(ICoords2D vector) {
PreCon.notNull(vector);
_x -= vector.getX();
_z -= vector.getZ();
return this;
}
@Override
public Vector2D subtract2D(ICoords2Di vector) {
PreCon.notNull(vector);
_x -= vector.getX();
_z -= vector.getZ();
return this;
}
@Override
public Vector2D subtract2D(double scalar) {
_x -= scalar;
_z -= scalar;
return this;
}
@Override
public Vector2D subtract2DMax(double scalar, double value) {
_x = Math.max(_x - scalar, value);
_z = Math.max(_z - scalar, value);
return this;
}
@Override
public Vector2D subtract2DMin(double scalar, double value) {
_x = Math.min(_x - scalar, value);
_z = Math.min(_z - scalar, value);
return this;
}
@Override
public Vector2D subtractX(double value) {
_x -= value;
return this;
}
@Override
public Vector2D subtractZ(double value) {
_z -= value;
return this;
}
@Override
public Vector2D multiply2D(ICoords2D vector) {
PreCon.notNull(vector);
_x *= vector.getX();
_z *= vector.getZ();
return this;
}
@Override
public Vector2D multiply2D(ICoords2Di vector) {
PreCon.notNull(vector);
_x *= vector.getX();
_z *= vector.getZ();
return this;
}
@Override
public Vector2D multiply2D(double scalar) {
_x *= scalar;
_z *= scalar;
return this;
}
@Override
public Vector2D multiply2DMax(double scalar, double value) {
_x = Math.max(_x * scalar, value);
_z = Math.max(_z * scalar, value);
return this;
}
@Override
public Vector2D multiply2DMin(double scalar, double value) {
_x = Math.min(_x * scalar, value);
_z = Math.min(_z * scalar, value);
return this;
}
@Override
public Vector2D multiplyX(double value) {
_x *= value;
return this;
}
@Override
public Vector2D multiplyZ(double value) {
_z *= value;
return this;
}
@Override
public Vector2D average2D(ICoords2D vector) {
PreCon.notNull(vector);
_x = (_x + vector.getX()) * 0.5D;
_z = (_z + vector.getZ()) * 0.5D;
return this;
}
@Override
public Vector2D average2D(ICoords2Di vector) {
PreCon.notNull(vector);
_x = (_x + vector.getX()) * 0.5D;
_z = (_z + vector.getZ()) * 0.5D;
return this;
}
@Override
public Vector2D reverse2D() {
_x *= -1;
_z *= -1;
return this;
}
@Override
public Vector2D normalize() {
double magnitude = getMagnitude2D();
if (magnitude < 0.0D || magnitude > 0.0D) {
_x /= magnitude;
_z /= magnitude;
}
return this;
}
@Override
public Vector2D reset() {
_x = _z = 0;
return this;
}
@Override
public Vector2D abs() {
_x = Math.abs(_x);
_z = Math.abs(_z);
return this;
}
@Override
public float getAngle(ICoords2D vector) {
PreCon.notNull(vector);
// If vector, use vectors magnitude. Otherwise calculate
double oMag = vector instanceof IVector2D
? ((IVector2D) vector).getMagnitude2D()
: calculateMagnitude(vector);
double dot = this instanceof IVector3D && vector instanceof ICoords3D
? ((IVector3D) this).getDot3D((ICoords3D) vector)
: getDot2D(vector);
return FastMath.acos(dot / (getMagnitude2D() * oMag));
}
@Override
public float getAngle(ICoords2Di vector) {
PreCon.notNull(vector);
// If vector, use vectors magnitude. Otherwise calculate
double oMag = calculateMagnitude(vector);
double dot = getDot2D(vector);
return FastMath.acos(dot / (getMagnitude2D() * oMag));
}
@Override
public float getYawDelta(ICoords2D vector) {
PreCon.notNull(vector);
float localAngle = getYaw();
float otherAngle = vector instanceof IVector2D
? ((IVector2D) vector).getYaw()
: calculateYaw(vector);
return otherAngle - localAngle;
}
@Override
public float getYawDelta(ICoords2Di vector) {
PreCon.notNull(vector);
float localAngle = getYaw();
float otherAngle = calculateYaw(vector);
return otherAngle - localAngle;
}
@Override
public float getYaw() {
return calculateYaw(this);
}
@Override
public double getDot2D(ICoords2D vector) {
PreCon.notNull(vector);
return _x * vector.getX() + _z * vector.getZ();
}
@Override
public double getDot2D(ICoords2Di vector) {
PreCon.notNull(vector);
return _x * vector.getX() + _z * vector.getZ();
}
@Override
public double getMagnitude2D() {
return FastMath.sqrt(_x * _x + _z * _z);
}
@Override
public double getMagnitudeSquared2D() {
return _x * _x + _z * _z;
}
@Override
public double getDirection() {
double angle = FastMath.atan2(_z, _x);
float degrees = (float)Math.toDegrees(angle);
return Float.compare(degrees, 0.0f) == 0 ? degrees : -degrees;
}
@Override
public double getDistance2D(ICoords2D vector) {
PreCon.notNull(vector);
double deltaX = _x - vector.getX();
double deltaZ = _z - vector.getZ();
return FastMath.sqrt(deltaX * deltaX + deltaZ * deltaZ);
}
@Override
public double getDistance2D(ICoords2Di vector) {
PreCon.notNull(vector);
double deltaX = _x - vector.getX();
double deltaZ = _z - vector.getZ();
return FastMath.sqrt(deltaX * deltaX + deltaZ * deltaZ);
}
@Override
public double getDistanceSquared2D(ICoords2D vector) {
PreCon.notNull(vector);
double deltaX = _x - vector.getX();
double deltaZ = _z - vector.getZ();
return deltaX * deltaX + deltaZ * deltaZ;
}
@Override
public double getDistanceSquared2D(ICoords2Di vector) {
PreCon.notNull(vector);
double deltaX = _x - vector.getX();
double deltaZ = _z - vector.getZ();
return deltaX * deltaX + deltaZ * deltaZ;
}
@Override
public Vector getBukkitVector() {
return new Vector(_x, 0, _z);
}
@Override
public Vector getBukkitVector(Vector output) {
PreCon.notNull(output);
output.setX(_x);
output.setY(0);
output.setZ(_z);
return output;
}
@Override
public void serialize(IByteWriter writer) throws IOException {
writer.write(_x);
writer.write(_z);
}
@Override
public void deserialize(IByteReader reader) throws IOException {
_x = reader.getDouble();
_z = reader.getDouble();
}
@Override
public void serialize(IDataNode dataNode) {
dataNode.set("x", _x);
dataNode.set("z", _z);
}
@Override
public void deserialize(IDataNode dataNode) throws DeserializeException {
_x = dataNode.getDouble("x");
_z = dataNode.getDouble("z");
}
@Override
public String toString() {
return getClass().getSimpleName() + " { x:" + getX() + ", z:" + getZ() + '}';
}
@Override
public int hashCode() {
return (int)(_x * 100) ^ (int)(_z * 100);
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof Vector2D) {
Vector2D other = (Vector2D)obj;
return Double.compare(other.getX(), getX()) == 0
&& Double.compare(other.getZ(), getZ()) == 0;
}
return false;
}
private static double calculateMagnitude(ICoords2D coords) {
if (coords instanceof ICoords3D) {
ICoords3D coords3D = (ICoords3D)coords;
return FastMath.sqrt(coords.getX() * coords.getX()
+ coords3D.getY() * coords3D.getY()
+ coords.getZ() * coords.getZ());
}
else {
return FastMath.sqrt(coords.getX() * coords.getX() + coords.getZ() * coords.getZ());
}
}
private static double calculateMagnitude(ICoords2Di coords) {
if (coords instanceof ICoords3Di) {
ICoords3Di coords3D = (ICoords3Di)coords;
return FastMath.sqrt(coords.getX() * coords.getX()
+ coords3D.getY() * coords3D.getY()
+ coords.getZ() * coords.getZ());
}
else {
return FastMath.sqrt(coords.getX() * coords.getX() + coords.getZ() * coords.getZ());
}
}
private static float calculateYaw(ICoords2D vector) {
float yaw = (float)Math.toDegrees(FastMath.atan2(vector.getX(), vector.getZ()));
return Float.compare(yaw, 0.0f) == 0 ? yaw : -yaw;
}
private static float calculateYaw(ICoords2Di vector) {
float yaw = (float)Math.toDegrees(FastMath.atan2(vector.getX(), vector.getZ()));
return Float.compare(yaw, 0.0f) == 0 ? yaw : -yaw;
}
}