Add custom cylinder

This commit is contained in:
numzero 2024-11-16 23:52:18 +03:00
parent ecb112794d
commit 7b0c09c3d7

View File

@ -2,6 +2,67 @@ use glam::{Vec3, Vec3Swizzles as _};
use crate::types::Ray; use crate::types::Ray;
pub struct Cylinder {
pub center: Vec3,
pub semiaxis: Vec3,
pub radius: f32,
}
impl Cylinder {
/// Split a vector into a component along the axis and one orthogonal to it.
fn split(&self, dir: Vec3) -> (f32, Vec3) {
let along = dir.dot(self.semiaxis) / self.semiaxis.length_squared();
(along, dir - along * self.semiaxis)
}
fn cap_inout(p: f32, d: f32) -> (f32, f32) {
((-d.signum() - p) / d.abs(), (d.signum() - p) / d.abs())
}
pub fn is_inside(&self, pt: Vec3) -> bool {
let (along, ortho) = self.split(pt - self.center);
along.abs() < 1. && ortho.length_squared() < self.radius.powi(2)
}
fn trace_inout(&self, ray: Ray) -> Option<(f32, f32)> {
let (pos_along, pos_ortho) = self.split(ray.pos - self.center);
let (dir_along, dir_ortho) = self.split(ray.dir);
let (t_cap_in, t_cap_out) = Self::cap_inout(pos_along, dir_along);
if dir_ortho.length_squared() < 1e-3 {
if pos_ortho.length_squared() >= self.radius.powi(2) {
return None;
}
return Some((t_cap_in, t_cap_out));
}
let (t_side_in, t_side_out) = solve_quadratic(
dir_ortho.length_squared(),
pos_ortho.dot(dir_ortho),
pos_ortho.length_squared() - self.radius.powi(2),
)?;
let t_in = f32::max(t_cap_in, t_side_in);
let t_out = f32::min(t_cap_out, t_side_out);
if t_out <= t_in {
return None;
}
Some((t_in, t_out))
}
pub fn trace_into(&self, ray: Ray) -> Option<f32> {
let (t, _) = self.trace_inout(ray)?;
if t < 0. {
return None;
}
Some(t)
}
pub fn trace_out_of(&self, ray: Ray) -> Option<f32> {
let (_, t) = self
.trace_inout(ray)
.expect("the ray starts inside so *has* to cross the boundary");
Some(t)
}
}
/// Цилиндр с центром в начале координат и осью вдоль оси Y. /// Цилиндр с центром в начале координат и осью вдоль оси Y.
pub struct YCylinder { pub struct YCylinder {
pub half_length: f32, pub half_length: f32,
@ -88,6 +149,23 @@ mod tests {
use crate::types::ray; use crate::types::ray;
use approx::assert_abs_diff_eq; use approx::assert_abs_diff_eq;
use glam::vec3; use glam::vec3;
use rand::{Rng, SeedableRng};
#[test]
fn test_split() {
let mut rng = rand_pcg::Pcg64Mcg::seed_from_u64(17);
let cyl = Cylinder {
center: vec3(1., 2., 3.),
semiaxis: vec3(4., 5., 6.),
radius: 7.,
};
for _ in 0..100 {
let dir = vec3(rng.gen(), rng.gen(), rng.gen());
let (along, ortho) = cyl.split(dir);
assert_abs_diff_eq!(along * cyl.semiaxis + ortho, dir, epsilon = 1e-5);
assert_abs_diff_eq!(cyl.semiaxis.dot(ortho), 0., epsilon = 1e-5);
}
}
#[test] #[test]
fn test_cylinder() { fn test_cylinder() {