From 2aee19d56f957889c372ad7de886bb7716bd9f41 Mon Sep 17 00:00:00 2001 From: Kade Date: Fri, 3 Mar 2023 16:51:17 -0500 Subject: [PATCH] feat!: arbitrary dimensional points (#23) * feat: arbitrary dimensions, first draft * refactor: math macros, loosen trait * docs: update for new point format * feat: tested, proper 2d point deserialization --- .gitignore | 1 + README.md | 23 ++-- benches/bench.rs | 24 ++-- src/lib.rs | 117 +++++------------ src/point.rs | 299 +++++++++++++++++++++++++++++++++++++++++++ src/serde.rs | 113 ++++++++++++++++ src/traits.rs | 6 +- tests/integration.rs | 14 +- 8 files changed, 485 insertions(+), 112 deletions(-) create mode 100644 src/point.rs create mode 100644 src/serde.rs diff --git a/.gitignore b/.gitignore index 4fffb2f..6d6c90f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +.vscode \ No newline at end of file diff --git a/README.md b/README.md index 5bd2393..c19e7dc 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ A Rust port of the Javascript [simplify-js](https://github.com/mourner/simplify- use simplify_polyline::*; fn main() { - let points = &[ - Point { x: 0.0, y: 0.0 }, Point { x: 1.0, y: 1.0 }, - Point { x: 2.0, y: 2.0 }, Point { x: 3.0, y: 3.0 }, - Point { x: 4.0, y: 4.0 } + let points = [ + Point { vec: [0.0, 0.0] }, Point { vec: [1.0, 1.0] }, + Point { vec: [2.0, 2.0] }, Point { vec: [3.0, 3.0] }, + Point { vec: [4.0, 4.0] } ]; // alternatively, use the point! macro - let points = &[ + let points = [ point!(0.0, 0.0), point!(1.0, 1.0), point!(2.0, 2.0), point!(3.0, 3.0), point!(4.0, 4.0) ]; @@ -24,15 +24,16 @@ fn main() { let points = points![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0), (3.0, 3.0), (4.0, 4.0)]; // low-quality simplification (fast) - let new_points = simplify(points, 1.0, false); + let new_points = simplify(&points, 1.0, false); // low-quality simplification (slower) - let new_points = simplify(points, 1.0, true); + let new_points = simplify(&points, 1.0, true); } ``` ## Features - `serde`, optional, defaults to off. Allows serializing/deserializing points. + - Note, this only works for some dimensions, and some formats. Read the docs for more info. ## Performance @@ -41,13 +42,13 @@ Measurements taken with an AMD Ryzen 7 5800x, in Pop!\_OS 22.04. | Test Case | simplify-polyline | simplify-js | | --------------------------------------- | ----------------: | ----------: | | 1118 Points, Low Quality, Tolerance 1 | 16.584 μs | 52.907 μs | -| 1118 Points, High Quality, Tolerance 1 | 30.910 μs | 85.653 μs | +| 1118 Points, High Quality, Tolerance 1 | 26.989 μs | 85.653 μs | | 1118 Points, Low Quality, Tolerance 5 | 3.987 μs | 12.840 μs | -| 1118 Points, High Quality, Tolerance 5 | 23.215 μs | 57.901 μs | +| 1118 Points, High Quality, Tolerance 5 | 19.497 μs | 57.901 μs | | 73752 Points, Low Quality, Tolerance 1 | 82.251 μs | 273.075 μs | -| 73752 Points, High Quality, Tolerance 1 | 1974.004 μs | 5376.344 μs | +| 73752 Points, High Quality, Tolerance 1 | 1933.700 μs | 5376.344 μs | | 73752 Points, Low Quality, Tolerance 5 | 54.150 μs | 181.554 μs | -| 73752 Points, High Quality, Tolerance 5 | 1460.742 μs | 3921.569 μs | +| 73752 Points, High Quality, Tolerance 5 | 1458.900 μs | 3921.569 μs | ## Contributing diff --git a/benches/bench.rs b/benches/bench.rs index fa01c0a..86b39d1 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -5,57 +5,65 @@ use simplify_polyline::{simplify, Point}; pub const BENCH_FIXTURE_1118: &str = include_str!("../fixtures/bench-1118.json"); pub const BENCH_FIXTURE_73752: &str = include_str!("../fixtures/bench-73752.json"); +fn fixture_1118() -> Vec> { + serde_json::from_str::>>(BENCH_FIXTURE_1118).unwrap() +} + +fn fixture_73752() -> Vec> { + serde_json::from_str::>>(BENCH_FIXTURE_73752).unwrap() +} + fn simplify_hq_1118_pts(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_1118).unwrap(); + let points = fixture_1118(); c.bench_function("simplify_hq_1118_pts", |b| { b.iter(|| simplify(&points, 1.0, true)) }); } fn simplify_lq_1118_pts(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_1118).unwrap(); + let points = fixture_1118(); c.bench_function("simplify_lq_1118_pts", |b| { b.iter(|| simplify(&points, 1.0, false)) }); } fn simplify_hq_1118_pts_tol5(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_1118).unwrap(); + let points = fixture_1118(); c.bench_function("simplify_hq_1118_pts_tol5", |b| { b.iter(|| simplify(&points, 5.0, true)) }); } fn simplify_lq_1118_pts_tol5(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_1118).unwrap(); + let points = fixture_1118(); c.bench_function("simplify_lq_1118_pts_tol5", |b| { b.iter(|| simplify(&points, 5.0, false)) }); } fn simplify_hq_73752_pts(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_73752).unwrap(); + let points = fixture_73752(); c.bench_function("simplify_hq_73752_pts", |b| { b.iter(|| simplify(&points, 1.0, true)) }); } fn simplify_lq_73752_pts(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_73752).unwrap(); + let points = fixture_73752(); c.bench_function("simplify_lq_73752_pts", |b| { b.iter(|| simplify(&points, 1.0, false)) }); } fn simplify_hq_73752_pts_tol5(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_73752).unwrap(); + let points = fixture_73752(); c.bench_function("simplify_hq_73752_pts_tol5", |b| { b.iter(|| simplify(&points, 5.0, true)) }); } fn simplify_lq_73752_pts_tol5(c: &mut Criterion) { - let points = serde_json::from_str::>>(BENCH_FIXTURE_73752).unwrap(); + let points = fixture_73752(); c.bench_function("simplify_lq_73752_pts_tol5", |b| { b.iter(|| simplify(&points, 5.0, false)) }); diff --git a/src/lib.rs b/src/lib.rs index db541a7..8ec89a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,98 +3,46 @@ pub use traits::ExtendedNumOps; +/// stub #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +pub mod serde; +mod point; mod traits; -/// A two-diemensional point, where the value of each coordinate must implement the -/// [ExtendedNumOps] trait. -/// -/// Example: -/// ``` -/// use simplify_polyline::*; -/// -/// let point = Point { x: 1.0, y: 1.0 }; -/// ``` -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Point { - /// The x coordinate value. - pub x: T, - /// The y coordinate value. - pub y: T, -} +pub use point::Point; -/// Creates a [Point] struct, used for input and output for [simplify]. A type should be specified -/// as the first argument that implements the [ExtendedNumOps] trait. -/// -/// Example -/// ``` -/// use simplify_polyline::*; -/// -/// let point = point!(1.0, 1.0); -/// ``` -#[macro_export] -macro_rules! point { - ($x:expr,$y:expr) => { - Point { x: $x, y: $y } - }; -} +fn get_sq_seg_dist( + pt: &Point, + start: &Point, + end: &Point, +) -> T { + let mut intersection = *start; + let difference = end - start; -/// Creates a &[[Point]] array, used for used for input and output for [simplify]. A type should -/// be specified as the first argument that implements the [ExtendedNumOps] trait. Point values -/// should be specified as length-2 tuples, where each value matches the input type, in (x, y) -/// order. -/// -/// Example -/// ``` -/// use simplify_polyline::*; -/// -/// let points = points![(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)]; -/// ``` -#[macro_export] -macro_rules! points { - ($(($x:expr,$y:expr)),*) => { - &[$(Point { x: $x, y: $y }),*] - }; -} - -fn get_sq_dist(p1: &Point, p2: &Point) -> T { - let dx = p2.x - p1.x; - let dy = p2.y - p1.y; - - dx * dx + dy * dy -} - -fn get_sq_seg_dist(pt: &Point, start: &Point, end: &Point) -> T { - let (mut x, mut y, mut dx, mut dy) = (start.x, start.y, end.x - start.x, end.y - start.y); - - if !dx.is_zero() || !dy.is_zero() { - let t = ((pt.x - x) * dx + (pt.y - y) * dy) / (dx * dx + dy * dy); + if !difference.is_origin() { + let t = ((pt - start) * difference).value_sum() / difference.sq_dist_origin(); if t > T::one() { - x = end.x; - y = end.y; + intersection = *end; } else if t > T::zero() { - x = x + (dx * t); - y = y + (dy * t); + intersection = intersection + (difference * t) } } - dx = pt.x - x; - dy = pt.y - y; - - dx * dx + dy * dy + (pt - intersection).sq_dist_origin() } -fn simplify_radial_dist(points: &[Point], tolerance: T) -> Vec> { +fn simplify_radial_dist( + points: &[Point], + tolerance: T, +) -> Vec> { let mut prev_point = points[0]; let mut new_points = vec![prev_point]; let mut point = prev_point; for pt in points.iter().skip(1) { point = *pt; - if get_sq_dist(pt, &prev_point) > tolerance { + if pt.sq_dist(&prev_point) > tolerance { new_points.push(*pt); prev_point = *pt; } @@ -107,12 +55,12 @@ fn simplify_radial_dist(points: &[Point], tolerance: T) -> new_points } -fn simplify_dp_step( - points: &[Point], +fn simplify_dp_step( + points: &[Point], first: usize, last: usize, tolerance: T, - simplified: &mut Vec>, + simplified: &mut Vec>, ) { let mut max_sq_dist = tolerance; let mut max_index = 0; @@ -136,7 +84,10 @@ fn simplify_dp_step( } } -fn simplify_douglas_peucker(points: &[Point], tolerance: T) -> Vec> { +fn simplify_douglas_peucker( + points: &[Point], + tolerance: T, +) -> Vec> { let mut simplified = vec![points[0]]; simplify_dp_step(points, 0, points.len() - 1, tolerance, &mut simplified); simplified.push(points[points.len() - 1]); @@ -156,18 +107,16 @@ fn simplify_douglas_peucker(points: &[Point], tolerance: T /// algorithm. /// - `false`: the list of points are first filtered using a simple radial distance algorithm, /// and then passed to the the Douglas-Peucker algorithm for final simplification. -pub fn simplify( - points: &[Point], - tolerance: f64, +pub fn simplify( + points: &[Point], + tolerance: T, high_quality: bool, -) -> Vec> { +) -> Vec> { if points.len() <= 2 { return points.to_vec(); } - let tolerance_t = T::from_f64(tolerance).unwrap_or_else(T::one); - - let tolerance_sq = tolerance_t * tolerance_t; + let tolerance_sq = tolerance * tolerance; let intermediate = if high_quality { points.to_vec() } else { diff --git a/src/point.rs b/src/point.rs new file mode 100644 index 0000000..d06e4a0 --- /dev/null +++ b/src/point.rs @@ -0,0 +1,299 @@ +use std::ops::{Add, Mul, Sub}; + +use crate::ExtendedNumOps; + +/// The basic input type for constructing and simplifying a polyline. It can have any number of components, and will +/// work with components that implement the [ExtendedNumOps] type. Dimensionality must be determined at compile time. +/// +/// This also implements some basic math operations between points, like: +/// - addition +/// - subtraction +/// - component-wise multiplication +/// - scalar multiplication +/// +/// ## Example +/// ``` +/// use simplify_polyline::*; +/// +/// let point2d: Point<2, i32> = Point { vec: [1, 1] }; +/// let another_point2d: Point<2, i32> = Point { vec: [2, 2] }; +/// +/// assert_eq!(point2d + another_point2d, Point { vec: [3, 3] }); +/// assert_eq!(point2d - another_point2d, Point { vec: [-1, -1] }); +/// assert_eq!(point2d * another_point2d, Point { vec: [2, 2] }); +/// assert_eq!(point2d * 7, Point { vec: [7, 7] }); +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Point { + /// The components of the point. + pub vec: [T; D], +} + +impl Point { + /// Computes the squared distance between two points. + #[inline(always)] + pub fn sq_dist(&self, other: &Point) -> T { + match D { + 2 => { + let xdist = other.vec[0] - self.vec[0]; + let ydist = other.vec[1] - self.vec[1]; + (xdist * xdist) + (ydist * ydist) + } + 3 => { + let xdist = other.vec[0] - self.vec[0]; + let ydist = other.vec[1] - self.vec[1]; + let zdist = other.vec[2] - self.vec[2]; + (xdist * xdist) + (ydist * ydist) + (zdist * zdist) + } + 4 => { + let xdist = other.vec[0] - self.vec[0]; + let ydist = other.vec[1] - self.vec[1]; + let zdist = other.vec[2] - self.vec[2]; + let wdist = other.vec[3] - self.vec[3]; + (xdist * xdist) + (ydist * ydist) + (zdist * zdist) + (wdist * wdist) + } + _ => self + .vec + .iter() + .zip(other.vec) + .map(|(a, b)| b - *a) + .reduce(|acc, v| acc + (v * v)) + .unwrap_or_else(|| T::zero()), + } + } + + /// Computes the squared distance between this point, and the origin. + #[inline(always)] + pub fn sq_dist_origin(&self) -> T { + match D { + 2 => (self.vec[0] * self.vec[0]) + (self.vec[1] * self.vec[1]), + 3 => { + (self.vec[0] * self.vec[0]) + + (self.vec[1] * self.vec[1]) + + (self.vec[2] * self.vec[2]) + } + 4 => { + (self.vec[0] * self.vec[0]) + + (self.vec[1] * self.vec[1]) + + (self.vec[2] * self.vec[2]) + + (self.vec[3] * self.vec[3]) + } + _ => self.sq_dist(&Point { + vec: [T::zero(); D], + }), + } + } + + /// Computes the sum of each value of the point. + #[inline(always)] + pub fn value_sum(&self) -> T { + match D { + 2 => self.vec[0] + self.vec[1], + 3 => self.vec[0] + self.vec[1] + self.vec[2], + 4 => self.vec[0] + self.vec[1] + self.vec[2] + self.vec[3], + _ => self + .vec + .into_iter() + .reduce(|acc, v| acc + v) + .unwrap_or_else(|| T::zero()), + } + } + + /// Checks if the point is the origin point (all values at zero). + #[inline(always)] + pub fn is_origin(&self) -> bool { + let zero = T::zero(); + match D { + 2 => self.vec[0] == zero && self.vec[1] == zero, + 3 => self.vec[0] == zero && self.vec[1] == zero && self.vec[2] == zero, + 4 => { + self.vec[0] == zero + && self.vec[1] == zero + && self.vec[2] == zero + && self.vec[3] == zero + } + _ => self.vec.iter().all(|v| v == &zero), + } + } +} + +macro_rules! impl_ref_op { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + impl<'a, const D: usize, T: ExtendedNumOps> $imp<$u> for &'a $t { + type Output = $t; + + #[inline(always)] + fn $method(self, other: $u) -> Self::Output { + $imp::$method(*self, other) + } + } + + impl<'b, const D: usize, T: ExtendedNumOps> $imp<&'b $u> for $t { + type Output = $t; + + #[inline(always)] + fn $method(self, other: &'b $u) -> Self::Output { + $imp::$method(self, *other) + } + } + + impl<'a, 'b, const D: usize, T: ExtendedNumOps> $imp<&'b $u> for &'a $t { + type Output = $t; + + #[inline(always)] + fn $method(self, other: &'b $u) -> Self::Output { + $imp::$method(*self, *other) + } + } + }; +} + +impl Add> for Point { + type Output = Point; + + #[inline(always)] + fn add(self, rhs: Point) -> Self::Output { + match D { + 2 => { + let mut new_values = [T::zero(); D]; + new_values[0] = self.vec[0] + rhs.vec[0]; + new_values[1] = self.vec[1] + rhs.vec[1]; + Point { vec: new_values } + } + _ => { + let mut i = 0usize; + let mut new_values = [T::zero(); D]; + while i < self.vec.len() { + new_values[i] = self.vec[i] + rhs.vec[i]; + i += 1; + } + Point { vec: new_values } + } + } + } +} +impl_ref_op!(impl Add, add for Point, Point); + +impl Sub> for Point { + type Output = Point; + + #[inline(always)] + fn sub(self, rhs: Point) -> Self::Output { + match D { + 2 => { + let mut new_values = [T::zero(); D]; + new_values[0] = self.vec[0] - rhs.vec[0]; + new_values[1] = self.vec[1] - rhs.vec[1]; + Point { vec: new_values } + } + _ => { + let mut i = 0usize; + let mut new_values = [T::zero(); D]; + while i < self.vec.len() { + new_values[i] = self.vec[i] - rhs.vec[i]; + i += 1; + } + Point { vec: new_values } + } + } + } +} +impl_ref_op!(impl Sub, sub for Point, Point); + +impl Mul> for Point { + type Output = Point; + + #[inline(always)] + fn mul(self, rhs: Point) -> Self::Output { + match D { + 2 => { + let mut new_values = [T::zero(); D]; + new_values[0] = self.vec[0] * rhs.vec[0]; + new_values[1] = self.vec[1] * rhs.vec[1]; + Point { vec: new_values } + } + _ => { + let mut i = 0usize; + let mut new_values = [T::zero(); D]; + while i < self.vec.len() { + new_values[i] = self.vec[i] * rhs.vec[i]; + i += 1; + } + Point { vec: new_values } + } + } + } +} +impl_ref_op!(impl Mul, mul for Point, Point); + +impl Mul for Point { + type Output = Point; + + #[inline(always)] + fn mul(self, rhs: T) -> Self::Output { + match D { + 2 => { + let mut new_values = [T::zero(); D]; + new_values[0] = self.vec[0] * rhs; + new_values[1] = self.vec[1] * rhs; + Point { vec: new_values } + } + _ => { + let mut i = 0usize; + let mut new_values = [T::zero(); D]; + while i < self.vec.len() { + new_values[i] = self.vec[i] * rhs; + i += 1; + } + Point { vec: new_values } + } + } + } +} +impl_ref_op!(impl Mul, mul for Point, T); + +/// Creates a single [Point], where the dimension is determined from the number of values specified, and the values all +/// must implement [ExtendedNumOps] and be of the same type. +/// +/// ## Example +/// ``` +/// use simplify_polyline::*; +/// +/// // 2d point +/// let point2d: Point<2, i32> = point!(1, 2); +/// assert_eq!(point2d.vec.len(), 2); +/// +/// // 8d point +/// let point8d: Point<8, i32> = point!(1, 2, 3, 4, 5, 6, 7, 8); +/// assert_eq!(point8d.vec.len(), 8); +/// ``` +#[macro_export] +macro_rules! point { + ($($values:expr),+) => { + Point { vec: [$($values),+] } + }; +} + +/// Creates a [Point] array, where the dimension is determined by the length of the tuples in the array, and the values +/// all must implement [ExtendedNumOps] and be of the same type. Point tuples must all be the same length. +/// +/// ## Example +/// ``` +/// use simplify_polyline::*; +/// +/// // 2d array +/// let points2d: [Point<2, f64>; 3] = points![(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)]; +/// assert_eq!(points2d.len(), 3); +/// assert_eq!(points2d[0].vec.len(), 2); +/// +/// // 4d array +/// let points4d: [Point<4, f64>; 2] = points![(1.0, 2.0, 3.0, 4.0), (5.0, 6.0, 7.0, 8.0)]; +/// assert_eq!(points4d.len(), 2); +/// assert_eq!(points4d[0].vec.len(), 4); +/// ``` +#[macro_export] +macro_rules! points { + ($(($($values:expr),+)),*) => { + [$(Point { vec: [$($values),+] }),*] + }; +} diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 0000000..b697bb9 --- /dev/null +++ b/src/serde.rs @@ -0,0 +1,113 @@ +//! # `serde` support +//! +//! Support is somewhat limited, only 2D point types are available at the moment. +//! +//! This will eagerly parse any map-like object with an `x` and `y` property that +//! holds a deserialize-able number type, even if other keys may be present in the map. +//! +//! Much of this implementation was designed around JSON input data. If assumptions made by that don't hold for +//! your particular input and this fails when it shouldn't, open an issue. +//! +//! ## Example (JSON) +//! +//! Using `serde_json`: +//! +//! ```rust +//! use simplify_polyline::*; +//! use serde_json::from_str; +//! +//! let point_json = r#"{ "x": 5, "y": 5 }"#; +//! let point: Point<2, f64> = serde_json::from_str(point_json).unwrap(); +//! ``` + +use crate::{ExtendedNumOps, Point}; +use serde::{ + de::{self, MapAccess, Visitor}, + Deserialize, +}; +use std::{ + fmt::{self, Formatter}, + marker::PhantomData, +}; + +struct Point2DVisitor(PhantomData); + +impl<'de, T: ExtendedNumOps + Deserialize<'de>> Visitor<'de> for Point2DVisitor { + type Value = Point<2, T>; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "a 2-dimensional point") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut x_value: Option = None; + let mut y_value: Option = None; + + loop { + if let Ok(maybe_entry) = map.next_entry::() { + if let Some((key, value)) = maybe_entry { + match key.as_str() { + "x" => x_value = Some(value), + "y" => y_value = Some(value), + _ => {} + } + } else { + break; + } + } + } + + match (x_value, y_value) { + (Some(x), Some(y)) => Ok(Point { vec: [x, y] }), + (None, Some(_)) => Err(de::Error::missing_field("x")), + (Some(_), None) => Err(de::Error::missing_field("y")), + _ => Err(de::Error::missing_field("x AND y")), + } + } +} + +impl<'de, T: ExtendedNumOps + Deserialize<'de>> Deserialize<'de> for Point<2, T> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_map(Point2DVisitor(PhantomData)) + } +} + +#[cfg(test)] +mod tests { + use crate::Point; + use serde_json::{from_value, json}; + + #[test] + fn err_map_no_x_or_y() { + let json_data = json!({ "other": "properties", "even_nums": 9 }); + let point = from_value::>(json_data); + assert!(point.is_err()); + } + + #[test] + fn err_map_no_x() { + let json_data = json!({ "other": "properties", "even_nums": 9, "y": 5 }); + let point = from_value::>(json_data); + assert!(point.is_err()); + } + + #[test] + fn err_map_no_y() { + let json_data = json!({ "other": "properties", "even_nums": 9, "x": 5 }); + let point = from_value::>(json_data); + assert!(point.is_err()); + } + + #[test] + fn ok_x_and_y() { + let json_data = json!({ "other": "properties", "even_nums": 9, "x": 5, "y": 5 }); + let point = from_value::>(json_data); + assert!(point.is_ok()); + } +} diff --git a/src/traits.rs b/src/traits.rs index 404eacb..0c1294d 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,5 +1,5 @@ -use num_traits::{FromPrimitive, Num}; +use num_traits::Num; /// Required traits a number type must implement to be used with [simplify](crate::simplify). -pub trait ExtendedNumOps: Num + Clone + Copy + PartialOrd + FromPrimitive {} -impl ExtendedNumOps for T where T: Num + Clone + Copy + PartialOrd + FromPrimitive {} +pub trait ExtendedNumOps: Num + Clone + Copy + PartialOrd {} +impl ExtendedNumOps for T where T: Num + Clone + Copy + PartialOrd {} diff --git a/tests/integration.rs b/tests/integration.rs index df5e823..3728942 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -2,14 +2,14 @@ use simplify_polyline::{point, points, simplify, Point}; #[test] fn returns_empty_vec_if_no_points() { - let result = simplify::(&[], 1.0, false); + let result = simplify::<2, f64>(&[], 1.0, false); assert_eq!(result.len(), 0); } #[test] fn returns_same_vec_if_one_point() { - let input1 = points![(0.0, 0.0)]; - let result1 = simplify(input1, 1.0, false); + let input1: [Point<2, f64>; 1] = points![(0.0, 0.0)]; + let result1 = simplify(&input1, 1.0, false); assert_eq!(result1[0], input1[0]); let input2 = point!(0.0, 0.0); @@ -19,9 +19,11 @@ fn returns_same_vec_if_one_point() { #[test] fn matches_expected_output() { - let input = serde_json::from_str::>>(include_str!("../fixtures/test-case.json")); - let expected_output = - serde_json::from_str::>>(include_str!("../fixtures/test-case-output.json")); + let input = + serde_json::from_str::>>(include_str!("../fixtures/test-case.json")); + let expected_output = serde_json::from_str::>>(include_str!( + "../fixtures/test-case-output.json" + )); assert!(input.is_ok()); assert!(expected_output.is_ok());