/*!
This crate provides a high performance, low level 2D rasterization library
with support for rendering paths of various styles into alpha or subpixel
masks.

Broadly speaking, support is provided for the following:
- 256x anti-aliased rasterization (8-bit alpha or 32-bit RGBA subpixel alpha)
- Pixel perfect hit testing with customizable coverage threshold
- Non-zero and even-odd fills
- Stroking with the standard set of joins and caps
  (separate start and end caps are possible)
- Numerically stable dashing for smooth dash offset animation
- Vertex traversal for marker placement
- Stepped distance traversal for animation or text-on-path support
- Abstract representation of path data that imposes no policy on storage

While this crate is general purpose, in the interest of interoperability and
familiarity, the feature set was chosen specifically to accommodate the
requirements of the
[SVG path specification](https://www.w3.org/TR/SVG/paths.html).

Furthermore, the rasterized masks are nearly identical to those generated by
Skia (sans slight AA differences) and as such, should yield images that are
equivalent to those produced by modern web browsers.

# Rendering

Due to the large configuration space for styling and rendering paths, the
builder pattern is used pervasively. The [`Mask`] struct is the builder
used for rasterization. For example, to render a simple triangle into a
64x64 8-bit alpha mask:

```rust
use zeno::{Mask, PathData};

// The target buffer that will contain the mask
let mut mask = [0u8; 64 * 64];

// Create a new mask with some path data
Mask::new("M 8,56 32,8 56,56 Z")
    // Choose an explicit size for the target
    .size(64, 64)
    // Finally, render the path into the target
    .render_into(&mut mask, None);
```

Note that, in this case, the path itself is supplied as a string in SVG path
data format. This crate provides several different kinds of path data by
default along with support for custom implementations. See the
[`PathData`] trait for more detail.

The previous example did not provide a style, so a non-zero
[`Fill`] was chosen by default. Let's render the same path with
a 4 pixel wide stroke and a round line join:

```rust
use zeno::{Join, Mask, PathData, Stroke};

let mut mask = [0u8; 64 * 64];

Mask::new("M 8,56 32,8 56,56 Z")
    .size(64, 64)
    .style(Stroke::new(4.0).join(Join::Round))
    .render_into(&mut mask, None);
```

Or to make it a bit more dashing:

```rust
use zeno::{Cap, Join, Mask, PathData, Stroke};

let mut mask = [0u8; 64 * 64];

Mask::new("M 8,56 32,8 56,56 Z")
    .style(
        Stroke::new(4.0)
            .join(Join::Round)
            .cap(Cap::Round)
            // dash accepts a slice of dash lengths and an initial dash offset
            .dash(&[10.0, 12.0, 0.0], 0.0),
    )
    .size(64, 64)
    .render_into(&mut mask, None);
```

See the [`Stroke`] builder struct for all available options.

So far, we've generated our masks into fixed buffers with explicit sizes. It is
often the case that it is preferred to ignore all empty space and render a path
into a tightly bound mask of dynamic size. This can be done by eliding the call
for the size method:

```rust
use zeno::{Mask, PathData};

// Dynamic buffer that will contain the mask
let mut mask = Vec::new();

let placement = Mask::new("M 8,56 32,8 56,56 Z")
    // Insert an inspect call here to access the computed dimensions
    .inspect(|format, width, height| {
        // Make sure our buffer is the correct size
        mask.resize(format.buffer_size(width, height), 0);
    })
    .render_into(&mut mask, None);
```

The call to size has been replaced with a call to inspect which injects a
closure into the call chain giving us the opportunity to extend our buffer to
the appropriate size. Note also that the render method has a return value that
has been captured here. This [`Placement`] struct describes the dimensions of
the resulting mask along with an offset that should be applied during
composition to compensate for the removal of any empty space.

Finally, it is possible to render without a target buffer, in which case the
rasterizer will allocate and return a new `Vec<u8>` containing the mask:

```rust
use zeno::{Mask, PathData};

// mask is a Vec<u8>
let (mask, placement) = Mask::new("M 8,56 32,8 56,56 Z")
    // Calling render() instead of render_into() will allocate a buffer
    // for you that is returned along with the placement
    .render();
```

Both [`Mask`] and [`Stroke`] offer large sets of options for fine-grained
control of styling and rasterization including offsets, scaling,
transformations, formats, coordinate spaces and more. See
their respective documentation for more detail.

# Hit testing

Hit testing is the process of determining if a point is within the region that
would be painted by the path. A typical use case is to determine if a user's
cursor is hovering over a particular path. The process generally follows the
same form as rendering:

```rust
use zeno::{HitTest, PathData};

// A 20x10 region with the right half covered by the path
let hit_test = HitTest::new("M10,0 10,10 20,10 20,0 Z");

assert_eq!(hit_test.test([15, 5]), true);
assert_eq!(hit_test.test([5, 5]), false);
```

Due to the fact that paths are anti-aliased, the hit test builder offers a
threshold option that determines how much "coverage" is required for a hit test
to pass at a particular point.

```rust
use zeno::{HitTest, PathData};

let mut hit_test = HitTest::new("M2.5,0 2.5,2 5,2 5,0 Z");

// Require full coverage for a successful hit test
hit_test.threshold(255);
assert_eq!(hit_test.test([2, 0]), false);

// Succeed for any non-zero coverage
hit_test.threshold(0);
assert_eq!(hit_test.test([2, 0]), true);
```

See the [`HitTest`] type for more detail.

# Path building

While SVG paths are a reasonable choice for static storage, there sometimes
arise cases where paths must be built dynamically at runtime:

```rust
use zeno::{Command, Mask, PathBuilder, PathData};

// Create a vector to store the path commands
let mut path: Vec<Command> = Vec::new();

// Construct the path with chained method calls
path.move_to([8, 56]).line_to([32, 8]).line_to([56, 56]).close();

// Ensure it is equal to the equivalent SVG path
assert!((&path).commands().eq("M 8,56 32,8 56,56 Z".commands()));

// &Vec<Command> is also valid path data
Mask::new(&path).render(); // ...
```

Here, a vector of [`Command`]s is used to store the path data and the
[`PathBuilder`] trait provides the extension methods necessary for
building a path.

Beyond the four basic path commands, the path builder trait also provides
arcs (and position relative versions of all previous commands) along with
rectangles, round rectangles, ellipses and circles:

```rust
use zeno::{Angle, ArcSize, ArcSweep, Command, PathBuilder, PathData};

let mut path: Vec<Command> = Vec::new();

path.move_to([1, 2]).rel_arc_to(
    8.0,
    4.0,
    Angle::from_degrees(30.0),
    ArcSize::Small,
    ArcSweep::Positive,
    [10, 4],
);

assert!((&path).commands().eq("M1,2 a8,4,30,0,1,10,4".commands()));
```

Along with incremental building of paths, path builder can also be used as a
"sink" for capturing the result of the application of a style and transform
to some path data. For example, it is possible to store the output of a stroke
style to avoid the cost of stroke evaluation for future rendering or hit test
operations with the use of the [`apply`] function:

```rust
use zeno::{apply, Cap, Command, PathBuilder, PathData, Stroke};

let mut stroke: Vec<Command> = Vec::new();

apply("L10,0", Stroke::new(4.0).cap(Cap::Round), None, &mut stroke);
```

[`PathBuilder`] is only implemented for `Vec<Command>` by default, but
custom implementations are possible to support capturing and building
paths into other data structures.

# Traversal

Path traversal involves incremental evaluation of a path by some metric. This
crate currently provides two methods of traversal.

The [`Vertices`] iterator yields a variant of the [`Vertex`] enum at the
beginning and end of each subpath and between each path command. Each variant
provides all the geometric information necessary to place SVG style markers.

The [`Walk`] type is an iterator-like type that allows for
stepping along the path by arbitrary distances. Each step yields the position
on the path at the next distance along with a vector describing the
left-ward direction from the path at that point. This is useful for animating
objects along a path, or for rendering text attached to a path.

# Transient memory allocations

The algorithms in this crate make a concerted effort to avoid dynamic
allocations where possible, but paths of significant size or complexity
may cause spills into temporary heap memory. Specifically, stroke evaluation
and rasterization may cause heap allocations.

To amortize the cost of these, the appropriately named
[`Scratch`] struct is available. This type contains internal
heap allocated storage and provides replacement methods for functions that may
allocate. In addition, the [`Mask::with_scratch`] and [`HitTest::with_scratch`]
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")]
mod hit_test;
#[cfg(feature = "eval")]
mod mask;
mod path_builder;
mod path_data;
#[cfg(feature = "eval")]
mod raster;
#[cfg(feature = "eval")]
mod scratch;
mod segment;
#[cfg(feature = "eval")]
mod stroke;
mod style;
mod svg_parser;
#[cfg(feature = "eval")]
mod traversal;

pub use command::{Command, Verb};
pub use geometry::{Angle, Bounds, Origin, Placement, Point, Transform, Vector};
#[cfg(feature = "eval")]
pub use hit_test::HitTest;
#[cfg(feature = "eval")]
pub use mask::{Format, Mask};
pub use path_builder::{ArcSize, ArcSweep, PathBuilder};
#[cfg(feature = "eval")]
pub use path_data::{apply, bounds};
pub use path_data::{length, PathData};
#[cfg(feature = "eval")]
pub use scratch::Scratch;
pub use style::*;
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.
        #[allow(dead_code)]
        trait F32Ext {
            $(
            fn $fpname(self, $($argname:$argty),*) -> $ret;
            )*
        }

        #[cfg(feature = "std")]
        impl F32Ext for f32 {
            $(
            fn $fpname(self, $($argname:$argty),*) -> $ret {
                // This intrinsic 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 intrinsic.
                <$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 alloc::vec::Vec;
}
