/* * __ .__ .__ ._____. * _/ |_ _______ __|__| ____ | | |__\_ |__ ______ * \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/ * | | ( <_> > <| \ \___| |_| || \_\ \\___ \ * |__| \____/__/\_ \__|\___ >____/__||___ /____ > * \/ \/ \/ \/ * * 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; public class Vec4D implements ReadonlyVec4D, Cloneable { /** X coordinate */ //@XmlAttribute(required = true) public float x; /** Y coordinate */ //@XmlAttribute(required = true) public float y; /** Z coordinate */ //@XmlAttribute(required = true) public float z; /** W coordinate (weight) */ //@XmlAttribute(required = true) public float w; public Vec4D() { } public Vec4D(float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; } public Vec4D(roVec3D v, float w) { this.x = v.x(); this.y = v.y(); this.z = v.z(); this.w = w; } public Vec4D(ReadonlyVec4D v) { set(v); } public Vec4D abs() { x = Math.abs(x); y = Math.abs(y); z = Math.abs(z); w = Math.abs(w); return this; } public final Vec4D add(ReadonlyVec4D v) { return new Vec4D(x + v.x(), y + v.y(), z + v.z(), w + v.w()); } public final Vec4D addScaled(ReadonlyVec4D t, float s) { return new Vec4D(s * t.x(), s * t.y(), s * t.z(), s * t.w()); } public final Vec4D addScaledSelf(ReadonlyVec4D t, float s) { x += s * t.x(); y += s * t.y(); z += s * t.z(); w += s * t.w(); return this; } public final Vec4D addSelf(ReadonlyVec4D v) { this.x += v.x(); this.y += v.y(); this.z += v.z(); this.w += v.w(); return this; } public final Vec4D addXYZ(float xx, float yy, float zz) { return new Vec4D(x + xx, y + yy, z + zz, w); } public final Vec4D addXYZ(roVec3D v) { return new Vec4D(x + v.x(), y + v.y(), z + v.z(), w); } public final Vec4D addXYZSelf(float xx, float yy, float zz) { x += xx; y += yy; z += zz; return this; } public final Vec4D addXYZSelf(roVec3D v) { this.x += v.x(); this.y += v.y(); this.z += v.z(); return this; } /** * Returns the (4-space) angle in radians between this vector and the vector * parameter; the return value is constrained to the range [0,PI]. * * @param v * the other vector * @return the angle in radians in the range [0,PI] */ public final float angleBetween(ReadonlyVec4D v) { double vDot = dot(v) / (magnitude() * v.magnitude()); if (vDot < -1.0) { vDot = -1.0; } if (vDot > 1.0) { vDot = 1.0; } return (float) (Math.acos(vDot)); } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } } public int compareTo(ReadonlyVec4D v) { if (x == v.x() && y == v.y() && z == v.z() && w == v.w()) { return 0; } float a = magSquared(); float b = v.magSquared(); if (a < b) { return -1; } return +1; } public final Vec4D copy() { return new Vec4D(this); } public final float distanceTo(ReadonlyVec4D v) { if (v != null) { final float dx = x - v.x(); final float dy = y - v.y(); final float dz = z - v.z(); final float dw = w - v.z(); return (float) Math.sqrt(dx * dx + dy * dy + dz * dz + dw * dw); } else { return Float.NaN; } } public final float distanceToSquared(ReadonlyVec4D v) { if (v != null) { final float dx = x - v.x(); final float dy = y - v.y(); final float dz = z - v.z(); final float dw = w - v.z(); return dx * dx + dy * dy + dz * dz + dw * dw; } else { return Float.NaN; } } public final float dot(ReadonlyVec4D v) { return (x * v.x() + y * v.y() + z * v.z() + w * v.w()); } /** * Returns true if the Object v is of type ReadonlyVec4D 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 { ReadonlyVec4D vv = (ReadonlyVec4D) v; return (x == vv.x() && y == vv.y() && z == vv.z() && w == vv.w()); } catch (NullPointerException | ClassCastException e) { return false; } } /** * Returns true if the Object v is of type Vec4D 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(ReadonlyVec4D v) { try { return (x == v.x() && y == v.y() && z == v.z() && w == v.w()); } catch (NullPointerException e) { return false; } } public boolean equalsWithTolerance(ReadonlyVec4D 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; } diff = w - v.w(); if (Float.isNaN(diff)) { return false; } return !((diff < 0 ? -diff : diff) > tolerance); } catch (NullPointerException e) { return false; } } public Vec4D getAbs() { return copy().abs(); } public final Vec4D getInvertedXYZ() { return copy().invertXYZ(); } public Vec4D getMapped(ScaleMap map) { return new Vec4D((float) map.getClippedValueFor(x), (float) map.getClippedValueFor(y), (float) map.getClippedValueFor(z), (float) map.getClippedValueFor(w)); } public Vec4D getMappedXYZ(ScaleMap map) { return new Vec4D((float) map.getClippedValueFor(x), (float) map.getClippedValueFor(y), (float) map.getClippedValueFor(z), w); } public Vec4D getNormalized() { return copy().normalize(); } public Vec4D getNormalizedTo(float len) { return copy().normalizeTo(len); } public Vec4D getRotatedAroundAxis(roVec3D axis, float theta) { return copy().rotateAroundAxis(axis, theta); } public Vec4D getRotatedX(float theta) { return copy().rotateX(theta); } public Vec4D getRotatedY(float theta) { return copy().rotateY(theta); } public Vec4D getRotatedZ(float theta) { return copy().rotateZ(theta); } public Vec4D getRoundedXYZTo(float prec) { return copy().roundXYZTo(prec); } public Vec4D getUnweighted() { return copy().unweight(); } public Vec4D getWeighted() { return copy().weight(); } /** * Returns a hash code value based on the data values in this object. Two * different Vec4D objects with identical data values (i.e., Vec4D.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); bits = 31L * bits + VecMathUtil.floatToIntBits(w); return (int) (bits ^ (bits >> 32)); } public final Vec4D interpolateTo(ReadonlyVec4D v, float t) { return copy().interpolateToSelf(v, t); } public final Vec4D interpolateTo(ReadonlyVec4D v, float f, InterpolateStrategy s) { return new Vec4D(s.interpolate(x, v.x(), f), s.interpolate(y, v.y(), f), s.interpolate(z, v.z(), f), s.interpolate(w, v.w(), f)); } public final Vec4D interpolateToSelf(ReadonlyVec4D v, float t) { this.x += (v.x() - x) * t; this.y += (v.y() - y) * t; this.z += (v.z() - z) * t; this.w += (v.w() - w) * t; return this; } public final Vec4D interpolateToSelf(ReadonlyVec4D 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); w = s.interpolate(w, v.w(), f); return this; } public final Vec4D invertXYZ() { this.x *= -1; this.y *= -1; this.z *= -1; return this; } public final boolean isZeroVector() { return Math.abs(x) < MathUtils.EPS && Math.abs(y) < MathUtils.EPS && Math.abs(z) < MathUtils.EPS && Math.abs(w) < MathUtils.EPS; } public final float magnitude() { return (float) Math.sqrt(x * x + y * y + z * z + w * w); } public final float magSquared() { return x * x + y * y + z * z + w * w; } /** * Normalizes the vector so that its magnitude = 1. * * @return itself */ public final Vec4D normalize() { float mag = (float) Math.sqrt(x * x + y * y + z * z); if (mag > 0) { mag = 1f / mag; x *= mag; y *= mag; z *= mag; w *= mag; } return this; } /** * Normalizes the vector to the given length. * * @param len * desired length * @return itself */ public final Vec4D 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; w *= mag; } return this; } /** * Rotates the vector around the giving axis. * * @param axis * rotation axis vector * @param theta * rotation angle (in radians) * * @return itself */ public final Vec4D rotateAroundAxis(roVec3D 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 Vec4D 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 Vec4D 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 Vec4D 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 Vec4D roundXYZTo(float prec) { x = MathUtils.roundTo(x, prec); y = MathUtils.roundTo(y, prec); z = MathUtils.roundTo(z, prec); return this; } public final Vec4D scale(float s) { return new Vec4D(x * s, y * s, z * s, w * s); } public final Vec4D scale(float xx, float yy, float zz, float ww) { return new Vec4D(x * xx, y * yy, z * zz, w * ww); } public Vec4D scale(ReadonlyVec4D s) { return copy().scaleSelf(s); } public final Vec4D scaleSelf(float s) { this.x *= s; this.y *= s; this.z *= s; this.w *= s; return this; } public Vec4D scaleSelf(ReadonlyVec4D s) { this.x *= s.x(); this.y *= s.y(); this.z *= s.z(); this.w *= s.w(); return this; } public final Vec4D scaleXYZSelf(float s) { this.x *= s; this.y *= s; this.z *= s; return this; } public final Vec4D scaleXYZSelf(float xscale, float yscale, float zscale) { this.x *= xscale; this.y *= yscale; this.z *= zscale; return this; } /** * Overrides coordinates with the given values. * * @param x * the x * @param y * the y * @param z * the z * @param w * the w * * @return itself */ public Vec4D set(float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } public final Vec4D set(ReadonlyVec4D v) { this.x = v.x(); this.y = v.y(); this.z = v.z(); this.w = v.w(); return this; } public Vec4D setW(float w) { this.w = w; return this; } public Vec4D setX(float x) { this.x = x; return this; } public final Vec4D setXYZ(roVec3D v) { this.x = v.x(); this.y = v.y(); this.z = v.z(); return this; } public Vec4D setY(float y) { this.y = y; return this; } public Vec4D setZ(float z) { this.z = z; return this; } public final Vec4D sub(ReadonlyVec4D v) { return new Vec4D(x - v.x(), y - v.y(), z - v.z(), w - v.w()); } public final Vec4D subSelf(ReadonlyVec4D v) { this.x -= v.x(); this.y -= v.y(); this.z -= v.z(); this.w -= v.w(); return this; } public final Vec4D subXYZ(float xx, float yy, float zz) { return new Vec4D(x - xx, y - yy, z - zz, w); } public final Vec4D subXYZ(roVec3D v) { return new Vec4D(x - v.x(), y - v.y(), z - v.z(), w); } public final Vec4D subXYZSelf(float xx, float yy, float zz) { this.x -= xx; this.y -= yy; this.z -= zz; return this; } public final Vec4D subXYZSelf(roVec3D v) { this.x -= v.x(); this.y -= v.y(); this.z -= v.z(); return this; } public final Vec3D to3D() { return new Vec3D(x, y, z); } public float[] toArray() { return new float[] { x, y, z, w }; } public String toString() { return "[x=" + x + ", y=" + y + ", z=" + z + ", w=" + w + ']'; } public final Vec4D translate(float xx, float yy, float zz) { this.x += w * xx; this.y += w * yy; this.z += w * zz; return this; } /** * Divides each coordinate bythe weight with and sets the coordinate to the * newly calculatd ones. */ public final Vec4D unweight() { float iw = Math.abs(w) > MathUtils.EPS ? 1f / w : 0; x *= iw; y *= iw; z *= iw; return this; } public final Vec3D unweightInto(Vec3D out) { float iw = Math.abs(w) > MathUtils.EPS ? 1f / w : 0; out.x = x * iw; out.y = y * iw; out.z = z * iw; return out; } /** * @return the w */ public final float w() { return w; } /** * Multiplies the weight with each coordinate and sets the coordinate to the * newly calculatd ones. * * @return itself */ public final Vec4D weight() { x *= w; y *= w; z *= w; return this; } public final XYZ weightInto(Vec3D out) { out.x = x * w; out.y = y * w; out.z = z * w; return out; } /** * @return the x */ public final float x() { return x; } /** * @return the y */ public final float y() { return y; } /** * @return the z */ public final float z() { return z; } }