1use num_traits::{One, Zero};
2
3use crate::cartesian::{CartesianPoint2d, Orientation};
4
5#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7pub struct Segment<Point>(pub Point, pub Point);
8
9impl<P: CartesianPoint2d> Segment<P> {
10 pub fn distance_to_point_sq<Point: CartesianPoint2d<Num = P::Num>>(
17 &self,
18 point: &Point,
19 ) -> P::Num {
20 if self.0.equal(&self.1) {
21 return self.0.distance_sq(point);
22 }
23
24 let ds = self.1.sub(&self.0);
25 let dp = point.sub(&self.0);
26 let ds_len = ds.magnitude_sq();
27
28 let r = dp.magnitude_sq() / ds_len;
29 if r <= P::Num::zero() {
30 self.0.distance_sq(point)
31 } else if r >= P::Num::one() {
32 self.1.distance_sq(point)
33 } else {
34 let s = (dp.dy() * ds.dx() - dp.dx() * ds.dy()) / ds_len;
35 (s * s) * ds_len
36 }
37 }
38
39 pub fn intersects<Point: CartesianPoint2d<Num = P::Num>>(
41 &self,
42 other: &Segment<Point>,
43 ) -> bool {
44 fn on_segment<Num: num_traits::Num + PartialOrd>(
45 p: &impl CartesianPoint2d<Num = Num>,
46 q: &impl CartesianPoint2d<Num = Num>,
47 r: &impl CartesianPoint2d<Num = Num>,
48 ) -> bool {
49 let x_max = if p.x() >= r.x() { p.x() } else { r.x() };
50 let x_min = if p.x() <= r.x() { p.x() } else { r.x() };
51 let y_max = if p.y() >= r.y() { p.x() } else { r.x() };
52 let y_min = if p.y() <= r.y() { p.x() } else { r.x() };
53
54 q.x() <= x_max && q.x() >= x_min && q.y() <= y_max && q.y() >= y_min
55 }
56
57 let o1 = Orientation::triplet(&self.0, &other.0, &self.1);
58 let o2 = Orientation::triplet(&self.0, &other.1, &self.1);
59 let o3 = Orientation::triplet(&other.0, &self.0, &other.1);
60 let o4 = Orientation::triplet(&other.0, &self.1, &other.1);
61
62 if o1 != o2 && o3 != o4 {
63 return true;
64 }
65
66 if o1 == Orientation::Collinear && on_segment(&self.0, &other.0, &self.1) {
67 return true;
68 }
69 if o2 == Orientation::Collinear && on_segment(&self.0, &other.1, &self.1) {
70 return true;
71 }
72 if o3 == Orientation::Collinear && on_segment(&other.0, &self.0, &other.1) {
73 return true;
74 }
75 if o4 == Orientation::Collinear && on_segment(&other.0, &self.1, &other.1) {
76 return true;
77 }
78
79 false
80 }
81}