Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for no-alloc #327

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ categories = ["graphics"]
features = ["mint", "schemars", "serde"]

[features]
default = ["std"]
std = []
default = ["std", "alloc"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My brain is fried, but can't this just be default = ["std"] since the line below handles enabling "alloc"?

std = ["alloc"]
alloc = ["serde/alloc", "smallvec"]

[dependencies]
smallvec = "1.10"
[dependencies.smallvec]
version = "1.10"
optional = true

[dependencies.arrayvec]
version = "0.7.1"
Expand All @@ -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]
Expand Down
16 changes: 16 additions & 0 deletions src/bezpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<PathEl>);

/// The element of a Bézier path.
Expand Down Expand Up @@ -173,6 +175,7 @@ pub struct MinDistance {
pub t2: f64,
}

#[cfg(feature = "alloc")]
impl BezPath {
/// Create a new path.
pub fn new() -> BezPath {
Expand Down Expand Up @@ -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));
Expand All @@ -506,6 +510,7 @@ fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) {
}
}

#[cfg(feature = "alloc")]
impl FromIterator<PathEl> for BezPath {
fn from_iter<T: IntoIterator<Item = PathEl>>(iter: T) -> Self {
let el_vec: Vec<_> = iter.into_iter().collect();
Expand All @@ -517,6 +522,7 @@ impl FromIterator<PathEl> 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<core::slice::Iter<'a, PathEl>>;
Expand All @@ -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<PathEl>;
Expand All @@ -535,6 +542,7 @@ impl IntoIterator for BezPath {
}
}

#[cfg(feature = "alloc")]
impl Extend<PathEl> for BezPath {
fn extend<I: IntoIterator<Item = PathEl>>(&mut self, iter: I) {
self.0.extend(iter);
Expand All @@ -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<Item = PathEl>,
tolerance: f64,
Expand Down Expand Up @@ -662,6 +671,7 @@ impl Mul<PathSeg> for Affine {
}
}

#[cfg(feature = "alloc")]
impl Mul<BezPath> for Affine {
type Output = BezPath;

Expand All @@ -670,6 +680,7 @@ impl Mul<BezPath> for Affine {
}
}

#[cfg(feature = "alloc")]
impl<'a> Mul<&'a BezPath> for Affine {
type Output = BezPath;

Expand Down Expand Up @@ -704,6 +715,7 @@ impl Mul<PathSeg> for TranslateScale {
}
}

#[cfg(feature = "alloc")]
impl Mul<BezPath> for TranslateScale {
type Output = BezPath;

Expand All @@ -712,6 +724,7 @@ impl Mul<BezPath> for TranslateScale {
}
}

#[cfg(feature = "alloc")]
impl<'a> Mul<&'a BezPath> for TranslateScale {
type Output = BezPath;

Expand Down Expand Up @@ -1277,6 +1290,7 @@ impl From<QuadBez> for PathSeg {
}
}

#[cfg(feature = "alloc")]
impl Shape for BezPath {
type PathElementsIter<'iter> = core::iter::Copied<core::slice::Iter<'iter, PathEl>>;

Expand Down Expand Up @@ -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())
}
Expand Down Expand Up @@ -1406,6 +1421,7 @@ impl<const N: usize> Shape for [PathEl; N] {
self.iter().copied()
}

#[cfg(feature = "alloc")]
fn to_path(&self, _tolerance: f64) -> BezPath {
BezPath::from_vec(self.to_vec())
}
Expand Down
12 changes: 10 additions & 2 deletions src/cubicbez.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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<QuadSpline> {
(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<QuadSpline> {
if n == 1 {
return self
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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<Vec<QuadSpline>> {
let mut result = Vec::new();
let mut split_order = 0;
Expand Down
17 changes: 15 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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::*;
Expand Down
10 changes: 9 additions & 1 deletion src/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down