Add custom cylinder
This commit is contained in:
parent
ecb112794d
commit
7b0c09c3d7
|
|
@ -2,6 +2,67 @@ use glam::{Vec3, Vec3Swizzles as _};
|
|||
|
||||
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.
|
||||
pub struct YCylinder {
|
||||
pub half_length: f32,
|
||||
|
|
@ -88,6 +149,23 @@ mod tests {
|
|||
use crate::types::ray;
|
||||
use approx::assert_abs_diff_eq;
|
||||
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]
|
||||
fn test_cylinder() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user