use fj_math::Segment;
use super::{HorizontalRayToTheRight, Intersect};
impl Intersect for (&HorizontalRayToTheRight<2>, &Segment<2>) {
type Intersection = RaySegmentIntersection;
fn intersect(self) -> Option<Self::Intersection> {
let (ray, segment) = self;
let [a, b] = segment.points();
let [lower, upper] = if a.v <= b.v { [a, b] } else { [b, a] };
let [left, right] = if a.u <= b.u { [a, b] } else { [b, a] };
if ray.origin.v > upper.v {
return None;
}
if ray.origin.v < lower.v {
return None;
}
if ray.origin.v == lower.v && lower.v == upper.v {
if ray.origin.u > right.u {
return None;
}
if ray.origin.u == a.u {
return Some(RaySegmentIntersection::RayStartsOnOnFirstVertex);
}
if ray.origin.u == b.u {
return Some(RaySegmentIntersection::RayStartsOnSecondVertex);
}
if ray.origin.u > left.u && ray.origin.u < right.u {
return Some(RaySegmentIntersection::RayStartsOnSegment);
}
return Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel);
}
let pa = robust::Coord {
x: lower.u,
y: lower.v,
};
let pb = robust::Coord {
x: upper.u,
y: upper.v,
};
let pc = robust::Coord {
x: ray.origin.u,
y: ray.origin.v,
};
let orient2d = robust::orient2d(pa, pb, pc);
if orient2d == 0. {
if ray.origin.v == a.v {
return Some(RaySegmentIntersection::RayStartsOnOnFirstVertex);
}
if ray.origin.v == b.v {
return Some(RaySegmentIntersection::RayStartsOnSecondVertex);
}
return Some(RaySegmentIntersection::RayStartsOnSegment);
}
if orient2d > 0. {
if ray.origin.v == upper.v {
return Some(RaySegmentIntersection::RayHitsUpperVertex);
}
if ray.origin.v == lower.v {
return Some(RaySegmentIntersection::RayHitsLowerVertex);
}
return Some(RaySegmentIntersection::RayHitsSegment);
}
None
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RaySegmentIntersection {
RayHitsSegment,
RayHitsLowerVertex,
RayHitsUpperVertex,
RayHitsSegmentAndAreParallel,
RayStartsOnSegment,
RayStartsOnOnFirstVertex,
RayStartsOnSecondVertex,
}
#[cfg(test)]
mod tests {
use fj_math::Segment;
use crate::algorithms::intersect::Intersect;
use super::{HorizontalRayToTheRight, RaySegmentIntersection};
#[test]
fn ray_is_left_of_segment() {
let ray = HorizontalRayToTheRight::from([0., 2.]);
let below = Segment::from([[1., 0.], [1., 1.]]);
let above = Segment::from([[1., 3.], [1., 4.]]);
let same_level = Segment::from([[1., 1.], [1., 3.]]);
assert!((&ray, &below).intersect().is_none());
assert!((&ray, &above).intersect().is_none());
assert!(matches!(
(&ray, &same_level).intersect(),
Some(RaySegmentIntersection::RayHitsSegment)
));
}
#[test]
fn ray_is_right_of_segment() {
let ray = HorizontalRayToTheRight::from([1., 2.]);
let same_level = Segment::from([[0., 1.], [0., 3.]]);
assert!((&ray, &same_level).intersect().is_none());
}
#[test]
fn ray_overlaps_with_segment_along_x_axis() {
let ray = HorizontalRayToTheRight::from([1., 1.]);
let no_hit = Segment::from([[0., 0.], [2., 3.]]);
let hit_segment = Segment::from([[0., 0.], [3., 2.]]);
let hit_upper = Segment::from([[0., 0.], [2., 1.]]);
let hit_lower = Segment::from([[0., 2.], [2., 1.]]);
assert!((&ray, &no_hit).intersect().is_none());
assert!(matches!(
(&ray, &hit_segment).intersect(),
Some(RaySegmentIntersection::RayHitsSegment)
));
assert!(matches!(
(&ray, &hit_upper).intersect(),
Some(RaySegmentIntersection::RayHitsUpperVertex),
));
assert!(matches!(
(&ray, &hit_lower).intersect(),
Some(RaySegmentIntersection::RayHitsLowerVertex),
));
}
#[test]
fn ray_starts_on_segment() {
let ray = HorizontalRayToTheRight::from([1., 1.]);
let hit_segment = Segment::from([[0., 0.], [2., 2.]]);
let hit_upper = Segment::from([[0., 0.], [1., 1.]]);
let hit_lower = Segment::from([[1., 1.], [2., 2.]]);
assert!(matches!(
(&ray, &hit_segment).intersect(),
Some(RaySegmentIntersection::RayStartsOnSegment)
));
assert!(matches!(
(&ray, &hit_upper).intersect(),
Some(RaySegmentIntersection::RayStartsOnSecondVertex),
));
assert!(matches!(
(&ray, &hit_lower).intersect(),
Some(RaySegmentIntersection::RayStartsOnOnFirstVertex),
));
}
#[test]
fn ray_and_segment_are_parallel_and_on_same_level() {
let ray = HorizontalRayToTheRight::from([2., 0.]);
let left = Segment::from([[0., 0.], [1., 0.]]);
let right = Segment::from([[3., 0.], [4., 0.]]);
assert!((&ray, &left).intersect().is_none());
assert!(matches!(
(&ray, &right).intersect(),
Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel)
));
}
#[test]
fn ray_starts_on_parallel_segment() {
let ray = HorizontalRayToTheRight::from([2., 0.]);
let left = Segment::from([[0., 0.], [2., 0.]]);
let overlapping = Segment::from([[1., 0.], [3., 0.]]);
let right = Segment::from([[2., 0.], [4., 0.]]);
assert!(matches!(
(&ray, &left).intersect(),
Some(RaySegmentIntersection::RayStartsOnSecondVertex)
));
assert!(matches!(
(&ray, &overlapping).intersect(),
Some(RaySegmentIntersection::RayStartsOnSegment),
));
assert!(matches!(
(&ray, &right).intersect(),
Some(RaySegmentIntersection::RayStartsOnOnFirstVertex),
));
}
}