use super::super::draw;
use super::super::{BarContext, ProgressStyle};
use crate::{BrailleGrid, DotmaxError};
use std::f32::consts::PI;
pub fn styles() -> Vec<Box<dyn ProgressStyle>> {
vec![
Box::new(QubitSuperposition),
Box::new(BlochSphere),
Box::new(WavefunctionCollapse),
Box::new(QuantumTunneling),
Box::new(HarmonicOscillator),
Box::new(ParticleInBox),
Box::new(SpinPrecession),
Box::new(EnergyLevels),
Box::new(Decoherence),
Box::new(QuantumWalk),
]
}
#[inline]
fn line(grid: &mut BrailleGrid, x0: i32, y0: i32, x1: i32, y1: i32) {
let dx = (x1 - x0).abs();
let dy = (y1 - y0).abs();
let steps = dx.max(dy).max(1);
for i in 0..=steps {
let px = x0 + (x1 - x0) * i / steps;
let py = y0 + (y1 - y0) * i / steps;
draw::dot_i(grid, px, py);
}
}
struct QubitSuperposition;
impl ProgressStyle for QubitSuperposition {
fn name(&self) -> &str {
"qubit-superposition"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Qubit |0⟩↔|1⟩ superposition: state oscillates, collapses to |1⟩ as progress reaches 100%"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cw, ch) = grid.dimensions();
if cw == 0 || ch == 0 {
return Ok(());
}
let omega = 2.5_f32;
let osc = (omega * ctx.time).sin(); let theta = PI * osc * (1.0 - ctx.eased);
let beta_sq = (theta / 2.0).sin().powi(2) * (1.0 - ctx.eased) + ctx.eased; let alpha_sq = (1.0 - beta_sq).max(0.0);
let (dw, dh) = draw::dot_dims(grid);
let _ = (dw, dh);
for cy in 0..ch {
for cx in 0..cw {
let t = cx as f32 / cw.max(1) as f32;
let col = ctx.palette.sample(t * beta_sq);
draw::tint_row(grid, cy, cx, cx, col);
}
}
let col_w = (cw / 2).max(1);
let gap = col_w.min(2);
let left_x = cw / 4;
let right_x = (3 * cw / 4).min(cw.saturating_sub(1));
let alpha_lv = (alpha_sq * 8.0).round() as usize;
let beta_lv = (beta_sq * 8.0).round() as usize;
for cy in 0..ch {
let row_from_bottom = ch - 1 - cy;
let alpha_rows = (alpha_lv * ch / 8).min(ch);
if row_from_bottom < alpha_rows {
for dx in 0..gap.max(1) {
if left_x + dx < cw {
draw::vblock(grid, left_x + dx, cy, 8);
}
}
}
let beta_rows = (beta_lv * ch / 8).min(ch);
if row_from_bottom < beta_rows {
for dx in 0..gap.max(1) {
if right_x + dx < cw {
draw::vblock(grid, right_x + dx, cy, 8);
}
}
}
}
Ok(())
}
}
struct BlochSphere;
impl ProgressStyle for BlochSphere {
fn name(&self) -> &str {
"bloch-sphere"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Bloch sphere: wireframe sphere + state vector precessing with time, polar angle from progress"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw == 0 || dh == 0 {
return Ok(());
}
let cx = (dw / 2) as i32;
let cy = (dh / 2) as i32;
let rx = (dw / 2).saturating_sub(1) as i32; let ry = (dh / 2).saturating_sub(1) as i32;
let eq_steps = (rx * 2 + 8).max(8) as usize * 2;
let mut prev: Option<(i32, i32)> = None;
for s in 0..=eq_steps {
let a = s as f32 / eq_steps as f32 * 2.0 * PI;
let px = cx + (rx as f32 * a.cos()) as i32;
let py = cy + (ry as f32 * 0.35 * a.sin()) as i32; draw::dot_i(grid, px, py);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, px, py);
}
prev = Some((px, py));
}
let me_steps = (ry * 2 + 8).max(8) as usize * 2;
let mut prev: Option<(i32, i32)> = None;
for s in 0..=me_steps {
let a = s as f32 / me_steps as f32 * 2.0 * PI;
let px = cx + (rx as f32 * 0.35 * a.sin()) as i32; let py = cy + (ry as f32 * a.cos()) as i32;
draw::dot_i(grid, px, py);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, px, py);
}
prev = Some((px, py));
}
let sil_steps = ((rx + ry) * 2 + 16).max(16) as usize;
let mut prev: Option<(i32, i32)> = None;
for s in 0..=sil_steps {
let a = s as f32 / sil_steps as f32 * 2.0 * PI;
let px = cx + (rx as f32 * a.cos()) as i32;
let py = cy + (ry as f32 * a.sin()) as i32;
draw::dot_i(grid, px, py);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, px, py);
}
prev = Some((px, py));
}
let theta = PI * ctx.eased;
let phi = 2.0 * PI * ctx.time * 0.5;
let vx = theta.sin() * phi.cos(); let vz = theta.cos(); let tip_x = cx + (vx * rx as f32 * 0.85) as i32;
let tip_y = cy - (vz * ry as f32 * 0.85) as i32; line(grid, cx, cy, tip_x, tip_y);
draw::dot_i(grid, tip_x, tip_y);
draw::dot_i(grid, tip_x + 1, tip_y);
draw::dot_i(grid, tip_x - 1, tip_y);
draw::dot_i(grid, tip_x, tip_y + 1);
draw::dot_i(grid, tip_x, tip_y - 1);
draw::dot_i(grid, cx, cy - ry);
draw::dot_i(grid, cx, cy + ry);
let (cw, ch) = grid.dimensions();
let filled_cells = (ctx.eased * cw as f32).round() as usize;
for cell_x in 0..filled_cells.min(cw) {
let t = cell_x as f32 / cw.max(1) as f32;
let col = ctx.palette.sample(t);
for cell_y in 0..ch {
draw::tint_row(grid, cell_y, cell_x, cell_x, col);
}
}
Ok(())
}
}
struct WavefunctionCollapse;
impl ProgressStyle for WavefunctionCollapse {
fn name(&self) -> &str {
"wavefunction-collapse"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Wavefunction collapse: |ψ(x)|² spreads as a broad Gaussian then sharpens to a spike at 100%"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cw, ch) = grid.dimensions();
if cw == 0 || ch == 0 {
return Ok(());
}
let cwf = cw as f32;
let sigma_max = cwf * 0.35;
let sigma_min = cwf.max(4.0) * 0.025;
let sigma = sigma_max * (1.0 - ctx.eased) + sigma_min;
let center_norm = 0.5_f32 + 0.15 * (ctx.time * 1.8).sin() * (1.0 - ctx.eased);
let mean = center_norm * cwf;
for cx in 0..cw {
let xf = cx as f32 + 0.5;
let exponent = -0.5 * ((xf - mean) / sigma.max(0.1)).powi(2);
let amplitude = exponent.exp().clamp(0.0, 1.0);
for cy in 0..ch {
let row_norm = 1.0 - cy as f32 / ch as f32; if amplitude >= row_norm {
draw::vblock(grid, cx, cy, 8);
} else {
let partial = ((amplitude - (row_norm - 1.0 / ch as f32)) * ch as f32 * 8.0)
.clamp(0.0, 8.0) as usize;
if partial > 0 {
draw::vblock(grid, cx, cy, partial);
}
}
}
let t = cx as f32 / cwf;
let col = ctx.palette.sample(t * amplitude + ctx.eased * (1.0 - t));
for cy in 0..ch {
draw::tint_row(grid, cy, cx, cx, col);
}
}
Ok(())
}
}
struct QuantumTunneling;
impl ProgressStyle for QuantumTunneling {
fn name(&self) -> &str {
"quantum-tunneling"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Quantum tunneling: wave packet hits a barrier, transmitted amplitude grows with progress"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw == 0 || dh == 0 {
return Ok(());
}
let dwf = dw as f32;
let dhf = dh as f32;
let mid_y = (dhf / 2.0) as i32;
let barrier_x0 = (dwf * 0.55) as usize;
let barrier_x1 = (dwf * 0.65) as usize;
let barrier_h = (dhf * 0.7).max(2.0) as usize;
let bar_top = ((dhf - barrier_h as f32) / 2.0) as usize;
for y in bar_top..(bar_top + barrier_h).min(dh) {
draw::dot(grid, barrier_x0.min(dw.saturating_sub(1)), y);
draw::dot(grid, barrier_x1.min(dw.saturating_sub(1)), y);
}
draw::hline(
grid,
barrier_x0,
barrier_x1.min(dw.saturating_sub(1)),
bar_top,
);
draw::hline(
grid,
barrier_x0,
barrier_x1.min(dw.saturating_sub(1)),
(bar_top + barrier_h)
.saturating_sub(1)
.min(dh.saturating_sub(1)),
);
let decay = (1.0 - ctx.eased).max(0.01);
for xi in barrier_x0..=barrier_x1.min(dw.saturating_sub(1)) {
let d = xi.saturating_sub(barrier_x0) as f32;
let envelope =
(-decay * 2.5 * d / (barrier_x1.saturating_sub(barrier_x0) as f32).max(1.0)).exp();
if (d as usize % (2.max((1.0 / envelope.max(0.1)) as usize))) == 0 {
draw::dot(grid, xi, mid_y as usize);
}
}
let k = 4.0 * PI / dwf; let omega = k * 2.0; let refl_amp = (1.0 - ctx.eased).sqrt();
let inc_amp = 1.0_f32;
let wave_amp = (dhf * 0.35).max(1.0);
let mut prev: Option<(i32, i32)> = None;
for xi in 0..barrier_x0 {
let xf = xi as f32;
let incident = inc_amp * (k * xf - omega * ctx.time).sin();
let reflected = refl_amp * (k * xf + omega * ctx.time).sin();
let total = (incident + reflected) * wave_amp;
let py = (mid_y as f32 - total) as i32;
let py = py.clamp(0, dh as i32 - 1);
draw::dot_i(grid, xi as i32, py);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, xi as i32, py);
}
prev = Some((xi as i32, py));
}
let trans_amp = ctx.eased.sqrt();
let mut prev: Option<(i32, i32)> = None;
for xi in (barrier_x1 + 1)..dw {
let xf = xi as f32;
let transmitted = trans_amp * (k * xf - omega * ctx.time).sin() * wave_amp;
let py = (mid_y as f32 - transmitted) as i32;
let py = py.clamp(0, dh as i32 - 1);
draw::dot_i(grid, xi as i32, py);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, xi as i32, py);
}
prev = Some((xi as i32, py));
}
draw::hline(grid, 0, dw.saturating_sub(1), mid_y as usize);
let (cw, ch) = grid.dimensions();
let split_cell = barrier_x1 / 2;
for cx in split_cell.min(cw)..cw {
let t = cx as f32 / cw.max(1) as f32;
let col = ctx.palette.sample(t * ctx.eased);
for cy in 0..ch {
draw::tint_row(grid, cy, cx, cx, col);
}
}
Ok(())
}
}
struct HarmonicOscillator;
impl ProgressStyle for HarmonicOscillator {
fn name(&self) -> &str {
"harmonic-oscillator"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"QHO eigenstate: |ψₙ|² density plotted — energy level n steps up the ladder with progress"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw == 0 || dh == 0 {
return Ok(());
}
let dwf = dw as f32;
let dhf = dh as f32;
let n_max = 6_usize;
let n = (ctx.eased * (n_max + 1) as f32).floor() as usize;
let n = n.min(n_max);
let x_max = 3.5_f32 + n as f32 * 0.4;
let hermite = |x: f32| -> f32 {
if n == 0 {
return 1.0;
}
let mut h_prev2 = 1.0_f32;
let mut h_prev1 = 2.0 * x;
if n == 1 {
return h_prev1;
}
let mut h = 0.0_f32;
for k in 2..=(n as i32) {
h = 2.0 * x * h_prev1 - 2.0 * (k - 1) as f32 * h_prev2;
h_prev2 = h_prev1;
h_prev1 = h;
}
h
};
let mut peak = 0.0_f32;
let norm_samples = 64_usize;
for i in 0..norm_samples {
let xf = -x_max + 2.0 * x_max * i as f32 / norm_samples as f32;
let hn = hermite(xf);
let psi = hn * (-xf * xf / 2.0).exp();
let dens = psi * psi;
if dens > peak {
peak = dens;
}
}
let peak = peak.max(1e-9);
let phase = ctx.time * 0.8;
let mut prev: Option<(i32, i32)> = None;
for xi in 0..dw {
let xf = -x_max + 2.0 * x_max * xi as f32 / dwf;
let hn = hermite(xf);
let psi = hn * (-xf * xf / 2.0).exp();
let density = (psi * psi / peak).clamp(0.0, 1.0);
let shimmer = 1.0 + 0.08 * (2.0 * xf + phase).cos();
let display_h = (density * shimmer.clamp(0.0, 1.1) * dhf).clamp(0.0, dhf);
let bot = dh as i32 - 1;
let top = (bot as f32 - display_h) as i32;
let top = top.clamp(0, bot);
let mid_dot = (top + bot) / 2;
draw::dot_i(grid, xi as i32, mid_dot);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, xi as i32, mid_dot);
}
prev = Some((xi as i32, mid_dot));
}
draw::hline(grid, 0, dw.saturating_sub(1), dh.saturating_sub(1));
let (cw, ch) = grid.dimensions();
for cx in 0..cw {
let t = cx as f32 / cw.max(1) as f32;
let level_t = n as f32 / n_max as f32;
let col = ctx.palette.sample(t * 0.5 + level_t * 0.5);
for cy in 0..ch {
draw::tint_row(grid, cy, cx, cx, col);
}
}
Ok(())
}
}
struct ParticleInBox;
impl ProgressStyle for ParticleInBox {
fn name(&self) -> &str {
"particle-in-box"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Particle in a box: sin²(nπx/L) standing mode — mode n steps up with progress, walls visible"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw == 0 || dh == 0 {
return Ok(());
}
let dhf = dh as f32;
let n_max = 7_usize;
let n = (ctx.eased * n_max as f32).ceil() as usize;
let n = n.clamp(1, n_max);
draw::vline(grid, 0, 0, dh.saturating_sub(1));
draw::vline(grid, dw.saturating_sub(1), 0, dh.saturating_sub(1));
let x_start = 1_usize;
let x_end = dw.saturating_sub(2);
let box_w = x_end.saturating_sub(x_start).max(1);
let phase = ctx.time * 1.2;
let mut prev: Option<(i32, i32)> = None;
for xi in x_start..=x_end {
let xrel = (xi - x_start) as f32 / box_w as f32; let density = (n as f32 * PI * xrel).sin().powi(2).clamp(0.0, 1.0);
let shimmer = 0.85 + 0.15 * (n as f32 * PI * xrel - phase * n as f32).sin().abs();
let display_h = (density * shimmer * (dhf - 2.0)).clamp(0.0, dhf - 2.0);
let base = dh.saturating_sub(2) as i32;
let peak_dot = (base as f32 - display_h) as i32;
let peak_dot = peak_dot.clamp(0, base);
draw::dot_i(grid, xi as i32, peak_dot);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, xi as i32, peak_dot);
}
prev = Some((xi as i32, peak_dot));
}
draw::hline(grid, 0, dw.saturating_sub(1), dh.saturating_sub(1));
let (cw, ch) = grid.dimensions();
let x_start_c = 0_usize;
let x_end_c = cw;
for cx in x_start_c..x_end_c {
let xrel = cx as f32 / cw.max(1) as f32;
let density = (n as f32 * PI * xrel).sin().powi(2).clamp(0.0, 1.0);
let col = ctx.palette.sample(density);
for cy in 0..ch {
draw::tint_row(grid, cy, cx, cx, col);
}
}
Ok(())
}
}
struct SpinPrecession;
impl ProgressStyle for SpinPrecession {
fn name(&self) -> &str {
"spin-precession"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Spin precession: Larmor cone — polar tilt from progress, azimuth rotates with time"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw == 0 || dh == 0 {
return Ok(());
}
let dwf = dw as f32;
let dhf = dh as f32;
let origin_x = (dwf / 2.0) as i32;
let origin_y = (dhf * 0.55) as i32;
let len = (dhf * 0.4 + dwf * 0.15).min(dhf * 0.7).max(2.0);
let theta = PI / 2.0 * ctx.eased; let phi = 2.0 * PI * ctx.time * 0.7;
let tip_x = origin_x + (len * theta.sin() * phi.cos()) as i32;
let tip_y = origin_y - (len * theta.cos()) as i32;
let z_top = (origin_y as f32 - len * 1.1) as i32;
let z_bot = (origin_y as f32 + len * 0.3) as i32;
for y in z_top.max(0)..=z_bot.min(dh as i32 - 1) {
if y % 2 == 0 {
draw::dot_i(grid, origin_x, y);
}
}
let tip_x2 = origin_x - (len * theta.sin() * phi.cos()) as i32;
let tip_y2 = tip_y; line(grid, origin_x, origin_y, tip_x, tip_y);
line(grid, origin_x, origin_y, tip_x2, tip_y2);
let circle_rx = (len * theta.sin()).abs() as i32;
let circle_ry = (circle_rx as f32 * 0.35) as i32; let circ_steps = (circle_rx * 2 + 8).max(8) as usize;
let mut prev: Option<(i32, i32)> = None;
for s in 0..=circ_steps {
let a = s as f32 / circ_steps as f32 * 2.0 * PI;
let px = origin_x + (circle_rx as f32 * a.cos()) as i32;
let py = tip_y + (circle_ry as f32 * a.sin()) as i32;
if (s / 2) % 2 == 0 {
draw::dot_i(grid, px, py);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, px, py);
}
}
prev = Some((px, py));
}
line(grid, origin_x, origin_y, tip_x, tip_y);
draw::dot_i(grid, tip_x, tip_y);
draw::dot_i(grid, tip_x + 1, tip_y);
draw::dot_i(grid, tip_x, tip_y + 1);
draw::dot_i(grid, origin_x, origin_y);
let (cw, ch) = grid.dimensions();
let filled = (ctx.eased * cw as f32).round() as usize;
for cx in 0..filled.min(cw) {
let t = cx as f32 / cw.max(1) as f32;
let col = ctx.palette.sample(t);
for cy in 0..ch {
draw::tint_row(grid, cy, cx, cx, col);
}
}
Ok(())
}
}
struct EnergyLevels;
impl ProgressStyle for EnergyLevels {
fn name(&self) -> &str {
"energy-levels"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Energy level diagram: Aufbau filling — electrons populate orbitals from bottom as progress rises"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw == 0 || dh == 0 {
return Ok(());
}
let dwf = dw as f32;
let dhf = dh as f32;
let n_levels = ((dh / 3).max(2)).min(8) as usize;
let n_electrons_max = n_levels * 2;
let n_filled = (ctx.eased * n_electrons_max as f32).round() as usize;
for lv in 0..n_levels {
let y_frac = 1.0 - (lv as f32 + 0.5) / n_levels as f32;
let y_dot = (y_frac * (dhf - 2.0) + 1.0) as usize;
let y_dot = y_dot.min(dh.saturating_sub(1));
let lw = (dwf * 0.4).max(4.0) as usize;
let lx0 = ((dwf - lw as f32) / 2.0) as usize;
let lx1 = (lx0 + lw).min(dw.saturating_sub(1));
draw::hline(grid, lx0, lx1, y_dot);
let elec_base = lv * 2; for spin in 0..2_usize {
let elec_idx = elec_base + spin;
if elec_idx >= n_filled {
break;
}
let ex = if spin == 0 {
lx0.saturating_sub(3)
} else {
(lx1 + 3).min(dw.saturating_sub(1))
};
draw::dot_i(grid, ex as i32, y_dot as i32);
draw::dot_i(grid, ex as i32 - 1, y_dot as i32);
draw::dot_i(grid, ex as i32 + 1, y_dot as i32);
draw::dot_i(grid, ex as i32, y_dot as i32 - 1);
draw::dot_i(grid, ex as i32, y_dot as i32 + 1);
}
draw::dot(grid, 0, y_dot);
draw::dot(grid, 1, y_dot);
}
draw::vline(grid, 0, 0, dh.saturating_sub(1));
let (cw, ch) = grid.dimensions();
let filled_levels = (n_filled + 1) / 2; for lv in 0..filled_levels.min(n_levels) {
let y_frac = 1.0 - (lv as f32 + 0.5) / n_levels as f32;
let cy = (y_frac * (ch as f32 - 1.0)) as usize;
let cy = cy.min(ch.saturating_sub(1));
let t = lv as f32 / n_levels.max(1) as f32;
let col = ctx.palette.sample(1.0 - t); draw::tint_row(grid, cy, 0, cw.saturating_sub(1), col);
}
Ok(())
}
}
struct Decoherence;
impl ProgressStyle for Decoherence {
fn name(&self) -> &str {
"decoherence"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Decoherence: sharp interference fringes wash out to flat noise as entanglement grows"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw == 0 || dh == 0 {
return Ok(());
}
let dwf = dw as f32;
let dhf = dh as f32;
let visibility = 1.0 - ctx.eased;
let k = 6.0 * 2.0 * PI / dwf.max(1.0);
let phase = ctx.time * 1.5;
let mut prev: Option<(i32, i32)> = None;
for xi in 0..dw {
let xf = xi as f32;
let intensity = 0.5 * (1.0 + visibility * (k * xf + phase).cos());
let intensity = intensity.clamp(0.0, 1.0);
let display_h = (intensity * (dhf - 1.0)).clamp(0.0, dhf - 1.0);
let py = (dhf - 1.0 - display_h) as i32;
let py = py.clamp(0, dh as i32 - 1);
draw::dot_i(grid, xi as i32, py);
if let Some((lx, ly)) = prev {
line(grid, lx, ly, xi as i32, py);
}
prev = Some((xi as i32, py));
}
let zero_y = (dhf * 0.5) as usize;
for xi in (0..dw).step_by(4) {
draw::dot(grid, xi, zero_y.min(dh.saturating_sub(1)));
}
let (cw, ch) = grid.dimensions();
for cx in 0..cw {
let t = 1.0 - visibility; let col = ctx.palette.sample(t);
for cy in 0..ch {
draw::tint_row(grid, cy, cx, cx, col);
}
}
Ok(())
}
}
struct QuantumWalk;
impl ProgressStyle for QuantumWalk {
fn name(&self) -> &str {
"quantum-walk"
}
fn theme(&self) -> &str {
"quantum"
}
fn describe(&self) -> &str {
"Quantum random walk: bimodal spreading distribution — spreads linearly (not √n) with progress"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cw, ch) = grid.dimensions();
if cw == 0 || ch == 0 {
return Ok(());
}
let cwf = cw as f32;
let chf = ch as f32;
let n_max = 40_usize;
let n_steps = (ctx.eased * n_max as f32).round() as usize;
let n_steps = n_steps.max(1);
let nf = n_steps as f32;
let sigma = nf / (2.0_f32).sqrt();
let drift_phase = ctx.time * 0.4;
let mut densities = vec![0.0_f32; cw];
let mut max_d = 0.0_f32;
for cx in 0..cw {
let x = (cx as f32 / cwf - 0.5) * 2.0 * nf;
let arg = nf * nf - x * x;
let envelope = if arg > 0.0 { 1.0 / arg.sqrt() } else { 0.0 };
let ripple = 1.0 + 0.25 * (PI * x / sigma.max(0.1) + drift_phase).cos();
let d = envelope * ripple.max(0.0);
densities[cx] = d;
if d > max_d {
max_d = d;
}
}
let max_d = max_d.max(1e-9);
for cx in 0..cw {
let norm = (densities[cx] / max_d).clamp(0.0, 1.0);
for cy in 0..ch {
let row_thresh = 1.0 - cy as f32 / chf;
if norm >= row_thresh {
draw::vblock(grid, cx, cy, 8);
} else {
let partial =
((norm - (row_thresh - 1.0 / chf)) * chf * 8.0).clamp(0.0, 8.0) as usize;
if partial > 0 {
draw::vblock(grid, cx, cy, partial);
}
}
}
let t = cx as f32 / cwf;
let col = ctx.palette.sample(t * norm + (1.0 - norm) * 0.2);
for cy in 0..ch {
draw::tint_row(grid, cy, cx, cx, col);
}
}
let centre_cell = cw / 2;
draw::vblock(
grid,
centre_cell.min(cw.saturating_sub(1)),
ch.saturating_sub(1),
1,
);
Ok(())
}
}