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()); } }