Skip to content

rupurt/txtplot

Repository files navigation

txtplot

High-performance terminal plotting for Rust.

txtplot renders mathematical plots, 3D visualizations, games, and complex terminal interfaces using Unicode Braille and block characters plus ANSI colors.

Unlike many TUI plotting libraries, txtplot is designed for speed: it uses flat contiguous buffers, renderer-specific cell encodings, clipping, and a zero-allocation rendering path to support real-time terminal graphics.

License: MIT.

Project Navigation

This repository carries a small set of foundational documents for users, contributors, and agents:

  • AGENTS.md - implementation guidance for AI agents and contributors
  • CONSTITUTION.md - project principles, decision hierarchy, and compatibility posture
  • ARCHITECTURE.md - module boundaries, data flow, and extension seams
  • GUIDE.md - user guide for plots, 3D visuals, games, and terminal interfaces
  • CONFIGURATION.md - toolchain, shell, and API-level configuration surfaces
  • RELEASE.md - release checklist and versioning process

If you use Nix, nix develop provides the Rust toolchain plus cargo-nextest, just, keel, and sift.

Key Features

  • High resolution: 8 sub-pixels per character (Braille 2x4). A 100x50 terminal yields a 200x200 effective pixel canvas.
  • Pluggable cell renderers: Braille (2x4), HalfBlock (1x2), and Quadrant (2x2)
  • Performance-oriented design:
    • flat buffers for cache-friendly access
    • zero-allocation rendering via render_to
    • Cohen-Sutherland line clipping to discard off-screen geometry before rasterization
  • Advanced pixel and color control:
    • unset_pixel and toggle_pixel
    • color blending modes with Overwrite and KeepFirst
    • cell background colors for terminal panels and HUD-style layouts
    • styled text with foreground, background, plus Normal / Bold / Dim intensity
    • cell-space UI helpers with text_screen(), text_screen_styled(), label_screen(), and panel_screen()
  • High-level chart aesthetics:
    • legend(): automatic, opaque legend boxes with color markers
    • anchored_text(): effortless label placement at TopLeft, Center, etc.
    • opaque UI panels that clear underlying raster data for professional overlays
  • Drawing primitives:
    • lines, circles, polygons
    • filled shapes via rect_filled and circle_filled
    • text overlay support
  • Ready-to-use charts:
    • scatter(), line_chart(), bar_chart(), pie_chart(), plot_function()
    • linear and log10 axes through AxisScale
  • Auto-range and axis helpers for chart setup
  • Exported 3D building blocks in txtplot::three_d:
    • Vec3, Projection, and ZBuffer
    • screen projection helpers plus z-buffered raster helpers
    • sphere, torus, and triangle mesh generators

Installation

Add this to your Cargo.toml:

[dependencies]
txtplot = "0.1.0"
colored = "2.0"

Quick Start

use colored::Color;
use txtplot::ChartContext;

fn main() {
    let mut chart = ChartContext::new(60, 15);
    chart.draw_grid(10, 4, Some(Color::BrightBlack));
    chart.draw_axes((0.0, 10.0), (-1.5, 1.5), Some(Color::White));
    chart.plot_function(|x: f64| x.sin(), 0.0, 10.0, Some(Color::Cyan));
    chart.plot_function(
        |x: f64| (x * 0.5).cos() * 0.5,
        0.0,
        10.0,
        Some(Color::Magenta),
    );
    chart.text("sin(x)", 0.75, 0.85, Some(Color::Cyan));
    chart.text("0.5*cos(0.5x)", 0.56, 0.10, Some(Color::Magenta));

    println!("{}", chart.canvas.render());
}

Styled Text Labels

Use text_styled() or the screen-space styled text helpers when you need label emphasis without changing the label content:

use colored::Color;
use txtplot::{ChartContext, TextStyle};

fn main() {
    let mut chart = ChartContext::new(40, 10);
    chart.text_styled(
        "core-node",
        0.45,
        0.75,
        TextStyle::new().with_foreground(Color::BrightWhite).bold(),
    );
    chart.text_styled(
        "outer-node",
        0.55,
        0.30,
        TextStyle::new().with_foreground(Color::BrightBlack).dim(),
    );

    println!("{}", chart.canvas.render());
}

Anchored Text and Legends

Place labels and legends effortlessly using ChartAnchor without calculating pixel offsets:

use colored::Color;
use txtplot::{ChartAnchor, ChartContext, TextStyle};

fn main() {
    let mut chart = ChartContext::new(60, 15);
    chart.plot_function(|x| x.sin(), 0.0, 10.0, Some(Color::Cyan));

    // Place a legend in the top right
    let entries = [("Sine Wave", TextStyle::new().with_foreground(Color::Cyan))];
    chart.legend(ChartAnchor::TopRight, &entries);

    // Anchor a title or status label
    chart.anchored_text_styled(
        "LIVE MONITOR",
        ChartAnchor::TopLeft,
        TextStyle::new().with_foreground(Color::Yellow).bold(),
    );

    println!("{}", chart.canvas.render());
}

Logarithmic Axes

use colored::Color;
use txtplot::{AxisScale, ChartContext};

fn main() {
    let mut chart = ChartContext::new(60, 15);
    chart.set_scales(AxisScale::Linear, AxisScale::Log10);

    let points = vec![(1.0, 1.0), (2.0, 10.0), (3.0, 100.0), (4.0, 1000.0)];
    let (range_x, range_y) = ChartContext::get_auto_range_scaled(
        &points,
        0.05,
        AxisScale::Linear,
        AxisScale::Log10,
    );

    chart.draw_axes(range_x, range_y, Some(Color::White));
    chart.line_chart(&points, Some(Color::Cyan));
    println!("{}", chart.canvas.render());
}

Zero-Allocation Render Loop

If you are building a real-time app, avoid render() and use render_to():

use std::fmt;
use colored::Color;
use txtplot::ChartContext;

fn main() -> fmt::Result {
    let mut chart = ChartContext::new(60, 15);
    chart.draw_axes((0.0, 10.0), (-1.0, 1.0), Some(Color::White));
    chart.plot_function(|x: f64| x.sin(), 0.0, 10.0, Some(Color::Cyan));

    let mut buffer = String::with_capacity(8000);
    chart.canvas.render_to(&mut buffer, true, Some("60 FPS UI"))?;
    print!("{buffer}");
    Ok(())
}

Renderer Showcase

Braille remains the default renderer, and HalfBlockCanvas / HalfBlockChartContext plus QuadrantCanvas / QuadrantChartContext provide alternate encodings. The showcase example renders the same chart and raster scene through all three renderers:

cargo run --release --example renderer_showcase

Runtime Renderer Selection

If you want to choose a renderer from CLI or config input while still constructing a concrete generic chart or canvas, use RendererKind together with with_renderer!:

cargo run --release --example runtime_renderer -- halfblock

3D Surface Example

txtplot does not ship a full scene graph, but it now exports reusable 3D math, projection, and z-buffer helpers under txtplot::three_d. The surface example projects a volatility mesh into terminal pixels, uses a z-buffer for occlusion, and overlays a gradient-ascent path:

cargo run --release --example vol_surface

t-SNE and Nearest-Neighbor Graph

The examples also include a small self-contained manifold-learning demo: it builds clustered 6D data, runs a lightweight t-SNE embedding, and overlays the original-space 3-nearest-neighbor graph on top of the 2D layout.

cargo run --release --example tsne_neighbors

Coordinate System and Pixel API

To avoid mathematical confusion, txtplot offers two coordinate modes and multiple pixel operators:

Coordinate Mode Origin (0,0) Y Direction Best For
Cartesian Bottom-left Grows up Math plots, functions, charts
Screen Top-left Grows down UI, games, sprites, 3D projections

Pixel manipulation methods:

  • set_pixel / set_pixel_screen: turn a dot on
  • unset_pixel / unset_pixel_screen: turn a dot off
  • toggle_pixel_screen: flip the current state of a dot

Cell-space HUD methods:

  • text_screen: write text in top-left screen coordinates
  • label_screen: write text with per-cell background styling
  • panel_screen: draw boxed UI regions for dashboards and overlays

Demo Output

The following block is captured from cargo run --example demo using the built-in render_no_color path:

A) Plain Render (render_no_color):
⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀
⠀⠀⠈⠉⠢⢄⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⡠⠔⠉⠁⠀⠀
⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠈⢆⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⡠⠃⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⢀⡠⠔⠉⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠉⠉⠉⠉⠁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

Examples and Demos

The repository includes advanced examples and demos:

  1. Function and chart gallery
cargo run --example demo
  1. Primitive shapes and blending
cargo run --release --example primitives_demo
  1. 3D volatility surface with gradient ascent
cargo run --release --example vol_surface
  1. Braille, half-block, and quadrant renderer showcase
cargo run --release --example renderer_showcase
  1. Runtime renderer selection from CLI input
cargo run --release --example runtime_renderer -- quadrant
  1. t-SNE embedding with nearest-neighbor graph
cargo run --release --example tsne_neighbors
  1. 3D gallery with camera, z-buffer, and zoom
cargo run --release --example 3dengine
  1. Solar system Kepler 3D
cargo run --release --example solarsystem_kepler
  1. Sprite engine
cargo run --release --example sprite_demo
  1. Interactive fractals
cargo run --release --example fractalmove

Performance

txtplot is optimized for real-time terminal rendering.

In a benchmark with a 236x104 sub-pixel canvas filled with trigonometric noise and particles on a modern machine:

  • Debug mode: about 60 FPS
  • Release mode: about 1600+ FPS

Use just bench or cargo bench --bench canvas_benchmark to run the current Criterion suite. It now includes renderer comparison groups for chart-heavy and raster-heavy scenes across Braille, HalfBlock, and Quadrant output.

Roadmap

Detailed implementation planning now lives in ARCHITECTURE.md. The list below is only the user-facing summary.

  • Flat contiguous buffers for memory efficiency
  • Explicit coordinate APIs for screen and cartesian modes
  • Cohen-Sutherland line clipping
  • Zero-allocation rendering via render_to
  • Filled primitives and erasers
  • Color blending policies
  • Logarithmic scaling support
  • Automatic legend box
  • Trait-based pluggable terminal renderers
  • Runtime renderer selection helpers

License

This project is licensed under the MIT License.

About

High-performance terminal plotting for Rust.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages