458 lines
8.1 KiB
Java
458 lines
8.1 KiB
Java
package xyz.marsavic.gfxlab;
|
|
|
|
import xyz.marsavic.geometry.Vector;
|
|
import xyz.marsavic.gfxlab.elements.Immutable;
|
|
import xyz.marsavic.random.RNG;
|
|
import xyz.marsavic.utils.Numeric;
|
|
|
|
import java.util.function.DoubleBinaryOperator;
|
|
import java.util.function.DoubleUnaryOperator;
|
|
|
|
@Immutable
|
|
public class Vec3 {
|
|
public static final Vec3 ZERO = xyz(0, 0, 0);
|
|
public static final Vec3 ONES = xyz(1, 1, 1);
|
|
public static final Vec3 EX = xyz(1, 0, 0);
|
|
public static final Vec3 EY = xyz(0, 1, 0);
|
|
public static final Vec3 EZ = xyz(0, 0, 1);
|
|
public static final Vec3 EXY = xyz(1, 1, 0);
|
|
public static final Vec3 EYZ = xyz(0, 1, 1);
|
|
public static final Vec3 EZX = xyz(1, 0, 1);
|
|
public static final Vec3 EXYZ = xyz(1, 1, 1);
|
|
public static final Vec3[] E = {EX, EY, EZ};
|
|
|
|
public static final Vec3 P012 = xyz(0, 1, 2);
|
|
public static final Vec3 P021 = xyz(0, 2, 1);
|
|
public static final Vec3 P102 = xyz(1, 0, 2);
|
|
public static final Vec3 P120 = xyz(1, 2, 0);
|
|
public static final Vec3 P201 = xyz(2, 0, 1);
|
|
public static final Vec3 P210 = xyz(2, 1, 0);
|
|
public static final Vec3[] PERMUTATIONS = {P012, P021, P102, P120, P201, P210};
|
|
|
|
private final double x, y, z;
|
|
|
|
|
|
|
|
public Vec3(double x, double y, double z) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
}
|
|
|
|
|
|
public static Vec3 xyz(double x, double y, double z) {
|
|
return new Vec3(x, y, z);
|
|
}
|
|
|
|
|
|
public static Vec3 fromArray(int[] i) {
|
|
return xyz(i[0], i[1], i[2]);
|
|
}
|
|
|
|
|
|
public static Vec3 fromArray(double[] i) {
|
|
return xyz(i[0], i[1], i[2]);
|
|
}
|
|
|
|
|
|
public static Vec3 xp(double x, Vector p) {
|
|
return xyz(x, p.x(), p.y());
|
|
}
|
|
|
|
|
|
public static Vec3 yp(double y, Vector p) {
|
|
return xyz(p.x(), y, p.y());
|
|
}
|
|
|
|
|
|
public static Vec3 zp(double z, Vector p) {
|
|
return xyz(p.x(), p.y(), z);
|
|
}
|
|
|
|
|
|
|
|
|
|
public double x() {
|
|
return x;
|
|
}
|
|
|
|
|
|
public double y() {
|
|
return y;
|
|
}
|
|
|
|
|
|
public double z() {
|
|
return z;
|
|
}
|
|
|
|
|
|
public double get(int i) {
|
|
return switch (i) {
|
|
case 0 -> x();
|
|
case 1 -> y();
|
|
case 2 -> z();
|
|
default -> throw new IllegalArgumentException();
|
|
};
|
|
}
|
|
|
|
|
|
public double[] toArray() {
|
|
return new double[] { x(), y(), z() };
|
|
}
|
|
|
|
|
|
public int[] toArrayInt() {
|
|
return new int[] { (int) x(), (int) y(), (int) z() };
|
|
}
|
|
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (o == null) {
|
|
return false;
|
|
}
|
|
if (o instanceof Vec3 vec3) {
|
|
return
|
|
(vec3.x() == x()) &&
|
|
(vec3.y() == y()) &&
|
|
(vec3.z() == z())
|
|
;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
long temp =
|
|
Double.doubleToLongBits(x) +
|
|
31 * Double.doubleToLongBits(y) +
|
|
997 * Double.doubleToLongBits(z);
|
|
return (int) (temp ^ (temp >>> 32));
|
|
}
|
|
|
|
public Vec3 add(Vec3 o) {
|
|
return xyz(x() + o.x(), y() + o.y(), z() + o.z());
|
|
}
|
|
|
|
|
|
public Vec3 sub(Vec3 o) {
|
|
return xyz(x() - o.x(), y() - o.y(), z() - o.z());
|
|
}
|
|
|
|
|
|
public Vec3 mul(double k) {
|
|
return xyz(x() * k, y() * k, z() * k);
|
|
}
|
|
|
|
|
|
public Vec3 mul(Vec3 o) {
|
|
return xyz(x() * o.x(), y() * o.y(), z() * o.z());
|
|
}
|
|
|
|
|
|
public Vec3 div(double k) {
|
|
return xyz(x() / k, y() / k, z() / k);
|
|
}
|
|
|
|
|
|
public Vec3 div(Vec3 o) {
|
|
return xyz(x() / o.x(), y() / o.y(), z() / o.z());
|
|
}
|
|
|
|
|
|
|
|
public double lengthSquared() {
|
|
return x() * x() + y() * y() + z() * z();
|
|
}
|
|
|
|
|
|
public double length() {
|
|
return Math.sqrt(lengthSquared());
|
|
}
|
|
|
|
|
|
public Vec3 normalized_() {
|
|
return div(length());
|
|
}
|
|
|
|
|
|
public Vec3 normalizedTo(double l) {
|
|
return mul(l / length());
|
|
}
|
|
|
|
|
|
public Vec3 inverse() {
|
|
return xyz(-x(), -y(), -z());
|
|
}
|
|
|
|
|
|
public double dot(Vec3 o) {
|
|
return x() * o.x() + y() * o.y() + z() * o.z();
|
|
}
|
|
|
|
|
|
public Vec3 cross(Vec3 o) {
|
|
return xyz(
|
|
y() * o.z() - z() * o.y(),
|
|
z() * o.x() - x() * o.z(),
|
|
x() * o.y() - y() * o.x()
|
|
);
|
|
}
|
|
|
|
|
|
public Vec3 projection(Vec3 d) {
|
|
return d.mul(this.dot(d) / d.lengthSquared());
|
|
}
|
|
|
|
|
|
public Vec3 projectionN(Vec3 d_) {
|
|
return d_.mul(this.dot(d_));
|
|
}
|
|
|
|
|
|
public Vec3 rejection(Vec3 d) {
|
|
return this.sub(projection(d));
|
|
}
|
|
|
|
|
|
public Vec3 rejectionN(Vec3 d_) {
|
|
return this.sub(projectionN(d_));
|
|
}
|
|
|
|
|
|
public double min() {
|
|
return Math.min(Math.min(x(), y()), z());
|
|
}
|
|
|
|
|
|
public double max() {
|
|
return Math.max(Math.max(x(), y()), z());
|
|
}
|
|
|
|
|
|
public Vec3 minIndicator() {
|
|
if (x() < y()) {
|
|
return x() < z() ? Vec3.EX : Vec3.EZ;
|
|
} else {
|
|
return y() < z() ? Vec3.EY : Vec3.EZ;
|
|
}
|
|
}
|
|
|
|
|
|
public Vec3 maxIndicator() {
|
|
if (x() > y()) {
|
|
return x() > z() ? Vec3.EX : Vec3.EZ;
|
|
} else {
|
|
return y() > z() ? Vec3.EY : Vec3.EZ;
|
|
}
|
|
}
|
|
|
|
|
|
public int minIndex() {
|
|
if (x() < y()) {
|
|
return x() < z() ? 0 : 2;
|
|
} else {
|
|
return y() < z() ? 1 : 2;
|
|
}
|
|
}
|
|
|
|
|
|
public int maxIndex() {
|
|
if (x() > y()) {
|
|
return x() > z() ? 0 : 2;
|
|
} else {
|
|
return y() > z() ? 1 : 2;
|
|
}
|
|
}
|
|
|
|
|
|
public Vec3 ranks() {
|
|
if (x() < y()) {
|
|
if (y() < z()) {
|
|
return P012;
|
|
} else {
|
|
if (x() < z()) {
|
|
return P021;
|
|
} else {
|
|
return P201;
|
|
}
|
|
}
|
|
} else {
|
|
if (x() < z()) {
|
|
return P102;
|
|
} else {
|
|
if (y() < z()) {
|
|
return P120;
|
|
} else {
|
|
return P210;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public Vec3 sign() {
|
|
return xyz(Numeric.sign(x()), Numeric.sign(y()), Numeric.sign(z()));
|
|
}
|
|
|
|
|
|
public Vec3 abs() {
|
|
return xyz(Math.abs(x()), Math.abs(y()), Math.abs(z()));
|
|
}
|
|
|
|
|
|
public boolean allZero() {
|
|
return (x() == 0) && (y() == 0) && (z() == 0);
|
|
}
|
|
|
|
public boolean anyZero() {
|
|
return (x() == 0) || (y() == 0) || (z() == 0);
|
|
}
|
|
|
|
|
|
public Vec3 floor() {
|
|
return xyz(Math.floor(x()), Math.floor(y()), Math.floor(z()));
|
|
}
|
|
|
|
public Vec3 floor(Vec3 d) {
|
|
return this.div(d).floor().mul(d);
|
|
}
|
|
|
|
public Vec3 ceil() {
|
|
return xyz(Math.ceil(x()), Math.ceil(y()), Math.ceil(z()));
|
|
}
|
|
|
|
public Vec3 ceil(Vec3 d) {
|
|
return this.div(d).ceil().mul(d);
|
|
}
|
|
|
|
public Vec3 round() {
|
|
return xyz(Math.round(x()), Math.round(y()), Math.round(z()));
|
|
}
|
|
|
|
public Vec3 round(Vec3 d) {
|
|
return this.div(d).round().mul(d);
|
|
}
|
|
|
|
/** Snaps each component to floor or ceil, depending on whether the corresponding component of s is not one. */
|
|
|
|
public Vec3 snap(Vec3 s) {
|
|
return Vec3.xyz (
|
|
s.x() != 1 ? Math.floor(x()) : Math.ceil(x()),
|
|
s.y() != 1 ? Math.floor(y()) : Math.ceil(y()),
|
|
s.z() != 1 ? Math.floor(z()) : Math.ceil(z())
|
|
);
|
|
}
|
|
|
|
|
|
|
|
public double product() {
|
|
return x() * y() * z();
|
|
}
|
|
|
|
|
|
public Vec3 f(DoubleUnaryOperator f) {
|
|
return Vec3.f(f, this);
|
|
}
|
|
|
|
|
|
public Vec3 f(DoubleBinaryOperator f, Vec3 v) {
|
|
return Vec3.f(f, this, v);
|
|
}
|
|
|
|
|
|
public Vec3[] split() {
|
|
return new Vec3[] {
|
|
Vec3.xyz(x(), 0, 0),
|
|
Vec3.xyz(0, y(), 0),
|
|
Vec3.xyz(0, 0, z()),
|
|
};
|
|
}
|
|
|
|
|
|
public Vec3 only(int i) {
|
|
return switch (i) {
|
|
case 0 -> Vec3.xyz(x(), 0, 0);
|
|
case 1 -> Vec3.xyz(0, y(), 0);
|
|
case 2 -> Vec3.xyz(0, 0, z());
|
|
default -> throw new IllegalArgumentException();
|
|
};
|
|
}
|
|
|
|
|
|
public Vector p01() {
|
|
return Vector.xy(x(), y());
|
|
}
|
|
|
|
public Vector p12() {
|
|
return Vector.xy(y(), z());
|
|
}
|
|
|
|
/** The result is obtained by applying the affine transform that maps 0 to -1 and 1 to 1. */
|
|
public Vec3 ZOtoMP() {
|
|
return mul(2).sub(Vec3.EXYZ);
|
|
}
|
|
|
|
/** The result is obtained by applying the affine transform that maps -1 to 0 and 1 to 1. */
|
|
public Vec3 MPtoZO() {
|
|
return add(Vec3.EXYZ).mul(0.5);
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
public String toString() {
|
|
return String.format("(%6.2f, %6.2f, %6.2f)", x(), y(), z());
|
|
}
|
|
|
|
|
|
public static Vec3 f(DoubleUnaryOperator f, Vec3 u) {
|
|
return Vec3.xyz(
|
|
f.applyAsDouble(u.x()),
|
|
f.applyAsDouble(u.y()),
|
|
f.applyAsDouble(u.z())
|
|
);
|
|
}
|
|
|
|
|
|
public static Vec3 f(DoubleBinaryOperator f, Vec3 u, Vec3 v) {
|
|
return Vec3.xyz(
|
|
f.applyAsDouble(u.x(), v.x()),
|
|
f.applyAsDouble(u.y(), v.y()),
|
|
f.applyAsDouble(u.z(), v.z())
|
|
);
|
|
}
|
|
|
|
|
|
public static Vec3 min(Vec3 a, Vec3 b) {
|
|
return xyz(
|
|
Math.min(a.x(), b.x()),
|
|
Math.min(a.y(), b.y()),
|
|
Math.min(a.z(), b.z())
|
|
);
|
|
}
|
|
|
|
|
|
public static Vec3 max(Vec3 a, Vec3 b) {
|
|
return xyz(
|
|
Math.max(a.x(), b.x()),
|
|
Math.max(a.y(), b.y()),
|
|
Math.max(a.z(), b.z())
|
|
);
|
|
}
|
|
|
|
|
|
public static Vec3 lerp(Vec3 v0, Vec3 v1, double t) {
|
|
return v0.mul(1 - t).add(v1.mul(t));
|
|
}
|
|
|
|
|
|
public static Vec3 random(RNG rng) {
|
|
return Vec3.xyz(rng.nextDouble(), rng.nextDouble(), rng.nextDouble());
|
|
}
|
|
|
|
}
|