Skip to content

Commit

Permalink
Add infinite line primitive
Browse files Browse the repository at this point in the history
This includes adding a PlaneIntersection trait which now also allows us
to perform triangle intersections with infinite lines and finite lines.
  • Loading branch information
lbirkert committed Aug 16, 2023
1 parent dac93d9 commit fd06051
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 78 deletions.
6 changes: 3 additions & 3 deletions crates/kelocam-core/benches/ray_intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub fn ray_triangle_intersection(bencher: &mut Bencher) {
ray = Ray::new(origin, normal);
}

bencher.iter(|| test::black_box(&ray).intersect(test::black_box(&triangle)));
bencher.iter(|| test::black_box(&triangle).intersect(test::black_box(&ray)));
}

#[bench]
Expand All @@ -42,7 +42,7 @@ pub fn ray_plane_intersection(bencher: &mut Bencher) {
ray = Ray::new(origin, normal);
}

bencher.iter(|| test::black_box(&ray).intersect(test::black_box(&plane)))
bencher.iter(|| test::black_box(&plane).intersect(test::black_box(&ray)))
}

#[bench]
Expand All @@ -63,5 +63,5 @@ pub fn ray_square_intersection(bencher: &mut Bencher) {
ray = Ray::new(origin, normal);
}

bencher.iter(|| test::black_box(&ray).intersect(test::black_box(&square)))
bencher.iter(|| test::black_box(&square).intersect(test::black_box(&ray)))
}
25 changes: 11 additions & 14 deletions crates/kelocam-core/src/primitives/line.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use nalgebra::Vector3;
use nalgebra::{UnitVector3, Vector3};

use super::{BoundingBox, Geometry, Plane};
use super::{plane::PlaneIntersection, BoundingBox, Geometry};

#[derive(Debug)]
pub struct Line {
Expand All @@ -12,33 +12,30 @@ impl Line {
pub fn new(a: Vector3<f32>, b: Vector3<f32>) -> Self {
Self { a, b }
}
}

/// Returns the intersection of this line with a plane (if any).
pub fn intersect_plane(&self, plane: &Plane) -> Option<Vector3<f32>> {
Self::intersect_plane_raw(&self.a, &self.b, plane)
}

impl PlaneIntersection for Line {
/// Returns the intersection of a line with start and endpoints on a plane (if any).
pub fn intersect_plane_raw(
a: &Vector3<f32>,
b: &Vector3<f32>,
plane: &Plane,
fn intersect_plane_raw(
&self,
origin: &Vector3<f32>,
normal: &UnitVector3<f32>,
) -> Option<Vector3<f32>> {
let s = (a - b).dot(&plane.normal);
let s = (self.a - self.b).dot(normal);

// => line and plane parallel
if s == 0.0 {
return None;
}

let t = (plane.origin - b).dot(&plane.normal) / s;
let t = (origin - self.b).dot(normal) / s;

// => point not on line
if !(0.0..=1.0).contains(&t) {
return None;
}

Some(b.lerp(a, t))
Some(self.b.lerp(&self.a, t))
}
}

Expand Down
63 changes: 41 additions & 22 deletions crates/kelocam-core/src/primitives/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{collections::HashSet, io::Cursor};

use nalgebra::{Matrix4, UnitVector3, Vector3};

use super::{ray::RayIntersection, BoundingBox, Geometry, Line, Path3, Plane, Ray, Triangle};
use super::{plane::PlaneIntersection, BoundingBox, Geometry, Line, Path3, Plane, Ray, Triangle};

#[derive(Debug, Clone)]
pub struct Mesh {
Expand All @@ -20,22 +20,47 @@ impl Mesh {
.map(|stl| Self::new(stl.triangles.into_iter().map(Triangle::from_stl).collect()))
}

/// Perform a ray intersection with this mesh.
/// Returns the intersection point if any, otherwise None.
pub fn intersect_ray_raw(triangles: &[Triangle], ray: &Ray) -> Option<Vector3<f32>> {
let mut intersection_dist = std::f32::INFINITY;
let mut intersection_point = None;
/// Perform an intersection with this mesh.
/// Returns the intersection points.
pub fn intersect_raw<T>(triangles: &[Triangle], entity: &T) -> Vec<Vector3<f32>>
where
T: PlaneIntersection,
{
let mut intersections = Vec::new();
for triangle in triangles.iter() {
if let Some(point) = triangle.intersect_ray(ray) {
let dist = (point - ray.origin).magnitude_squared();
if let Some(point) = triangle.intersect(entity) {
intersections.push(point)
}
}
intersections
}

if dist < intersection_dist {
intersection_dist = dist;
intersection_point = Some(point);
}
/// Perform an intersection with this mesh.
/// Returns the intersection points.
pub fn intersect<T>(&self, entity: &T) -> Vec<Vector3<f32>>
where
T: PlaneIntersection,
{
Self::intersect_raw(&self.triangles, entity)
}

/// Perform a ray intersection with this mesh. Returns the point closest to the ray origin.
pub fn intersect_ray_raw(triangles: &[Triangle], ray: &Ray) -> Option<Vector3<f32>> {
let mut intersection_dist = std::f32::INFINITY;
let mut intersection = None;
for point in Self::intersect_raw(triangles, ray) {
let dist = (point - ray.origin).magnitude_squared();
if dist < intersection_dist {
intersection_dist = dist;
intersection = Some(point);
}
}
intersection_point
intersection
}

/// Perform a ray intersection with this mesh. Returns the point closest to the ray origin.
pub fn intersect_ray(&self, ray: &Ray) -> Option<Vector3<f32>> {
Self::intersect_ray_raw(&self.triangles, ray)
}

/// Slice a model using a plane. This returns the outline of the cross section.
Expand All @@ -58,9 +83,9 @@ impl Mesh {
let a = triangle.a;
let b = triangle.b;
let c = triangle.c;
let pa = Line::intersect_plane_raw(&a, &b, plane);
let pb = Line::intersect_plane_raw(&b, &c, plane);
let pc = Line::intersect_plane_raw(&c, &a, plane);
let pa = plane.intersect(&Line::new(a, b));
let pb = plane.intersect(&Line::new(b, c));
let pc = plane.intersect(&Line::new(c, a));

let seg = match (pa, pb, pc) {
(Some(pa), Some(pb), _) => (pa, pb),
Expand Down Expand Up @@ -194,12 +219,6 @@ impl Mesh {
}
}

impl RayIntersection for Mesh {
fn intersect_ray(&self, ray: &Ray) -> Option<Vector3<f32>> {
Self::intersect_ray_raw(&self.triangles, ray)
}
}

impl BoundingBox for Mesh {
fn bb_min(&self) -> Vector3<f32> {
let mut min = Vector3::from_element(std::f32::INFINITY);
Expand Down
20 changes: 16 additions & 4 deletions crates/kelocam-core/src/primitives/plane.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use nalgebra::{UnitVector3, Vector3};

use super::{ray::RayIntersection, Geometry, Ray};
use super::{Geometry, Ray};

#[derive(Debug)]
pub struct Plane {
Expand Down Expand Up @@ -36,11 +36,23 @@ impl Plane {

Some(ray.origin + ray.normal.scale(t))
}

pub fn intersect<T>(&self, entity: &T) -> Option<Vector3<f32>>
where
T: PlaneIntersection,
{
entity.intersect_plane(self)
}
}

impl RayIntersection for Plane {
fn intersect_ray(&self, ray: &Ray) -> Option<Vector3<f32>> {
Self::intersect_ray_raw(&self.origin, &self.normal, ray)
pub trait PlaneIntersection {
fn intersect_plane_raw(
&self,
origin: &Vector3<f32>,
normal: &UnitVector3<f32>,
) -> Option<Vector3<f32>>;
fn intersect_plane(&self, plane: &Plane) -> Option<Vector3<f32>> {
self.intersect_plane_raw(&plane.origin, &plane.normal)
}
}

Expand Down
62 changes: 53 additions & 9 deletions crates/kelocam-core/src/primitives/ray.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use nalgebra::{UnitVector3, Vector3};

use super::Plane;
use super::{plane::PlaneIntersection, Plane};

#[derive(Debug)]
pub struct Ray {
Expand All @@ -10,7 +10,6 @@ pub struct Ray {

impl Ray {
pub fn new(origin: Vector3<f32>, normal: UnitVector3<f32>) -> Self {
assert!((1.0 - normal.dot(&normal)).abs() < 0.001);
Self { origin, normal }
}

Expand Down Expand Up @@ -41,15 +40,60 @@ impl Ray {
None
}
}
}

impl PlaneIntersection for Ray {
fn intersect_plane_raw(
&self,
origin: &Vector3<f32>,
normal: &UnitVector3<f32>,
) -> Option<Vector3<f32>> {
let a = normal.dot(&self.normal);

// Ray and plane parallel
if a == 0.0 {
return None;
}

let t = (origin - self.origin).dot(normal) / a;

// Plane is behind ray
if t < 0.0 {
return None;
}

Some(self.origin + self.normal.scale(t))
}
}

// Works like a ray but also in the other direction
#[derive(Debug)]
pub struct InfiniteLine {
pub normal: UnitVector3<f32>,
pub origin: Vector3<f32>,
}

pub fn intersect<T>(&self, object: &T) -> Option<Vector3<f32>>
where
T: RayIntersection,
{
object.intersect_ray(self)
impl InfiniteLine {
pub fn new(origin: Vector3<f32>, normal: UnitVector3<f32>) -> Self {
Self { origin, normal }
}
}

pub trait RayIntersection {
fn intersect_ray(&self, ray: &Ray) -> Option<Vector3<f32>>;
impl PlaneIntersection for InfiniteLine {
fn intersect_plane_raw(
&self,
origin: &Vector3<f32>,
normal: &UnitVector3<f32>,
) -> Option<Vector3<f32>> {
let a = normal.dot(&self.normal);

// Line and plane parallel
if a == 0.0 {
return None;
}

let t = (origin - self.origin).dot(normal) / a;

Some(self.origin + self.normal.scale(t))
}
}
26 changes: 16 additions & 10 deletions crates/kelocam-core/src/primitives/square.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use nalgebra::{UnitVector3, Vector3};

use super::{ray::RayIntersection, BoundingBox, Geometry, Plane, Ray};
use super::{plane::PlaneIntersection, BoundingBox, Geometry};

#[derive(Debug)]
pub struct Square {
Expand All @@ -20,16 +20,19 @@ impl Square {
Self { a, ab, ac, normal }
}

/// Perform a ray intersection with this square.
/// Perform an intersection with this square.
/// Returns the intersection point if any, otherwise None.
pub fn intersect_ray_raw(
pub fn intersect_raw<T>(
a: &Vector3<f32>,
ab: &Vector3<f32>,
ac: &Vector3<f32>,
normal: &UnitVector3<f32>,
ray: &Ray,
) -> Option<Vector3<f32>> {
let p = Plane::intersect_ray_raw(a, normal, ray)?;
entity: &T,
) -> Option<Vector3<f32>>
where
T: PlaneIntersection,
{
let p = entity.intersect_plane_raw(a, normal)?;

let ap = p - a;

Expand All @@ -41,11 +44,14 @@ impl Square {
None
}
}
}

impl RayIntersection for Square {
fn intersect_ray(&self, ray: &Ray) -> Option<Vector3<f32>> {
Self::intersect_ray_raw(&self.a, &self.ab, &self.ac, &self.normal, ray)
/// Perform an intersection with this square.
/// Returns the intersection point if any, otherwise None.
pub fn intersect<T>(&self, entity: &T) -> Option<Vector3<f32>>
where
T: PlaneIntersection,
{
Self::intersect_raw(&self.a, &self.ab, &self.ac, &self.normal, entity)
}
}

Expand Down
24 changes: 15 additions & 9 deletions crates/kelocam-core/src/primitives/triangle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use nalgebra::{UnitVector3, Vector3};

use super::{ray::RayIntersection, BoundingBox, Geometry, Plane, Ray};
use super::{plane::PlaneIntersection, BoundingBox, Geometry};

#[derive(Debug, Clone)]
pub struct Triangle {
Expand Down Expand Up @@ -31,15 +31,18 @@ impl Triangle {

/// Perform a ray intersection with this triangle.
/// Returns the intersection point if any, otherwise None.
pub fn intersect_ray_raw(
pub fn intersect_raw<T>(
a: &Vector3<f32>,
b: &Vector3<f32>,
c: &Vector3<f32>,
normal: &UnitVector3<f32>,
ray: &Ray,
) -> Option<Vector3<f32>> {
entity: &T,
) -> Option<Vector3<f32>>
where
T: PlaneIntersection,
{
// Algorithm from https://math.stackexchange.com/questions/4322/check-whether-a-point-is-within-a-3d-triangle#28552
let p = Plane::intersect_ray_raw(a, normal, ray)?;
let p = entity.intersect_plane_raw(a, normal)?;

let n = (b - a).cross(&(c - a));
let nl = n.magnitude_squared();
Expand All @@ -60,11 +63,14 @@ impl Triangle {
None
}
}
}

impl RayIntersection for Triangle {
fn intersect_ray(&self, ray: &Ray) -> Option<Vector3<f32>> {
Self::intersect_ray_raw(&self.a, &self.b, &self.c, &self.normal, ray)
/// Perform a ray intersection with this triangle.
/// Returns the intersection point if any, otherwise None.
pub fn intersect<T>(&self, entity: &T) -> Option<Vector3<f32>>
where
T: PlaneIntersection,
{
Self::intersect_raw(&self.a, &self.b, &self.c, &self.normal, entity)
}
}

Expand Down
Loading

0 comments on commit fd06051

Please sign in to comment.