diff --git a/Cargo.toml b/Cargo.toml index eef331d..b47a5c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,10 @@ homepage = "https://github.com/dfrg/zeno" readme = "README.md" [dependencies] +libm = { version = "0.2.7", default-features = false, optional = true } [features] -default = ["eval"] +default = ["eval", "std"] eval = [] +std = [] + diff --git a/src/geometry.rs b/src/geometry.rs index 042f26a..31aeefc 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,5 +1,7 @@ //! Geometric primitives. +use crate::F32Ext; + use core::borrow::Borrow; use core::ops::{Add, Div, Mul, Sub}; diff --git a/src/lib.rs b/src/lib.rs index 7d1e0b6..c25a16a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,6 +264,13 @@ constructors are provided which take a scratch instance as an argument and redirect all transient allocations to the reusable storage. */ +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(any(feature = "std", feature = "libm")))] +compile_error! { "Either the std or libm feature must be enabled" } + +extern crate alloc; + mod command; mod geometry; #[cfg(feature = "eval")] @@ -301,7 +308,77 @@ pub use svg_parser::validate_svg; #[cfg(feature = "eval")] pub use traversal::{Vertex, Vertices, Walk}; +macro_rules! define_f32_ext { + ($($fpname:ident($($argname:ident: $argty:ty),*) -> $ret:ty => $libmname:ident;)*) => { + /// An extension trait defining floating point operations. + trait F32Ext { + $( + fn $fpname(self, $($argname:$argty),*) -> $ret; + )* + } + + #[cfg(feature = "std")] + impl F32Ext for f32 { + $( + fn $fpname(self, $($argname:$argty),*) -> $ret { + // This instrinsic is natively defined in libstd. + f32::$fpname(self, $($argname),*) + } + )* + } + + #[cfg(all(not(feature = "std"), feature = "libm"))] + impl F32Ext for f32 { + $( + fn $fpname(self, $($argname:$argty),*) -> $ret { + // Use the libm version of this instrinsic. + <$ret>::libm_cvt(libm::$libmname( + self.into(), + $(($argname).into()),* + ) as _) + } + )* + } + } +} + +define_f32_ext! { + abs() -> f32 => fabs; + acos() -> f32 => acos; + atan2(x:f32) -> f32 => atan2; + ceil() -> f32 => ceil; + cos() -> f32 => cos; + floor() -> f32 => floor; + sin_cos() -> (f32, f32) => sincos; + sqrt() -> f32 => sqrt; + powf(x:f32) -> f32 => powf; + powi(x:i32) -> f32 => pow; + tan() -> f32 => tan; +} + +#[cfg(all(not(feature = "std"), feature = "libm"))] +trait LibmCvt { + type Input; + fn libm_cvt(input: Self::Input) -> Self; +} + +#[cfg(all(not(feature = "std"), feature = "libm"))] +impl LibmCvt for f32 { + type Input = f64; + fn libm_cvt(input: f64) -> f32 { + input as f32 + } +} + +#[cfg(all(not(feature = "std"), feature = "libm"))] +impl LibmCvt for (f32, f32) { + type Input = (f64, f64); + fn libm_cvt((a, b): (f64, f64)) -> (f32, f32) { + (a as f32, b as f32) + } +} + // Prep for no_std support when core supports FP intrinsics. mod lib { - pub use std::vec::Vec; + pub use alloc::vec::Vec; } diff --git a/src/path_builder.rs b/src/path_builder.rs index 3094fe0..127f950 100644 --- a/src/path_builder.rs +++ b/src/path_builder.rs @@ -2,6 +2,7 @@ use super::command::Command; use super::geometry::{Angle, BoundsBuilder, Point, Transform}; +use super::F32Ext; use crate::lib::Vec; diff --git a/src/segment.rs b/src/segment.rs index 8a39249..ad940cb 100644 --- a/src/segment.rs +++ b/src/segment.rs @@ -2,6 +2,7 @@ use super::command::Command; use super::geometry::*; +use super::F32Ext; use core::borrow::Borrow;