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;
|
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() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user