From c73497846b4dfcf64309fc69bf52bd512ac1eda5 Mon Sep 17 00:00:00 2001 From: Finn Bear <finnbearlabs@gmail.com> Date: Wed, 27 Apr 2022 20:37:50 -0700 Subject: [PATCH] Document the slides. --- src/slide/s1_title.rs | 25 ++++++++++++------------- src/slide/s2_introduction.rs | 2 ++ src/slide/s3_complexity.rs | 15 +++++++++++++++ src/slide/s4_automata.rs | 10 ++++++++++ src/slide/s5_fractals.rs | 28 +++++++++++++++++++++++++++- src/slide/s7_mosaic.rs | 1 + 6 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/slide/s1_title.rs b/src/slide/s1_title.rs index 947ecd2..8a983ba 100644 --- a/src/slide/s1_title.rs +++ b/src/slide/s1_title.rs @@ -3,6 +3,7 @@ use crate::{ctx_img, img, Margin, Slide}; use eframe::egui; use eframe::egui::{Frame, Ui, Vec2, Window}; +/// The first slide in the cartoon. #[derive(Default)] pub struct Title { examples: Vec<egui::TextureHandle>, @@ -11,6 +12,7 @@ pub struct Title { impl Slide for Title { fn show(&mut self, ui: &mut Ui) { if self.examples.is_empty() { + // For now, these images are somewhat like placeholders. self.examples = vec![ ctx_img!(ui.ctx(), "raymarching0.png"), ctx_img!(ui.ctx(), "raymarching1.png"), @@ -20,19 +22,16 @@ impl Slide for Title { ]; } - ui.scope(|ui| { - ui.style_mut().spacing.window_margin = Margin::same(4.0); - - for example in &self.examples { - Window::new(example.name()) - .title_bar(false) - .resizable(false) - .frame(Frame::window(&ui.style()).margin(Margin::same(5.0))) - .show(ui.ctx(), |ui| { - ui.image(example, Vec2::splat(128.0)); - }); - } - }); + for example in &self.examples { + Window::new(example.name()) + .title_bar(false) + .resizable(false) + // Reduce margin of example images. + .frame(Frame::window(&ui.style()).margin(Margin::same(5.0))) + .show(ui.ctx(), |ui| { + ui.image(example, Vec2::splat(128.0)); + }); + } Window::new("title") .title_bar(false) diff --git a/src/slide/s2_introduction.rs b/src/slide/s2_introduction.rs index fe97817..6c476ea 100644 --- a/src/slide/s2_introduction.rs +++ b/src/slide/s2_introduction.rs @@ -1,6 +1,7 @@ use crate::{fade_in, Margin, Slide}; use eframe::egui::{Context, Frame, Ui}; +/// A slide that sets some expectations about generative art. #[derive(Default)] pub struct Introduction { state: IntroductionState, @@ -11,6 +12,7 @@ enum IntroductionState { #[default] Weaknesses, Strengths { + /// What time we started fading in the strengths. transition_time: f64, }, } diff --git a/src/slide/s3_complexity.rs b/src/slide/s3_complexity.rs index 92e09ce..3dd1dfa 100644 --- a/src/slide/s3_complexity.rs +++ b/src/slide/s3_complexity.rs @@ -4,27 +4,36 @@ use eframe::egui::text_edit::{CCursorRange, TextEditState}; use eframe::egui::{Align2, Frame, TextEdit, Vec2, Widget}; use eframe::epaint::text::cursor::CCursor; +/// Introduction for emergent complexity. #[derive(Default)] pub struct Complexity { state: ComplexityState, last_time: f64, } +/// What state we are in, with respect to the action of selecting "Complex" and replacing it +/// with "Simple." #[derive(Default, Eq, PartialEq, Debug)] enum ComplexityState { + /// Haven't started the transition yet. #[default] Before, + /// Selected this many characters of "Complex." Selecting(usize), + /// Typed this many characters of "Simple." Typing(usize), + /// Animation over. After, } impl Slide for Complexity { fn transition(&mut self, _ctx: &Context) -> bool { if self.state == ComplexityState::Before { + // If we haven't started the animation, start it. self.state = ComplexityState::Selecting(0); false } else { + // Otherwise, continue to next slide. true } } @@ -33,11 +42,15 @@ impl Slide for Complexity { Window::new("complexity") .title_bar(false) .resizable(false) + // Don't show window background. .frame(Frame::none()) .anchor(Align2::CENTER_CENTER, Vec2::ZERO) + // Width was determined anecdotally. Height is arbitrary. .fixed_size(Vec2::new(440.0, 100.0)) .show(ui.ctx(), |ui| { ui.ctx().request_repaint(); + + // TODO(finnb) replace with [`Governor`]. let time = ui.input().time; let time_delta = (time - self.last_time).min(1.0); let made_progress = time_delta > 0.1; @@ -85,8 +98,10 @@ impl Slide for Complexity { .desired_width(f32::INFINITY) .ui(ui); if state.ccursor_range().is_some() { + // Necessary to render selecting. text_edit.request_focus(); } else { + // We're done selecting. text_edit.surrender_focus(); } TextEdit::store_state(ui.ctx(), text_edit.id, state); diff --git a/src/slide/s4_automata.rs b/src/slide/s4_automata.rs index 5ba5385..4239c4b 100644 --- a/src/slide/s4_automata.rs +++ b/src/slide/s4_automata.rs @@ -5,15 +5,21 @@ use eframe::egui::style::Margin; use eframe::egui::{Color32, Frame, Ui}; use rand::Rng; +/// Conway's Game of Life, etc. pub struct Automata { + /// Persistent state of the grid. life: Grid, + /// For knowing when to advance the simulation. governor: Governor, } impl Default for Automata { fn default() -> Self { let mut life = Grid::default(); + // Unique identifier for the grid. life.name = "life"; + + // Fill grid with random cells. for y in 0..life.size_cells { for x in 0..life.size_cells { if rand::thread_rng().gen_bool(0.5) { @@ -22,6 +28,7 @@ impl Default for Automata { } } + // This would replace the grid with a single oscillator. /* life.clear_fill(); life.set_fill(5, 5, Color32::BLACK); @@ -44,7 +51,9 @@ impl Slide for Automata { }); }); + // Need to continuously animate the grid, or at least poll the governor. ui.ctx().request_repaint(); + if self.governor.ready(ui.ctx().input().time, 0.2) { self.life = conways_game_of_life(&self.life); } @@ -52,6 +61,7 @@ impl Slide for Automata { } } +/// Run one iteration of Conway's Game of Life on the grid, producing a new grid. fn conways_game_of_life(grid: &Grid) -> Grid { let mut new = grid.clone(); new.clear_fill(); diff --git a/src/slide/s5_fractals.rs b/src/slide/s5_fractals.rs index 037baa4..adafb70 100644 --- a/src/slide/s5_fractals.rs +++ b/src/slide/s5_fractals.rs @@ -11,27 +11,37 @@ use crate::slide::s5_fractals::rectangle::rectangle; use crate::Slide; use eframe::egui::style::Margin; +/// Mandelbrot, etc. pub struct Fractals { + /// Where we are in the transitions. state: FractalsState, + /// Pseudocode display. code: Code, + /// Persistent grid state. grid: Grid, } #[derive(Default)] #[allow(unused)] enum FractalsState { + /// Nothing on screen (except header). #[default] Before, + /// Fading in grid. Grid { + /// When we started fading in the grid. fade_start: f64, }, Rectangle { + /// TODO(finnb): How many pixels of the grid we've filled in. pixel: usize, }, Circle { + /// TODO(finnb): How many pixels of the grid we've filled in. pixel: usize, }, Mandelbrot { + /// TODO(finnb): How many pixels of the grid we've filled in. pixel: usize, }, } @@ -40,15 +50,19 @@ impl Default for Fractals { fn default() -> Self { const HORIZONTAL_OFFSET: f32 = 275.0; + // Code goes on the left. let mut code = Code::default(); code.name = "fractal_code"; code.offset_x = -HORIZONTAL_OFFSET; + // Grid goes on the right. let mut grid = Grid::default(); grid.name = "fractal_grid"; grid.offset_x = HORIZONTAL_OFFSET; grid.size_cells = 256; + + // Grid cell stroke aliases too much at this resolution. grid.stroke_width = 0.0; Self { @@ -68,7 +82,13 @@ impl Slide for Fractals { fade_start: ctx.input().time, } } - FractalsState::Grid { .. } => self.state = FractalsState::Rectangle { pixel: 0 }, + FractalsState::Grid { .. } => { + // Make sure we finished fading in. + self.code.alpha = 1.0; + self.grid.alpha = 1.0; + + self.state = FractalsState::Rectangle { pixel: 0 } + } FractalsState::Rectangle { .. } => self.state = FractalsState::Circle { pixel: 0 }, FractalsState::Circle { .. } => self.state = FractalsState::Mandelbrot { pixel: 0 }, FractalsState::Mandelbrot { .. } => return true, @@ -85,13 +105,16 @@ impl Slide for Fractals { ui.ctx().request_repaint(); + // The function that will be used to color the grid. let algo: Box<dyn Fn(f32, f32) -> bool>; match self.state { FractalsState::Before => { + // Don't proceed to render the code/grid. return; } FractalsState::Grid { fade_start } => { + // Fade in the code and grid. let elapsed = ui.ctx().input().time - fade_start; let alpha = (elapsed * 2.0).min(1.0) as f32; self.code.alpha = alpha; @@ -112,8 +135,11 @@ impl Slide for Fractals { } } + // TODO: Different demos may need different resolutions. const RESOLUTION: usize = 256; + // Paint the grid. + // TODO(finnb): Paint pixel by pixel. for (gx, gy, c) in self.grid.iter_mut() { // 0 to 1. let nx = (gx as f32 + 0.5) / RESOLUTION as f32; diff --git a/src/slide/s7_mosaic.rs b/src/slide/s7_mosaic.rs index 36241f1..e04e522 100644 --- a/src/slide/s7_mosaic.rs +++ b/src/slide/s7_mosaic.rs @@ -8,6 +8,7 @@ pub struct Mosaic {} impl Slide for Mosaic { fn show(&mut self, ui: &mut Ui) { + // TODO: Finish this slide. Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { ui.heading("Going Further"); ui.add_space(8.0); -- GitLab