Why it is shaped this way
Vision
The argument, and the five rules.
Malevich is terminal plotting for Rust: a small grammar of marks, honest axes, millions of points. A plot is a plain value, and rendering is a pure function of that value and a frame. The first look at any data should happen in the terminal, and it should be excellent. Reaching for a browser, or for matplotlib, is then a choice, not a necessity.
The goal is not the biggest chart catalog; it is the smallest vocabulary that composes into one. Better a few strict rules than lots of features. One black square on a plain ground beats a mural.
That means three commitments:
- Eight marks, and the catalog follows. Line, Points, Bars, Area, Cells, Range, Rule, Text. That is the alphabet. Give them a statistics layer and shared scales, and the charts the catalog has names for are words you spell with them. A chart type is a preset: the short spelling of one of those words, the same drawing as writing the word out in full.
- Every claim can be checked. The true chart draws every point. Aggregation has to land on the same pixels. A preset has to print the same bytes as the plot it abbreviates. An advertised number has a bench behind it, and every chart in these docs is program output, verified in CI. A claim is one assert away from proof.
- Every terminal gets an answer. Output steps down a ladder: real pixels, then octants, quadrants, ASCII. Color fades from truecolor down to plain. It never fails, never probes where an escape is unsafe, and never owns the screen.
A chart library usually grows by accretion. One function per chart type. One option per request. Malevich does not. The plot is written once, as data. Everything after it is a stage reading the same spec:
- Construction stacks layers on shared scales. The plot carries no terminal,
no thread, and no global. It is
Clone + Send + Syncbecause there is nothing in it that could not be. - Resolution unions the layers' domains, places ticks, and lays out furniture, per frame, at render time. Large layers reduce to the raster here, pixel-identically.
- Rasterization draws marks onto a subpixel surface, or onto real pixels where the terminal speaks them. The same mark code serves both fidelities.
- Encoding writes the surface as a
String. Glyphs and SGR for a terminal. Spans for a notebook card. Rectangles and text runs for a host that draws with SVG. Cells for a TUI buffer. The terminal is a category, not a device. Every host that draws the cell grid is one, and the encoder is the only thing that differs.
Adoption may follow. It is never chased. No chart-type races, no config surface for its own sake, no dependency bazaar.
The rules#
Five rules, one axis each. They say what a chart means, what the core owns, what is true, where that has to hold, and how a claim earns its place.
- The plot is the spec. A plot is layers, scales, and furniture. That is data, complete and serializable. Rendering is a pure function of plot and frame. The frame is run state, not plot state, so one spec renders concurrently at many sizes. Environment reading lives only in named conveniences, never inside render.
- The grammar is closed. Eight marks, a statistics layer, four position scales. A feature must be a composition of existing concepts. A new concept must pay for itself across many features. Every preset is provably its own grammar expansion, so the front door never forks the grammar.
- The full draw is the truth. The oracle is drawing every point.
Aggregation reproduces its pixels exactly. Extremes survive.
NaNis a visible gap. Out-of-range data clips rather than smears. Quantization is disclosed. Nothing is sampled away silently. - Every terminal is answered. Rendering degrades down declared ladders, and it never fails. Furniture sheds before data. ASCII always works. Piped output is clean plain text. No escape byte is written where it is not safe. The library never owns the terminal.
- Claims are measured; figures are output. Every advertised number comes from the bench suite and is recorded with its machine and date. Every chart in the documentation is spliced program output. It is regenerated, diffed, and failed in CI when stale. No figure is typed by hand.
Principles#
Constraints the vision names, without arguing them on this page. One file per principle. The type names in each "Spelled today" section may rot. The rest must not.
- Presets are packaging
- The frame is run state
- The full draw is the oracle
- What earns a concept
- Degradation is the contract
- The axes are the product
- Conversion lives at the rim
The name#
Kazimir Malevich painted a black square on a plain ground and meant it: a small vocabulary of geometric forms, composed deliberately. That is the design budget of this library.