diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-08-25 12:36:37 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-08-25 12:36:37 +0200 |
| commit | e5de70f2c1dcac767bd34a6b90ac1797a7acb433 (patch) | |
| tree | 25a8c8f339fb5b496b460391ee06a9effeb64855 /src/screen.rs | |
| parent | 6b909d95ec07848136a6f337db28318c1cd46c60 (diff) | |
| parent | d7bc21d19e168f3a99f54e5ba4866ed49f1187f1 (diff) | |
Merge branch 'rust'
Diffstat (limited to 'src/screen.rs')
| -rw-r--r-- | src/screen.rs | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/screen.rs b/src/screen.rs new file mode 100644 index 0000000..2c701ea --- /dev/null +++ b/src/screen.rs @@ -0,0 +1,242 @@ +use crate::renderer::Renderer; +use rand::{Rng, rng}; +use std::fmt::Write; + +#[derive(Clone)] +pub enum Screen { + Circle, + Checkerboard, + Gradients, + Mirrors, + Random, + Sprinkles, +} + +impl Screen { + pub fn random() -> Self { + let mut rng = rng(); + let screens = [ + Self::Circle, + Self::Checkerboard, + Self::Gradients, + Self::Mirrors, + Self::Random, + Self::Sprinkles, + ]; + let index = rng.random_range(0..screens.len()); + screens.get(index).unwrap_or(&Self::Random).clone() + } + + pub fn render(&self, modulation: u8, width: u16, height: u16, renderer: &Renderer) -> String { + match self { + Screen::Circle => render_circle(modulation, width, height, renderer), + Screen::Checkerboard => render_checkerboard(modulation, width, height, renderer), + Screen::Gradients => render_gradients(modulation, width, height, renderer), + Screen::Mirrors => render_mirrors(modulation, width, height, renderer), + Screen::Random => render_random(modulation, width, height, renderer), + Screen::Sprinkles => render_sprinkles(modulation, width, height, renderer), + } + } +} + +fn render_circle(_modulation: u8, width: u16, height: u16, renderer: &Renderer) -> String { + let mut response = String::new(); + let mut rng = rng(); + + let circles = if width > height { height } else { width }; + + for i in 0..circles { + let center_x = (width / 2) + 1; + let center_y = (height / 2) + 1; + + let red = rng.random_range(0..=255); + let blue = rng.random_range(0..=255); + let green = rng.random_range(0..=255); + + for j in 0..180 { + let angle = 2.0 * f64::from(j) * (std::f64::consts::PI / 180.0); + + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let x = (f64::from(center_x) + angle.sin() * f64::from(i)).round() as u16; + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let y = (f64::from(center_y) + angle.cos() * f64::from(i)).round() as u16; + + if x <= width && x > 0 && y <= height && y > 0 { + let position = format!("\x1B[{y};{x}H"); // Move cursor to y,x (CSI: y;x H) + let _ = write!( + response, + "{}{} ", + position, + renderer.render(red, green, blue) + ); + } + } + } + + response +} + +fn render_checkerboard(modulation: u8, width: u16, height: u16, renderer: &Renderer) -> String { + let mut response = String::new(); + + // Calculate offset based on modulation + // Full cycle every 25 steps (5x5 grid) + let cycle_pos = modulation % 25; + let offset_x = u16::from(cycle_pos / 5); + let offset_y = u16::from(cycle_pos / 5); + + // Use modulation / 25 as seed to get consistent colors during each cycle + let color_seed = u64::from(modulation / 25); + + // Generate two colors based on the current cycle + let red1 = u8::try_from((color_seed * 17 + 42) % 256).unwrap_or(0); + let green1 = u8::try_from((color_seed * 31 + 73) % 256).unwrap_or(0); + let blue1 = u8::try_from((color_seed * 47 + 91) % 256).unwrap_or(0); + + let red2 = u8::try_from((color_seed * 23 + 137) % 256).unwrap_or(0); + let green2 = u8::try_from((color_seed * 41 + 163) % 256).unwrap_or(0); + let blue2 = u8::try_from((color_seed * 37 + 197) % 256).unwrap_or(0); + + for i in 0..height { + for j in 0..width { + // Calculate which checker square we're in, accounting for offset + let checker_x = (j + offset_x) / 5; + let checker_y = (i + offset_y) / 5; + + // Checkerboard pattern: alternate colors + let is_first_color = (checker_x + checker_y) % 2 == 0; + + if is_first_color { + response.push_str(&renderer.render(red1, green1, blue1)); + } else { + response.push_str(&renderer.render(red2, green2, blue2)); + } + response.push(' '); + } + + if i < height - 1 { + response.push('\n'); + } + } + + response +} + +fn render_gradients(modulation: u8, width: u16, height: u16, renderer: &Renderer) -> String { + let mut response = String::new(); + + for i in 0..height { + for j in 0..width { + let red = + ((u32::from(modulation) + u32::from(i)) * 255 / u32::from(height).max(1)) % 255; + let blue = + ((u32::from(modulation) + u32::from(j)) * 255 / u32::from(width).max(1)) % 255; + let green = ((u32::from(modulation) + u32::from(i) * u32::from(j)) * 255 + / (u32::from(width) * u32::from(height)).max(1)) + % 255; + + response.push_str(&renderer.render(red as u8, green as u8, blue as u8)); + response.push(' '); + } + + if i < height - 1 { + response.push('\n'); + } + } + + response +} + +fn render_mirrors(modulation: u8, width: u16, height: u16, renderer: &Renderer) -> String { + let mut rng = rng(); + let scale = 2 + rng.random_range(0..5); + let scaled_height = height.saturating_div(scale).max(1); + let scaled_width = width.saturating_div(scale).max(1); + + let mut response = vec![String::new(); height as usize]; + + for i in 0..scaled_height { + let mut row = Vec::new(); + for j in 0..scaled_width { + let red = + ((u32::from(modulation) + u32::from(i)) * 255 / u32::from(height).max(1)) % 255; + let blue = + ((u32::from(modulation) + u32::from(j)) * 255 / u32::from(width).max(1)) % 255; + let green = ((u32::from(modulation) + u32::from(i) * u32::from(j)) * 255 + / (u32::from(width) * u32::from(height)).max(1)) + % 255; + + let cell = format!("{} ", renderer.render(red as u8, green as u8, blue as u8)); + row.push(cell); + } + + let mut row_text = String::new(); + for _ in 0..scale { + row.reverse(); + row_text.push_str(&row.join("")); + } + + for j in 1..scale { + let top_idx = (j.saturating_mul(i)) as usize; + let bottom_idx = height.saturating_sub(1).saturating_sub(j.saturating_mul(i)) as usize; + if let Some(line) = response.get_mut(top_idx) { + line.clone_from(&row_text); + } + if let Some(line) = response.get_mut(bottom_idx) { + line.clone_from(&row_text); + } + } + } + + response.join("\n") +} + +fn render_random(_modulation: u8, width: u16, height: u16, renderer: &Renderer) -> String { + let mut response = String::new(); + let mut rng = rng(); + + for i in 0..height { + for _j in 0..width { + let red = rng.random_range(0..=255); + let blue = rng.random_range(0..=255); + let green = rng.random_range(0..=255); + + response.push_str(&renderer.render(red, green, blue)); + response.push(' '); + } + + if i < height - 1 { + response.push('\n'); + } + } + + response +} + +fn render_sprinkles(_modulation: u8, width: u16, height: u16, renderer: &Renderer) -> String { + let mut response = String::new(); + let mut rng = rng(); + + let max_sprinkle_count = (width * height) / 2; + let min_sprinkle_count = (width * height) / 8; + let sprinkle_count = rng.random_range(min_sprinkle_count..=max_sprinkle_count); + + let red = rng.random_range(0..=255); + let blue = rng.random_range(0..=255); + let green = rng.random_range(0..=255); + + for _ in 0..sprinkle_count { + let x = rng.random_range(1..=width); + let y = rng.random_range(1..=height); + + let position = format!("\x1B[{y};{x}H"); // Move cursor to y,x + let _ = write!( + response, + "{}{} ", + position, + renderer.render(red, green, blue) + ); + } + + response +} |