termplt 0.1.2

A Rust library for rendering 2D plots directly in Kitty-compatible terminals
Documentation
# termplt

A Rust library for rendering 2D plots directly in [Kitty](https://sw.kovidgoyal.net/kitty/)-compatible terminals. Data goes in, pixel-perfect graphs come out; no GUI, no image files, no browser!

termplt uses the [Kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) to transmit rendered plots as RGB pixel data via APC escape sequences, so graphs display inline in your terminal.

## Features

- **Generic numeric types** — plot `i32`, `u32`, `f32`, `f64`, or any type satisfying basic arithmetic traits
- **Multiple series** — overlay multiple data series on a single graph with independent styling
- **Marker styles** — filled/hollow circles and squares with configurable size and color
- **Line drawing** — optional solid connecting lines between points
- **Axes and grid lines** — automatic axis rendering with numeric tick labels
- **Axis limits** — optionally constrain x/y ranges with automatic point clipping
- **Configurable canvas** — set dimensions, background color, and buffer padding
- **Bitmap text** — built-in 10x11 pixel font for labels and numeric annotations
- **Image display** — render PNG, RGB, and RGBA images inline via Kitty protocol

## CLI Usage

termplt includes a command-line executable for rendering plots without writing Rust code. Data is provided inline or via files, with optional per-series styling.

```bash
# Simple line plot
termplt --data "(1,1),(2,4),(3,9),(4,16)"
```
<img width="517" height="518" alt="image" src="https://github.com/user-attachments/assets/64f9f8a0-3631-4c6f-baf4-782694f673e1" />

```
# Scatter plot (no connecting lines)
termplt --data_file test_data/random_clusters.csv --line_style None
```
<img width="517" height="518" alt="image" src="https://github.com/user-attachments/assets/97be7e79-5224-44c3-b9a3-e7676887ef4e" />

```
# Multiple series with automatic color cycling
termplt --data_file sine.csv --data_file cosine.csv
```
<img width="517" height="518" alt="image" src="https://github.com/user-attachments/assets/4a9a3f03-2ff5-452f-a904-3006e8b4f3ed" />

```
# Custom styling
termplt --data_file test_data/lissajous.csv --marker_style HollowCircle --marker_color Cyan --marker_size 4 \
  --line_color Cyan --line_thickness 1
```
<img width="517" height="518" alt="image" src="https://github.com/user-attachments/assets/24d56a94-9013-40bf-b4cb-eaa38082c7a6" />

```
# Line-only plot (no markers)
termplt --data_file data.csv --marker_style None --line_color Lime --line_thickness 1
```
<img width="517" height="518" alt="image" src="https://github.com/user-attachments/assets/18e2f854-94c1-4a8a-ae4e-c446683488cf" />

### CLI Flags

| Flag | Description |
|---|---|
| `--data "(x,y),(x,y),..."` | Inline data points |
| `--data_file <path>` | Read x,y data from a file (CSV, TSV, or whitespace-delimited) |
| `--marker_style <style>` | `FilledCircle`, `HollowCircle`, `FilledSquare`, `HollowSquare`, `None` |
| `--marker_color <color>` | Named color (e.g. `Blue`, `DARK_RED`, `lime`) |
| `--marker_size <pixels>` | Marker radius in pixels (default: 2) |
| `--line_style <style>` | `Solid` (default) or `None` (scatter plot) |
| `--line_color <color>` | Named color for connecting line |
| `--line_thickness <pixels>` | Line thickness in pixels (default: 0) |
| `--help` | Show usage help |
| `--help colors` | List all available color names |
| `--help markers` | List all available marker styles |

Style flags apply to the immediately preceding `--data` or `--data_file`. Repeat data flags for multiple series — each gets independent styling with automatic color/marker cycling when styles are not specified.

Data files support CSV headers (auto-detected and skipped), `#` comment lines, and blank lines.

## Requirements

- A Kitty-compatible terminal (Kitty, WezTerm, or any terminal supporting the [Kitty graphics protocol]https://sw.kovidgoyal.net/kitty/graphics-protocol/)
- Rust 2024 edition

## Quick Start

Add `termplt` to your `Cargo.toml`:

```toml
[dependencies]
termplt = "0.1.0"
```

### Plotting a sine wave

```rust
use std::f32;
use termplt::plotting::{
    axes::{Axes, AxesPositioning},
    canvas::{BufferType, TerminalCanvas},
    colors,
    graph::Graph,
    grid_lines::GridLines,
    line::LineStyle,
    marker::MarkerStyle,
    point::Point,
    series::Series,
    text::TextStyle,
};
use termplt::kitty_graphics::ctrl_seq::{PixelFormat, Transmission};
use termplt::terminal_commands::{images::Image, responses::TermCommand};

fn main() {
    let num_points = 100;
    let points: Vec<Point<f32>> = (0..=num_points)
        .map(|x| {
            let x = (x as f32) * (360. / (num_points as f32));
            Point::new(x, (x * f32::consts::PI / 180.).sin())
        })
        .collect();

    let width = 800;
    let height = 600;
    let bytes = TerminalCanvas::new(width, height, colors::BLACK)
        .with_buffer(BufferType::Uniform(80))
        .with_graph(
            Graph::new()
                .with_series(
                    Series::new(&points)
                        .with_marker_style(MarkerStyle::FilledCircle {
                            size: 2,
                            color: colors::LIME,
                        })
                        .with_line_style(LineStyle::Solid {
                            color: colors::LIME,
                            thickness: 0,
                        }),
                )
                .with_axes(Axes::new(
                    AxesPositioning::XY(LineStyle::Solid {
                        color: colors::GHOST_WHITE,
                        thickness: 1,
                    }),
                    TextStyle::with_color(colors::WHITE),
                ))
                .with_grid_lines(GridLines::XY(LineStyle::Solid {
                    color: colors::GRAY,
                    thickness: 0,
                })),
        )
        .draw()
        .unwrap()
        .get_bytes();

    Image::new(
        PixelFormat::Rgb { width, height },
        Transmission::Direct(bytes),
    )
    .unwrap()
    .display()
    .unwrap();
}
```

## Architecture

The rendering pipeline flows through four stages:

```
User data (generic T: Graphable)
  → Scale to pixel coordinates
    → Render to in-memory canvas (RGB pixel buffer)
      → Transmit via Kitty APC escape sequences
```

Key abstractions:

| Module | Purpose |
|---|---|
| `plotting::common` | `Graphable` trait, type conversion, coordinate transforms |
| `plotting::graph` | `Graph` — composes series, axes, grid lines, and limits |
| `plotting::canvas` | `TerminalCanvas` — orchestrates rendering to pixel buffer |
| `plotting::series` | `Series` — data points with marker and line styles |
| `kitty_graphics` | Kitty protocol encoding and command chunking |
| `terminal_commands` | Image display and terminal interaction |

## Building and Testing

```bash
cargo build       # Build the library and CLI binary
cargo test        # Run all unit tests
cargo clippy      # Lint
cargo run -- --data_file data.csv   # Render a plot (requires Kitty-compatible terminal)
```

### Test Data Generation

A Python script is included to generate sample data files for validating CLI behavior:

```bash
python3 scripts/generate_test_data.py
```

This creates 13 data files in `test_data/` covering sine/cosine, polynomials, exponentials, parametric curves (circle, Lissajous), random scatter, Gaussian clusters, and more. The script prints example `cargo run` commands for each dataset.

## License

This project is licensed under the [MIT License](LICENSE).