Skip to content

Commit db54413

Browse files
Sellig6792vil02
authored andcommitted
Implements Ramer-Douglas-Peucker simplification algorithm (#710)
* Implements Ramer-Douglas-Peucker simplification algorithm * Apply requested changes * Cargo format * Cargo clippy * Apply requested changes * style: use slice --------- Co-authored-by: Piotr Idzik <[email protected]>
1 parent c4c5068 commit db54413

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
* [Jarvis Scan](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/jarvis_scan.rs)
116116
* [Point](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/point.rs)
117117
* [Polygon Points](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/polygon_points.rs)
118+
* [Ramer-Douglas-Peucker](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/ramer_douglas_peucker.rs)
118119
* [Segment](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/segment.rs)
119120
* Graph
120121
* [Astar](https://github.com/TheAlgorithms/Rust/blob/master/src/graph/astar.rs)

src/geometry/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ mod graham_scan;
33
mod jarvis_scan;
44
mod point;
55
mod polygon_points;
6+
mod ramer_douglas_peucker;
67
mod segment;
78

89
pub use self::closest_points::closest_points;
910
pub use self::graham_scan::graham_scan;
1011
pub use self::jarvis_scan::jarvis_march;
1112
pub use self::point::Point;
1213
pub use self::polygon_points::lattice_points;
14+
pub use self::ramer_douglas_peucker::ramer_douglas_peucker;
1315
pub use self::segment::Segment;

src/geometry/ramer_douglas_peucker.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use crate::geometry::Point;
2+
3+
pub fn ramer_douglas_peucker(points: &[Point], epsilon: f64) -> Vec<Point> {
4+
if points.len() < 3 {
5+
return points.to_vec();
6+
}
7+
let mut dmax = 0.0;
8+
let mut index = 0;
9+
let end = points.len() - 1;
10+
11+
for i in 1..end {
12+
let d = perpendicular_distance(&points[i], &points[0], &points[end]);
13+
if d > dmax {
14+
index = i;
15+
dmax = d;
16+
}
17+
}
18+
19+
if dmax > epsilon {
20+
let mut results = ramer_douglas_peucker(&points[..=index], epsilon);
21+
results.pop();
22+
results.extend(ramer_douglas_peucker(&points[index..], epsilon));
23+
results
24+
} else {
25+
vec![points[0].clone(), points[end].clone()]
26+
}
27+
}
28+
29+
fn perpendicular_distance(p: &Point, a: &Point, b: &Point) -> f64 {
30+
let num = (b.y - a.y) * p.x - (b.x - a.x) * p.y + b.x * a.y - b.y * a.x;
31+
let den = a.euclidean_distance(b);
32+
num.abs() / den
33+
}
34+
35+
#[cfg(test)]
36+
mod tests {
37+
use super::*;
38+
39+
macro_rules! test_perpendicular_distance {
40+
($($name:ident: $test_case:expr,)*) => {
41+
$(
42+
#[test]
43+
fn $name() {
44+
let (p, a, b, expected) = $test_case;
45+
assert_eq!(perpendicular_distance(&p, &a, &b), expected);
46+
assert_eq!(perpendicular_distance(&p, &b, &a), expected);
47+
}
48+
)*
49+
};
50+
}
51+
52+
test_perpendicular_distance! {
53+
basic: (Point::new(4.0, 0.0), Point::new(0.0, 0.0), Point::new(0.0, 3.0), 4.0),
54+
basic_shifted_1: (Point::new(4.0, 1.0), Point::new(0.0, 1.0), Point::new(0.0, 4.0), 4.0),
55+
basic_shifted_2: (Point::new(2.0, 1.0), Point::new(-2.0, 1.0), Point::new(-2.0, 4.0), 4.0),
56+
}
57+
58+
#[test]
59+
fn test_ramer_douglas_peucker_polygon() {
60+
let a = Point::new(0.0, 0.0);
61+
let b = Point::new(1.0, 0.0);
62+
let c = Point::new(2.0, 0.0);
63+
let d = Point::new(2.0, 1.0);
64+
let e = Point::new(2.0, 2.0);
65+
let f = Point::new(1.0, 2.0);
66+
let g = Point::new(0.0, 2.0);
67+
let h = Point::new(0.0, 1.0);
68+
let polygon = vec![
69+
a.clone(),
70+
b,
71+
c.clone(),
72+
d,
73+
e.clone(),
74+
f,
75+
g.clone(),
76+
h.clone(),
77+
];
78+
let epsilon = 0.7;
79+
let result = ramer_douglas_peucker(&polygon, epsilon);
80+
assert_eq!(result, vec![a, c, e, g, h]);
81+
}
82+
83+
#[test]
84+
fn test_ramer_douglas_peucker_polygonal_chain() {
85+
let a = Point::new(0., 0.);
86+
let b = Point::new(2., 0.5);
87+
let c = Point::new(3., 3.);
88+
let d = Point::new(6., 3.);
89+
let e = Point::new(8., 4.);
90+
91+
let points = vec![a.clone(), b, c, d, e.clone()];
92+
93+
let epsilon = 3.; // The epsilon is quite large, so the result will be a single line
94+
let result = ramer_douglas_peucker(&points, epsilon);
95+
assert_eq!(result, vec![a, e]);
96+
}
97+
98+
#[test]
99+
fn test_less_than_three_points() {
100+
let a = Point::new(0., 0.);
101+
let b = Point::new(1., 1.);
102+
103+
let epsilon = 0.1;
104+
105+
assert_eq!(ramer_douglas_peucker(&[], epsilon), vec![]);
106+
assert_eq!(
107+
ramer_douglas_peucker(&[a.clone()], epsilon),
108+
vec![a.clone()]
109+
);
110+
assert_eq!(
111+
ramer_douglas_peucker(&[a.clone(), b.clone()], epsilon),
112+
vec![a, b]
113+
);
114+
}
115+
}

0 commit comments

Comments
 (0)