Skip to content

Commit

Permalink
Merge pull request #918 from hannobraun/ray
Browse files Browse the repository at this point in the history
Make ray casting code public, clean it up
  • Loading branch information
hannobraun authored Aug 5, 2022
2 parents 9f7842c + dd8a32b commit e7a626e
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 30 deletions.
1 change: 1 addition & 0 deletions crates/fj-kernel/src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod transform;
mod triangulate;

pub mod intersection;
pub mod ray_cast;

pub use self::{
approx::{CycleApprox, FaceApprox, InvalidTolerance, Tolerance},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
//! Ray casting
use fj_math::{Point, Segment};

pub struct HorizontalRayToTheRight {
pub origin: Point<2>,
/// A horizontal ray that goes to the right
///
/// For in-kernel use, we don't need anything more flexible, and being exactly
/// horizontal simplifies some calculations.
pub struct HorizontalRayToTheRight<const D: usize> {
/// The point where the ray originates
pub origin: Point<D>,
}

impl HorizontalRayToTheRight {
pub fn hits_segment(&self, segment: impl Into<Segment<2>>) -> Option<Hit> {
impl HorizontalRayToTheRight<2> {
/// Determine whether the ray hits the given line segment
pub fn hits_segment(
&self,
segment: impl Into<Segment<2>>,
) -> Option<RaySegmentHit> {
let [a, b] = segment.into().points();
let [lower, upper] = if a.v <= b.v { [a, b] } else { [b, a] };
let right = if a.u > b.u { a } else { b };
Expand All @@ -26,7 +37,7 @@ impl HorizontalRayToTheRight {
return None;
}

return Some(Hit::Parallel);
return Some(RaySegmentHit::Parallel);
}

let pa = robust::Coord {
Expand All @@ -46,22 +57,22 @@ impl HorizontalRayToTheRight {
// ray starts on the line or left of it

if self.origin.v == upper.v {
return Some(Hit::UpperVertex);
return Some(RaySegmentHit::UpperVertex);
}
if self.origin.v == lower.v {
return Some(Hit::LowerVertex);
return Some(RaySegmentHit::LowerVertex);
}

return Some(Hit::Segment);
return Some(RaySegmentHit::Segment);
}

None
}
}

impl<P> From<P> for HorizontalRayToTheRight
impl<P, const D: usize> From<P> for HorizontalRayToTheRight<D>
where
P: Into<Point<2>>,
P: Into<Point<D>>,
{
fn from(point: P) -> Self {
Self {
Expand All @@ -70,19 +81,25 @@ where
}
}

/// A hit between a ray and a line segment
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Hit {
pub enum RaySegmentHit {
/// The ray hit the segment itself
Segment,

/// The ray hit the lower vertex of the segment
LowerVertex,

/// The ray hit the upper vertex of the segment
UpperVertex,

/// The ray hit the whole segment, as it is parallel to the ray
Parallel,
}

#[cfg(test)]
mod tests {
use super::{Hit, HorizontalRayToTheRight};
use super::{HorizontalRayToTheRight, RaySegmentHit};

#[test]
fn hits_segment_right() {
Expand All @@ -94,7 +111,10 @@ mod tests {

assert!(ray.hits_segment(below).is_none());
assert!(ray.hits_segment(above).is_none());
assert!(matches!(ray.hits_segment(same_level), Some(Hit::Segment)));
assert!(matches!(
ray.hits_segment(same_level),
Some(RaySegmentHit::Segment)
));
}

#[test]
Expand All @@ -116,14 +136,17 @@ mod tests {
let hit_lower = [[0., 2.], [2., 1.]];

assert!(ray.hits_segment(no_hit).is_none());
assert!(matches!(ray.hits_segment(hit_segment), Some(Hit::Segment)));
assert!(matches!(
ray.hits_segment(hit_segment),
Some(RaySegmentHit::Segment)
));
assert!(matches!(
ray.hits_segment(hit_upper),
Some(Hit::UpperVertex),
Some(RaySegmentHit::UpperVertex),
));
assert!(matches!(
ray.hits_segment(hit_lower),
Some(Hit::LowerVertex),
Some(RaySegmentHit::LowerVertex),
));
}

Expand All @@ -135,14 +158,17 @@ mod tests {
let hit_upper = [[0., 0.], [1., 1.]];
let hit_lower = [[1., 1.], [2., 2.]];

assert!(matches!(ray.hits_segment(hit_segment), Some(Hit::Segment)));
assert!(matches!(
ray.hits_segment(hit_segment),
Some(RaySegmentHit::Segment)
));
assert!(matches!(
ray.hits_segment(hit_upper),
Some(Hit::UpperVertex),
Some(RaySegmentHit::UpperVertex),
));
assert!(matches!(
ray.hits_segment(hit_lower),
Some(Hit::LowerVertex),
Some(RaySegmentHit::LowerVertex),
));
}

Expand All @@ -155,7 +181,13 @@ mod tests {
let right = [[3., 0.], [4., 0.]];

assert!(ray.hits_segment(left).is_none());
assert!(matches!(ray.hits_segment(overlapping), Some(Hit::Parallel)));
assert!(matches!(ray.hits_segment(right), Some(Hit::Parallel)));
assert!(matches!(
ray.hits_segment(overlapping),
Some(RaySegmentHit::Parallel)
));
assert!(matches!(
ray.hits_segment(right),
Some(RaySegmentHit::Parallel)
));
}
}
1 change: 0 additions & 1 deletion crates/fj-kernel/src/algorithms/triangulate/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
mod delaunay;
mod polygon;
mod ray;

use fj_interop::{debug::DebugInfo, mesh::Mesh};
use fj_math::Point;
Expand Down
23 changes: 15 additions & 8 deletions crates/fj-kernel/src/algorithms/triangulate/polygon.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use fj_interop::debug::{DebugInfo, TriangleEdgeCheck};
use fj_math::{Point, PolyChain, Segment};

use crate::objects::Surface;

use super::ray::{Hit, HorizontalRayToTheRight};
use crate::{
algorithms::ray_cast::{HorizontalRayToTheRight, RaySegmentHit},
objects::Surface,
};

pub struct Polygon {
surface: Surface,
Expand Down Expand Up @@ -103,7 +104,7 @@ impl Polygon {
}
}

// We haven't rules out that the triangle is a polygon hole. Since we
// We haven't ruled out that the triangle is a polygon hole. Since we
// checked all its edges, this means we now know for certain that is is.
if might_be_hole {
return false;
Expand Down Expand Up @@ -162,12 +163,18 @@ impl Polygon {
let hit = ray.hits_segment(edge);

let count_hit = match (hit, previous_hit) {
(Some(Hit::Segment), _) => {
(Some(RaySegmentHit::Segment), _) => {
// We're hitting a segment right-on. Clear case.
true
}
(Some(Hit::UpperVertex), Some(Hit::LowerVertex))
| (Some(Hit::LowerVertex), Some(Hit::UpperVertex)) => {
(
Some(RaySegmentHit::UpperVertex),
Some(RaySegmentHit::LowerVertex),
)
| (
Some(RaySegmentHit::LowerVertex),
Some(RaySegmentHit::UpperVertex),
) => {
// If we're hitting a vertex, only count it if we've hit
// the other kind of vertex right before.
//
Expand All @@ -183,7 +190,7 @@ impl Polygon {
// passing through anything.
true
}
(Some(Hit::Parallel), _) => {
(Some(RaySegmentHit::Parallel), _) => {
// A parallel edge must be completely ignored. Its
// presence won't change anything, so we can treat it as
// if it wasn't there, and its neighbors were connected
Expand Down

0 comments on commit e7a626e

Please sign in to comment.