Skip to content

Commit 525205b

Browse files
PeanutbutterWarriorThijsRayAmarasShadowMitia
authored
Implement Graham Scan in Rust (#964)
* Implement Graham Scan in Rust * Add Thijs * Remove compiled binary * Define ordering of points for sorting * Clean up code * Change points to match other implementations * Apply requested changes from #479 * Fix function signature * Add function comment * Change algorithm to simpler version from python * Fix points to match other languages * Adjust line numbers for rust * Apply clippy suggestions Co-authored-by: Thijs Raymakers <thijs@raymakers.nl> Co-authored-by: Sammy Plat <amaras@vivaldi.net> Co-authored-by: Dimitri Belopopsky <ShadowMitia@users.noreply.github.com>
1 parent 28287e3 commit 525205b

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

CONTRIBUTORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,4 @@ This file lists everyone, who contributed to this repo and wanted to show up her
6363
- Henrik Abel Christensen
6464
- K. Shudipto Amin
6565
- Peanutbutter_Warrior
66+
- Thijs Raymakers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use std::cmp::Ordering;
2+
3+
#[derive(Debug, PartialEq, Copy, Clone)]
4+
struct Point {
5+
x: f64,
6+
y: f64,
7+
}
8+
9+
impl Eq for Point {}
10+
11+
impl PartialOrd for Point {
12+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
13+
if self.y == other.y {
14+
self.x.partial_cmp(&other.x)
15+
} else {
16+
self.y.partial_cmp(&other.y)
17+
}
18+
}
19+
}
20+
21+
// Defines an order for Points so they can be sorted
22+
impl Ord for Point {
23+
fn cmp(&self, other: &Self) -> Ordering {
24+
// Neither field of Point will be NaN, so this is safe
25+
self.partial_cmp(other).unwrap()
26+
}
27+
}
28+
29+
// Determines whether the angle abc is clockwise, counter-clockwise or colinear
30+
// result > 0 : counter-clockwise
31+
// result = 0 : colinear
32+
// result < 0 : clockwise
33+
fn counter_clockwise(a: &Point, b: &Point, c: &Point) -> f64 {
34+
(b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
35+
}
36+
37+
// Calculate the polar angle of a point relative to a reference point.
38+
fn polar_angle(reference: &Point, point: &Point) -> f64 {
39+
(point.y - reference.y).atan2(point.x - reference.x)
40+
}
41+
42+
fn graham_scan(mut points: Vec<Point>) -> Vec<Point> {
43+
if points.is_empty() {
44+
return Vec::new();
45+
}
46+
47+
// Unwrap is safe because length is > 0
48+
let start = *points.iter().min().unwrap();
49+
points.retain(|a| a != &start);
50+
points.sort_unstable_by(|a, b| polar_angle(&start, a).partial_cmp(&polar_angle(&start, b)).unwrap());
51+
52+
let mut hull: Vec<Point> = vec![start, points[0], points[1]];
53+
54+
for pt in points[2..points.len()].iter() {
55+
while counter_clockwise(&hull[hull.len() - 2], &hull[hull.len() - 1], pt) < 0.0 {
56+
hull.pop();
57+
}
58+
hull.push(*pt);
59+
}
60+
hull
61+
}
62+
63+
fn main() {
64+
let points = vec![
65+
Point { x: -5.0, y: 2.0 },
66+
Point { x: 5.0, y: 7.0 },
67+
Point { x: -6.0, y: -12.0 },
68+
Point { x: -14.0, y: -14.0 },
69+
Point { x: 9.0, y: 9.0 },
70+
Point { x: -1.0, y: -1.0 },
71+
Point { x: -10.0, y: 11.0 },
72+
Point { x: -6.0, y: 15.0 },
73+
Point { x: -6.0, y: -8.0 },
74+
Point { x: 15.0, y: -9.0 },
75+
Point { x: 7.0, y: -7.0 },
76+
Point { x: -2.0, y: -9.0 },
77+
Point { x: 6.0, y: -5.0 },
78+
Point { x: 0.0, y: 14.0 },
79+
Point { x: 2.0, y: 8.0 },
80+
];
81+
82+
let hull_points = graham_scan(points);
83+
println!("{:#?}", hull_points);
84+
}

contents/graham_scan/graham_scan.md

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ We can find whether a rotation is counter-clockwise with trigonometric functions
3232
[import:18-20, lang="cpp"](code/cpp/graham_scan.cpp)
3333
{% sample lang="coco" %}
3434
[import:4-8, lang="coconut"](code/coconut/graham_scan.coco)
35+
{% sample lang="rs" %}
36+
[import:33-35, lang: "rust"](code/rust/graham_scan.rs)
3537
{% endmethod %}
3638

3739
If the output of this function is 0, the points are collinear.
@@ -66,6 +68,8 @@ In the end, the code should look something like this:
6668
[import:26-62, lang="cpp"](code/cpp/graham_scan.cpp)
6769
{% sample lang="coco" %}
6870
[import:17-30, lang="coconut"](code/coconut/graham_scan.coco)
71+
{% sample lang="rs" %}
72+
[import:42-61, lang: "rust"](code/rust/graham_scan.rs)
6973
{% endmethod %}
7074

7175
### Bibliography
@@ -95,6 +99,8 @@ In the end, the code should look something like this:
9599
[import, lang="cpp"](code/cpp/graham_scan.cpp)
96100
{%sample lang="coco" %}
97101
[import, lang="coconut"](code/coconut/graham_scan.coco)
102+
{% sample lang="rs" %}
103+
[import, lang: "rust"](code/rust/graham_scan.rs)
98104
{% endmethod %}
99105

100106
<script>

0 commit comments

Comments
 (0)