use std::error::Error;
use image::{ImageBuffer, Luma};
use indicatif::{ProgressBar, ProgressStyle};
use nalgebra::{Vector2, vector};
use polydf::{NearestOptions, is_definitely_far, nearest_t_with_derivative};
fn curve(t: f32) -> Vector2<f32> {
let s = 0.04 * t.sqrt() + 5.0;
let theta = s.sin();
let (st, ct) = theta.sin_cos();
vector![t * ct, t * st]
}
fn curve_d(t: f32) -> Vector2<f32> {
let sqrt_t = t.sqrt();
let s = 0.04 * sqrt_t + 5.0;
let theta = s.sin();
let (st, ct) = theta.sin_cos();
let k = 0.02 * sqrt_t * s.cos();
vector![ct - k * st, st + k * ct]
}
fn main() -> Result<(), Box<dyn Error>> {
let width: u32 = std::env::var("RENDER_WIDTH")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1024);
let height: u32 = std::env::var("RENDER_HEIGHT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1024);
let x_min = 0.0f32;
let x_max = 500_000.0f32; let y_min = -250_000.0f32; let y_max = 250_000.0f32;
let threshold: f32 = std::env::var("RENDER_THRESHOLD")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(50_000.0);
let t_range = 0.0f32..=500_000.0f32;
let speed_upper_bound = Some(16.0f32);
let samples: usize = std::env::var("RENDER_SAMPLES")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(16);
let opts = NearestOptions {
samples,
..Default::default()
};
let mut img: ImageBuffer<Luma<u8>, Vec<u8>> = ImageBuffer::new(width, height);
let sx = if width > 1 {
(x_max - x_min) / (width - 1) as f32
} else {
0.0
};
let sy = if height > 1 {
(y_max - y_min) / (height - 1) as f32
} else {
0.0
};
let pb = ProgressBar::new(height as u64);
pb.set_style(
ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} rows ({eta})",
)
.unwrap()
.progress_chars("#>-")
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "),
);
for y in 0..height {
let wy = y_min + (y as f32) * sy;
for x in 0..width {
let wx = x_min + (x as f32) * sx;
let p = vector![wx, wy];
let lum = if is_definitely_far(
p,
curve,
t_range.clone(),
threshold,
speed_upper_bound,
opts.samples,
) {
255u8
} else {
let res = nearest_t_with_derivative(p, curve, curve_d, t_range.clone(), opts);
let d = res.distance;
((d / threshold).clamp(0.0, 1.0) * 255.0).round() as u8
};
img.put_pixel(x, height - 1 - y, Luma([lum]));
}
pb.inc(1);
}
pb.finish_with_message("render complete");
let out_path =
std::env::var("RENDER_OUT").unwrap_or_else(|_| "curve_derivative.png".to_string());
img.save(&out_path)?;
eprintln!("Wrote {}", out_path);
Ok(())
}