Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ray casting code public, clean it up #918

Merged
merged 5 commits into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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