use super::super::draw;
use super::super::{BarContext, ProgressStyle};
use crate::{BrailleGrid, Color, DotmaxError};
pub fn styles() -> Vec<Box<dyn ProgressStyle>> {
vec![
Box::new(Skyscraper),
Box::new(SuspensionBridge),
Box::new(GothicArch),
Box::new(BrickWall),
Box::new(Pyramid),
Box::new(Scaffolding),
Box::new(GeodesicDome),
Box::new(SpiralStaircase),
Box::new(RomanAqueduct),
Box::new(ClassicalColumn),
Box::new(Blueprint),
Box::new(TowerCrane),
Box::new(Colosseum),
]
}
fn paint(grid: &mut BrailleGrid, x: i32, y: i32, c: Color) {
let (dw, dh) = draw::dot_dims(grid);
if x < 0 || y < 0 || x as usize >= dw || y as usize >= dh {
return;
}
draw::dot(grid, x as usize, y as usize);
let _ = grid.set_cell_color(x as usize / 2, y as usize / 4, c);
}
fn paint_h(grid: &mut BrailleGrid, x0: i32, x1: i32, y: i32, c: Color) {
for x in x0.min(x1)..=x0.max(x1) {
paint(grid, x, y, c);
}
}
fn paint_v(grid: &mut BrailleGrid, x: i32, y0: i32, y1: i32, c: Color) {
for y in y0.min(y1)..=y0.max(y1) {
paint(grid, x, y, c);
}
}
fn paint_line(grid: &mut BrailleGrid, x0: i32, y0: i32, x1: i32, y1: i32, c: Color) {
let (dx, dy) = ((x1 - x0).abs(), (y1 - y0).abs());
let (sx, sy) = (if x0 < x1 { 1 } else { -1 }, if y0 < y1 { 1 } else { -1 });
let (mut x, mut y, mut err) = (x0, y0, dx - dy);
loop {
paint(grid, x, y, c);
if x == x1 && y == y1 {
break;
}
let e2 = 2 * err;
if e2 > -dy {
err -= dy;
x += sx;
}
if e2 < dx {
err += dx;
y += sy;
}
}
}
fn hash3(a: i32, b: i32, c: i32) -> f32 {
let mut h = (a as u32).wrapping_mul(0x9E37_79B1)
^ (b as u32).wrapping_mul(0x85EB_CA77)
^ (c as u32).wrapping_mul(0xC2B2_AE3D);
h ^= h >> 15;
h = h.wrapping_mul(0x2C1B_3C6D);
h ^= h >> 12;
(h & 0xFFFF) as f32 / 65535.0
}
struct Skyscraper;
impl ProgressStyle for Skyscraper {
fn name(&self) -> &str {
"skyscraper"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Glass towers top out one by one across a skyline; lit windows twinkle, beacons blink, a crane swings on the tower going up"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 4 || dh < 4 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let ground = hi - 1;
let steel = Color::rgb(120, 140, 172);
let lit = Color::rgb(255, 214, 120);
let frame = Color::rgb(150, 120, 90);
let crane = Color::rgb(250, 190, 40);
let beacon = Color::rgb(255, 60, 50);
const PLAN: [(i32, f32); 6] = [
(6, 0.72),
(7, 1.0),
(5, 0.6),
(6, 0.88),
(5, 0.68),
(7, 0.95),
];
let mut towers = Vec::new();
let mut x = 2;
while x < wi - 3 {
let (tw, hf) = PLAN[towers.len() % PLAN.len()];
let tw = tw.min(wi - 1 - x);
if tw < 4 {
break;
}
towers.push((x, tw, hf));
x += tw + 3;
}
let built = ctx.eased * towers.len().max(1) as f32;
let tick = (ctx.time * 1.5) as i32;
for (k, &(x0, tw, hf)) in towers.iter().enumerate() {
let frac = (built - k as f32).clamp(0.0, 1.0);
if frac <= 0.0 {
continue;
}
let full_h = (((hi - 3) as f32 * hf).round() as i32).max(3);
let h = ((full_h as f32 * frac).ceil() as i32).max(1);
let top = ground - h;
let done = frac >= 1.0;
let crown = ground - full_h + full_h / 4; for y in top..ground {
let inset = i32::from(tw >= 7 && y < crown);
let (l, r) = (x0 + inset, x0 + tw - 1 - inset);
let skeleton = !done && y < top + 3;
let c = if skeleton { frame } else { steel };
paint(grid, l, y, c);
paint(grid, r, y, c);
if skeleton {
if (ground - y) % 2 == 0 {
paint_h(grid, l, r, y, frame);
}
} else if (ground - y) % 2 == 1 {
for wx in l + 1..r {
if hash3(wx, y, tick + k as i32 * 7) < 0.66 {
paint(grid, wx, y, lit);
}
}
}
}
let inset = i32::from(tw >= 7 && top < crown);
paint_h(grid, x0 + inset, x0 + tw - 1 - inset, top, steel);
let mid = x0 + tw / 2;
if done {
paint_v(grid, mid, top - 2, top - 1, steel);
if (ctx.time * 2.0 + k as f32).sin() > 0.0 {
paint(grid, mid, top - 3, beacon);
}
} else {
let mast = x0 + 1;
paint_v(grid, mast, top - 4, top - 1, crane);
let reach = ((ctx.time * 1.3).cos() * (tw + 6) as f32) as i32;
paint_h(grid, mast - 3, mast + reach, top - 4, crane);
let hook = mast + reach * 3 / 4;
paint_v(grid, hook, top - 3, top - 2, crane);
}
}
paint_h(grid, 0, wi - 1, ground, Color::rgb(90, 90, 104));
Ok(())
}
}
struct SuspensionBridge;
impl ProgressStyle for SuspensionBridge {
fn name(&self) -> &str {
"suspension-bridge"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Red pylons and a sagging main cable; the deck is launched span by span with suspenders, a car crosses, water ripples below"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 8 || dh < 4 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let red = Color::rgb(222, 78, 44);
let deck_c = Color::rgb(190, 190, 200);
let water = Color::rgb(50, 120, 210);
let car = Color::rgb(255, 220, 90);
let deck_y = (hi * 5 / 8).max(2);
let water_y = deck_y + 3;
let t1 = wi * 27 / 100;
let t2 = wi - 1 - t1;
let mid = (t1 + t2) as f32 / 2.0;
let half = ((t2 - t1) as f32 / 2.0).max(1.0);
let top = 1;
let sag = (deck_y - 1 - top) as f32;
for y in water_y..hi {
for x in 0..wi {
let v = (x as f32 * 0.45 + ctx.time * 3.0 + y as f32 * 2.1).sin();
if v > 0.55 {
paint(grid, x, y, water);
}
}
}
let cable_y = |x: i32| -> i32 {
let xf = x as f32;
if x >= t1 && x <= t2 {
let u = (xf - mid) / half;
(deck_y as f32 - 1.0 - sag * u * u).round() as i32
} else {
let (from, len) = if x < t1 {
(t1 as f32 - xf, t1 as f32)
} else {
(xf - t2 as f32, (wi - 1 - t2) as f32)
};
let s = (from / len.max(1.0)).clamp(0.0, 1.0);
(top as f32 + sag * s + 1.5 * s * (1.0 - s)).round() as i32
}
};
let mut prev = cable_y(0);
for x in 0..wi {
let y = cable_y(x);
paint_v(grid, x, prev, y, red);
prev = y;
}
let built = (ctx.eased * wi as f32).round() as i32;
for x in 0..built.min(wi) {
if x % 3 == 0 && x > 0 && x < wi - 1 {
let cy = cable_y(x);
if cy < deck_y - 1 {
paint_v(grid, x, cy + 1, deck_y - 1, red);
}
}
paint(grid, x, deck_y, deck_c);
if x % 2 == 0 {
paint(grid, x, deck_y + 1, deck_c);
}
}
for &tx in &[t1, t2] {
paint_v(grid, tx - 1, 0, hi - 1, red);
paint_v(grid, tx + 1, 0, hi - 1, red);
paint_h(grid, tx - 1, tx + 1, 0, red);
paint_h(grid, tx - 1, tx + 1, deck_y / 2, red);
}
if built > 6 {
let span = (built - 4).max(1) as f32;
let tri = 1.0 - ((ctx.time * 0.35).fract() * 2.0 - 1.0).abs();
let cx = (tri * span) as i32;
paint_h(grid, cx, cx + 3, deck_y - 1, car);
paint_h(grid, cx + 1, cx + 2, deck_y - 2, car);
}
Ok(())
}
}
struct GothicArch;
impl ProgressStyle for GothicArch {
fn name(&self) -> &str {
"gothic-arch"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Pointed lancet arches rise bay by bay on pinnacled piers, then fill with shimmering stained glass"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 14 || dh < 6 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let stone = Color::rgb(214, 196, 160);
let glass = [
Color::rgb(60, 100, 230),
Color::rgb(210, 40, 70),
Color::rgb(245, 190, 60),
Color::rgb(60, 180, 110),
Color::rgb(150, 80, 220),
];
let ground = hi - 1;
let bay = 12;
let span = bay - 2;
let n = ((wi - 2) / bay).max(1);
let off = ((wi - n * bay - 2) / 2) & !1; let spring = hi * 9 / 16;
let r = span as f32 * 0.85;
let tick = (ctx.time * 2.0) as i32;
let built = ctx.eased * n as f32;
paint_h(grid, 0, wi - 1, ground, stone);
let arc = |dx: i32| -> f32 {
let d = dx.min(span - 1 - dx) as f32;
let c = r - d; (r * r - c * c).max(0.0).sqrt()
};
for k in 0..n {
let px = off + k * bay; let x0 = px + 2; let f = (built - k as f32).clamp(0.0, 1.0);
if f <= 0.0 {
continue;
}
if f >= 0.85 {
for dx in 0..span {
let ay = spring - arc(dx).round() as i32;
for y in ay + 1..=ground {
if (dx + y) % 4 == 0 || (dx - y).rem_euclid(4) == 0 {
continue; }
let cell = (x0 + dx) / 2 * 31 + y / 4;
let g = glass[(hash3(cell, k, tick) * 4.99) as usize];
paint(grid, x0 + dx, y, g);
}
}
}
let pier_f = (f / 0.4).min(1.0);
let pier_top = ground - ((ground - 1) as f32 * pier_f).round() as i32;
for &p in &[px, px + bay] {
paint_v(grid, p, pier_top, ground, stone);
paint_v(grid, p + 1, pier_top.max(spring), ground, stone);
}
if pier_f >= 1.0 {
paint(grid, px, 0, stone);
paint(grid, px + bay, 0, stone);
}
let arch_f = ((f - 0.4) / 0.45).clamp(0.0, 1.0);
let reach = (arch_f * (span / 2) as f32).round() as i32;
let mut prev: Option<(i32, i32)> = None;
for dx in 0..span {
let side = dx.min(span - 1 - dx);
if side >= reach {
prev = None;
continue;
}
let y = spring - arc(dx).round() as i32;
let x = x0 + dx;
match prev {
Some((pxx, py)) => paint_line(grid, pxx, py, x, y, stone),
None => paint(grid, x, y, stone),
}
prev = Some((x, y));
}
}
Ok(())
}
}
struct BrickWall;
impl ProgressStyle for BrickWall {
fn name(&self) -> &str {
"brick-wall"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Red bricks in running bond are laid one by one, course by course, while a trowel taps the newest brick into its mortar bed"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 8 || dh < 4 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let reds = [
Color::rgb(178, 62, 44),
Color::rgb(198, 82, 54),
Color::rgb(158, 54, 40),
Color::rgb(210, 104, 66),
];
let steel = Color::rgb(200, 200, 212);
let handle = Color::rgb(150, 100, 60);
let brick = 8; let courses = (hi / 4).max(1);
let per = (wi + brick / 2) / brick + 1;
let total = (courses * per) as f32;
let laid = ctx.eased * total;
let whole = laid as i32;
let mut current: Option<(i32, i32)> = None;
for c in 0..courses {
let y0 = hi - 4 * (c + 1); let off = (c % 2) * (brick / 2);
for b in 0..per {
let idx = c * per + b;
if idx > whole {
break;
}
let x0 = b * brick - off;
let x1 = (x0 + brick - 2).min(wi - 1);
if x1 < 0 {
continue;
}
let colour = reds[(hash3(b, c, 3) * 3.99) as usize];
if idx == whole {
paint_h(grid, x0.max(0), x1, y0, colour);
paint_h(grid, x0.max(0), x1, y0 + 2, colour);
paint_v(grid, x0.max(0), y0, y0 + 2, colour);
paint_v(grid, x1, y0, y0 + 2, colour);
current = Some((x1, y0));
} else {
for y in y0..y0 + 3 {
paint_h(grid, x0.max(0), x1, y, colour);
}
}
}
}
if let Some((x, y)) = current {
let tap = ((ctx.time * 6.0).sin() * 0.5 + 0.5).round() as i32;
let (tx, ty) = (x + 2, y - 3 + tap);
paint_h(grid, tx, tx + 3, ty + 1, steel);
paint_h(grid, tx + 1, tx + 2, ty + 2, steel);
paint_line(grid, tx + 3, ty, tx + 5, ty - 2, handle);
}
Ok(())
}
}
struct Pyramid;
impl ProgressStyle for Pyramid {
fn name(&self) -> &str {
"pyramid"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Three stepped sandstone pyramids rise course by course under a desert sun; sunlit and shadowed faces, gold capstone"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 8 || dh < 4 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let lit = Color::rgb(232, 198, 132);
let shade = Color::rgb(168, 124, 78);
let gold = Color::rgb(255, 214, 90);
let sun = Color::rgb(255, 160, 60);
let sand = Color::rgb(200, 170, 110);
let ground = hi - 1;
let max_c = ((ground - 1) / 2).max(1);
let plan = [
(0.23, max_c),
(0.59, (max_c * 6 / 7).max(1)),
(0.86, (max_c * 4 / 7).max(1)),
];
let total: i32 = plan.iter().map(|p| p.1).sum();
let laid = ctx.eased * total as f32;
let (sx, sy) = (wi - 22, 3);
for dy in -2i32..=2 {
let half = 2 - dy.abs() / 2;
paint_h(grid, sx - half, sx + half, sy + dy, sun);
}
for x in 0..wi {
if (x as f32 + ctx.time * 6.0).rem_euclid(7.0) < 5.0 {
paint(grid, x, ground, sand);
}
}
let mut before = 0;
for (pi, &(fx, courses)) in plan.iter().enumerate() {
let cx = (wi as f32 * fx) as i32;
for c in 0..courses {
let f = (laid - (before + c) as f32).clamp(0.0, 1.0);
if f <= 0.0 {
break;
}
let hw = (courses - c) * 2;
let (x0, x1) = (cx - hw, cx + hw);
let reach = x0 + ((x1 - x0 + 1) as f32 * f) as i32;
let capstone = pi == 0 && c == courses - 1 && f >= 1.0;
for y in [ground - 1 - 2 * c, ground - 2 - 2 * c] {
for x in x0..reach.min(x1 + 1) {
let col = if capstone {
gold
} else if x < cx {
lit
} else {
shade
};
paint(grid, x, y, col);
}
}
}
before += courses;
}
Ok(())
}
}
struct Scaffolding;
impl ProgressStyle for Scaffolding {
fn name(&self) -> &str {
"scaffolding"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Steel standards, ledgers, timber boards and cross braces go up bay by bay, lift by lift; a bucket is hoisted on the newest bay"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 12 || dh < 4 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let steel = Color::rgb(176, 186, 202);
let brace = Color::rgb(124, 134, 150);
let bucket = Color::rgb(240, 180, 40);
let board = Color::rgb(178, 116, 62);
let rope = Color::rgb(140, 140, 150);
let ground = hi - 1;
let bay = 10;
let nb = ((wi - 2) / bay).max(1);
let lifts = ((hi - 1) / 5).max(1);
let lift_h = (hi - 1) / lifts;
let total = (nb * lifts) as f32;
let placed = ctx.eased * total;
paint_h(grid, 0, wi - 1, ground, Color::rgb(100, 96, 90));
for b in 0..nb {
let x0 = 1 + b * bay;
let x1 = x0 + bay;
for l in 0..lifts {
let f = (placed - (b * lifts + l) as f32).clamp(0.0, 1.0);
if f <= 0.0 {
continue;
}
let yb = ground - l * lift_h; let yt = yb - lift_h; let rise = (f * 2.0).min(1.0);
let ytop = yb - (lift_h as f32 * rise).round() as i32;
if f >= 0.5 {
let (bx0, bx1) = if (b + l) % 2 == 0 { (x0, x1) } else { (x1, x0) };
paint_line(grid, bx0, yb, bx1, yt, brace);
}
paint_v(grid, x0, ytop, yb, steel);
paint_v(grid, x1, ytop, yb, steel);
if f >= 0.5 {
paint_h(grid, x0, x1, yt, steel);
}
if f >= 0.75 {
paint_h(grid, x0 + 1, x1 - 1, yt + 1, board);
}
}
}
let cur = (placed as i32 / lifts).min(nb - 1);
let hx = 1 + cur * bay + bay / 2;
let bob = ((ctx.time * 1.2).sin() * 0.5 + 0.5) * (hi - 5) as f32;
let by = 1 + bob as i32;
if ctx.eased < 1.0 {
paint_v(grid, hx, 0, by, rope);
paint_h(grid, hx - 1, hx + 1, by + 1, bucket);
paint_h(grid, hx - 1, hx + 1, by + 2, bucket);
}
Ok(())
}
}
struct GeodesicDome;
impl ProgressStyle for GeodesicDome {
fn name(&self) -> &str {
"geodesic-dome"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"A triangulated geodesic hemisphere assembles band by band from the base up; light glints across its hubs"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 8 || dh < 6 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let strut = Color::rgb(150, 180, 220);
let hub = Color::rgb(240, 248, 255);
let base = Color::rgb(214, 184, 142);
let ground = hi - 1;
let ry = (ground - 1) as f32;
let rx = (ry * 1.6).min(wi as f32 / 2.0 - 1.0);
let cx = wi as f32 / 2.0;
let rings = 4;
let seg = 8; let built = ctx.eased * rings as f32;
let glint = (ctx.time * 0.5).fract() * seg as f32;
let hub_at = |k: i32, j: f32| -> (i32, i32) {
let phi = k as f32 / rings as f32 * std::f32::consts::FRAC_PI_2;
let theta = (j + 0.5 * (k % 2) as f32) / seg as f32 * std::f32::consts::PI;
(
(cx - rx * phi.cos() * theta.cos()).round() as i32,
(ground as f32 - ry * phi.sin()).round() as i32,
)
};
paint_h(grid, 0, wi - 1, ground, base);
for k in 0..rings {
let f = (built - k as f32).clamp(0.0, 1.0);
if f <= 0.0 {
break;
}
let odd = k % 2 == 1;
let upto = ((f * seg as f32).ceil() as i32).min(seg + 1 - i32::from(odd));
for j in 0..upto {
let a = hub_at(k, j as f32);
if j < seg {
let b = hub_at(k, (j + 1) as f32);
if !(odd && j == seg - 1) {
paint_line(grid, a.0, a.1, b.0, b.1, strut);
}
}
for dj in [-1, 0] {
let jj = j + dj + i32::from(odd);
if (0..=seg).contains(&jj) {
let b = hub_at(k + 1, jj as f32);
paint_line(grid, a.0, a.1, b.0, b.1, strut);
}
}
}
for j in 0..upto {
if (j as f32 - glint).abs() < 1.0 {
let a = hub_at(k, j as f32);
paint(grid, a.0, a.1, hub);
}
}
}
Ok(())
}
}
struct SpiralStaircase;
impl ProgressStyle for SpiralStaircase {
fn name(&self) -> &str {
"spiral-staircase"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Looking down a stairwell: treads are laid along a spiralling brass handrail down to the newel post; the view slowly turns"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 8 || dh < 6 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let post = Color::rgb(196, 146, 76);
let tread = Color::rgb(232, 222, 200);
let rail = Color::rgb(214, 150, 70);
let riser = Color::rgb(178, 166, 146);
let (cx, cy) = (wi as f32 / 2.0, (hi - 1) as f32 / 2.0);
let ry = cy;
let rx = (ry * 3.2).min(cx - 1.0); let turns = 1.5;
let sweep = turns * std::f32::consts::TAU;
let shrink = 0.8; let gap = shrink / turns; let spin = ctx.time * 0.4;
let at = |u: f32, r: f32| -> (i32, i32) {
let a = u * sweep + spin;
(
(cx + rx * r * a.cos()).round() as i32,
(cy + ry * r * a.sin()).round() as i32,
)
};
let samples = 360;
let mut prev = at(0.0, 1.0);
for s in 1..=samples {
let u = s as f32 / samples as f32;
let p = at(u, 1.0 - shrink * u);
paint_line(grid, prev.0, prev.1, p.0, p.1, rail);
prev = p;
}
let tau = std::f32::consts::TAU;
let n = 22.0;
let laid = ctx.eased * n;
for y in 0..hi {
for x in 0..wi {
let (ex, ey) = ((x as f32 - cx) / rx, (y as f32 - cy) / ry);
let rho = (ex * ex + ey * ey).sqrt();
let a = (ey.atan2(ex) - spin).rem_euclid(tau);
let mut m = 0.0;
loop {
let u = (a + m * tau) / sweep;
if u > 1.0 {
break;
}
let r = 1.0 - shrink * u;
if rho < r - 0.04 && rho > r - gap + 0.04 {
let k = u * n;
if k < laid && k.fract() > 0.2 {
let c = if (k as i32) % 2 == 0 { tread } else { riser };
paint(grid, x, y, c);
}
break;
}
m += 1.0;
}
}
}
let (nx, ny) = (cx.round() as i32, cy.round() as i32);
paint_h(grid, nx - 1, nx, ny, post);
paint_h(grid, nx - 1, nx, ny + 1, post);
Ok(())
}
}
struct RomanAqueduct;
impl ProgressStyle for RomanAqueduct {
fn name(&self) -> &str {
"roman-aqueduct"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Two tiers of round stone arches advance across a river, with the water channel on top flowing as each span is finished"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 4 || dh < 4 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let stone = Color::rgb(214, 184, 142);
let water = Color::rgb(70, 150, 230);
let floor = (hi / 8).max(1); let impost = hi / 2; let ground = hi - 1;
let lower = (ctx.eased * (wi + 24) as f32) as i32;
let (upper, channel) = (lower - 12, lower - 24);
let opening = |x: i32, y: i32, pitch: i32, span: i32, crown: i32, spring: i32| {
let ux = (x + pitch / 2).rem_euclid(pitch) - (pitch - span) / 2;
if ux < 0 || ux >= span || y < crown {
return false;
}
let r = (spring - crown).max(1) as f32;
let dx = ux as f32 + 0.5 - span as f32 / 2.0;
let dy = (spring - y).max(0) as f32;
dx * dx * (r * r) / ((span as f32 / 2.0).powi(2)) + dy * dy <= r * r
};
for x in 0..wi {
for y in 0..hi {
let lit = if y < floor {
x < channel && y == 0
} else if y == floor {
x < channel
} else if y < impost {
x < upper && !opening(x, y, 6, 4, floor + 2, impost - 2)
} else {
x < lower && (y == impost || !opening(x, y, 12, 9, impost + 2, ground - 2))
};
if lit {
paint(grid, x, y, stone);
}
}
if x < channel && ((x as f32 - ctx.time * 10.0).rem_euclid(8.0)) < 3.0 {
paint(grid, x, (floor - 1).max(1), water);
}
let open = |x| opening(x, ground, 12, 9, impost + 2, ground - 2);
if (x as f32 * 0.7 + ctx.time * 3.0).sin() > 0.2 && open(x & !1) && open(x | 1) {
paint(grid, x, ground, water);
}
}
Ok(())
}
}
struct ClassicalColumn;
impl ProgressStyle for ClassicalColumn {
fn name(&self) -> &str {
"classical-column"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Ionic columns rise drum by drum on a stepped base, gain scrolled capitals, then carry the entablature; sunlight sweeps the marble"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 12 || dh < 8 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let marble = Color::rgb(206, 200, 186);
let glint = Color::rgb(255, 252, 240);
let entab = Color::rgb(214, 184, 142);
let step_c = Color::rgb(150, 140, 126);
let rope = Color::rgb(140, 140, 150);
let ground = hi - 1;
let base_y = ground - 2;
let cap_y = hi * 3 / 16; let shaft_top = cap_y + 3;
let shaft_h = (base_y - shaft_top).max(1);
let pitch = 14;
let n = ((wi - 4) / pitch).max(1);
let off = (wi - n * pitch) / 2;
let built = ctx.eased * n as f32;
let sun = (ctx.time * 0.25).fract() * (wi + 20) as f32 - 10.0;
paint_h(grid, 0, wi - 1, ground, step_c);
paint_h(grid, 2, wi - 3, ground - 1, step_c);
for k in 0..n {
let cx = off + k * pitch + pitch / 2;
let f = (built - k as f32).clamp(0.0, 1.0);
if f <= 0.0 {
continue;
}
let c = if (cx as f32 - sun).abs() < 5.0 {
glint
} else {
marble
};
paint_h(grid, cx - 3, cx + 3, base_y, c);
let shaft_f = (f / 0.8).min(1.0);
let rows = (shaft_h as f32 * shaft_f).round() as i32;
let top = base_y - rows;
for &dx in &[-2, 0, 2] {
paint_v(grid, cx + dx, top, base_y - 1, c);
}
if shaft_f < 1.0 {
let bob = ((ctx.time * 2.0).sin() * 0.5 + 0.5) * 3.0;
let dy = top - 3 - bob.round() as i32;
paint_v(grid, cx, 0, dy - 1, rope);
paint_h(grid, cx - 2, cx + 2, dy, c);
paint_h(grid, cx - 2, cx + 2, dy + 1, c);
} else {
paint_h(grid, cx - 4, cx + 4, cap_y, c);
paint_h(grid, cx - 3, cx + 3, cap_y + 1, c);
for &s in &[-1, 1] {
paint(grid, cx + s * 4, cap_y + 1, c);
paint(grid, cx + s * 4, cap_y + 2, c);
paint(grid, cx + s * 3, cap_y + 2, c);
}
}
if f >= 1.0 {
let (l, r) = (cx - pitch / 2, cx + pitch / 2);
paint_h(grid, l, r, cap_y - 1, entab);
for x in l..=r {
if (x - off) % 3 == 0 {
paint(grid, x, cap_y - 2, entab);
}
}
paint_h(grid, l - 1, r + 1, cap_y - 3, entab);
}
}
Ok(())
}
}
struct Blueprint;
impl ProgressStyle for Blueprint {
fn name(&self) -> &str {
"blueprint"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Blueprint drafted: graph grid first, then walls, windows, and dimensions"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 4 || dh < 4 {
return Ok(());
}
if ctx.eased > 0.0 {
let grid_spacing = 4usize;
let grid_p = (ctx.eased / 0.2).min(1.0);
let grid_cols = ((dw / grid_spacing) as f32 * grid_p) as usize;
let grid_rows = ((dh / grid_spacing) as f32 * grid_p) as usize;
for gx in 0..=grid_cols {
let x = gx * grid_spacing;
let mut y = 0usize;
while y < dh {
draw::dot(grid, x.min(dw.saturating_sub(1)), y);
y += 2;
}
}
for gy in 0..=grid_rows {
let y = gy * grid_spacing;
let mut x = 0usize;
while x < dw {
draw::dot(grid, x, y.min(dh.saturating_sub(1)));
x += 2;
}
}
}
if ctx.eased > 0.2 {
let wall_p = ((ctx.eased - 0.2) / 0.4).min(1.0);
let x0 = dw / 8;
let x1 = dw - dw / 8;
let y0 = dh / 8;
let y1 = dh - dh / 8;
let perim = 2 * ((x1 - x0) + (y1 - y0));
let drawn = (wall_p * perim as f32) as usize;
let top_len = x1 - x0;
let right_len = y1 - y0;
let bot_len = x1 - x0;
let left_len = y1 - y0;
let mut rem = drawn;
let seg = rem.min(top_len);
draw::hline(grid, x0, x0 + seg, y0);
rem = rem.saturating_sub(seg);
let seg = rem.min(right_len);
draw::vline(grid, x1, y0, y0 + seg);
rem = rem.saturating_sub(seg);
let seg = rem.min(bot_len);
draw::hline(grid, x1.saturating_sub(seg), x1, y1);
rem = rem.saturating_sub(seg);
let seg = rem.min(left_len);
draw::vline(grid, x0, y1.saturating_sub(seg), y1);
}
if ctx.eased > 0.6 {
let int_p = ((ctx.eased - 0.6) / 0.25).min(1.0);
let mid_x = dw / 2;
let y0 = dh / 8;
let y1 = dh - dh / 8;
let wall_len = (y1 - y0).saturating_sub(4); let drawn = (int_p * wall_len as f32) as usize;
draw::vline(grid, mid_x, y0, y0 + drawn);
}
if ctx.eased > 0.85 {
let dim_p = (ctx.eased - 0.85) / 0.15;
let y_dim = (dh / 8).saturating_sub(2);
let x_end = (dim_p * dw as f32) as usize;
draw::hline(grid, 0, x_end.min(dw.saturating_sub(1)), y_dim);
draw::dot(grid, 0, y_dim);
let tail = x_end.min(dw.saturating_sub(1));
draw::dot(grid, tail, y_dim);
}
let pen_x = (ctx.eased * dw as f32).round() as usize;
let pen_y = ((ctx.time * 1.5).sin() * dh as f32 * 0.1 + dh as f32 * 0.5) as usize;
draw::dot(
grid,
pen_x.min(dw.saturating_sub(1)),
pen_y.min(dh.saturating_sub(1)),
);
let (cw, ch) = grid.dimensions();
let blue = crate::Color::rgb(30, 80, 200);
let light = crate::Color::rgb(100, 160, 255);
for cx2 in 0..cw {
for cy2 in 0..ch {
let t = cx2 as f32 / cw.max(1) as f32;
let color = {
let r = (blue.r as f32 + (light.r as f32 - blue.r as f32) * t) as u8;
let g = (blue.g as f32 + (light.g as f32 - blue.g as f32) * t) as u8;
let b = (blue.b as f32 + (light.b as f32 - blue.b as f32) * t) as u8;
crate::Color::rgb(r, g, b)
};
draw::tint_row(grid, cy2, cx2, cx2, color);
}
}
Ok(())
}
}
struct TowerCrane;
impl ProgressStyle for TowerCrane {
fn name(&self) -> &str {
"tower-crane"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"A lattice tower crane with counterweight and trolley hoists steel beams while the building beside it rises floor by floor"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 24 || dh < 10 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let yellow = Color::rgb(250, 190, 40);
let weight = Color::rgb(150, 150, 162);
let cab = Color::rgb(90, 160, 230);
let cable = Color::rgb(130, 130, 140);
let beam = Color::rgb(214, 90, 50);
let concrete = Color::rgb(176, 176, 186);
let ground = hi - 1;
let mx = (wi / 6).max(12);
let (apex, top_chord, jib_y) = (0, 2, 4);
let jib_end = wi - 2;
let tail = mx - 10;
let (bx0, bx1) = (mx + 12, wi - 4);
let floors = ((ground - jib_y - 4) / 2).max(1);
let level = ctx.eased * floors as f32;
let whole = level as i32;
for f in 0..=whole.min(floors - 1) {
let y = ground - 2 * (f + 1);
let len = if f < whole {
bx1 - bx0
} else {
((level - whole as f32) * (bx1 - bx0) as f32) as i32
};
if len <= 0 {
continue;
}
paint_h(grid, bx0, bx0 + len, y, concrete);
let mut x = bx0;
while x <= bx0 + len {
paint(grid, x, y + 1, concrete);
x += 6;
}
}
let roof = ground - 2 * whole;
paint_line(grid, mx, apex, tail, jib_y, cable);
paint_line(grid, mx, apex, (mx + jib_end) / 2, top_chord, cable);
paint_line(grid, mx - 1, jib_y, mx, apex, yellow);
paint_line(grid, mx + 1, jib_y, mx, apex, yellow);
paint_v(grid, mx - 1, jib_y, ground, yellow);
paint_v(grid, mx + 1, jib_y, ground, yellow);
let mut y = jib_y;
while y < ground {
let (a, b) = if (y / 3) % 2 == 0 { (-1, 1) } else { (1, -1) };
paint_line(grid, mx + a, y, mx + b, y + 3, yellow);
y += 3;
}
paint_h(grid, mx, jib_end, jib_y, yellow);
paint_h(grid, mx, jib_end - 6, top_chord, yellow);
let mut x = mx;
while x + 2 < jib_end - 6 {
paint_line(grid, x, jib_y, x + 2, top_chord, yellow);
paint_line(grid, x + 2, top_chord, x + 4, jib_y, yellow);
x += 4;
}
paint_h(grid, tail, mx, jib_y, yellow);
for wy in jib_y..jib_y + 3 {
paint_h(grid, tail, tail + 3, wy, weight);
}
paint_h(grid, mx + 2, mx + 3, jib_y + 1, cab);
paint_h(grid, mx + 2, mx + 3, jib_y + 2, cab);
let reach = (jib_end - 6 - (mx + 6)).max(1) as f32;
let tri = 1.0 - ((ctx.time * 0.18).fract() * 2.0 - 1.0).abs();
let tx = mx + 6 + (tri * reach) as i32;
paint_h(grid, tx - 1, tx + 1, jib_y + 1, yellow);
let floor_y = if tx >= bx0 - 3 && tx <= bx1 + 3 {
roof - 2
} else {
ground - 1
};
let bob = (ctx.time * 1.1).sin() * 0.5 + 0.5;
let hook = jib_y + 3 + ((floor_y - jib_y - 3).max(0) as f32 * bob) as i32;
paint_v(grid, tx, jib_y + 2, hook - 1, cable);
paint_h(grid, tx - 3, tx + 3, hook, beam);
paint_h(grid, 0, wi - 1, ground, Color::rgb(140, 110, 80));
Ok(())
}
}
struct Colosseum;
impl ProgressStyle for Colosseum {
fn name(&self) -> &str {
"colosseum"
}
fn theme(&self) -> &str {
"architecture"
}
fn describe(&self) -> &str {
"Three tiers of travertine arches under a windowed attic rise left to right, the far end broken in a ruined profile"
}
fn render(&self, grid: &mut BrailleGrid, ctx: &BarContext) -> Result<(), DotmaxError> {
let (dw, dh) = draw::dot_dims(grid);
if dw < 8 || dh < 8 {
return Ok(());
}
grid.enable_color_support();
let (wi, hi) = (dw as i32, dh as i32);
let stone = Color::rgb(214, 190, 146);
let light = Color::rgb(250, 226, 178);
let tier = (hi / 4).max(2);
let built = (ctx.eased * wi as f32).round() as i32;
let sweep = (ctx.time * 0.3).fract() * (wi + 16) as f32 - 8.0;
let ruin_from = wi * 6 / 10;
let ruin_to = wi * 85 / 100;
let top_at = |x: i32| -> i32 {
if x < ruin_from {
0
} else if x >= ruin_to {
2 * tier
} else {
let t = (x - ruin_from) * 2 * tier / (ruin_to - ruin_from).max(1);
t - t % 2 + (x % 3 == 0) as i32
}
};
for x in 0..built.min(wi) {
let c = if (x as f32 - sweep).abs() < 6.0 {
light
} else {
stone
};
for y in top_at(x)..hi {
let band = y / tier; let row = y % tier;
let ux = (x + 3).rem_euclid(6);
let solid = if band == 0 {
!((1..=2).contains(&row) && (x / 6) % 2 == 0 && (2..4).contains(&ux))
} else if row == 0 {
true } else {
let inside = (1..5).contains(&ux);
let crown = row == 1 && (ux == 1 || ux == 4);
!inside || crown
};
if solid {
paint(grid, x, y, c);
}
}
}
Ok(())
}
}