Skip to content

Latest commit

 

History

History
270 lines (212 loc) · 7.36 KB

File metadata and controls

270 lines (212 loc) · 7.36 KB

bgraph Cheatsheet

btop-style Braille terminal graphs for ratatui | MSRV: 1.74.0

[dependencies]
bgraph = "0.1"
ratatui = "0.29"

Core Concepts

Type Purpose Pattern
Graph<'a> Static data visualization Widget
TimeSeries Scrolling time-series StatefulWidget + TimeSeriesState
DualGraph Side-by-side graphs Widget
DualTimeSeries Dual scrolling series StatefulWidget + DualTimeSeriesState
MultiGraph Overlaid series Widget
MultiTimeSeries Multi scrolling series StatefulWidget + MultiTimeSeriesState
Meter Horizontal percentage bar Widget
MiniGraph<'a> 1-row braille sparkline Widget
MiniTimeSeries Scrolling 1-row sparkline StatefulWidget + MiniTimeSeriesState
VerticalGauge Vertical percentage bar Widget
BrailleVerticalGauge Braille vertical gauge (4x/8x) Widget

Quick Start

1. Static Graph (implement DataSource)

use bgraph::{Graph, DataSource};

struct Sine;
impl DataSource for Sine {
    fn sample(&self, x: f32) -> f32 { (x * 6.28).sin() }
}

let graph = Graph::new(&Sine)
    .x_range(0.0, 1.0)
    .y_range(-1.0, 1.0)
    .style(Style::default().fg(Color::Cyan));
frame.render_widget(graph, area);

2. Static Graph (closure)

use bgraph::{Graph, FnDataSource};

let data = vec![0.2, 0.5, 0.8, 1.0, 0.7];
let source = FnDataSource::new(move |x| {
    let i = (x * (data.len() - 1) as f32) as usize;
    data.get(i).copied().unwrap_or(0.0)
});
frame.render_widget(Graph::new(&source), area);

3. Scrolling Time Series

use bgraph::{TimeSeries, TimeSeriesState};

// Create state (once)
let mut state = TimeSeriesState::with_range(100, 0.0, 100.0);

// Update loop
state.push(new_value);

// Render
frame.render_stateful_widget(TimeSeries::new(), area, &mut state);

4. Dual Graphs

use bgraph::{DualTimeSeries, DualTimeSeriesState, SplitDirection};

let mut state = DualTimeSeriesState::with_ranges(
    100,
    (0.0, 100.0),
    (0.0, 100.0),
);
state.push(val1, val2);

// Horizontal: left | right (default)
let dual = DualTimeSeries::new().split_ratio(0.5);

// Vertical: top / bottom
let dual = DualTimeSeries::new()
    .split_direction(SplitDirection::Vertical)
    .split_ratio(0.5);

frame.render_stateful_widget(dual, area, &mut state);

5. Multi-Series (e.g., 4 CPU cores)

use bgraph::{MultiTimeSeries, MultiTimeSeriesState, LegendPosition};

let mut state = MultiTimeSeriesState::with_range(4, 100, 0.0, 100.0);
for i in 0..4 { state.set_label(i, format!("Core {}", i)); }

// Update each series
state.push(0, core0); state.push(1, core1); // ...

let multi = MultiTimeSeries::new()
    .show_legend(true)
    .legend_position(LegendPosition::TopRight);
frame.render_stateful_widget(multi, area, &mut state);

6. Meter (btop-style bar)

use bgraph::{Meter, ColorGradient};

// Simple percentage bar with gradient coloring
let meter = Meter::new(75)  // 75%
    .gradient(ColorGradient::three_point(
        Color::Green, Color::Yellow, Color::Red
    ))
    .show_percentage(true);
frame.render_widget(meter, area);

7. MiniTimeSeries (btop-style per-core sparkline)

use bgraph::{MiniTimeSeries, MiniTimeSeriesState, ColorGradient};

// Create state for 1-row sparkline
let mut state = MiniTimeSeriesState::with_range(60, 0.0, 100.0);

// Push values (O(1) ring buffer)
state.push(cpu_percent);

// Render with gradient
let sparkline = MiniTimeSeries::new()
    .gradient(ColorGradient::three_point(
        Color::Green, Color::Yellow, Color::Red
    ));
frame.render_stateful_widget(sparkline, area, &mut state);

API Reference

TimeSeriesState

TimeSeriesState::new(capacity)              // Auto Y-range
TimeSeriesState::with_range(cap, min, max)  // Fixed Y-range
state.push(value)                           // O(1) append
state.clear()                               // Reset buffer
state.len() / state.is_empty()              // Buffer status

ColorGradient

// Convenience constructors
let gradient = ColorGradient::three_point(Color::Blue, Color::Yellow, Color::Red);
let gradient = ColorGradient::two_point(Color::Green, Color::Red);

// Or build manually
let gradient = ColorGradient::new()
    .add_stop(0.0, Color::Blue)
    .add_stop(0.5, Color::Yellow)
    .add_stop(1.0, Color::Red);

let color = gradient.get_color(0.5);  // Returns interpolated color

// Pre-compute for performance
let table: [Color; 101] = gradient.precompute();
let color = table[75];  // Fast lookup

GradientMode

use bgraph::GradientMode;

// Value mode (default): color by data value
Graph::new(&data).gradient(g.clone()).gradient_mode(GradientMode::Value);

// Position mode (btop-style): color by row Y position
// Top rows = hot, bottom rows = cool
Graph::new(&data).gradient(g.clone()).gradient_mode(GradientMode::Position);

RenderMode

.render_mode(RenderMode::Braille)  // High-res (default)
.render_mode(RenderMode::Block)    // Fallback

Height Snapping (for clean scale divisions)

use bgraph::{snap_height, snap_graph_area};

// Snap raw height to clean division (multiples of 50, 20, 10, or 4)
let snapped = snap_height(12);  // Returns 10

// Snap a Rect and center it vertically
let graph_area = snap_graph_area(area);

// Custom base for non-percentage ranges
let snapped = snap_height_with_base(15, 8);  // Returns 8
let graph_area = snap_graph_area_with_base(area, 8);

Why snap? Braille uses 5 sub-levels per row. Snapping to nice heights (4, 10, 20, 50) ensures:

  • Each row = round percentage (25%, 10%, 5%, 2%)
  • Each braille level = clean value (5%, 2%, 1%, 0.4%)
  • 50% mark always lands on row boundary

Contracts

Guarantee Details
O(1) push VecDeque ring buffer; no allocations after capacity reached
Y-range .with_range() = fixed; .new() = auto-calculated per frame
Thread-safe State structs are Send + Sync
No unsafe #![deny(unsafe_code)] enforced

Common Patterns

Heat-map gradient:

let heatmap = ColorGradient::new()
    .add_stop(0.0, Color::Rgb(0, 0, 255))    // Blue (cold)
    .add_stop(0.5, Color::Rgb(255, 255, 0))  // Yellow
    .add_stop(1.0, Color::Rgb(255, 0, 0));   // Red (hot)

Custom series styles:

MultiTimeSeries::new().styles(vec![
    Style::default().fg(Color::Red),
    Style::default().fg(Color::Green),
    Style::default().fg(Color::Blue),
])

Zero-line reference:

Graph::new(&source).zero_line(true)

Exports

use bgraph::{
    // Core
    Graph, DataSource, FnDataSource, RenderMode, ColorGradient, GradientMode,
    // Time Series
    TimeSeries, TimeSeriesState,
    // Dual
    DualGraph, DualTimeSeries, DualTimeSeriesState, SplitDirection,
    // Multi
    Series, MultiGraph, MultiTimeSeries, MultiTimeSeriesState, LegendPosition,
    // btop-style Widgets
    Meter, MiniGraph, MiniTimeSeries, MiniTimeSeriesState,
    // Layout Utilities
    snap_height, snap_height_with_base, snap_graph_area, snap_graph_area_with_base,
};