-
-
Save kindlychung/07a20a2441ef35e1cd1f2a55d6087836 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Compiling algorithms_in_a_nutshell v0.1.0 (file:///Users/kaiyin/Documents/workspace-eclipse-neon/algorithms_in_a_nutshell) | |
error: `i` does not live long enough | |
--> src/convex_hull.rs:231:15 | |
| | |
229 | (0..5).into_iter().map(|j| { | |
| --- capture occurs here | |
230 | let j = j as f64; | |
231 | Point::new(i, j) | |
| ^ does not live long enough | |
232 | }) | |
233 | }).collect::<BTreeSet<Point>>(); | |
| - - borrowed value needs to live until here | |
| | | |
| borrowed value only lives until here | |
error: aborting due to previous error | |
Build failed, waiting for other jobs to finish... | |
error: Could not compile `algorithms_in_a_nutshell`. | |
To learn more, run the command again with --verbose. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::cmp::Ordering::Equal; | |
use std::cmp::Ordering; | |
use std::ops::{Add, Sub, Mul}; | |
use std::collections::BTreeSet; | |
#[derive(Debug, Copy, Clone)] | |
struct Point { | |
x: f64, | |
y: f64, | |
} | |
impl PartialEq for Point { | |
fn eq(&self, other: &Point) -> bool { | |
if self.x.is_nan() & other.x.is_nan() & self.y.is_nan() & other.y.is_nan() { | |
return true; | |
} | |
if self.x.is_nan() & other.x.is_nan() { | |
return self.y == other.y; | |
} | |
if self.y.is_nan() & other.y.is_nan() { | |
return self.x == other.x; | |
} | |
(self.x == other.x) & (self.y == other.y) | |
} | |
} | |
impl Eq for Point {} | |
impl PartialOrd for Point { | |
fn partial_cmp(&self, other: &Point) -> Option<Ordering> { | |
Some(self.cmp(other)) | |
} | |
} | |
impl Ord for Point { | |
fn cmp(&self, other: &Point) -> Ordering { | |
let c = self.x.partial_cmp(&other.x).unwrap(); | |
if c == Equal { | |
self.y.partial_cmp(&other.y).unwrap() | |
} else { | |
c | |
} | |
} | |
} | |
impl Point { | |
fn new(x: f64, y: f64) -> Point { | |
if x.is_nan() | y.is_nan() { | |
panic!("Coordinates cannot be NaN!"); | |
} | |
Point { | |
x: x, | |
y: y, | |
} | |
} | |
// Euclidean distance | |
fn distance(&self, other: &Point) -> f64 { | |
((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt() | |
} | |
// Draw a horizontal line through this point, connect this point with the other, and measure the angle between these two lines. | |
fn angle(&self, other: &Point) -> f64 { | |
(other.y - self.y).atan2(other.x - self.x) | |
} | |
} | |
impl Add for Point { | |
type Output = Point; | |
fn add(self, rhs: Point) -> Point { | |
Point::new(self.x + rhs.x, self.y + rhs.y) | |
} | |
} | |
impl Sub for Point { | |
type Output = Point; | |
fn sub(self, rhs: Point) -> Point { | |
Point::new(self.x - rhs.x, self.y - rhs.y) | |
} | |
} | |
// dot product | |
impl Mul for Point { | |
type Output = f64; | |
fn mul(self, rhs: Point) -> f64 { | |
self.x * rhs.x + self.y * rhs.y | |
} | |
} | |
#[test] | |
fn test_point() { | |
use std::f64::consts::PI; | |
let p1 = Point::new(0.0, 0.0); | |
let p2 = Point::new(0.0, 1.0); | |
assert_eq!(p1.angle(&p2), PI/2.0); | |
assert_eq!(p1.distance(&p2), 1.0); | |
let p1 = Point::new(0.0, 0.0); | |
let p2 = Point::new(1.0, 1.0); | |
assert_eq!(p1.angle(&p2), PI/4.0); | |
assert_eq!(p1.distance(&p2), 2.0f64.sqrt()); | |
let p1 = Point::new(0.0, 0.0); | |
let p2 = Point::new(1.0, -1.0); | |
assert_eq!(p1.angle(&p2), -PI/4.0); | |
assert_eq!(p1.distance(&p2), 2.0f64.sqrt()); | |
} | |
#[derive(PartialEq,Eq,Debug)] | |
struct Triangle { | |
p0: Point, | |
p1: Point, | |
p2: Point, | |
} | |
impl Triangle { | |
fn new(p0: Point, p1: Point, p2: Point) -> Triangle { | |
// Sort by x-coordinate to make sure the first point is the leftmost and lowest. | |
let mut v: Vec<Point> = vec![p0, p1, p2]; | |
v.sort(); | |
Triangle { | |
p0: v[0], | |
p1: v[1], | |
p2: v[2], | |
} | |
} | |
fn range_x(&self) -> (f64, f64) { | |
(self.p0.x, self.p2.x) | |
} | |
fn range_y(&self) -> (f64, f64) { | |
let mut v = vec![self.p0, self.p1, self.p2]; | |
v.sort_by(|a, b| { | |
a.y.partial_cmp(&(b.y)).unwrap_or(Equal) | |
}); | |
(v[0].y, v[2].y) | |
} | |
// Barycentric Technique, check whether point is in triangle, see http://blackpawn.com/texts/pointinpoly/ | |
fn contains(&self, p: Point) -> bool { | |
let v0 = self.p2 - self.p0; | |
let v1 = self.p1 - self.p0; | |
let v2 = p - self.p0; | |
let dot00 = v0 * v0; | |
let dot01 = v0 * v1; | |
let dot02 = v0 * v2; | |
let dot11 = v1 * v1; | |
let dot12 = v1 * v2; | |
let inv_denom = 1.0f64 / (dot00 * dot11 - dot01 * dot01); | |
let u = (dot11 * dot02 - dot01 * dot12) * inv_denom; | |
let v = (dot00 * dot12 - dot01 * dot02) * inv_denom; | |
(u >= 0.0) & (v >= 0.0) & (u + v <= 1.0) | |
} | |
} | |
#[macro_export] | |
macro_rules! btreeset { | |
($($x: expr),*) => {{ | |
let mut set = ::std::collections::BTreeSet::new(); | |
$( set.insert($x); )* | |
set | |
}} | |
} | |
fn convex_hull_naive(points: &BTreeSet<Point>) -> Vec<Point> { | |
// you must have at least 3 points, otherwise there is no hull | |
assert!(points.len() > 2); | |
// a fn for removing just one point from the set | |
let minus_one = |p: &Point| { | |
points.difference(&(btreeset!(p.clone()))).cloned().collect::<BTreeSet<Point>>() | |
}; | |
// set of points that are marked as internal | |
let mut p_internal_set: BTreeSet<Point> = BTreeSet::new(); | |
// check permutations of 4 points, check if the fourth point is contained in the triangle | |
for p_i in points { | |
let minus_i = minus_one(&p_i); | |
for p_j in &minus_i { | |
let minus_j = minus_one(&p_j); | |
for p_k in &minus_j { | |
let minus_k = minus_one(&p_k); | |
for p_m in &minus_k { | |
if Triangle::new(p_i.clone(), p_j.clone(), p_k.clone()).contains(p_m.clone()) { | |
p_internal_set.insert(p_m.clone()); | |
} | |
} | |
} | |
} | |
} | |
// set of points that are not internal | |
let mut hull = points.difference(&p_internal_set).cloned().collect::<Vec<Point>>(); | |
// sort by coordinates so that the first point is the leftmost | |
hull.sort(); | |
let head = hull[0].clone(); | |
let angle_to_head = |p: &Point| { | |
if p == &head { | |
0.0 | |
} else { | |
head.angle(p) | |
} | |
}; | |
// sort by the angle with the first point | |
// when that is equal, sort by x coord | |
// what that is equal, sort by y coord | |
hull.sort_by(|a, b| { | |
let angle_a = angle_to_head(&a); | |
let angle_b = angle_to_head(&b); | |
let res1 = angle_a.partial_cmp(&angle_b).unwrap(); | |
if res1 == Equal { | |
let res2 = a.x.partial_cmp(&b.x).unwrap(); | |
if res2 == Equal { | |
let res3 = b.y.partial_cmp(&a.y).unwrap(); | |
res3 | |
} else { | |
res2 | |
} | |
} else { | |
res1 | |
} | |
}); | |
hull | |
} | |
#[test] | |
fn test_convex_hull_naive() { | |
let points = (0..5).into_iter().flat_map(|i| { | |
let i = i as f64; | |
(0..5).into_iter().map(|j| { | |
let j = j as f64; | |
Point::new(i, j) | |
}) | |
}).collect::<BTreeSet<Point>>(); | |
let hull = convex_hull_naive(&points); | |
let hull_should_be = vec![ | |
Point::new(0.0, 0.0), | |
Point::new(1.0, 0.0), | |
Point::new(2.0, 0.0), | |
Point::new(3.0, 0.0), | |
Point::new(3.0, 1.0), | |
Point::new(3.0, 2.0), | |
Point::new(3.0, 3.0), | |
Point::new(2.0, 3.0), | |
Point::new(1.0, 3.0), | |
Point::new(0.0, 3.0), | |
Point::new(0.0, 2.0), | |
Point::new(0.0, 1.0), | |
]; | |
assert_eq!(hull, hull_should_be); | |
} | |
#[test] | |
fn test_triangle() { | |
let p2 = Point::new(0.0, 0.0); | |
let p1 = Point::new(0.0, 1.0); | |
let p0 = Point::new(1.0, 1.0); | |
let t = Triangle::new(p0, p1, p2); | |
assert_eq!(t.range_x(), (0.0, 1.0)); | |
assert_eq!(t.range_y(), (0.0, 1.0)); | |
assert_eq!(t.p0, p2); | |
assert_eq!(t.p1, p1); | |
assert_eq!(t.p2, p0); | |
// triangle should contain its vertices | |
assert!(t.contains(p0)); | |
assert!(t.contains(p1)); | |
assert!(t.contains(p2)); | |
// triangle should contain points on any side | |
assert!(t.contains(Point::new(0.5, 0.5))); | |
assert!(t.contains(Point::new(0.3, 0.3))); | |
assert!(t.contains(Point::new(0.2, 0.2))); | |
assert!(t.contains(Point::new(0.1, 0.1))); | |
assert!(t.contains(Point::new(0.0, 0.1))); | |
assert!(t.contains(Point::new(0.0, 0.2))); | |
assert!(t.contains(Point::new(0.1, 1.0))); | |
assert!(t.contains(Point::new(0.2, 1.0))); | |
assert!(!t.contains(Point::new(0.2, 1.1))); | |
// strictly inside the triangle | |
assert!(t.contains(Point::new(0.5, 0.51))); | |
assert!(t.contains(Point::new(0.51, 0.51))); | |
assert!(t.contains(Point::new(0.51, 0.52))); | |
let p2 = Point::new(0.0, 0.0); | |
let p1 = Point::new(0.5, 1.0); | |
let p0 = Point::new(1.0, 0.5); | |
let t = Triangle::new(p0, p1, p2); | |
assert_eq!(t.range_x(), (0.0, 1.0)); | |
assert_eq!(t.range_y(), (0.0, 1.0)); | |
assert_eq!(t.p0, p2); | |
assert_eq!(t.p1, p1); | |
assert_eq!(t.p2, p0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment