114 lines
3.4 KiB
Java
114 lines
3.4 KiB
Java
package eu.jonahbauer.raytracing.math;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.util.Optional;
|
|
|
|
public record Vec3(double x, double y, double z) {
|
|
public static final Vec3 ZERO = new Vec3(0, 0, 0);
|
|
public static final Vec3 UNIT_X = new Vec3(1, 0, 0);
|
|
public static final Vec3 UNIT_Y = new Vec3(0, 1, 0);
|
|
public static final Vec3 UNIT_Z = new Vec3(0, 0, 1);
|
|
|
|
public Vec3 {
|
|
if (!Double.isFinite(x) || !Double.isFinite(y) || !Double.isFinite(z)) {
|
|
throw new IllegalArgumentException("x, y and z must be finite");
|
|
}
|
|
}
|
|
|
|
public static @NotNull Vec3 random() {
|
|
return random(false);
|
|
}
|
|
|
|
public static @NotNull Vec3 random(boolean unit) {
|
|
var random = new Vec3(
|
|
2 * Math.random() - 1,
|
|
2 * Math.random() - 1,
|
|
2 * Math.random() - 1
|
|
);
|
|
return unit ? random.unit() : random;
|
|
}
|
|
|
|
public static @NotNull Vec3 reflect(@NotNull Vec3 vec, @NotNull Vec3 normal) {
|
|
return vec.minus(normal.times(2 * normal.times(vec)));
|
|
}
|
|
|
|
public static @NotNull Optional<Vec3> refract(@NotNull Vec3 vec, @NotNull Vec3 normal, double ri) {
|
|
vec = vec.unit();
|
|
var cosTheta = Math.min(- vec.times(normal), 1.0);
|
|
var sinTheta = Math.sqrt(1 - cosTheta * cosTheta);
|
|
if (ri * sinTheta > 1) return Optional.empty();
|
|
|
|
var rOutPerp = vec.plus(normal.times(cosTheta)).times(ri);
|
|
var rOutParallel = normal.times(- Math.sqrt(Math.abs(1 - rOutPerp.squared())));
|
|
return Optional.of(rOutPerp.plus(rOutParallel));
|
|
}
|
|
|
|
public static @NotNull Vec3 rotate(@NotNull Vec3 vec, @NotNull Vec3 axis, double angle) {
|
|
Vec3 vxp = axis.cross(vec);
|
|
Vec3 vxvxp = axis.cross(vxp);
|
|
return vec.plus(vxp.times(Math.sin(angle))).plus(vxvxp.times(1 - Math.cos(angle)));
|
|
}
|
|
|
|
public @NotNull Vec3 plus(@NotNull Vec3 b) {
|
|
return new Vec3(this.x + b.x, this.y + b.y, this.z + b.z);
|
|
}
|
|
|
|
public @NotNull Vec3 minus(@NotNull Vec3 b) {
|
|
return new Vec3(this.x - b.x, this.y - b.y, this.z - b.z);
|
|
}
|
|
|
|
public double times(@NotNull Vec3 b) {
|
|
return this.x * b.x + this.y * b.y + this.z * b.z;
|
|
}
|
|
|
|
public @NotNull Vec3 times(double b) {
|
|
return new Vec3(this.x * b, this.y * b, this.z * b);
|
|
}
|
|
|
|
public @NotNull Vec3 neg() {
|
|
return new Vec3(-x, -y, -z);
|
|
}
|
|
|
|
public @NotNull Vec3 cross(@NotNull Vec3 b) {
|
|
return new Vec3(
|
|
this.y() * b.z() - b.y() * this.z(),
|
|
this.z() * b.x() - b.z() * this.x(),
|
|
this.x() * b.y() - b.x() * this.y()
|
|
);
|
|
}
|
|
|
|
public @NotNull Vec3 div(double b) {
|
|
return new Vec3(this.x / b, this.y / b, this.z / b);
|
|
}
|
|
|
|
public double squared() {
|
|
return this.x * this.x + this.y * this.y + this.z * this.z;
|
|
}
|
|
|
|
public double length() {
|
|
return Math.sqrt(squared());
|
|
}
|
|
|
|
public boolean isNearZero() {
|
|
var s = 1e-8;
|
|
return Math.abs(x) < s && Math.abs(y) < s && Math.abs(z) < s;
|
|
}
|
|
|
|
public @NotNull Vec3 unit() {
|
|
return div(length());
|
|
}
|
|
|
|
public @NotNull Vec3 withX(double x) {
|
|
return new Vec3(x, y, z);
|
|
}
|
|
|
|
public @NotNull Vec3 withY(double y) {
|
|
return new Vec3(x, y, z);
|
|
}
|
|
|
|
public @NotNull Vec3 withZ(double z) {
|
|
return new Vec3(x, y, z);
|
|
}
|
|
}
|