/* * __ .__ .__ ._____. * _/ |_ _______ __|__| ____ | | |__\_ |__ ______ * \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/ * | | ( <_> > <| \ \___| |_| || \_\ \\___ \ * |__| \____/__/\_ \__|\___ >____/__||___ /____ > * \/ \/ \/ \/ * * Copyright (c) 2006-2011 Karsten Schmidt * * This library 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 2.1 of the License, or (at your option) any later version. * * http://creativecommons.org/licenses/LGPL/2.1/ * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ package spimedb.util.geom; import spimedb.util.math.InterpolateStrategy; import spimedb.util.math.MathUtils; import spimedb.util.math.ScaleMap; import java.io.Serializable; import java.util.Random; /** * Comprehensive 3D vector class with additional basic intersection and * collision detection features. */ public class Vec3D implements Comparable<roVec3D>, roVec3D, Serializable { final public boolean hasX() { return Float.isNaN(x); } final public boolean hasY() { return Float.isNaN(y); } final public boolean hasZ() { return Float.isNaN(z); } public static enum Axis { X(Vec3D.X_AXIS), Y(Vec3D.Y_AXIS), Z(Vec3D.Z_AXIS); private final roVec3D vector; private Axis(roVec3D v) { this.vector = v; } public roVec3D getVector() { return vector; } } /** Defines positive X axis. */ public static final roVec3D X_AXIS = new Vec3D(1, 0, 0); /** Defines positive Y axis. */ public static final roVec3D Y_AXIS = new Vec3D(0, 1, 0); /** Defines positive Z axis. */ public static final roVec3D Z_AXIS = new Vec3D(0, 0, 1); /** Defines the zero vector. */ public static final roVec3D ZERO = new Vec3D();; /** * Defines vector with all coords set to Float.MIN_VALUE. Useful for * bounding box operations. */ public static final roVec3D MIN_VALUE = new Vec3D(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); /** * Defines vector with all coords set to Float.MAX_VALUE. Useful for * bounding box operations. */ public static final roVec3D MAX_VALUE = new Vec3D(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE); public static final roVec3D NEG_MAX_VALUE = new Vec3D( -Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE); /** * Creates a new vector from the given angle in the XY plane. The Z * component of the vector will be zero. * * The resulting vector for theta=0 is equal to the positive X axis. * * @param theta * the theta * * @return new vector in the XY plane */ public static final XYZ fromXYTheta(float theta) { return new Vec3D((float) Math.cos(theta), (float) Math.sin(theta), 0); } /** * Creates a new vector from the given angle in the XZ plane. The Y * component of the vector will be zero. * * The resulting vector for theta=0 is equal to the positive X axis. * * @param theta * the theta * * @return new vector in the XZ plane */ public static final XYZ fromXZTheta(float theta) { return new Vec3D((float) Math.cos(theta), 0, (float) Math.sin(theta)); } /** * Creates a new vector from the given angle in the YZ plane. The X * component of the vector will be zero. * * The resulting vector for theta=0 is equal to the positive Y axis. * * @param theta * the theta * * @return new vector in the YZ plane */ public static final XYZ fromYZTheta(float theta) { return new Vec3D(0, (float) Math.cos(theta), (float) Math.sin(theta)); } /** * Constructs a new vector consisting of the largest components of both * vectors. * * @param b * the b * @param a * the a * * @return result as new vector */ public static final Vec3D max(roVec3D a, roVec3D b) { return new Vec3D(Math.max(a.x(), b.x()), Math.max(a.y(), b.y()), Math.max(a.z(), b.z())); } /** * Constructs a new vector consisting of the smallest components of both * vectors. * * @param b * comparing vector * @param a * the a * * @return result as new vector */ public static final Vec3D min(roVec3D a, roVec3D b) { return new Vec3D(MathUtils.min(a.x(), b.x()), MathUtils.min(a.y(), b.y()), MathUtils.min(a.z(), b.z())); } /** * Static factory method. Creates a new random unit vector using the Random * implementation set as default for the {@link MathUtils} class. * * @return a new random normalized unit vector. */ public static final XYZ randomVector() { return randomVector(MathUtils.RND); } /** * Static factory method. Creates a new random unit vector using the given * Random generator instance. I recommend to have a look at the * https://uncommons-maths.dev.java.net library for a good choice of * reliable and high quality random number generators. * * @param rnd * the rnd * * @return a new random normalized unit vector. */ public static final XYZ randomVector(Random rnd) { Vec3D v = new Vec3D(rnd.nextFloat() * 2 - 1, rnd.nextFloat() * 2 - 1, rnd.nextFloat() * 2 - 1); return v.normalize(); } /** X coordinate. */ //@XmlAttribute(required = true) public float x; /** Y coordinate. */ //@XmlAttribute(required = true) public float y; /** Z coordinate. */ //@XmlAttribute(required = true) public float z; /** * Creates a new zero vector. */ public Vec3D() { } /** * Creates a new vector with the given coordinates. * * @param x * the x * @param y * the y * @param z * the z */ public Vec3D(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public Vec3D(float[] v) { this.x = v[0]; this.y = v[1]; this.z = v[2]; } /** * Creates a new vector with the coordinates of the given vector. * * @param v * vector to be copied */ public Vec3D(XYZ v) { this.x = v.x(); this.y = v.y(); this.z = v.z(); } /** * Abs. * * @return the vec3 d */ public final Vec3D abs() { x = Math.abs(x); y = Math.abs(y); z = Math.abs(z); return this; } public final Vec3D plus(float a, float b, float c) { return new Vec3D(x + a, y + b, z + c); } public Vec3D plus(roVec3D v) { return new Vec3D(x + v.x(), y + v.y(), z + v.z()); } public final Vec3D plus(Vec3D v) { return new Vec3D(x + v.x, y + v.y, z + v.z); } /** * Adds vector {a,b,c} and overrides coordinates with result. * * @param a * X coordinate * @param b * Y coordinate * @param c * Z coordinate * * @return itself */ public final XYZ addSelf(float a, float b, float c) { x += a; y += b; z += c; return this; } public final Vec3D addSelf(XYZ v) { x += v.x(); y += v.y(); z += v.z(); return this; } /** * Adds vector v and overrides coordinates with result. * * @param v * vector to add * * @return itself */ public final Vec3D addSelf(Vec3D v) { x += v.x; y += v.y; z += v.z; return this; } public final float angleBetween(XYZ v) { return (float) Math.acos(dot(v)); } public final float angleBetween(XYZ v, boolean forceNormalize) { float theta; if (forceNormalize) { theta = getNormalized().dot(v.getNormalized()); } else { theta = dot(v); } return (float) Math.acos(theta); } /** * Sets all vector components to 0. * * @return itself */ public roVec3D zero() { x = y = z = 0; return this; } public int compareTo(roVec3D v) { if (x == v.x() && y == v.y() && z == v.z()) { return 0; } float a = magSquared(); float b = v.magSquared(); if (a < b) { return -1; } return +1; } // /** // * Forcefully fits the vector in the given AABB. // * // * @param box // * the box // * // * @return itself // */ // public XYZ constrain(AABB box) { // return constrain(box.getMin(), box.getMax()); // } /** * Forcefully fits the vector in the given AABB specified by the 2 given * points. * * @param min * @param max * @return itself */ public XYZ constrain(Vec3D min, Vec3D max) { x = MathUtils.clip(x, min.x, max.x); y = MathUtils.clip(y, min.y, max.y); z = MathUtils.clip(z, min.z, max.z); return this; } public Vec3D copy() { return new Vec3D(this); } public final Vec3D cross(XYZ v) { return new Vec3D(y * v.z() - v.y() * z, z * v.x() - v.z() * x, x * v.y() - v.x() * y); } public final Vec3D cross(Vec3D v) { return new Vec3D(y * v.z - v.y * z, z * v.x - v.z * x, x * v.y - v.x * y); } public final Vec3D crossInto(XYZ v, Vec3D result) { final float vx = v.x(); final float vy = v.y(); final float vz = v.z(); result.x = y * vz - vy * z; result.y = z * vx - vz * x; result.z = x * vy - vx * y; return result; } /** * Calculates cross-product with vector v. The resulting vector is * perpendicular to both the current and supplied vector and overrides the * current. * * @param v * the v * * @return itself */ public final Vec3D crossSelf(Vec3D v) { final float cx = y * v.z - v.y * z; final float cy = z * v.x - v.z * x; z = x * v.y - v.x * y; y = cy; x = cx; return this; } public final float dot(XYZ v) { return x * v.x() + y * v.y() + z * v.z(); } public final float dot(Vec3D v) { return x * v.x + y * v.y + z * v.z; } /** * Returns true if the Object v is of type ReadonlyVec3D and all of the data * members of v are equal to the corresponding data members in this vector. * * @param v * the Object with which the comparison is made * @return true or false */ public boolean equals(Object v) { try { roVec3D vv = (roVec3D) v; return (x == vv.x() && y == vv.y() && z == vv.z()); } catch (NullPointerException e) { return false; } catch (ClassCastException e) { return false; } } /** * Returns true if the Object v is of type ReadonlyVec3D and all of the data * members of v are equal to the corresponding data members in this vector. * * @param v * the vector with which the comparison is made * @return true or false */ public boolean equals(roVec3D v) { try { return (x == v.x() && y == v.y() && z == v.z()); } catch (NullPointerException e) { return false; } } public boolean equalsWithTolerance(roVec3D v, float tolerance) { try { float diff = x - v.x(); if (Float.isNaN(diff)) { return false; } if ((diff < 0 ? -diff : diff) > tolerance) { return false; } diff = y - v.y(); if (Float.isNaN(diff)) { return false; } if ((diff < 0 ? -diff : diff) > tolerance) { return false; } diff = z - v.z(); if (Float.isNaN(diff)) { return false; } if ((diff < 0 ? -diff : diff) > tolerance) { return false; } return true; } catch (NullPointerException e) { return false; } } /** * Replaces the vector components with integer values of their current * values. * * @return itself */ public final XYZ floor() { x = MathUtils.floor(x); y = MathUtils.floor(y); z = MathUtils.floor(z); return this; } /** * Replaces the vector components with the fractional part of their current * values. * * @return itself */ public final XYZ frac() { x -= MathUtils.floor(x); y -= MathUtils.floor(y); z -= MathUtils.floor(z); return this; } public final Vec3D getAbs() { return new Vec3D(this).abs(); } public XYZ getCartesian() { return copy().toCartesian(); } /** * Identifies the closest cartesian axis to this vector. If at leat two * vector components are equal, no unique decision can be made and the * method returns null. * * @return Axis enum or null */ public final Axis getClosestAxis() { float ax = Math.abs(x); float ay = Math.abs(y); float az = Math.abs(z); if (ax > ay && ax > az) { return Axis.X; } if (ay > ax && ay > az) { return Axis.Y; } if (az > ax && az > ay) { return Axis.Z; } return null; } public final float getComponent(Axis id) { switch (id) { case X: return x; case Y: return y; case Z: return z; } throw new IllegalArgumentException(); } public final float getComponent(int id) { switch (id) { case 0: return x; case 1: return y; case 2: return z; } throw new IllegalArgumentException("index must be 0, 1 or 2"); } // /* // * (non-Javadoc) // * // * @see toxi.geom.ReadonlyVec3D#getConstrained(toxi.geom.AABB) // */ // public final XYZ getConstrained(AABB box) { // return new Vec3D(this).constrain(box); // } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getFloored() */ public final XYZ getFloored() { return new Vec3D(this).floor(); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getFrac() */ public final XYZ getFrac() { return new Vec3D(this).frac(); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getInverted() */ public final Vec3D getInverted() { return new Vec3D(-x, -y, -z); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getLimited(float) */ public final XYZ getLimited(float lim) { if (magSquared() > lim * lim) { return getNormalizedTo(lim); } return new Vec3D(this); } public XYZ getMapped(ScaleMap map) { return new Vec3D((float) map.getClippedValueFor(x), (float) map.getClippedValueFor(y), (float) map.getClippedValueFor(z)); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getNormalizedTo(float) */ public final Vec3D getNormalizedTo(float len) { return new Vec3D(this).normalizeTo(len); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getReciprocal() */ public final XYZ getReciprocal() { return copy().reciprocal(); } public final XYZ getReflected(roVec3D normal) { return copy().reflect(normal); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getRotatedAroundAxis(toxi.geom.Vec3D, float) */ public final XYZ getRotatedAroundAxis(roVec3D axis, float theta) { return new Vec3D(this).rotateAroundAxis(axis, theta); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getRotatedX(float) */ public final XYZ getRotatedX(float theta) { return new Vec3D(this).rotateX(theta); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getRotatedY(float) */ public final XYZ getRotatedY(float theta) { return new Vec3D(this).rotateY(theta); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getRotatedZ(float) */ public final XYZ getRotatedZ(float theta) { return new Vec3D(this).rotateZ(theta); } public XYZ getRoundedTo(float prec) { return copy().roundTo(prec); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#getSignum() */ public final Vec3D getSignum() { return new Vec3D(this).signum(); } public XYZ getSpherical() { return copy().toSpherical(); } /** * Returns a hash code value based on the data values in this object. Two * different Vec3D objects with identical data values (i.e., Vec3D.equals * returns true) will return the same hash code value. Two objects with * different data members may return the same hash value, although this is * not likely. * * @return the integer hash code value */ public int hashCode() { long bits = 1L; bits = 31L * bits + VecMathUtil.floatToIntBits(x); bits = 31L * bits + VecMathUtil.floatToIntBits(y); bits = 31L * bits + VecMathUtil.floatToIntBits(z); return (int) (bits ^ (bits >> 32)); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#headingXY() */ public final float headingXY() { return (float) Math.atan2(y, x); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#headingXZ() */ public final float headingXZ() { return (float) Math.atan2(z, x); } /* * (non-Javadoc) * * @see toxi.geom.ReadonlyVec3D#headingYZ() */ public final float headingYZ() { return (float) Math.atan2(y, z); } public roVec3D immutable() { return this; } /** * Interpolates the vector towards the given target vector, using linear * interpolation. * * @param v * target vector * @param f * interpolation factor (should be in the range 0..1) * * @return itself, result overrides current vector */ public final Vec3D interpolateToSelf(roVec3D v, float f) { x += (v.x() - x) * f; y += (v.y() - y) * f; z += (v.z() - z) * f; return this; } /** * Interpolates the vector towards the given target vector, using the given * {@link InterpolateStrategy}. * * @param v * target vector * @param f * interpolation factor (should be in the range 0..1) * @param s * InterpolateStrategy instance * * @return itself, result overrides current vector */ public final XYZ interpolateToSelf(roVec3D v, float f, InterpolateStrategy s) { x = s.interpolate(x, v.x(), f); y = s.interpolate(y, v.y(), f); z = s.interpolate(z, v.z(), f); return this; } /** * Scales vector uniformly by factor -1 ( v = -v ), overrides coordinates * with result. * * @return itself */ public final XYZ invert() { x *= -1; y *= -1; z *= -1; return this; } public boolean isInAABB(Vec3D boxOrigin, Vec3D boxExtent) { float w = boxExtent.x; if (x < boxOrigin.x - w || x > boxOrigin.x + w) { return false; } w = boxExtent.y; if (y < boxOrigin.y - w || y > boxOrigin.y + w) { return false; } w = boxExtent.z; if (z < boxOrigin.z - w || z > boxOrigin.z + w) { return false; } return true; } public final boolean isMajorAxis(float tol) { float ax = Math.abs(x); float ay = Math.abs(y); float az = Math.abs(z); float itol = 1 - tol; if (ax > itol) { if (ay < tol) { return (az < tol); } } else if (ay > itol) { if (ax < tol) { return (az < tol); } } else if (az > itol) { if (ax < tol) { return (ay < tol); } } return false; } public final boolean isZeroVector() { return Math.abs(x) < MathUtils.EPS && Math.abs(y) < MathUtils.EPS && Math.abs(z) < MathUtils.EPS; } /** * Add random jitter to the vector in the range -j ... +j using the default * {@link Random} generator of {@link MathUtils}. * * @param j * the j * * @return the vec3 d */ public final XYZ jitter(float j) { return jitter(j, j, j); } /** * Adds random jitter to the vector in the range -j ... +j using the default * {@link Random} generator of {@link MathUtils}. * * @param jx * maximum x jitter * @param jy * maximum y jitter * @param jz * maximum z jitter * * @return itself */ public final XYZ jitter(float jx, float jy, float jz) { x += MathUtils.normalizedRandom() * jx; y += MathUtils.normalizedRandom() * jy; z += MathUtils.normalizedRandom() * jz; return this; } public final XYZ jitter(Random rnd, float j) { return jitter(rnd, j, j, j); } public final XYZ jitter(Random rnd, float jx, float jy, float jz) { x += MathUtils.normalizedRandom(rnd) * jx; y += MathUtils.normalizedRandom(rnd) * jy; z += MathUtils.normalizedRandom(rnd) * jz; return this; } public final XYZ jitter(Random rnd, Vec3D jitterVec) { return jitter(rnd, jitterVec.x, jitterVec.y, jitterVec.z); } /** * Adds random jitter to the vector in the range defined by the given vector * components and using the default {@link Random} generator of * {@link MathUtils}. * * @param jitterVec * the jitter vec * * @return itself */ public final XYZ jitter(Vec3D jitterVec) { return jitter(jitterVec.x, jitterVec.y, jitterVec.z); } /** * Limits the vector's magnitude to the length given. * * @param lim * new maximum magnitude * * @return itself */ public final Vec3D limit(float lim) { if (magSquared() > lim * lim) { return normalize().scaleSelf(lim); } return this; } /** * Max self. * * @param b * the b * * @return the vec3 d */ public final XYZ maxSelf(XYZ b) { x = Math.max(x, b.x()); y = Math.max(y, b.y()); z = Math.max(z, b.z()); return this; } /** * Min self. * * @param b * the b * * @return the vec3 d */ public final XYZ minSelf(XYZ b) { x = MathUtils.min(x, b.x()); y = MathUtils.min(y, b.y()); z = MathUtils.min(z, b.z()); return this; } /** * Applies a uniform modulo operation to the vector, using the same base for * all components. * * @param base * the base * * @return itself */ public final XYZ modSelf(float base) { x %= base; y %= base; z %= base; return this; } /** * Calculates modulo operation for each vector component separately. * * @param bx * the bx * @param by * the by * @param bz * the bz * * @return itself */ public final XYZ modSelf(float bx, float by, float bz) { x %= bx; y %= by; z %= bz; return this; } /** * Normalizes the vector so that its magnitude = 1. * * @return itself */ public final Vec3D normalize() { float mag = (float) Math.sqrt(x * x + y * y + z * z); if (mag > 0) { mag = 1f / mag; x *= mag; y *= mag; z *= mag; } return this; } /** * Normalizes the vector to the given length. * * @param len * desired length * @return itself */ public final Vec3D normalizeTo(float len) { float mag = (float) Math.sqrt(x * x + y * y + z * z); if (mag > 0) { mag = len / mag; x *= mag; y *= mag; z *= mag; } return this; } /** * Replaces the vector components with their multiplicative inverse. * * @return itself */ public final Vec3D reciprocal() { x = 1f / x; y = 1f / y; z = 1f / z; return this; } public final XYZ reflect(roVec3D normal) { return set(normal.scale(this.dot(normal) * 2).subSelf(this)); } /** * Rotates the vector around the giving axis. * * @param axis * rotation axis vector * @param theta * rotation angle (in radians) * * @return itself */ public final XYZ rotateAroundAxis(XYZ axis, float theta) { final float ax = axis.x(); final float ay = axis.y(); final float az = axis.z(); final float ux = ax * x; final float uy = ax * y; final float uz = ax * z; final float vx = ay * x; final float vy = ay * y; final float vz = ay * z; final float wx = az * x; final float wy = az * y; final float wz = az * z; final double si = Math.sin(theta); final double co = Math.cos(theta); float xx = (float) (ax * (ux + vy + wz) + (x * (ay * ay + az * az) - ax * (vy + wz)) * co + (-wy + vz) * si); float yy = (float) (ay * (ux + vy + wz) + (y * (ax * ax + az * az) - ay * (ux + wz)) * co + (wx - uz) * si); float zz = (float) (az * (ux + vy + wz) + (z * (ax * ax + ay * ay) - az * (ux + vy)) * co + (-vx + uy) * si); x = xx; y = yy; z = zz; return this; } /** * Rotates the vector by the given angle around the X axis. * * @param theta * the theta * * @return itself */ public final XYZ rotateX(float theta) { final float co = (float) Math.cos(theta); final float si = (float) Math.sin(theta); final float zz = co * z - si * y; y = si * z + co * y; z = zz; return this; } /** * Rotates the vector by the given angle around the Y axis. * * @param theta * the theta * * @return itself */ public final XYZ rotateY(float theta) { final float co = (float) Math.cos(theta); final float si = (float) Math.sin(theta); final float xx = co * x - si * z; z = si * x + co * z; x = xx; return this; } /** * Rotates the vector by the given angle around the Z axis. * * @param theta * the theta * * @return itself */ public final XYZ rotateZ(float theta) { final float co = (float) Math.cos(theta); final float si = (float) Math.sin(theta); final float xx = co * x - si * y; y = si * x + co * y; x = xx; return this; } public XYZ roundTo(float prec) { x = MathUtils.roundTo(x, prec); y = MathUtils.roundTo(y, prec); z = MathUtils.roundTo(z, prec); return this; } public XYZ scale(float a, float b, float c) { return new Vec3D(x * a, y * b, z * c); } public XYZ scale(roVec3D s) { return new Vec3D(x * s.x(), y * s.y(), z * s.z()); } public XYZ scale(Vec3D s) { return new Vec3D(x * s.x, y * s.y, z * s.z); } /** * Scales vector uniformly and overrides coordinates with result. * * @param s * scale factor * * @return itself */ public Vec3D scaleSelf(float s) { x *= s; y *= s; z *= s; return this; } /** * Scales vector non-uniformly by vector {a,b,c} and overrides coordinates * with result. * * @param a * scale factor for X coordinate * @param b * scale factor for Y coordinate * @param c * scale factor for Z coordinate * * @return itself */ public XYZ scaleSelf(float a, float b, float c) { x *= a; y *= b; z *= c; return this; } public XYZ scaleSelf(roVec3D s) { x *= s.x(); y *= s.y(); z *= s.z(); return this; } /** * Scales vector non-uniformly by vector v and overrides coordinates with * result. * * @param s * scale vector * * @return itself */ public Vec3D scaleSelf(Vec3D s) { x *= s.x; y *= s.y; z *= s.z; return this; } /** * Overrides coordinates with the given values. * * @param x * the x * @param y * the y * @param z * the z * * @return itself */ public Vec3D set(float x, float y, float z) { this.x = x; this.y = y; this.z = z; return this; } public Vec3D set(XYZ v) { x = v.x(); y = v.y(); z = v.z(); return this; } /** * Overrides coordinates with the ones of the given vector. * * @param v * vector to be copied * * @return itself */ public Vec3D set(Vec3D v) { x = v.x; y = v.y; z = v.z; return this; } public final XYZ setComponent(Axis id, float val) { switch (id) { case X: x = val; break; case Y: y = val; break; case Z: z = val; break; } return this; } public final XYZ setComponent(int id, float val) { switch (id) { case 0: x = val; break; case 1: y = val; break; case 2: z = val; break; } return this; } public XYZ setX(float x) { this.x = x; return this; } /** * Overrides XY coordinates with the ones of the given 2D vector. * * @param v * 2D vector * * @return itself */ public XYZ setXY(Vec2D v) { x = v.x; y = v.y; return this; } public XYZ setY(float y) { this.y = y; return this; } public XYZ setZ(float z) { this.z = z; return this; } public XYZ shuffle(int iterations) { float t; for (int i = 0; i < iterations; i++) { switch (MathUtils.random(3)) { case 0: t = x; x = y; y = t; break; case 1: t = x; x = z; z = t; break; case 2: t = y; y = z; z = t; break; } } return this; } /** * Replaces all vector components with the signum of their original values. * In other words if a components value was negative its new value will be * -1, if zero => 0, if positive => +1 * * @return itself */ public Vec3D signum() { x = (x < 0 ? -1 : x == 0 ? 0 : 1); y = (y < 0 ? -1 : y == 0 ? 0 : 1); z = (z < 0 ? -1 : z == 0 ? 0 : 1); return this; } /** * Rounds the vector to the closest major axis. Assumes the vector is * normalized. * * @return itself */ public final XYZ snapToAxis() { if (Math.abs(x) < 0.5f) { x = 0; } else { x = x < 0 ? -1 : 1; y = z = 0; } if (Math.abs(y) < 0.5f) { y = 0; } else { y = y < 0 ? -1 : 1; x = z = 0; } if (Math.abs(z) < 0.5f) { z = 0; } else { z = z < 0 ? -1 : 1; x = y = 0; } return this; } public final XYZ sub(float a, float b, float c) { return new Vec3D(x - a, y - b, z - c); } public final Vec3D sub(roVec3D v) { return new Vec3D(x - v.x(), y - v.y(), z - v.z()); } /** * Subtracts vector {a,b,c} and overrides coordinates with result. * * @param a * X coordinate * @param b * Y coordinate * @param c * Z coordinate * * @return itself */ public final XYZ subSelf(float a, float b, float c) { x -= a; y -= b; z -= c; return this; } public final Vec3D subSelf(roVec3D v) { x -= v.x(); y -= v.y(); z -= v.z(); return this; } /** * Subtracts vector v and overrides coordinates with result. * * @param v * vector to be subtracted * * @return itself */ public final Vec3D subSelf(Vec3D v) { x -= v.x; y -= v.y; z -= v.z; return this; } public final Vec2D to2DXY() { return new Vec2D(x, y); } public final Vec2D to2DXZ() { return new Vec2D(x, z); } public final Vec2D to2DYZ() { return new Vec2D(y, z); } public Vec4D to4D() { return new Vec4D(x, y, z, 1); } public Vec4D to4D(float w) { return new Vec4D(x, y, z, w); } public float[] toArray3() { return new float[] { x, y, z }; } public float[] toArray4(float w) { return new float[] { x, y, z, w }; } public final XYZ toCartesian() { final float a = (float) (x * Math.cos(z)); final float xx = (float) (a * Math.cos(y)); x = xx; final float yy = (float) (x * Math.sin(z)); y = yy; final float zz = (float) (a * Math.sin(y)); z = zz; return this; } public final XYZ toSpherical() { final float xx = Math.abs(x) <= MathUtils.EPS ? MathUtils.EPS : x; final float zz = z; final float radius = (float) Math.sqrt((xx * xx) + (y * y) + (zz * zz)); z = (float) Math.asin(y / radius); y = (float) Math.atan(zz / xx) + (xx < 0.0 ? MathUtils.PI : 0); x = radius; return this; } public static Vec3D v(float x, float y, float z) { return new Vec3D(x, y, z); } public String toString() { final StringBuffer sb = new StringBuffer(32); // sb.append("{x:").append(x).append(", y:").append(y).append(", z:") // .append(z).append("}"); sb.append("(").append(x).append(",").append(y).append(",") .append(z).append(')'); return sb.toString(); } public final float x() { return x; } public final float y() { return y; } public final float z() { return z; } }