kuva 0.3.0

Scientific plotting library in Rust with various backends.
Documentation
//! Scientific plotting library for bioinformatics, targeting SVG output with optional PNG and PDF backends.
//!
//! # Pipeline
//!
//! ```text
//! plot definition  →  Layout  →  Scene (primitives)  →  backend output
//! ```
//!
//! 1. Build a plot struct using its builder API (e.g. [`plot::scatter::ScatterPlot`]).
//! 2. Collect plots into a `Vec<`[`render::plots::Plot`]`>` — use `.into()` on any plot struct.
//! 3. Build a [`render::layout::Layout`] with [`render::layout::Layout::auto_from_plots`] and customise it.
//! 4. Call [`render_to_svg`] (or [`render_to_png`] / [`render_to_pdf`]) to get the output in one step.
//!
//! # Example
//!
//! ```rust
//! use kuva::prelude::*;
//!
//! let scatter = ScatterPlot::new()
//!     .with_data(vec![(1.0_f64, 2.0), (3.0, 4.0)])
//!     .with_color("steelblue");
//!
//! let plots: Vec<Plot> = vec![scatter.into()];
//! let svg = kuva::render_to_svg(plots, Layout::auto_from_plots(&[]));
//! assert!(svg.contains("<svg"));
//! ```
//!
//! # Feature flags
//!
//! | Feature      | Description |
//! |--------------|-------------|
//! | `png`        | Enables [`RasterBackend`] (direct pixel-buffer rasteriser) and the [`PngBackend`] compatibility shim. |
//! | `pdf`        | Enables [`PdfBackend`] for vector PDF output via `svg2pdf`. |
//! | `embed_font` | Enables [`backend::svg::SvgBackend::with_embedded_font`] — bakes DejaVu Sans into the SVG as a base64 `@font-face`. Adds `flate2` as a dependency but does **not** pull in `png` or `pdf`. |
//! | `cli`        | Enables the `kuva` CLI binary (pulls in `clap`). |
//! | `full`       | Enables `embed_font` + `png` + `pdf`. |
//!
//! # Math in labels
//!
//! Any label (title, axis labels, annotations, markdown body text) may contain
//! `$...$` math regions written in LaTeX-ish syntax: `$\sigma^2$`,
//! `$\frac{a}{b}$`, `$\sqrt{x^2 + y^2}$`. They are lowered to inline Unicode —
//! Greek letters, operators, super/subscripts, `\frac`→`a/b`, `\sqrt`→`√(…)` —
//! by every backend, including the terminal. Zero dependencies; always on.
//! Write a literal dollar as `\$`. See [`render::math::to_unicode`].
//!
//! # Fonts
//!
//! DejaVu Sans is bundled inside the crate. The PNG and PDF backends always load
//! it before scanning system fonts, so text renders correctly even in minimal
//! environments (containers, CI pipelines) with no installed fonts.
//!
//! SVG output references fonts by name and relies on the viewer to resolve them.
//! For self-contained SVGs that work anywhere, use
//! [`backend::svg::SvgBackend::with_embedded_font`] or the `--embed-font` CLI flag.
//! This bakes DejaVu Sans as a base64 `@font-face` block into the SVG at the cost
//! of roughly 1 MB of added file size.

pub mod backend;
pub mod plot;
pub mod prelude;
pub mod render;

#[cfg(any(feature = "embed_font", feature = "png", feature = "pdf"))]
pub(crate) mod fonts;

pub use backend::terminal::TerminalBackend;

#[cfg(feature = "png")]
pub use backend::png::PngBackend;

#[cfg(feature = "png")]
pub use backend::raster::RasterBackend;

#[cfg(feature = "pdf")]
pub use backend::pdf::PdfBackend;

pub use render::datetime::{ymd, ymd_hms, DateTimeAxis, DateUnit};
/// KDE bandwidth via Silverman's rule of thumb: `h = 1.06 σ n^{-1/5}`.
///
/// Use this together with [`simple_kde`] or [`simple_kde_reflect`] to
/// pre-compute a density curve before passing it to
/// [`plot::DensityPlot::from_curve`].  Pre-computing lets you inspect the y
/// range and set custom axis bounds before rendering.
pub use render::render_utils::silverman_bandwidth;

/// Gaussian kernel density estimate evaluated at `samples` equally-spaced
/// points spanning `[data_min − 3h, data_max + 3h]`.
///
/// Returns `(x, unnormalised_kernel_sum)` pairs; divide y by `n · h · √(2π)`
/// to get probability density.  For data bounded at a known limit use
/// [`simple_kde_reflect`] instead.
pub use render::render_utils::simple_kde;

pub use render::layout::{AxisLabelOverlap, AxisLine, TickAlign, TickFormat, TickPos};
pub use render::palette::Palette;
pub use render::render::render_calendar;
pub use render::render::render_phylo_tree;
pub use render::render::render_sankey;
pub use render::render::render_synteny;
pub use render::render::render_twin_y;
/// Like [`simple_kde`] but applies boundary reflection at `x_lo` and/or
/// `x_hi` so the curve does not bleed into physically impossible values.
///
/// Set `reflect_lo = true` when data cannot go below `x_lo` (e.g. identity
/// scores ≥ 0); set `reflect_hi = true` when data cannot exceed `x_hi`.
pub use render::render_utils::simple_kde_reflect;
pub use render::theme::Theme;

/// Render a collection of plots to an SVG string in one call.
///
/// See also [`render_to_png`] and [`render_to_pdf`] for raster and vector alternatives.
///
/// This collapses the four-step pipeline into a single expression:
///
/// ```rust
/// use kuva::prelude::*;
///
/// let scatter = ScatterPlot::new()
///     .with_data(vec![(1.0_f64, 2.0), (3.0, 4.0)])
///     .with_color("steelblue");
///
/// let plots: Vec<Plot> = vec![scatter.into()];
/// let svg = kuva::render_to_svg(plots, Layout::auto_from_plots(&[]));
/// assert!(svg.contains("<svg"));
/// ```
///
/// For fine-grained control — custom layout, twin axes, or embedded-font SVG —
/// use [`render::render::render_multiple`] and [`struct@backend::svg::SvgBackend`] directly.
pub fn render_to_svg(plots: Vec<render::plots::Plot>, layout: render::layout::Layout) -> String {
    let scene = render::render::render_multiple(plots, layout);
    backend::svg::SvgBackend.render_scene(&scene)
}

/// Render a collection of plots to a PNG byte vector in one call (requires feature `png`).
///
/// Delegates to [`RasterBackend`] via the [`PngBackend`] compatibility shim.
/// Prefer [`render_to_raster`] for new code.
///
/// `scale` is the pixel density multiplier: `1.0` matches the SVG logical size,
/// `2.0` gives retina/HiDPI quality.
///
/// For fine-grained control use [`render::render::render_multiple`] and
/// [`backend::raster::RasterBackend`] directly.
#[cfg(feature = "png")]
pub fn render_to_png(
    plots: Vec<render::plots::Plot>,
    layout: render::layout::Layout,
    scale: f32,
) -> Result<Vec<u8>, String> {
    let scene = render::render::render_multiple(plots, layout);
    backend::png::PngBackend::new()
        .with_scale(scale)
        .render_scene(&scene)
}

/// Render a collection of plots directly to a PNG byte vector (requires feature `png`).
///
/// Uses [`RasterBackend`]: geometry is rasterized directly to a pixel buffer
/// with no SVG round-trip. All text is rendered via `fontdue` (bundled DejaVu Sans).
/// This is the preferred high-performance path for PNG output.
///
/// `scale` is the pixel density multiplier: `2.0` gives retina/HiDPI quality.
#[cfg(feature = "png")]
pub fn render_to_raster(
    plots: Vec<render::plots::Plot>,
    layout: render::layout::Layout,
    scale: f32,
) -> Result<Vec<u8>, String> {
    let scene = render::render::render_multiple(plots, layout);
    backend::raster::RasterBackend::new()
        .with_scale(scale)
        .render_scene(&scene)
}

/// Render a collection of plots to a PDF byte vector in one call (requires feature `pdf`).
///
/// Returns `Err(String)` if SVG parsing or PDF conversion fails.
///
/// For fine-grained control use [`render::render::render_multiple`] and
/// [`backend::pdf::PdfBackend`] directly.
#[cfg(feature = "pdf")]
pub fn render_to_pdf(
    plots: Vec<render::plots::Plot>,
    layout: render::layout::Layout,
) -> Result<Vec<u8>, String> {
    let scene = render::render::render_multiple(plots, layout);
    backend::pdf::PdfBackend.render_scene(&scene)
}