#![allow(dead_code)]
use robust::orient2d;
use crate::constants::COLLINEARITY_TOLERANCE;
use crate::intersection::int_interval_interval::{IntervalConfig, int_interval_interval};
use crate::intersection::int_line_line::{LineLineConfig, int_line_line};
use crate::interval::interval;
use crate::utils::close_enough;
use crate::{line::line, point::Point, segment::Segment};
#[derive(Debug, PartialEq)]
pub enum SegmentSegmentConfig {
NoIntersection(),
OnePoint(Point, f64, f64),
OnePointTouching(Point, f64, f64),
TwoPoints(Point, Point, Point, Point),
TwoPointsTouching(Point, Point, Point, Point),
}
const ZERO: f64 = 0f64;
pub fn int_segment_segment(segment0: &Segment, segment1: &Segment) -> SegmentSegmentConfig {
let (seg0_origin, seg0_direction, seg0_extent) = segment0.get_centered_form();
let (seg1_origin, seg1_direction, seg1_extent) = segment1.get_centered_form();
let is_point0 = segment0.a.close_enough(segment0.b, f64::EPSILON);
let is_point1 = segment1.a.close_enough(segment1.b, f64::EPSILON);
if is_point0 && is_point1 {
if segment0.a.close_enough(segment1.a, f64::EPSILON) {
return SegmentSegmentConfig::OnePointTouching(segment0.a, ZERO, ZERO);
} else {
return SegmentSegmentConfig::NoIntersection();
}
}
if is_point0 {
let sign = orient2d(
robust::Coord {
x: segment1.a.x,
y: segment1.a.y,
},
robust::Coord {
x: segment1.b.x,
y: segment1.b.y,
},
robust::Coord {
x: segment0.a.x,
y: segment0.a.y,
},
);
if close_enough(sign, ZERO, COLLINEARITY_TOLERANCE) {
let dot = (segment1.b - segment1.a).dot(segment0.a - segment1.a);
let dist = (segment1.b - segment1.a).dot(segment1.b - segment1.a);
if dot >= ZERO && dot <= dist {
return SegmentSegmentConfig::OnePoint(segment0.a, ZERO, ZERO);
} else {
return SegmentSegmentConfig::NoIntersection();
}
} else {
return SegmentSegmentConfig::NoIntersection();
}
}
if is_point1 {
let sign = orient2d(
robust::Coord {
x: segment0.a.x,
y: segment0.a.y,
},
robust::Coord {
x: segment0.b.x,
y: segment0.b.y,
},
robust::Coord {
x: segment1.a.x,
y: segment1.a.y,
},
);
if close_enough(sign, ZERO, COLLINEARITY_TOLERANCE) {
let dot = (segment0.b - segment0.a).dot(segment1.a - segment0.a);
let dist = (segment0.b - segment0.a).dot(segment0.b - segment0.a);
if dot >= ZERO && dot <= dist {
return SegmentSegmentConfig::OnePoint(segment1.a, ZERO, ZERO);
} else {
return SegmentSegmentConfig::NoIntersection();
}
} else {
return SegmentSegmentConfig::NoIntersection();
}
}
let line0 = line(seg0_origin, seg0_direction);
let line1 = line(seg1_origin, seg1_direction);
let ll_result = int_line_line(&line0, &line1);
match ll_result {
LineLineConfig::ParallelDistinct() => SegmentSegmentConfig::NoIntersection(),
LineLineConfig::OnePoint(p, s0, s1) => {
if s0.abs() <= seg0_extent && s1.abs() <= seg1_extent {
if are_ends_towching(segment0, segment1) {
SegmentSegmentConfig::OnePointTouching(p, s0, s1)
} else {
SegmentSegmentConfig::OnePoint(p, s0, s1)
}
} else {
SegmentSegmentConfig::NoIntersection()
}
}
LineLineConfig::ParallelTheSame() => {
let diff = seg1_origin - seg0_origin;
let t = seg0_direction.dot(diff);
let interval0 = interval(-seg0_extent, seg0_extent);
let interval1 = interval(t - seg1_extent, t + seg1_extent);
let ii_result = int_interval_interval(interval0, interval1);
match ii_result {
IntervalConfig::NoOverlap() => SegmentSegmentConfig::NoIntersection(),
IntervalConfig::Overlap(_, _) => {
let (p0, p1, p2, p3) =
Point::sort_colinear_points(segment0.a, segment0.b, segment1.a, segment1.b);
if are_both_ends_towching(segment0, segment1) {
return SegmentSegmentConfig::TwoPointsTouching(
segment0.a, segment0.b, segment1.a, segment1.b,
);
}
if are_ends_towching(segment0, segment1) {
SegmentSegmentConfig::NoIntersection() } else {
SegmentSegmentConfig::TwoPoints(p0, p1, p2, p3)
}
}
IntervalConfig::Touching(_) => {
SegmentSegmentConfig::NoIntersection() }
}
}
}
}
fn are_ends_towching(segment0: &Segment, segment1: &Segment) -> bool {
segment0.a == segment1.a
|| segment0.a == segment1.b
|| segment0.b == segment1.a
|| segment0.b == segment1.b
}
fn are_both_ends_towching(segment0: &Segment, segment1: &Segment) -> bool {
(segment0.a == segment1.a && segment0.b == segment1.b)
|| (segment0.b == segment1.a && segment0.a == segment1.b)
}
pub fn if_really_intersecting_segment_segment(part0: &Segment, part1: &Segment) -> bool {
match int_segment_segment(part0, part1) {
SegmentSegmentConfig::NoIntersection()
| SegmentSegmentConfig::OnePointTouching(_, _, _)
| SegmentSegmentConfig::TwoPointsTouching(_, _, _, _) => false,
SegmentSegmentConfig::OnePoint(_, _, _) | SegmentSegmentConfig::TwoPoints(_, _, _, _) => {
true
}
}
}
#[cfg(test)]
mod test_int_segment_segment {
use crate::point::point;
use crate::segment::segment;
use super::*;
#[test]
fn test_no_intersection() {
let s0 = segment(point(0.0, 0.0), point(2.0, 2.0));
let s1 = segment(point(2.0, 1.0), point(4.0, -1.0));
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::NoIntersection()
);
assert!(if_really_intersecting_segment_segment(&s0, &s1) == false);
}
#[test]
fn test_no_intersection_parallel() {
let s0 = segment(point(0.0, 0.0), point(0.0, 2.0));
let s1 = segment(point(1.0, 0.0), point(1.0, 2.0));
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::NoIntersection()
);
assert!(if_really_intersecting_segment_segment(&s0, &s1) == false);
}
#[test]
fn test_no_intersection2() {
let sqrt_2_2 = std::f64::consts::SQRT_2 / 2.0;
let p0 = point(0.0, 0.0);
let p1 = point(sqrt_2_2, sqrt_2_2);
let delta = point(f64::EPSILON, 0.0);
let s0 = segment(p0, p1);
let s1 = segment(p0 + delta, p1 + delta);
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::NoIntersection()
);
assert!(if_really_intersecting_segment_segment(&s0, &s1) == false);
}
#[test]
fn test_parallel_overlaping() {
let ulp = std::f64::EPSILON * 2.0;
let s0 = segment(point(0.0, 0.0), point(2.0, 2.0));
let s1 = segment(point(1.0, 1.0), point(3.0, 3.0));
match int_segment_segment(&s0, &s1) {
SegmentSegmentConfig::TwoPoints(p0, p1, p2, p3) => {
assert!(p0.close_enough(point(0.0, 0.0), ulp));
assert!(p1.close_enough(point(1.0, 1.0), ulp));
assert!(p2.close_enough(point(2.0, 2.0), ulp));
assert!(p3.close_enough(point(3.0, 3.0), ulp));
assert!(if_really_intersecting_segment_segment(&s0, &s1) == true);
}
_ => assert!(false),
}
}
#[test]
fn test_parallel_overlaping2() {
let ulp = std::f64::EPSILON * 3.0;
let s0 = segment(point(0.0, 0.0), point(2.0, 2.0));
let s1 = segment(point(4.0, 4.0), point(-4.0, -4.0));
match int_segment_segment(&s0, &s1) {
SegmentSegmentConfig::TwoPoints(p0, p1, p2, p3) => {
assert!(p0.close_enough(point(4.0, 4.0), ulp));
assert!(p1.close_enough(point(2.0, 2.0), ulp));
assert!(p2.close_enough(point(0.0, 0.0), ulp));
assert!(p3.close_enough(point(-4.0, -4.0), ulp));
assert!(if_really_intersecting_segment_segment(&s0, &s1) == true);
}
_ => assert!(false),
}
}
#[test]
fn test_parallel_touching() {
let s0 = segment(point(0.0, 0.0), point(1.0, 0.0));
let s1 = segment(point(1.0, 0.0), point(4.0, 0.0));
assert!(int_segment_segment(&s0, &s1) == SegmentSegmentConfig::NoIntersection());
assert!(if_really_intersecting_segment_segment(&s0, &s1) == false);
}
#[test]
fn test_touching_at_ends() {
let sqrt_2 = std::f64::consts::SQRT_2;
let s0 = segment(point(0.0, 0.0), point(2.0, 2.0));
let s1 = segment(point(2.0, 2.0), point(4.0, 0.0));
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::OnePointTouching(point(2.0, 2.0), sqrt_2, -sqrt_2)
);
assert!(if_really_intersecting_segment_segment(&s0, &s1) == false);
}
#[test]
fn test_zero_size_segment_outside_segment() {
let s0 = segment(point(2.0, 0.0), point(2.0, 0.0));
let s1 = segment(point(0.0, 0.0), point(1.0, 0.0));
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::NoIntersection()
);
assert_eq!(
int_segment_segment(&s1, &s0),
SegmentSegmentConfig::NoIntersection()
);
}
#[test]
fn test_zero_size_segment_inside_segment() {
let s0 = segment(point(1.0, 0.0), point(1.0, 0.0));
let s1 = segment(point(0.0, 0.0), point(2.0, 0.0));
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::OnePoint(point(1.0, 0.0), ZERO, ZERO)
);
assert_eq!(
int_segment_segment(&s1, &s0),
SegmentSegmentConfig::OnePoint(point(1.0, 0.0), ZERO, ZERO)
);
}
#[test]
fn test_both_zero_size_segments_outside() {
let s0 = segment(point(2.0, 0.0), point(2.0, 0.0));
let s1 = segment(point(1.0, 0.0), point(1.0, 0.0));
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::NoIntersection()
);
assert_eq!(
int_segment_segment(&s1, &s0),
SegmentSegmentConfig::NoIntersection()
);
}
#[test]
fn test_perpendicular_intersection() {
let s0 = segment(point(0.0, 0.5), point(2.0, 0.5)); let s1 = segment(point(1.0, 0.0), point(1.0, 1.0)); match int_segment_segment(&s0, &s1) {
SegmentSegmentConfig::OnePoint(p, _, _) => {
assert_eq!(p, point(1.0, 0.5));
assert!(if_really_intersecting_segment_segment(&s0, &s1) == true);
}
_ => panic!("Expected intersection for perpendicular segments"),
}
}
#[test]
fn test_nearly_collinear_segments_not_intersecting() {
let eps = 1e-12;
let s0 = segment(point(0.0, 0.0), point(2.0, 0.0)); let s1 = segment(point(1.0, eps), point(3.0, eps)); assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::NoIntersection()
);
}
#[test]
fn test_t_junction_intersection() {
let s0 = segment(point(0.0, 0.0), point(2.0, 0.0)); let s1 = segment(point(1.0, -1.0), point(1.0, 1.0)); match int_segment_segment(&s0, &s1) {
SegmentSegmentConfig::OnePoint(p, _, _) => {
assert_eq!(p, point(1.0, 0.0));
assert!(if_really_intersecting_segment_segment(&s0, &s1) == true);
}
_ => panic!("Expected one point intersection"),
}
}
#[test]
fn test_nearly_parallel_segments_with_small_angle() {
let eps = 1e-10;
let s0 = segment(point(0.0, 0.0), point(1.0, 0.0));
let s1 = segment(point(0.0, 1.0), point(1.0, 1.0 + eps)); assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::NoIntersection()
);
}
#[test]
fn test_diagonal_intersection() {
let s0 = segment(point(0.0, 0.0), point(2.0, 2.0)); let s1 = segment(point(0.0, 2.0), point(2.0, 0.0)); match int_segment_segment(&s0, &s1) {
SegmentSegmentConfig::OnePoint(p, _, _) => {
assert_eq!(p, point(1.0, 1.0));
assert!(if_really_intersecting_segment_segment(&s0, &s1) == true);
}
_ => panic!("Expected intersection for crossing diagonals"),
}
}
#[test]
fn test_both_zero_size_segments_same_point() {
let s0 = segment(point(1.0, 1.0), point(1.0, 1.0));
let s1 = segment(point(1.0, 1.0), point(1.0, 1.0));
assert_eq!(
int_segment_segment(&s0, &s1),
SegmentSegmentConfig::OnePointTouching(point(1.0, 1.0), ZERO, ZERO)
);
}
#[test]
fn test_segments_at_very_large_coordinates() {
let scale = 1e10;
let s0 = segment(
point(0.0 * scale, 0.0 * scale),
point(2.0 * scale, 0.0 * scale),
);
let s1 = segment(
point(1.0 * scale, -1.0 * scale),
point(1.0 * scale, 1.0 * scale),
);
match int_segment_segment(&s0, &s1) {
SegmentSegmentConfig::OnePoint(p, _, _) => {
assert_eq!(p, point(1.0 * scale, 0.0 * scale));
}
_ => panic!("Expected intersection at large coordinates"),
}
}
#[test]
fn test_collinearity_tolerance_boundary() {
let s0 = segment(point(0.0, 0.0), point(1.0, 0.0));
let s1 = segment(point(0.5, 1e-11), point(1.5, 1e-11)); let res = int_segment_segment(&s0, &s1);
match res {
SegmentSegmentConfig::NoIntersection() => {
}
_ => {
}
}
}
#[test]
fn test_collinearity_within_tolerance() {
let s0 = segment(point(0.0, 0.0), point(2.0, 0.0));
let s1 = segment(point(0.5, 1e-11), point(1.5, 1e-11)); let res = int_segment_segment(&s0, &s1);
match res {
SegmentSegmentConfig::NoIntersection() => {
}
SegmentSegmentConfig::OnePoint(_, _, _) => {
}
_ => {
}
}
}
}