Skip to content

Commit

Permalink
Implement Graham Scan in Rust (algorithm-archivists#964)
Browse files Browse the repository at this point in the history
* 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 algorithm-archivists#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 <[email protected]>
Co-authored-by: Sammy Plat <[email protected]>
Co-authored-by: Dimitri Belopopsky <[email protected]>
  • Loading branch information
4 people authored Jan 12, 2022
1 parent 28287e3 commit 525205b
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ This file lists everyone, who contributed to this repo and wanted to show up her
- Henrik Abel Christensen
- K. Shudipto Amin
- Peanutbutter_Warrior
- Thijs Raymakers
84 changes: 84 additions & 0 deletions contents/graham_scan/code/rust/graham_scan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::cmp::Ordering;

#[derive(Debug, PartialEq, Copy, Clone)]
struct Point {
x: f64,
y: f64,
}

impl Eq for Point {}

impl PartialOrd for Point {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.y == other.y {
self.x.partial_cmp(&other.x)
} else {
self.y.partial_cmp(&other.y)
}
}
}

// Defines an order for Points so they can be sorted
impl Ord for Point {
fn cmp(&self, other: &Self) -> Ordering {
// Neither field of Point will be NaN, so this is safe
self.partial_cmp(other).unwrap()
}
}

// Determines whether the angle abc is clockwise, counter-clockwise or colinear
// result > 0 : counter-clockwise
// result = 0 : colinear
// result < 0 : clockwise
fn counter_clockwise(a: &Point, b: &Point, c: &Point) -> f64 {
(b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)
}

// Calculate the polar angle of a point relative to a reference point.
fn polar_angle(reference: &Point, point: &Point) -> f64 {
(point.y - reference.y).atan2(point.x - reference.x)
}

fn graham_scan(mut points: Vec<Point>) -> Vec<Point> {
if points.is_empty() {
return Vec::new();
}

// Unwrap is safe because length is > 0
let start = *points.iter().min().unwrap();
points.retain(|a| a != &start);
points.sort_unstable_by(|a, b| polar_angle(&start, a).partial_cmp(&polar_angle(&start, b)).unwrap());

let mut hull: Vec<Point> = vec![start, points[0], points[1]];

for pt in points[2..points.len()].iter() {
while counter_clockwise(&hull[hull.len() - 2], &hull[hull.len() - 1], pt) < 0.0 {
hull.pop();
}
hull.push(*pt);
}
hull
}

fn main() {
let points = vec![
Point { x: -5.0, y: 2.0 },
Point { x: 5.0, y: 7.0 },
Point { x: -6.0, y: -12.0 },
Point { x: -14.0, y: -14.0 },
Point { x: 9.0, y: 9.0 },
Point { x: -1.0, y: -1.0 },
Point { x: -10.0, y: 11.0 },
Point { x: -6.0, y: 15.0 },
Point { x: -6.0, y: -8.0 },
Point { x: 15.0, y: -9.0 },
Point { x: 7.0, y: -7.0 },
Point { x: -2.0, y: -9.0 },
Point { x: 6.0, y: -5.0 },
Point { x: 0.0, y: 14.0 },
Point { x: 2.0, y: 8.0 },
];

let hull_points = graham_scan(points);
println!("{:#?}", hull_points);
}
6 changes: 6 additions & 0 deletions contents/graham_scan/graham_scan.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ We can find whether a rotation is counter-clockwise with trigonometric functions
[import:18-20, lang="cpp"](code/cpp/graham_scan.cpp)
{% sample lang="coco" %}
[import:4-8, lang="coconut"](code/coconut/graham_scan.coco)
{% sample lang="rs" %}
[import:33-35, lang: "rust"](code/rust/graham_scan.rs)
{% endmethod %}

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

### Bibliography
Expand Down Expand Up @@ -95,6 +99,8 @@ In the end, the code should look something like this:
[import, lang="cpp"](code/cpp/graham_scan.cpp)
{%sample lang="coco" %}
[import, lang="coconut"](code/coconut/graham_scan.coco)
{% sample lang="rs" %}
[import, lang: "rust"](code/rust/graham_scan.rs)
{% endmethod %}

<script>
Expand Down

0 comments on commit 525205b

Please sign in to comment.