use super::OPUS;
use crate::kit::{bump, frac, hsl, lag, smooth};
use crate::paint::{Ink, Palette};
use crate::recipe::{Glyphs, Recipe};
use crate::stage::{Canvas, Shader, Stage};
pub(super) const RECIPE: Recipe = Recipe {
name: "Prism",
blurb: "a narrow band of the spectrum",
credit: OPUS,
needs: Glyphs::BASE,
summon: |_| Box::<Prism>::default(),
};
const BRIGHTEST: f64 = 90.0;
const RAY: f64 = 9.0;
#[derive(Default)]
struct Prism {
flow: f64,
}
impl Prism {
#[expect(
clippy::cast_precision_loss,
reason = "Quantum totals only shift an approximate visual hue; exact accounting remains upstream."
)]
fn hue(stage: &Stage) -> f64 {
0.03 * stage.t + stage.quanta as f64 / 3000.0
}
}
impl Shader for Prism {
fn tick(&mut self, stage: &Stage) {
let flow = if stage.busy() {
stage.flow( 0.5)
} else {
0.0
};
self.flow = lag(self.flow, flow, 0.6);
}
#[expect(
clippy::cast_precision_loss,
reason = "Caption indices and lengths are approximate visual coordinates; subpixel rounding cannot change transcript content."
)]
fn status(&self, stage: &Stage, palette: Palette, canvas: &mut Canvas) {
let hue = Prism::hue(stage);
let width = stage.caption.len() as f64;
let resting = palette.grey( 0.6);
if !stage.busy() {
let ray = frac(stage.t / RAY) * (width + 4.0) - 2.0;
let faint = hsl(hue, 0.45, 0.62);
canvas.mark = vec![Ink::of('◭').fg(palette.grey( 0.5)).bold(true)];
canvas.letters = stage.letters(|i, _| {
Ink::default().fg(faint.blend(resting, bump(i as f64 + 0.5 - ray, 2.0)))
});
return;
}
let dispersion = (self.flow / BRIGHTEST).clamp(0.0, 1.0);
let reach = 3.0 + dispersion * (width - 3.0);
let band = 0.04 + 0.12 * dispersion;
canvas.mark = vec![
Ink::of('◭')
.fg(hsl(hue, 0.35, 0.75))
.bold(true),
];
canvas.letters = stage.letters(|i, _| {
let x = (i as f64 + 0.5) / reach;
let lit = 1.0 - smooth( 0.85, 1.15, x);
let ray = hsl(
hue + band * (x - 0.5),
0.5,
0.64,
);
Ink::default().fg(ray.blend(resting, lit)).bold(lit > 0.5)
});
}
fn bullet(&self, stage: &Stage, palette: Palette) -> Ink {
Ink::of('·')
.fg(palette.grey(if stage.busy() { 0.85 } else { 0.4 }))
.bold(true)
}
}