diff --git a/Cargo.toml b/Cargo.toml index b1c4daad..d976d8a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,13 @@ categories = ["graphics"] features = ["mint", "schemars", "serde"] [features] -default = ["std"] -std = [] +default = ["std", "alloc"] +std = ["alloc"] +alloc = ["serde/alloc", "smallvec"] -[dependencies] -smallvec = "1.10" +[dependencies.smallvec] +version = "1.10" +optional = true [dependencies.arrayvec] version = "0.7.1" @@ -41,7 +43,7 @@ optional = true version = "1.0.105" optional = true default-features = false -features = ["alloc", "derive"] +features = ["derive"] # This is used for research but not really needed; maybe refactor. [dev-dependencies] diff --git a/src/bezpath.rs b/src/bezpath.rs index eddd03e7..aa713730 100644 --- a/src/bezpath.rs +++ b/src/bezpath.rs @@ -9,6 +9,7 @@ use core::iter::{Extend, FromIterator}; use core::mem; use core::ops::{Mul, Range}; +#[cfg(feature = "alloc")] use alloc::vec::Vec; use arrayvec::ArrayVec; @@ -103,6 +104,7 @@ use crate::common::FloatFuncs; #[derive(Clone, Default, Debug, PartialEq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg(feature = "alloc")] pub struct BezPath(Vec); /// The element of a Bézier path. @@ -173,6 +175,7 @@ pub struct MinDistance { pub t2: f64, } +#[cfg(feature = "alloc")] impl BezPath { /// Create a new path. pub fn new() -> BezPath { @@ -488,6 +491,7 @@ impl BezPath { /// Helper for reversing a subpath. /// /// The `els` parameter must not contain any `MoveTo` or `ClosePath` elements. +#[cfg(feature = "alloc")] fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) { let end_pt = els.last().and_then(|el| el.end_point()).unwrap_or(start_pt); reversed.push(PathEl::MoveTo(end_pt)); @@ -506,6 +510,7 @@ fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) { } } +#[cfg(feature = "alloc")] impl FromIterator for BezPath { fn from_iter>(iter: T) -> Self { let el_vec: Vec<_> = iter.into_iter().collect(); @@ -517,6 +522,7 @@ impl FromIterator for BezPath { /// /// Note: the semantics are slightly different from simply iterating over the /// slice, as it returns `PathEl` items, rather than references. +#[cfg(feature = "alloc")] impl<'a> IntoIterator for &'a BezPath { type Item = PathEl; type IntoIter = core::iter::Cloned>; @@ -526,6 +532,7 @@ impl<'a> IntoIterator for &'a BezPath { } } +#[cfg(feature = "alloc")] impl IntoIterator for BezPath { type Item = PathEl; type IntoIter = alloc::vec::IntoIter; @@ -535,6 +542,7 @@ impl IntoIterator for BezPath { } } +#[cfg(feature = "alloc")] impl Extend for BezPath { fn extend>(&mut self, iter: I) { self.0.extend(iter); @@ -549,6 +557,7 @@ const TO_QUAD_TOL: f64 = 0.1; /// See [`BezPath::flatten`] for more discussion. /// This signature is a bit more general, allowing flattening of `&[PathEl]` slices /// and other iterators yielding `PathEl`. +#[cfg(feature = "alloc")] pub fn flatten( path: impl IntoIterator, tolerance: f64, @@ -662,6 +671,7 @@ impl Mul for Affine { } } +#[cfg(feature = "alloc")] impl Mul for Affine { type Output = BezPath; @@ -670,6 +680,7 @@ impl Mul for Affine { } } +#[cfg(feature = "alloc")] impl<'a> Mul<&'a BezPath> for Affine { type Output = BezPath; @@ -704,6 +715,7 @@ impl Mul for TranslateScale { } } +#[cfg(feature = "alloc")] impl Mul for TranslateScale { type Output = BezPath; @@ -712,6 +724,7 @@ impl Mul for TranslateScale { } } +#[cfg(feature = "alloc")] impl<'a> Mul<&'a BezPath> for TranslateScale { type Output = BezPath; @@ -1277,6 +1290,7 @@ impl From for PathSeg { } } +#[cfg(feature = "alloc")] impl Shape for BezPath { type PathElementsIter<'iter> = core::iter::Copied>; @@ -1366,6 +1380,7 @@ impl<'a> Shape for &'a [PathEl] { self.iter().copied() } + #[cfg(feature = "alloc")] fn to_path(&self, _tolerance: f64) -> BezPath { BezPath::from_vec(self.to_vec()) } @@ -1406,6 +1421,7 @@ impl Shape for [PathEl; N] { self.iter().copied() } + #[cfg(feature = "alloc")] fn to_path(&self, _tolerance: f64) -> BezPath { BezPath::from_vec(self.to_vec()) } diff --git a/src/cubicbez.rs b/src/cubicbez.rs index 3a59f8f3..c817e2cf 100644 --- a/src/cubicbez.rs +++ b/src/cubicbez.rs @@ -3,12 +3,17 @@ //! Cubic Bézier segments. +#[cfg(feature = "alloc")] use alloc::vec; +#[cfg(feature = "alloc")] use alloc::vec::Vec; use core::ops::{Mul, Range}; +#[cfg(feature = "alloc")] +use crate::QuadSpline; + use crate::MAX_EXTREMA; -use crate::{Line, QuadSpline, Vec2}; +use crate::{Line, Vec2}; use arrayvec::ArrayVec; use crate::common::{ @@ -100,11 +105,13 @@ impl CubicBez { /// /// Returns `None` if no suitable approximation is found within the given /// tolerance. + #[cfg(feature = "alloc")] pub fn approx_spline(&self, accuracy: f64) -> Option { (1..=MAX_SPLINE_SPLIT).find_map(|n| self.approx_spline_n(n, accuracy)) } // Approximate a cubic curve with a quadratic spline of `n` curves + #[cfg(feature = "alloc")] fn approx_spline_n(&self, n: usize, accuracy: f64) -> Option { if n == 1 { return self @@ -687,7 +694,7 @@ impl ParamCurveExtrema for CubicBez { let d2 = self.p3 - self.p2; one_coord(&mut result, d0.x, d1.x, d2.x); one_coord(&mut result, d0.y, d1.y, d2.y); - result.sort_by(|a, b| a.partial_cmp(b).unwrap()); + result.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); result } } @@ -734,6 +741,7 @@ impl Iterator for ToQuads { /// Ensures that the resulting splines have the same number of control points. /// /// Rust port of cu2qu [cubic_approx_quadratic](https://github.com/fonttools/fonttools/blob/3b9a73ff8379ab49d3ce35aaaaf04b3a7d9d1655/Lib/fontTools/cu2qu/cu2qu.py#L322) +#[cfg(feature = "alloc")] pub fn cubics_to_quadratic_splines(curves: &[CubicBez], accuracy: f64) -> Option> { let mut result = Vec::new(); let mut split_order = 0; diff --git a/src/lib.rs b/src/lib.rs index 3c5563a8..3841062a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,8 +61,11 @@ //! math functionality. The `std` feature is enabled by default, but can be //! disabled, as long as the `libm` feature is enabled. This is useful for //! `no_std` environments. However, note that the `libm` crate is not as -//! efficient as the standard library, and that this crate still uses the -//! `alloc` crate regardless. +//! efficient as the standard library. +//! +//! The `alloc` feature (which is enabled by default) may be turned off to +//! disable all allocations. This removes support for [`BezPath`] and all +//! features (including curve fitting and offsets) that depend on it. //! //! [`Piet`]: https://docs.rs/piet //! [`Druid`]: https://docs.rs/druid @@ -78,10 +81,12 @@ clippy::bool_to_int_with_if )] #![cfg_attr(all(not(feature = "std"), not(test)), no_std)] +#![cfg_attr(not(feature = "alloc"), allow(dead_code, unused_imports))] #[cfg(not(any(feature = "std", feature = "libm")))] compile_error!("kurbo requires either the `std` or `libm` feature"); +#[cfg(feature = "alloc")] extern crate alloc; mod affine; @@ -91,21 +96,26 @@ mod circle; pub mod common; mod cubicbez; mod ellipse; +#[cfg(feature = "alloc")] mod fit; mod insets; mod line; mod mindist; +#[cfg(feature = "alloc")] pub mod offset; mod param_curve; mod point; mod quadbez; +#[cfg(feature = "alloc")] mod quadspline; mod rect; mod rounded_rect; mod rounded_rect_radii; mod shape; +#[cfg(feature = "alloc")] pub mod simplify; mod size; +#[cfg(feature = "alloc")] mod stroke; #[cfg(feature = "std")] mod svg; @@ -118,18 +128,21 @@ pub use crate::bezpath::*; pub use crate::circle::*; pub use crate::cubicbez::*; pub use crate::ellipse::*; +#[cfg(feature = "alloc")] pub use crate::fit::*; pub use crate::insets::*; pub use crate::line::*; pub use crate::param_curve::*; pub use crate::point::*; pub use crate::quadbez::*; +#[cfg(feature = "alloc")] pub use crate::quadspline::*; pub use crate::rect::*; pub use crate::rounded_rect::*; pub use crate::rounded_rect_radii::*; pub use crate::shape::*; pub use crate::size::*; +#[cfg(feature = "alloc")] pub use crate::stroke::*; #[cfg(feature = "std")] pub use crate::svg::*; diff --git a/src/shape.rs b/src/shape.rs index c850dab9..763a65a5 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -3,7 +3,10 @@ //! A generic trait for shapes. -use crate::{segments, BezPath, Circle, Line, PathEl, Point, Rect, RoundedRect, Segments}; +use crate::{segments, Circle, Line, PathEl, Point, Rect, RoundedRect, Segments}; + +#[cfg(feature = "alloc")] +use crate::BezPath; /// A generic trait for open and closed shapes. /// @@ -64,12 +67,14 @@ pub trait Shape: Sized { /// The `tolerance` parameter is the same as for [`path_elements`]. /// /// [`path_elements`]: Shape::path_elements + #[cfg(feature = "alloc")] fn to_path(&self, tolerance: f64) -> BezPath { self.path_elements(tolerance).collect() } #[deprecated(since = "0.7.0", note = "Use path_elements instead")] #[doc(hidden)] + #[cfg(feature = "alloc")] fn to_bez_path(&self, tolerance: f64) -> Self::PathElementsIter<'_> { self.path_elements(tolerance) } @@ -81,12 +86,14 @@ pub trait Shape: Sized { /// The `tolerance` parameter is the same as for [`path_elements()`]. /// /// [`path_elements()`]: Shape::path_elements + #[cfg(feature = "alloc")] fn into_path(self, tolerance: f64) -> BezPath { self.to_path(tolerance) } #[deprecated(since = "0.7.0", note = "Use into_path instead")] #[doc(hidden)] + #[cfg(feature = "alloc")] fn into_bez_path(self, tolerance: f64) -> BezPath { self.into_path(tolerance) } @@ -181,6 +188,7 @@ impl<'a, T: Shape> Shape for &'a T { (*self).path_elements(tolerance) } + #[cfg(feature = "alloc")] fn to_path(&self, tolerance: f64) -> BezPath { (*self).to_path(tolerance) }