1
1
use crate :: geometry:: Point ;
2
2
use std:: cmp:: Ordering ;
3
3
4
- fn point_cmp ( p1 : & Point , p2 : & Point ) -> Ordering {
4
+ fn cmp_x ( p1 : & Point , p2 : & Point ) -> Ordering {
5
5
let acmp = f64_cmp ( & p1. x , & p2. x ) ;
6
6
match acmp {
7
7
Ordering :: Equal => f64_cmp ( & p1. y , & p2. y ) ,
@@ -16,14 +16,19 @@ fn f64_cmp(a: &f64, b: &f64) -> Ordering {
16
16
/// returns the two closest points
17
17
/// or None if there are zero or one point
18
18
pub fn closest_points ( points : & [ Point ] ) -> Option < ( Point , Point ) > {
19
- let mut points: Vec < Point > = points. to_vec ( ) ;
20
- points. sort_by ( point_cmp) ;
19
+ let mut points_x: Vec < Point > = points. to_vec ( ) ;
20
+ points_x. sort_by ( cmp_x) ;
21
+ let mut points_y = points_x. clone ( ) ;
22
+ points_y. sort_by ( |p1 : & Point , p2 : & Point | -> Ordering { p1. y . partial_cmp ( & p2. y ) . unwrap ( ) } ) ;
21
23
22
- closest_points_aux ( & points , 0 , points . len ( ) )
24
+ closest_points_aux ( & points_x , points_y , 0 , points_x . len ( ) )
23
25
}
24
26
27
+ // We maintain two vectors with the same points, one sort by x coordinates and one sorted by y
28
+ // coordinates.
25
29
fn closest_points_aux (
26
- points : & [ Point ] ,
30
+ points_x : & [ Point ] ,
31
+ points_y : Vec < Point > ,
27
32
mut start : usize ,
28
33
mut end : usize ,
29
34
) -> Option < ( Point , Point ) > {
@@ -35,24 +40,38 @@ fn closest_points_aux(
35
40
36
41
if n <= 3 {
37
42
// bruteforce
38
- let mut min = points [ 0 ] . euclidean_distance ( & points [ 1 ] ) ;
39
- let mut pair = ( points [ 0 ] . clone ( ) , points [ 1 ] . clone ( ) ) ;
43
+ let mut min = points_x [ 0 ] . euclidean_distance ( & points_x [ 1 ] ) ;
44
+ let mut pair = ( points_x [ 0 ] . clone ( ) , points_x [ 1 ] . clone ( ) ) ;
40
45
41
46
for i in 1 ..n {
42
47
for j in ( i + 1 ) ..n {
43
- let new = points [ i] . euclidean_distance ( & points [ j] ) ;
48
+ let new = points_x [ i] . euclidean_distance ( & points_x [ j] ) ;
44
49
if new < min {
45
50
min = new;
46
- pair = ( points [ i] . clone ( ) , points [ j] . clone ( ) ) ;
51
+ pair = ( points_x [ i] . clone ( ) , points_x [ j] . clone ( ) ) ;
47
52
}
48
53
}
49
54
}
50
55
return Some ( pair) ;
51
56
}
52
57
53
58
let mid = ( start + end) / 2 ;
54
- let left = closest_points_aux ( points, start, mid) ;
55
- let right = closest_points_aux ( points, mid, end) ;
59
+ let mid_x = points_x[ mid] . x ;
60
+
61
+ // Separate points into y_left and y_right vectors based on their x-coordinate. Since y is
62
+ // already sorted by y_axis, y_left and y_right will also be sorted.
63
+ let mut y_left = vec ! [ ] ;
64
+ let mut y_right = vec ! [ ] ;
65
+ for point in & points_y {
66
+ if point. x < mid_x {
67
+ y_left. push ( point. clone ( ) ) ;
68
+ } else {
69
+ y_right. push ( point. clone ( ) ) ;
70
+ }
71
+ }
72
+
73
+ let left = closest_points_aux ( points_x, y_left, start, mid) ;
74
+ let right = closest_points_aux ( points_x, y_right, mid, end) ;
56
75
57
76
let ( mut min_sqr_dist, mut pair) = match ( left, right) {
58
77
( Some ( ( l1, l2) ) , Some ( ( r1, r2) ) ) => {
@@ -69,28 +88,24 @@ fn closest_points_aux(
69
88
( None , None ) => unreachable ! ( ) ,
70
89
} ;
71
90
72
- let mid_x = points[ mid] . x ;
73
91
let dist = min_sqr_dist;
74
- while points [ start] . x < mid_x - dist {
92
+ while points_x [ start] . x < mid_x - dist {
75
93
start += 1 ;
76
94
}
77
- while points [ end - 1 ] . x > mid_x + dist {
95
+ while points_x [ end - 1 ] . x > mid_x + dist {
78
96
end -= 1 ;
79
97
}
80
98
81
- let mut mids: Vec < & Point > = points[ start..end] . iter ( ) . collect ( ) ;
82
- mids. sort_by ( |a, b| f64_cmp ( & a. y , & b. y ) ) ;
83
-
84
- for ( i, e) in mids. iter ( ) . enumerate ( ) {
99
+ for ( i, e) in points_y. iter ( ) . enumerate ( ) {
85
100
for k in 1 ..8 {
86
- if i + k >= mids . len ( ) {
101
+ if i + k >= points_y . len ( ) {
87
102
break ;
88
103
}
89
104
90
- let new = e. euclidean_distance ( mids [ i + k] ) ;
105
+ let new = e. euclidean_distance ( & points_y [ i + k] ) ;
91
106
if new < min_sqr_dist {
92
107
min_sqr_dist = new;
93
- pair = ( ( * e) . clone ( ) , mids [ i + k] . clone ( ) ) ;
108
+ pair = ( ( * e) . clone ( ) , points_y [ i + k] . clone ( ) ) ;
94
109
}
95
110
}
96
111
}
0 commit comments