veloren_common/util/
lines.rs1use vek::{LineSegment2, LineSegment3, Vec2, Vec3};
2
3pub fn closest_points_3d(n: LineSegment3<f32>, m: LineSegment3<f32>) -> (Vec3<f32>, Vec3<f32>) {
5 let p1 = n.start;
6 let p2 = n.end;
7 let p3 = m.start;
8 let p4 = m.end;
9
10 let d1 = p2 - p1;
11 let d2 = p4 - p3;
12 let d21 = p3 - p1;
13
14 let v22 = d2.dot(d2);
15 let v11 = d1.dot(d1);
16 let v21 = d2.dot(d1);
17 let v21_1 = d21.dot(d1);
18 let v21_2 = d21.dot(d2);
19
20 let denom = v21 * v21 - v22 * v11;
21
22 let (s, t) = if denom == 0.0 {
23 let s = 0.0;
24 let t = (v11 * s - v21_1) / v21;
25 (s, t)
26 } else {
27 let s = (v21_2 * v21 - v22 * v21_1) / denom;
28 let t = (-v21_1 * v21 + v11 * v21_2) / denom;
29 (s, t)
30 };
31
32 let (s, t) = (s.clamp(0.0, 1.0), t.clamp(0.0, 1.0));
33
34 let p_a = p1 + s * d1;
35 let p_b = p3 + t * d2;
36
37 (p_a, p_b)
38}
39
40pub enum LineIntersection<T>
42where
43 T: num_traits::Float + Copy,
44{
45 Point(Vec2<T>),
47 Coincident,
49 Parallel,
51}
52
53pub fn line_intersection_2d<T>(n: LineSegment2<T>, m: LineSegment2<T>) -> LineIntersection<T>
74where
75 T: num_traits::Float + Copy,
76{
77 let a = n.end.x - n.start.x;
78 let b = -(m.end.x - m.start.x);
79 let c = m.start.x - n.start.x;
80 let d = n.end.y - n.start.y;
81 let e = -(m.end.y - m.start.y);
82 let f = m.start.y - n.start.y;
83
84 let num1 = c * e - b * f;
85 let num2 = a * f - c * d;
86 let denom = a * e - b * d;
87
88 if denom.abs() < T::epsilon() {
89 if num1.abs() < T::epsilon() && num2.abs() < T::epsilon() {
90 return LineIntersection::Coincident;
92 }
93 return LineIntersection::Parallel;
95 }
96
97 LineIntersection::Point(Vec2::new(
98 n.start.x + (num1 / denom) * a,
99 n.start.y + (num1 / denom) * d,
100 ))
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use vek::{LineSegment2, Vec2};
107
108 #[macro_export]
109 macro_rules! vec_to_line_segment2 {
110 ($vec:expr) => {
111 LineSegment2 {
112 start: Vec2::new($vec[0], $vec[1]),
113 end: Vec2::new($vec[2], $vec[3]),
114 }
115 };
116 }
117
118 #[test]
119 fn test_intersecting_line() {
120 let l1 = [0.0f32, 0.0, 2.0, 2.0];
121 let l2 = [0.0f32, 2.0, 2.0, 0.0];
122 let n = vec_to_line_segment2!(l1);
123 let m = vec_to_line_segment2!(l2);
124 match line_intersection_2d(n, m) {
125 LineIntersection::Point(p) => {
126 assert!((p.x - 1.0).abs() < 1e-6);
127 assert!((p.y - 1.0).abs() < 1e-6);
128 },
129 _ => panic!("Should intersect at (1, 1)"),
130 }
131 }
132
133 #[test]
134 fn test_parallel_lines() {
135 let l1 = [0.0f64, 0.0, 1.0, 1.0];
136 let l2 = [0.0f64, 1.0, 1.0, 2.0];
137 let n = vec_to_line_segment2!(l1);
138 let m = vec_to_line_segment2!(l2);
139 match line_intersection_2d(n, m) {
140 LineIntersection::Parallel => {},
141 _ => panic!("Should be parallel"),
142 }
143 }
144
145 #[test]
146 fn test_coincident_lines() {
147 let l1 = [0.0f32, 0.0, 1.0, 1.0];
148 let l2 = [0.5f32, 0.5, 1.5, 1.5];
149 let n = vec_to_line_segment2!(l1);
150 let m = vec_to_line_segment2!(l2);
151 match line_intersection_2d(n, m) {
152 LineIntersection::Coincident => {},
153 _ => panic!("Should be coincident"),
154 }
155 }
156
157 #[test]
158 fn test_almost_parallel_lines() {
159 let l1 = [0.0f64, 0.0, 1.0, 1.0];
160 let l2 = [0.0f64, 1.0, 1.0, 1.999999];
161 let n = vec_to_line_segment2!(l1);
162 let m = vec_to_line_segment2!(l2);
163 let expect = [1000000.0000823, 1000000.0000823];
164 match line_intersection_2d(n, m) {
165 LineIntersection::Point(p) => {
166 assert!((p.x - expect[0]).abs() < 1e-6);
167 assert!((p.y - expect[1]).abs() < 1e-6);
168 },
169 _ => panic!("Should intersect"),
170 }
171 }
172}