Expand description
§Ruviz - High-Performance Rust Plotting Library
A modern, high-performance 2D plotting library for Rust that combines matplotlib’s comprehensiveness with Makie’s performance-oriented design, while maintaining Rust’s safety and ergonomics.
§Features
- High Performance: <100ms for 100K points, <1s for 1M points
- Zero Unsafe Public API: Memory safety without compromising performance
- 30+ Plot Types: Distribution, categorical, polar, regression, composite plots
- Publication Quality: PNG/SVG export with custom themes
- Large Dataset Support: DataShader-style aggregation for 100M+ points
- Cross Platform: Linux, macOS, Windows, WASM support
- Reactive Animation: Signal-based animation with time-varying data
§Quick Start
Create plots with minimal boilerplate using top-level convenience functions:
use ruviz::prelude::*;
// Line plot - one line of code!
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
line(&x, &y).title("Sine Wave").save("sine.png")?;
// Scatter plot
scatter(&x, &y).title("Points").marker(MarkerStyle::Circle).save("scatter.png")?;
// Bar chart
let cats = ["A", "B", "C", "D"];
let vals = [10.0, 25.0, 15.0, 30.0];
bar(&cats, &vals).title("Sales").save("bar.png")?;§Reactive Animation (New!)
Create smooth animations with Signal-based reactive data. Define your animation once and render at any time point.
§Basic Animation with record! Macro
use ruviz::prelude::*;
use ruviz::record;
// Frame-based animation
record!("wave.gif", 60, |t| {
let phase = t.time * 2.0 * std::f64::consts::PI;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&xi| (xi + phase).sin()).collect();
line(&x, &y).title(format!("t = {:.2}s", t.time))
})?;
// Duration-based animation (2 seconds at 30fps)
record!("bounce.gif", 2 secs, |t| {
let y = t.ease_over(easing::ease_out_bounce, 100.0, 0.0, 2.0);
scatter(&[0.0], &[y]).title("Bouncing Ball")
})?;
// Custom framerate
record!("smooth.gif", 3 secs @ 60 fps, |t| {
let x = t.lerp_over(0.0, 10.0, 3.0);
line(&[0.0, x], &[0.0, x]).title("Growing Line")
})?;§Signal-Based Reactive Plots
Use Signal<T> for pull-based animation values that are evaluated at render time:
use ruviz::prelude::*;
use ruviz::animation::signal;
// Create signals that vary over time
let amplitude = signal::lerp(0.0, 2.0, 3.0); // 0 to 2 over 3 seconds
let frequency = signal::ease(easing::ease_in_out_quad, 1.0, 5.0, 3.0);
// Compose signals
let y_data = signal::of(move |t| {
let amp = amplitude.at(t);
let freq = frequency.at(t);
(0..100).map(|i| {
let x = i as f64 * 0.1;
amp * (x * freq).sin()
}).collect::<Vec<f64>>()
});
// Use with reactive title
let title = signal::of(|t| format!("Wave Animation - t={:.2}s", t));
// Create plot with reactive data (evaluated at render time)
let plot = Plot::new()
.title_signal(title)
.line_signal(&x_data, y_data);
// Record using reactive plot
record!("reactive.gif", &plot, 3 secs)?;§Reactive Labels
Make titles and axis labels change during animation:
use ruviz::prelude::*;
use ruviz::animation::signal;
// Dynamic title showing current time
let title = signal::of(|t| format!("Simulation: {:.1}s", t));
// Dynamic axis label
let ylabel = signal::of(|t| {
if t < 1.0 { "Accelerating".to_string() }
else if t < 2.0 { "Constant Velocity".to_string() }
else { "Decelerating".to_string() }
});
Plot::new()
.title_signal(title)
.xlabel("Time")
.ylabel_signal(ylabel)
.line(&x, &y)
.save("dynamic_labels.png")?;§Signal Composition
Combine multiple signals for complex animations:
use ruviz::animation::signal;
// Basic signal constructors
let constant = signal::constant(42.0); // Always returns 42
let time = signal::time(); // Returns current time
let linear = signal::lerp(0.0, 100.0, 2.0); // Linear interpolation
let eased = signal::ease(easing::ease_out_bounce, 100.0, 0.0, 2.0);
// Transform signals
let doubled = linear.map(|v| v * 2.0);
// Combine two signals
let combined = signal::zip(linear.clone(), eased, |a, b| a + b);
// Combine three signals
let rgb = signal::zip3(
signal::lerp(0.0, 255.0, 1.0),
signal::lerp(255.0, 0.0, 1.0),
signal::constant(128.0),
|r, g, b| (r as u8, g as u8, b as u8)
);
// Custom signal from closure
let sine_wave = signal::of(|t| (t * std::f64::consts::TAU).sin());§Gallery
Click any image to view full size. Expand sections below to see code examples.
§Plot Types
![]() | ![]() | ![]() |
| Line Plot | Scatter Plot | Bar Chart |
![]() | ![]() | ![]() |
| Histogram | Box Plot | Heatmap |
![]() | ![]() | ![]() |
| KDE Plot | ECDF Plot | Pie Chart |
![]() | ![]() | ![]() |
| Error Bar | Violin Plot | Contour Plot |
![]() | ![]() | |
| Polar Plot | Radar Chart |
§Advanced Plot Types (30+ Total)
ruviz provides comprehensive plot type coverage for scientific visualization:
| Category | Plot Types |
|---|---|
| Distribution | Violin, KDE (1D/2D), Boxen, ECDF, Strip, Swarm |
| Categorical | Grouped Bar, Stacked Bar, Horizontal Bar |
| Composition | Pie, Donut, Area, Stacked Area |
| Continuous | Contour, Hexbin, Fill Between |
| Error | Error Bars (symmetric/asymmetric) |
| Discrete | Step, Stem |
| Regression | Regression Plot, Residual Plot |
| Polar | Polar Plot, Radar/Spider Chart |
| Composite | Joint Plot, Pair Plot |
| Vector | Quiver Plot |
| Hierarchical | Dendrogram |
See plots module and Plot Types Guide for details.
Plot Types Code Examples
Line Plot
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
Plot::new()
.title("Sine Wave")
.xlabel("x")
.ylabel("sin(x)")
.line(&x, &y)
.end_series()
.save("line_plot.png")?;Scatter Plot
use ruviz::prelude::*;
let x: Vec<f64> = (0..50).map(|i| i as f64 * 0.2).collect();
let y: Vec<f64> = x.iter().enumerate()
.map(|(i, &v)| v.sin() + (i as f64 * 0.1).sin() * 0.3)
.collect();
Plot::new()
.title("Scatter Plot")
.xlabel("x")
.ylabel("y")
.scatter(&x, &y)
.end_series()
.save("scatter_plot.png")?;Bar Chart
use ruviz::prelude::*;
let categories = vec!["A", "B", "C", "D", "E"];
let values = vec![23.0, 45.0, 56.0, 78.0, 32.0];
Plot::new()
.title("Bar Chart")
.xlabel("Category")
.ylabel("Value")
.bar(&categories, &values)
.end_series()
.save("bar_chart.png")?;Histogram
use ruviz::prelude::*;
// Generate sample data
let data: Vec<f64> = (0..1000).map(|i| {
let u1 = ((i * 7 + 13) % 1000) as f64 / 1000.0;
let u2 = ((i * 11 + 17) % 1000) as f64 / 1000.0;
(-2.0 * u1.max(0.001).ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos()
}).collect();
Plot::new()
.title("Histogram")
.xlabel("Value")
.ylabel("Frequency")
.histogram(&data, None)
.end_series()
.save("histogram.png")?;Box Plot
use ruviz::prelude::*;
use ruviz::plots::boxplot::BoxPlotConfig;
let data = vec![
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0,
35.0, 40.0, -5.0, // Outliers
];
Plot::new()
.title("Box Plot")
.xlabel("Distribution")
.ylabel("Values")
.boxplot(&data, Some(BoxPlotConfig::new()))
.end_series()
.save("boxplot.png")?;Heatmap
use ruviz::prelude::*;
// Create 2D data (distance from center)
let data: Vec<Vec<f64>> = (0..10).map(|i| {
(0..10).map(|j| {
((i as f64 - 5.0).powi(2) + (j as f64 - 5.0).powi(2)).sqrt()
}).collect()
}).collect();
Plot::new()
.title("Heatmap")
.xlabel("X")
.ylabel("Y")
.heatmap(&data, None)
.end_series()
.save("heatmap.png")?;§Styling Options
Styling Code Examples
Line Styles
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
Plot::new()
.title("Line Styles")
.legend_position(LegendPosition::Best)
.line(&x, &x.iter().map(|&v| v.sin() + 4.0).collect::<Vec<_>>())
.label("Solid").style(LineStyle::Solid)
.line(&x, &x.iter().map(|&v| v.sin() + 3.0).collect::<Vec<_>>())
.label("Dashed").style(LineStyle::Dashed)
.line(&x, &x.iter().map(|&v| v.sin() + 2.0).collect::<Vec<_>>())
.label("Dotted").style(LineStyle::Dotted)
.line(&x, &x.iter().map(|&v| v.sin() + 1.0).collect::<Vec<_>>())
.label("DashDot").style(LineStyle::DashDot)
.end_series()
.save("line_styles.png")?;Marker Styles
use ruviz::prelude::*;
let x: Vec<f64> = (0..5).map(|j| j as f64 * 2.0).collect();
Plot::new()
.title("Marker Styles")
.legend_position(LegendPosition::Best)
.scatter(&x, &vec![5.0; 5]).label("Circle").marker(MarkerStyle::Circle)
.scatter(&x, &vec![4.0; 5]).label("Square").marker(MarkerStyle::Square)
.scatter(&x, &vec![3.0; 5]).label("Triangle").marker(MarkerStyle::Triangle)
.scatter(&x, &vec![2.0; 5]).label("Diamond").marker(MarkerStyle::Diamond)
.scatter(&x, &vec![1.0; 5]).label("Star").marker(MarkerStyle::Star)
.end_series()
.save("marker_styles.png")?;Color Palette
use ruviz::prelude::*;
let palette = Color::default_palette();
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
Plot::new()
.title("Default Color Palette")
.legend_position(LegendPosition::Best)
.line(&x, &x.iter().map(|&v| v.sin() + 3.0).collect::<Vec<_>>())
.label("Color 1").color(palette[0])
.line(&x, &x.iter().map(|&v| v.sin() + 2.0).collect::<Vec<_>>())
.label("Color 2").color(palette[1])
.line(&x, &x.iter().map(|&v| v.sin() + 1.0).collect::<Vec<_>>())
.label("Color 3").color(palette[2])
.line(&x, &x.iter().map(|&v| v.sin()).collect::<Vec<_>>())
.label("Color 4").color(palette[3])
.end_series()
.save("colors.png")?;§Themes
Themes Code Examples
Default Theme
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
Plot::new()
.title("Default Theme")
.line(&x, &y)
.end_series()
.save("theme_default.png")?;Dark Theme
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
Plot::new()
.title("Dark Theme")
.theme(Theme::dark())
.line(&x, &y)
.end_series()
.save("theme_dark.png")?;Seaborn Theme
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
Plot::new()
.title("Seaborn Theme")
.theme(Theme::seaborn())
.line(&x, &y)
.end_series()
.save("theme_seaborn.png")?;Publication Theme
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
Plot::new()
.title("Publication Theme")
.theme(Theme::publication())
.line(&x, &y)
.end_series()
.save("theme_publication.png")?;§Layout
Layout Code Examples
Legend Positions
use ruviz::prelude::*;
let x: Vec<f64> = (0..50).map(|i| i as f64 * 0.1).collect();
let y_sin: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
let y_cos: Vec<f64> = x.iter().map(|&v| v.cos()).collect();
// Create plots with different legend positions
let plot_ul = Plot::new()
.title("UpperLeft")
.legend_position(LegendPosition::UpperLeft)
.line(&x, &y_sin).label("sin(x)")
.line(&x, &y_cos).label("cos(x)")
.end_series();
let plot_ur = Plot::new()
.title("UpperRight")
.legend_position(LegendPosition::UpperRight)
.line(&x, &y_sin).label("sin(x)")
.line(&x, &y_cos).label("cos(x)")
.end_series();
let plot_ll = Plot::new()
.title("LowerLeft")
.legend_position(LegendPosition::LowerLeft)
.line(&x, &y_sin).label("sin(x)")
.line(&x, &y_cos).label("cos(x)")
.end_series();
let plot_lr = Plot::new()
.title("LowerRight")
.legend_position(LegendPosition::LowerRight)
.line(&x, &y_sin).label("sin(x)")
.line(&x, &y_cos).label("cos(x)")
.end_series();
// Combine in 2x2 subplots
subplots(2, 2, 800, 600)?
.suptitle("Legend Positions")
.subplot_at(0, plot_ul)?
.subplot_at(1, plot_ur)?
.subplot_at(2, plot_ll)?
.subplot_at(3, plot_lr)?
.save("legend_positions.png")?;Subplots
use ruviz::prelude::*;
let x: Vec<f64> = (0..50).map(|i| i as f64 * 0.2).collect();
let plot_line = Plot::new()
.title("Line Plot")
.line(&x, &x.iter().map(|&v| v.sin()).collect::<Vec<_>>())
.end_series();
let plot_scatter = Plot::new()
.title("Scatter Plot")
.scatter(&x, &x.iter().map(|&v| v.cos()).collect::<Vec<_>>())
.end_series();
let plot_bar = Plot::new()
.title("Bar Chart")
.bar(&["Q1", "Q2", "Q3", "Q4"], &[28.0, 45.0, 38.0, 52.0])
.end_series();
let plot_multi = Plot::new()
.title("Comparison")
.legend_position(LegendPosition::UpperRight)
.line(&x, &x.iter().map(|&v| v.sin()).collect::<Vec<_>>()).label("sin")
.line(&x, &x.iter().map(|&v| v.cos()).collect::<Vec<_>>()).label("cos")
.end_series();
subplots(2, 2, 800, 600)?
.suptitle("Subplot Gallery")
.subplot_at(0, plot_line)?
.subplot_at(1, plot_scatter)?
.subplot_at(2, plot_bar)?
.subplot_at(3, plot_multi)?
.save("subplots.png")?;§Internationalization
Internationalization Code Examples
Japanese Labels
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
Plot::new()
.title("サイン波 (Sine Wave)")
.xlabel("時間 (s)")
.ylabel("振幅")
.line(&x, &y)
.label("sin(x)")
.legend_best()
.save("japanese_plot.png")?;Chinese Labels
use ruviz::prelude::*;
let categories = vec!["一月", "二月", "三月", "四月", "五月", "六月"];
let values = vec![28.0, 45.0, 38.0, 52.0, 47.0, 63.0];
Plot::new()
.title("月度销售数据")
.xlabel("月份")
.ylabel("销售额 (万元)")
.bar(&categories, &values)
.save("chinese_plot.png")?;§Animation
Smooth animations with the record! macro (requires animation feature):
See Animation Gallery for more examples.
§Quick Start
use ruviz::prelude::*;
let x = vec![0.0, 1.0, 2.0, 3.0, 4.0];
let y = vec![0.0, 1.0, 4.0, 9.0, 16.0];
Plot::new()
.line(&x, &y)
.title("Quadratic Function")
.xlabel("x")
.ylabel("y = x²")
.save("plot.png")?;§With Legend (matplotlib-style)
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let sin_y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
let cos_y: Vec<f64> = x.iter().map(|&v| v.cos()).collect();
Plot::new()
.title("Trigonometric Functions")
.line(&x, &sin_y).label("sin(x)")
.line(&x, &cos_y).label("cos(x)")
.end_series() // Finish series chain
.legend_best() // Enable legend (like plt.legend())
.save("trig.png")?;§Figure Size and DPI
use ruviz::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
Plot::new()
.size(8.0, 6.0) // 8×6 inches
.dpi(300) // 300 DPI = 2400×1800 pixels
.line(&x, &y)
.save("high_res.png")?;§Named Colors
use ruviz::prelude::*;
let x: Vec<f64> = (0..50).map(|i| i as f64 * 0.2).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
// Use named colors (no unwrap needed!)
let color = Color::named("coral").unwrap_or(Color::RED);
Plot::new()
.line(&x, &y).color(color)
.save("colored.png")?;Modules§
- axes
- Axis management and scaling
- core
- Core plotting functionality and main API
- data
- Data handling and trait definitions
- export
- Export functionality
- layout
- Layout and positioning
- plots
- Plot type implementations
- prelude
- Convenience re-exports for common usage
- render
- Rendering backend and styling
- simple
- Simple API for quick plotting with minimal code
- stats
- Statistical computations for advanced plot types
- style
- Unified style module for consistent theming and styling across all plot types
- text
- Text rendering and layout































