#![deny(warnings)]
#[cfg(test)]
use plotpx::*;
#[cfg(test)]
use std::time::Instant;
const EXAMPLES_DIR: &str = "docs/examples";
#[cfg(test)]
fn output_path(file: &str) -> String {
format!("{}/{}", EXAMPLES_DIR, file)
}
#[cfg(test)]
fn magnitude_test() {
println!("Running magnitude_test...");
let start = Instant::now();
const W: u32 = 512;
const H: u32 = 512;
let mut plot = Magnitude::new(W, H);
for y in 0..H {
for x in 0..W {
let magnitude = (x + y) as f32 / (W + H) as f32;
plot.add_point(x, y, magnitude);
}
}
let image = plot.render();
write_png(output_path("magnitude.png"), &image, W, H).unwrap();
let elapsed = start.elapsed();
println!(" Rust magnitude_test completed in {:?}", elapsed);
}
#[cfg(test)]
fn magnitude_test2() {
println!("Running magnitude_test2...");
let start = Instant::now();
const WIDTH: u32 = 512;
const HEIGHT: u32 = 512;
let mut plot = Magnitude::new(WIDTH, HEIGHT);
let center_x = WIDTH as f32 / 2.0;
let center_y = HEIGHT as f32 / 2.0;
let frequency = 20.0;
for y in 0..HEIGHT {
for x in 0..WIDTH {
let dx = x as f32 - center_x;
let dy = y as f32 - center_y;
let distance = (dx * dx + dy * dy).sqrt();
let max_distance = (center_x * center_x + center_y * center_y).sqrt();
let normalized_distance = distance / max_distance;
let magnitude = ((normalized_distance * frequency).sin() + 1.0) / 2.0;
plot.add_point(x, y, magnitude);
}
}
let image = plot.render();
write_png(output_path("magnitude2.png"), &image, WIDTH, HEIGHT).unwrap();
let elapsed = start.elapsed();
println!(" Rust magnitude_test2 completed in {:?}", elapsed);
}
#[cfg(test)]
fn magnitude_mapped_test() {
println!("Running magnitude_mapped_test...");
let start = Instant::now();
const W_DATA: u32 = 128;
const H_DATA: u32 = 128;
const W: u32 = 640;
const H: u32 = 480;
let mut plot = MagnitudeMapped::new(W_DATA, H_DATA, W, H);
for y in 0..H_DATA {
for x in 0..W_DATA {
let magnitude = (x + y) as f32 / (W_DATA + H_DATA) as f32;
plot.add_point(x, y, magnitude);
}
}
let image = plot.render();
write_png(output_path("magnitude_mapped.png"), &image, W, H).unwrap();
let elapsed = start.elapsed();
println!(" Rust magnitude_mapped_test completed in {:?}", elapsed);
}
#[cfg(test)]
fn magnitude_mapped_shrink_test() {
println!("Running magnitude_mapped_shrink_test...");
let start = Instant::now();
const W_DATA: u32 = 512;
const H_DATA: u32 = 512;
const W: u32 = 256;
const H: u32 = 256;
let mut plot = MagnitudeMapped::new(W_DATA, H_DATA, W, H);
for y in 0..H_DATA {
for x in 0..W_DATA {
let magnitude = (x + y) as f32 / (W_DATA + H_DATA) as f32;
plot.add_point(x, y, magnitude);
}
}
let image = plot.render();
write_png(
output_path("magnitude_mapped_shrink.png"),
&image,
W,
H,
)
.unwrap();
let elapsed = start.elapsed();
println!(
" Rust magnitude_mapped_shrink_test completed in {:?}",
elapsed
);
}
#[cfg(test)]
fn magnitude_with_annotations_demo() {
println!("Running magnitude_with_annotations_demo...");
let start = Instant::now();
const WIDTH: u32 = 1024;
const HEIGHT: u32 = 576;
let mut plot = Magnitude::new(WIDTH, HEIGHT);
for y in 0..HEIGHT {
for x in 0..WIDTH {
let fx = x as f32 / WIDTH as f32;
let fy = y as f32 / HEIGHT as f32;
let magnitude =
(fx * std::f32::consts::PI * 4.0).sin() * (fy * std::f32::consts::PI * 2.0).cos();
plot.add_point(x, y, magnitude);
}
}
let mut annotations = ChartAnnotations::default();
annotations.border_color = BorderColor::White;
annotations.title = Some(ChartTitle::new("Sine Wave Interference"));
annotations.x_axis = Some(
AxisConfig::new("Horizontal Position", 0.0, WIDTH as f32)
.with_units("px")
.with_tick_count(6)
.with_decimal_places(0),
);
annotations.y_axis = Some(
AxisConfig::new("Vertical Position", 0.0, HEIGHT as f32)
.with_units("px")
.with_tick_count(5)
.with_decimal_places(0),
);
let chart = plot.render_default_with_annotations(&annotations);
write_png(
output_path("magnitude_annotated.png"),
&chart.pixels,
chart.width,
chart.height,
)
.unwrap();
let elapsed = start.elapsed();
println!(
" Rust magnitude_with_annotations_demo completed in {:?}",
elapsed
);
}
#[cfg(test)]
fn generate_spiral(width: u32, height: u32, num_points: usize, turns: f32) -> Vec<(f32, f32)> {
let mut data = Vec::with_capacity(num_points);
let center_x = width as f32 / 2.0;
let center_y = height as f32 / 2.0;
let max_radius = (width.min(height) as f32) / 2.0;
for i in 0..num_points {
let t = i as f32 / num_points as f32;
let angle = turns * 2.0 * std::f32::consts::PI * t;
let radius = max_radius * t;
let x = center_x + radius * angle.cos();
let y = center_y + radius * angle.sin();
data.push((x, y));
}
data
}
#[cfg(test)]
fn plot_spiral(plot: &mut MagnitudeMapped, spiral_points: &[(f32, f32)], intensity: f32) {
plot.reset();
for &(x, y) in spiral_points {
let x = x as u32;
let y = y as u32;
if x < plot.input_width && y < plot.input_height {
plot.add_point(x, y, intensity);
}
}
}
#[cfg(test)]
fn magnitude_grid_plot() {
println!("Running magnitude_grid_plot...");
let start = Instant::now();
const INPUT_SIZE: u32 = 200;
const PLOT_SIZE: u32 = 150;
const GRID_SIZE: usize = 4;
let mut grid = MagnitudeMappedGrid::new(GRID_SIZE, INPUT_SIZE, INPUT_SIZE, PLOT_SIZE, PLOT_SIZE);
for row in 0..GRID_SIZE {
for col in 0..GRID_SIZE {
let plot = grid.get_plot(row, col);
let num_points = 150 + (row * 40) + (col * 30);
let turns = 1.5 + (row as f32 * 0.4) + (col as f32 * 0.2);
let spiral_points = generate_spiral(INPUT_SIZE, INPUT_SIZE, num_points, turns);
let intensity = 1.0;
plot_spiral(plot, &spiral_points, intensity);
}
}
let spiral_color_scheme: Vec<Rgba> = vec![
[20, 0, 100, 255], [50, 0, 200, 255], [0, 100, 255, 255], [0, 200, 200, 255], [0, 255, 100, 255], [100, 255, 0, 255], [200, 255, 0, 255], [255, 200, 0, 255], [255, 100, 0, 255], [255, 0, 100, 255], [200, 0, 200, 255], ];
let colors = make_color_scheme(&spiral_color_scheme, 128);
let image = grid.render(&colors);
let total_width = PLOT_SIZE * GRID_SIZE as u32;
let total_height = PLOT_SIZE * GRID_SIZE as u32;
write_png(
output_path("spiral_grid.png"),
&image,
total_width,
total_height,
)
.unwrap();
let elapsed = start.elapsed();
println!(" Rust magnitude_grid_plot completed in {:?}", elapsed);
}
#[cfg(test)]
fn heatmap_test() {
println!("Running heatmap_test...");
let start = Instant::now();
const W: u32 = 512;
const H: u32 = 512;
const NPOINTS: usize = 600;
let mut hm = Heatmap::new(W, H);
let data = generate_spiral(W, H, NPOINTS, 10.0);
for point in &data {
hm.add_point(point.0 as u32, point.1 as u32);
}
let image = hm.render();
write_png(output_path("heatmap.png"), &image, W, H).unwrap();
let elapsed = start.elapsed();
println!(" Rust heatmap_test completed in {:?}", elapsed);
}
#[cfg(test)]
fn spectrum_test_sine() {
println!("Running spectrum_test_sine...");
let start = Instant::now();
const BINS: u32 = 256;
const W: u32 = 640;
const H: u32 = 360;
let mut plot = Spectrum::new(BINS, W, H);
plot.style = BarStyle::Gradient;
plot.show_peaks = true;
plot.bar_width_factor = 0.8;
let mut magnitudes = vec![0.0f32; BINS as usize];
let peak_bin = 64;
magnitudes[peak_bin] = 1.0;
for i in 1..=10 {
if peak_bin >= i {
magnitudes[peak_bin - i] = 1.0 / (i * i) as f32;
}
if peak_bin + i < BINS as usize {
magnitudes[peak_bin + i] = 1.0 / (i * i) as f32;
}
}
plot.update(&magnitudes);
let image = plot.render();
write_png(output_path("spectrum_sine.png"), &image, W, H).unwrap();
let elapsed = start.elapsed();
println!(" Rust spectrum_test_sine completed in {:?}", elapsed);
}
#[cfg(test)]
fn spectrum_test_complex() {
println!("Running spectrum_test_complex...");
let start = Instant::now();
const BINS: u32 = 256;
const W: u32 = 640;
const H: u32 = 360;
let mut plot = Spectrum::new(BINS, W, H);
plot.style = BarStyle::Solid;
plot.show_peaks = true;
plot.bar_width_factor = 0.9;
let mut magnitudes = vec![0.0f32; BINS as usize];
let peaks = [32, 64, 96, 128, 192];
let amplitudes = [0.5, 1.0, 0.7, 0.3, 0.8];
for p in 0..5 {
let peak_bin = peaks[p];
let amplitude = amplitudes[p];
magnitudes[peak_bin] = amplitude;
for i in 1..=5 {
if peak_bin >= i {
magnitudes[peak_bin - i] = amplitude / (i * i) as f32;
}
if peak_bin + i < BINS as usize {
magnitudes[peak_bin + i] = amplitude / (i * i) as f32;
}
}
}
plot.update(&magnitudes);
let image = plot.render_with_colors(&make_color_scheme(INFERNO_KEY_COLORS, 128));
write_png(output_path("spectrum_complex.png"), &image, W, H).unwrap();
let elapsed = start.elapsed();
println!(" Rust spectrum_test_complex completed in {:?}", elapsed);
}
#[cfg(not(test))]
fn main() {
println!(
"Plotpx test images are only generated during `cargo test`. \
Run `cargo test -- --nocapture` to regenerate the PNG outputs in `{}`.",
EXAMPLES_DIR
);
}
#[cfg(test)]
mod tests {
use super::*;
fn ensure_examples_dir() {
if let Err(err) = std::fs::create_dir_all(EXAMPLES_DIR) {
panic!("failed to create examples directory: {}", err);
}
}
#[test]
fn generates_example_gallery() {
ensure_examples_dir();
let total_start = Instant::now();
magnitude_test();
magnitude_test2();
magnitude_mapped_test();
magnitude_mapped_shrink_test();
magnitude_with_annotations_demo();
magnitude_grid_plot();
heatmap_test();
spectrum_test_sine();
spectrum_test_complex();
let total_elapsed = total_start.elapsed();
println!("\nTotal Rust execution time: {:?}", total_elapsed);
}
}