Skip to main content

malevich/
lib.rs

1//! Terminal plotting: a small grammar of marks, honest axes, millions of points.
2//!
3//! Eight marks ([`Line`], [`Points`], [`Bars`], [`Area`], [`Cells`], [`Range`],
4//! [`Rule`], [`Text`]) compose over shared scales into the basic chart catalog;
5//! presets like [`line()`], [`hist`], [`box_plot`], and [`violin`] are one-line fronts
6//! over that grammar. Large series aggregate to the raster before drawing (M4 —
7//! pixel-exact for lines), axes use extended-Wilkinson tick placement with
8//! exact-decimal labels, and everything renders to a plain `String` — colored for
9//! your terminal via [`Frame::detect`], deterministic via [`Frame::plain`].
10//!
11//! ```
12//! use malevich::{Frame, Line, Plot, Rule};
13//!
14//! let steps: Vec<f64> = (0..100).map(f64::from).collect();
15//! let loss: Vec<f64> = steps.iter().map(|s| 4.0 * (-0.05 * s).exp() + 0.4).collect();
16//! let chart = Plot::new()
17//!     .layer(Line::xy(&steps[..], &loss[..]).label("loss"))
18//!     .layer(Rule::h(0.5).label("target"))
19//!     .title("training");
20//! println!("{}", chart.render(&Frame::plain(60, 14)));
21//! ```
22//!
23//! A plot is a plain value: `Clone + Send + Sync`, no global state, rendering is a
24//! pure function of plot and frame. [`Plot::render`] never fails — it sheds what it
25//! cannot draw — so building a plot inline needs no error handling. For a spec that
26//! arrives from deserialization or configuration, [`Plot::validate`] and
27//! [`Plot::try_render`] report the first problem as a typed [`Error`] instead.
28//!
29//! # Failure model
30//!
31//! Functions that return [`Result`] are the strict boundary: invalid data shapes,
32//! configuration, numeric domains, and bounded resource requests return a typed
33//! [`Error`] rather than asserting. A `try_` name distinguishes that checked twin
34//! when the same operation also has a convenience form, such as
35//! [`Cells::matrix`] / [`Cells::try_matrix`] and [`Plot::render`] /
36//! [`Plot::try_render`]. The `_with` suffix means “configured with an options
37//! value”; its return type, not the suffix, states whether the call is fallible.
38//! Plain mark constructors and one-call presets may panic on their documented
39//! programmer invariants, such as unequal paired channels. Infallible rendering is
40//! intentionally different: it sheds malformed retained content and excessive
41//! output instead of panicking.
42//!
43//! The modules follow the concepts (each defined in
44//! the repository's `docs/terminology.md`): [`mark`] for the primitives, [`stat`] for
45//! online accumulators, reducers, and batch transforms,
46//! [`scale`] for ticks and colormaps, [`render`] for the subpixel surface and
47//! charsets, [`stream`] for live charts, [`data`] for the ingestion rim.
48//!
49//! The gallery in `EXAMPLES.md` shows every chart type with its source, and
50//! `cargo run --example showcase` renders a colored tour in your terminal.
51//!
52//! # Features
53//!
54//! - `evcxr` — rich display for Evcxr Jupyter notebooks through
55//!   [`Plot::evcxr_display`] and the [`evcxr`] module, whose stdout protocol and
56//!   card colors let a crate draw its own types on the same background. The
57//!   cards themselves need no feature: [`Plot::to_html`] and [`Plot::to_svg`]
58//!   render the cell grid for any host that draws with HTML or SVG.
59//! - `ndarray` — one-dimensional arrays and views plot directly; contiguous
60//!   storage is zero-copy.
61//! - `pixel` — the plot panel as a real image (sixel, kitty graphics, or iTerm2
62//!   inline PNG) with text chrome around it: [`Plot::render_pixels`],
63//!   [`Plot::to_svg_pixels`] for an SVG host, the
64//!   [`pixel::Capabilities`] query API, and [`Plot::render_best`] picking the
65//!   best tier the terminal offers.
66//! - `ratatui` — [`PlotWidget`], a `ratatui` widget rendering any plot into a
67//!   `Buffer`; rendered stateful with a [`PlotState`], it becomes interactive:
68//!   hit-testing through the cached [`Mapping`], zoom and pan through a
69//!   [`Viewport`], and default mouse gestures fed via [`PlotState::on_mouse`].
70//!   Combined with `pixel`, the widget draws its panel as a real image
71//!   ([`PlotWidget::graphics`], emitted by
72//!   [`Graphics::present`](pixel::Graphics::present)) with the interaction
73//!   chrome rendered into the image itself.
74//! - `serde` — every spec type (plots, marks, scales, themes, frames)
75//!   round-trips through serde; `Document` is the versioned persistent envelope,
76//!   gaps survive JSON as `null`, and function-backed lines refuse to serialize
77//!   rather than lie.
78
79#![forbid(unsafe_code)]
80#![warn(missing_docs)]
81
82#[cfg(feature = "ratatui")]
83mod adapter;
84pub mod data;
85#[cfg(feature = "serde")]
86mod document;
87mod error;
88#[cfg(feature = "evcxr")]
89pub mod evcxr;
90pub mod mark;
91mod numeric;
92#[cfg(feature = "pixel")]
93pub mod pixel;
94pub mod plot;
95mod presets;
96pub mod render;
97pub mod scale;
98#[cfg(all(test, feature = "serde"))]
99mod serde_tests;
100pub mod stat;
101pub mod stream;
102mod theme;
103
104#[cfg(feature = "ratatui")]
105pub use adapter::{Mouse, MouseButton, PlotState, PlotWidget};
106#[cfg(feature = "serde")]
107pub use document::{Document, DocumentKind};
108pub use error::{Error, Result};
109pub use mark::{
110    Align, Area, Bars, Cells, Dash, Line, LineStyle, Mark, PointStyle, Points, Range, Rule, Text,
111};
112pub use plot::{Frame, Grid, Mapping, Panel, Plot, Viewport};
113pub use presets::{
114    BoxOptions, ContourLevels, ContourOptions, DensityOptions, DescribeOptions, EcdfOptions,
115    HeatmapOptions, Histogram2dOptions, HistogramOptions, StairsOptions, TableOptions,
116    TrendOptions, ViolinOptions, bar, box_plot, box_plot_with, contour, contour_with, contourf,
117    contourf_with, density, density_with, describe, describe_with, ecdf, ecdf_with, error_bars,
118    error_bars_asymmetric, heatmap, heatmap_with, hist, hist_with, hist2d, hist2d_with, line,
119    quiver, scatter, sparkline, stairs, stairs_with, table, table_with, trend, trend_with,
120    try_table, violin, violin_with,
121};
122pub use render::{Charset, Color, ColorMode, Raster, RasterCell};
123pub use scale::Scale;
124pub use theme::Theme;