Skip to content

Commit

Permalink
feat(simplify): add simplify function
Browse files Browse the repository at this point in the history
Adds a simplify function that removes points less
than the specified epsilon distance from an
imaginary line that passes through its 2 adjecent
points.
  • Loading branch information
tirithen committed May 3, 2024
1 parent be676f5 commit 418b98f
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ doc-images = []
libc = "0.2"
clipper2c-sys = "0.1.0"
thiserror = "1.0.59"
embed-doc-image = "0.1"

[dev-dependencies]
macroquad = "0.4.5"
embed-doc-image = "0.1"

[package.metadata.docs.rs]
# docs.rs uses a nightly compiler, so by instructing it to use our `doc-images` feature we
Expand Down
Binary file added doc-assets/simplify.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions examples/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ pub fn draw_path(path: &Path, color: Color) {
}
}

pub fn draw_paths_points(paths: &Paths, color: Color) {
for path in paths.iter() {
draw_path_points(path, color);
}
}

pub fn draw_path_points(path: &Path, color: Color) {
for point in path.iter() {
draw_circle(
point.x() as f32 * SCALE,
point.y() as f32 * SCALE,
6.0,
color,
);
}
}

pub fn circle_path(offset: (f64, f64), radius: f64, segments: usize) -> Paths {
let mut points = vec![];

Expand Down
20 changes: 20 additions & 0 deletions examples/simplify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use clipper2::*;
use helpers::{draw_paths, draw_paths_points};
use macroquad::prelude::*;

mod helpers;

#[macroquad::main("Simplify")]
async fn main() {
let path: Paths = vec![(1.0, 2.0), (1.0, 2.5), (1.2, 4.0), (1.8, 6.0)].into();
let path_simplified = simplify(path.offset(3.0, 0.0), 0.5, false);

loop {
clear_background(BLACK);
draw_paths(&path, SKYBLUE);
draw_paths(&path_simplified, SKYBLUE);
draw_paths_points(&path, GREEN);
draw_paths_points(&path_simplified, GREEN);
next_frame().await
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
//! * [inflate](inflate())
//! * [intersect](intersect())
//! * [point_in_polygon](point_in_polygon())
//! * [simplify](simplify())
//! * [union](union())
//! * [xor](xor())
//!
Expand Down
2 changes: 2 additions & 0 deletions src/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ mod difference;
mod inflate;
mod intersect;
mod pointinpolygon;
mod simplify;
mod union;
mod xor;

pub use difference::*;
pub use inflate::*;
pub use intersect::*;
pub use pointinpolygon::*;
pub use simplify::*;
pub use union::*;
pub use xor::*;
59 changes: 59 additions & 0 deletions src/operations/simplify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use clipper2c_sys::{clipper_delete_paths64, clipper_paths64_simplify, clipper_paths64_size};

use crate::{malloc, Paths, PointScaler};

/// This function removes points that are less than the specified epsilon
/// distance from an imaginary line that passes through its 2 adjacent points.
/// Logically, smaller epsilon values will be less aggressive in removing
/// points than larger epsilon values.
///
/// This function is strongly recommended following offsetting
/// (ie inflating/shrinking), especially when offsetting paths multiple times.
/// Offsetting often creates tiny segments that don't improve path quality.
/// Further these tiny segments can be at angles that have been affected by
/// integer rounding. While these tiny segments are too small to be noticeable
/// following a single offset procedure, they can degrade both the shape quality
/// and the performance of subsequent offsets.
///
/// # Examples
///
/// ```rust
/// use clipper2::*;
///
/// let path: Paths = vec![(1.0, 2.0), (1.0, 2.5), (1.2, 4.0), (1.8, 6.0)].into();
/// let path_simplified = simplify(path.offset(3.0, 0.0), 0.5, false);
///
/// dbg!(path, path_simplified);
/// ```
/// ![Image displaying the result of the simplify example](https://raw.githubusercontent.com/tirithen/clipper2/main/doc-assets/simplify.png)
///
/// For more details see [simplify](https://www.angusj.com/clipper2/Docs/Units/Clipper/Functions/SimplifyPaths.htm).
pub fn simplify<P: PointScaler>(paths: Paths<P>, epsilon: f64, is_open: bool) -> Paths<P> {
let epsilon = P::scale(epsilon);

unsafe {
let mem = malloc(clipper_paths64_size());
let paths_ptr = paths.to_clipperpaths64();
let result_ptr = clipper_paths64_simplify(mem, paths_ptr, epsilon, is_open.into());
clipper_delete_paths64(paths_ptr);
let result = Paths::from_clipperpaths64(result_ptr);
clipper_delete_paths64(result_ptr);
result
}
}

#[cfg(test)]
mod test {
use crate::Centi;

use super::*;

#[test]
fn test_simplify() {
let path = vec![(0.0, 1.0), (0.1, 0.3), (1.0, 0.0), (1.3, 0.0), (2.0, 0.0)];
let expected_output = vec![vec![(0.0, 1.0), (2.0, 0.0)]];

let output: Vec<Vec<(f64, f64)>> = simplify::<Centi>(path.into(), 1.0, true).into();
assert_eq!(output, expected_output);
}
}

0 comments on commit 418b98f

Please sign in to comment.