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(SmoothHbar),
Box::new(BlockyHbar),
Box::new(SegmentedLed),
Box::new(Equalizer),
Box::new(Thermometer),
Box::new(DitherRamp),
Box::new(BrickWall),
Box::new(BayerDither),
Box::new(StackedBars),
Box::new(Waterfall),
Box::new(DoubleEnded),
Box::new(GradientMeter),
]
}
struct SmoothHbar;
impl ProgressStyle for SmoothHbar {
fn name(&self) -> &str {
"smooth-hbar"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Eighth-precise sub-cell smooth bar — the gold standard crisp fill using draw::hbar"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (_, cells_h) = grid.dimensions();
for cy in 0..cells_h {
draw::hbar(grid, cy, ctx.eased);
}
Ok(())
}
}
struct BlockyHbar;
impl ProgressStyle for BlockyHbar {
fn name(&self) -> &str {
"blocky-hbar"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Full-cell █ snap only — explicit coarse counterpart to smooth-hbar, no sub-cell precision"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let filled = (ctx.eased * cells_w as f32).floor() as usize;
let filled = filled.min(cells_w);
for cy in 0..cells_h {
for cx in 0..filled {
draw::glyph(grid, cx, cy, '█');
}
}
Ok(())
}
}
struct SegmentedLed;
impl ProgressStyle for SegmentedLed {
fn name(&self) -> &str {
"segmented-led"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Discrete lit █ segments separated by single-cell gaps, like a VU meter"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let seg_w = 2usize;
let gap_w = 1usize;
let stride = (seg_w + gap_w).max(1);
let n_segs = (cells_w / stride).max(1);
let lit = (ctx.eased * n_segs as f32).round() as usize;
let lit = lit.min(n_segs);
for s in 0..lit {
let x0 = s * stride;
for cx in x0..(x0 + seg_w).min(cells_w) {
for cy in 0..cells_h {
draw::glyph(grid, cx, cy, '█');
}
}
}
Ok(())
}
}
struct Equalizer;
impl ProgressStyle for Equalizer {
fn name(&self) -> &str {
"equalizer"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Animated spectrum equalizer: vblock columns with sinusoidal heights, lit count gated by eased"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let lit_cols = (ctx.eased * cells_w as f32).round() as usize;
let lit_cols = lit_cols.min(cells_w);
let cells_h_f = cells_h as f32;
for cx in 0..lit_cols {
let freq = 1.0 + (cx as f32 / cells_w.max(1) as f32) * 3.0;
let phase = ctx.time * 2.5 + cx as f32 * 0.4;
let raw = (phase * freq).sin() * 0.5 + 0.5; let col_height_f = raw * cells_h_f;
let full_cells = col_height_f.floor() as usize;
let frac_eighth = ((col_height_f.fract() * 8.0).round() as usize).min(8);
for row in 0..full_cells.min(cells_h) {
let cy = cells_h.saturating_sub(1).saturating_sub(row);
draw::vblock(grid, cx, cy, 8);
}
if full_cells < cells_h && frac_eighth > 0 {
let cy = cells_h.saturating_sub(1).saturating_sub(full_cells);
draw::vblock(grid, cx, cy, frac_eighth);
}
}
Ok(())
}
}
struct Thermometer;
impl ProgressStyle for Thermometer {
fn name(&self) -> &str {
"thermometer"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Vertical column filling bottom-to-top with vblock eighths, like a mercury thermometer"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
if cells_w == 0 || cells_h == 0 {
return Ok(());
}
let col_x = cells_w / 2;
let total_eighths = (ctx.eased * cells_h as f32 * 8.0).round() as usize;
let full_cells = (total_eighths / 8).min(cells_h);
let rem_eighths = total_eighths % 8;
for row in 0..full_cells {
let cy = cells_h.saturating_sub(1).saturating_sub(row);
draw::vblock(grid, col_x, cy, 8);
}
if full_cells < cells_h && rem_eighths > 0 {
let cy = cells_h.saturating_sub(1).saturating_sub(full_cells);
draw::vblock(grid, col_x, cy, rem_eighths);
}
if cells_w >= 3 {
let col_x2 = cells_w - 1 - col_x;
if col_x2 != col_x {
for row in 0..full_cells {
let cy = cells_h.saturating_sub(1).saturating_sub(row);
draw::vblock(grid, col_x2, cy, 8);
}
if full_cells < cells_h && rem_eighths > 0 {
let cy = cells_h.saturating_sub(1).saturating_sub(full_cells);
draw::vblock(grid, col_x2, cy, rem_eighths);
}
}
}
Ok(())
}
}
struct DitherRamp;
impl ProgressStyle for DitherRamp {
fn name(&self) -> &str {
"dither-ramp"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Left-to-right shade ramp ░▒▓█ whose density front advances with eased progress"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let frontier = ctx.eased * cells_w as f32;
for cy in 0..cells_h {
for cx in 0..cells_w {
let cell_f = cx as f32;
let behind = frontier - cell_f;
let level = if behind <= 0.0 {
0 } else if behind < 1.0 {
(behind * 4.0).ceil() as usize
} else {
let t = (cell_f / frontier.max(1.0)).clamp(0.0, 1.0);
(t * 4.0).round() as usize
};
draw::shade(grid, cx, cy, level.min(4));
}
}
Ok(())
}
}
struct BrickWall;
impl ProgressStyle for BrickWall {
fn name(&self) -> &str {
"brick-wall"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Running-bond brick masonry pattern (alternating offset rows of █) filling with eased"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let brick_w: usize = 3;
let frontier = (ctx.eased * cells_w as f32).round() as usize;
let frontier = frontier.min(cells_w);
for cy in 0..cells_h {
let offset = if cy % 2 == 1 { brick_w / 2 } else { 0 };
let mut cx = 0usize;
while cx < frontier {
let brick_start = if cx == 0 && offset > 0 {
0
} else {
cx
};
let body = (brick_w.saturating_sub(1)).max(1);
for bx in 0..body {
let target = brick_start + bx;
if target < frontier && target < cells_w {
let col_in_cycle = (target + cells_w - offset) % brick_w;
if col_in_cycle < brick_w.saturating_sub(1) {
draw::glyph(grid, target, cy, '█');
}
}
}
cx += brick_w;
}
}
Ok(())
}
}
struct BayerDither;
const BAYER4: [[u8; 4]; 4] = [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]];
impl ProgressStyle for BayerDither {
fn name(&self) -> &str {
"bayer-dither"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"4×4 Bayer ordered-dither reveal — threshold rises with eased, producing a stippled fill"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let threshold = (ctx.eased * 16.0).round() as u8;
for cy in 0..cells_h {
for cx in 0..cells_w {
let bx = cx % 4;
let by = cy % 4;
let bval = BAYER4[by][bx];
if bval < threshold {
draw::glyph(grid, cx, cy, '█');
} else if bval == threshold && threshold > 0 {
draw::shade(grid, cx, cy, 2); }
}
}
Ok(())
}
}
struct StackedBars;
impl ProgressStyle for StackedBars {
fn name(&self) -> &str {
"stacked-bars"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Multi-row stacked bar chart: each row a differently-shaded series filling with eased"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let n_series = cells_h.max(1);
for cy in 0..cells_h {
let series_offset = (cy as f32 / n_series as f32) * 0.2;
let series_frac = (ctx.eased - series_offset).clamp(0.0, 1.0);
let filled = (series_frac * cells_w as f32).round() as usize;
let filled = filled.min(cells_w);
let shade_level = 4 - (cy % 4);
for cx in 0..filled {
draw::shade(grid, cx, cy, shade_level);
}
}
Ok(())
}
}
struct Waterfall;
impl ProgressStyle for Waterfall {
fn name(&self) -> &str {
"waterfall"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Shade glyphs cascading downward (driven by time) with intensity from a horizontal wave"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
let frontier = ctx.eased * cells_w as f32;
for cy in 0..cells_h {
for cx in 0..cells_w {
if cx as f32 >= frontier {
continue;
}
let wave = ((cx as f32 * 0.5 - ctx.time * 3.0 + cy as f32 * 0.8) * PI * 0.5).sin();
let intensity = (wave * 0.5 + 0.5).clamp(0.0, 1.0);
let level = (intensity * 4.0).round() as usize;
draw::shade(grid, cx, cy, level.min(4));
}
}
Ok(())
}
}
struct DoubleEnded;
impl ProgressStyle for DoubleEnded {
fn name(&self) -> &str {
"double-ended"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Bar fills simultaneously from both ends toward the centre using smooth hbar logic"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
if cells_w == 0 || cells_h == 0 {
return Ok(());
}
let half = cells_w as f32 / 2.0;
let eighths_total = (ctx.eased * half * 8.0).round() as usize;
let full_cells = (eighths_total / 8).min(cells_w / 2);
let rem_eighths = eighths_total % 8;
for cy in 0..cells_h {
for cx in 0..full_cells.min(cells_w) {
draw::glyph(grid, cx, cy, '█');
}
if rem_eighths > 0 && full_cells < cells_w {
draw::glyph(grid, full_cells, cy, draw::H_BLOCKS[rem_eighths]);
}
let r_start = cells_w.saturating_sub(full_cells);
for cx in r_start..cells_w {
draw::glyph(grid, cx, cy, '█');
}
if rem_eighths > 0 && r_start > 0 {
let pcx = r_start.saturating_sub(1);
if pcx > full_cells {
draw::glyph(grid, pcx, cy, draw::H_BLOCKS[rem_eighths]);
}
}
}
Ok(())
}
}
struct GradientMeter;
impl ProgressStyle for GradientMeter {
fn name(&self) -> &str {
"gradient-meter"
}
fn theme(&self) -> &str {
"blocks"
}
fn describe(&self) -> &str {
"Smooth hbar fill combined with a ░ shade backdrop on the unfilled track"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (cells_w, cells_h) = grid.dimensions();
for cy in 0..cells_h {
for cx in 0..cells_w {
draw::shade(grid, cx, cy, 1); }
}
let eighths_total = (ctx.eased * cells_w as f32 * 8.0).round() as usize;
let full_cells = (eighths_total / 8).min(cells_w);
let rem_eighths = eighths_total % 8;
for cy in 0..cells_h {
for cx in 0..full_cells {
draw::glyph(grid, cx, cy, '█');
}
if rem_eighths > 0 && full_cells < cells_w {
draw::glyph(grid, full_cells, cy, draw::H_BLOCKS[rem_eighths]);
}
}
Ok(())
}
}