From bfae19f3293e8b5c761b1b72df838f489301ea00 Mon Sep 17 00:00:00 2001
From: Finn Bear <finnbearlabs@gmail.com>
Date: Wed, 27 Apr 2022 23:00:00 -0700
Subject: [PATCH] Docs and modularity.

---
 src/cartoon.rs                  | 160 ++++++++++++++++++++++++++++++++
 src/color.rs                    |   2 -
 src/component.rs                |   3 +-
 src/component/code.rs           |   3 +-
 src/component/grid.rs           |   3 +-
 src/fade_in.rs                  |   1 +
 src/governor.rs                 |   2 +
 src/image.rs                    |   3 +-
 src/main.rs                     | 159 +------------------------------
 src/slide.rs                    |   4 +-
 src/slide/s1_title.rs           |   3 +-
 src/slide/s2_introduction.rs    |   4 +-
 src/slide/s3_complexity.rs      |   2 +-
 src/slide/s4_automata.rs        |   2 +-
 src/slide/s5_fractals.rs        |   2 +-
 src/slide/s5_fractals/README.md |   4 +
 src/slide/s6_computation.rs     |   2 +-
 src/slide/s7_mosaic.rs          |   2 +-
 src/slide/s8_conclusion.rs      |   2 +-
 19 files changed, 193 insertions(+), 170 deletions(-)
 create mode 100644 src/cartoon.rs
 create mode 100644 src/slide/s5_fractals/README.md

diff --git a/src/cartoon.rs b/src/cartoon.rs
new file mode 100644
index 0000000..1e414ea
--- /dev/null
+++ b/src/cartoon.rs
@@ -0,0 +1,160 @@
+use crate::fade_in::fade_in;
+use crate::slide::s1_title::Title;
+use crate::slide::s2_introduction::Introduction;
+use crate::slide::s3_complexity::Complexity;
+use crate::slide::s4_automata::Automata;
+use crate::slide::s5_fractals::Fractals;
+use crate::slide::s6_computation::Computation;
+use crate::slide::s7_mosaic::Mosaic;
+use crate::slide::s8_conclusion::Conclusion;
+use crate::slide::Slide;
+use eframe::egui::style::Margin;
+use eframe::egui::{Key, Style, TextStyle, Visuals};
+use eframe::epi::{Frame, Storage};
+use eframe::{egui, epi};
+
+/// Top-level state.
+pub struct Cartoon {
+    /// All slides (including their state).
+    slides: Vec<Box<dyn Slide>>,
+    /// Current index into [`slides`].
+    slide_index: usize,
+    /// When we started fading in the current slide, in seconds (with respect to `ctx.input().time`).
+    transition_time: f64,
+}
+
+// Default is used instead of a zero-argument constructor function, to be more idiomatic.
+impl Default for Cartoon {
+    fn default() -> Self {
+        Self {
+            slides: create_slides(),
+            slide_index: 0,
+            transition_time: 0.0,
+        }
+    }
+}
+
+/// Creates all the slides from default values. This will reset any and all animations and
+/// transitions built into the slides.
+///
+/// This is also how the chronology of the slideshow is determined.
+fn create_slides() -> Vec<Box<dyn Slide>> {
+    vec![
+        Box::new(Title::default()) as Box<dyn Slide>,
+        Box::new(Introduction::default()),
+        Box::new(Complexity::default()),
+        Box::new(Automata::default()),
+        Box::new(Fractals::default()),
+        Box::new(Computation::default()),
+        Box::new(Mosaic::default()),
+        Box::new(Conclusion::default()),
+    ]
+}
+
+impl epi::App for Cartoon {
+    /// The title of the window, which is mostly irrelevant.
+    fn name(&self) -> &str {
+        "Generative Art Cartoon"
+    }
+
+    /// Called once at window initialization.
+    fn setup(&mut self, ctx: &egui::Context, _frame: &Frame, _storage: Option<&dyn Storage>) {
+        // Top level style overrides.
+        let mut style = Style::default();
+        // This doesn't actually affect as many things as [`FadeIn`]'s fade duration.
+        style.animation_time = 0.5;
+        // Light theme.
+        style.visuals = Visuals::light();
+        // Larger margin for windows (which are used for more control when positing stuff).
+        style.spacing.window_margin = Margin::same(24.0);
+
+        // Increase font sizes a lot.
+        {
+            let header_style = style.text_styles.get_mut(&TextStyle::Heading).unwrap();
+            header_style.size = 48.0;
+        }
+        {
+            let body_style = style.text_styles.get_mut(&TextStyle::Body).unwrap();
+            body_style.size = 30.0;
+        }
+        {
+            let small_style = style.text_styles.get_mut(&TextStyle::Small).unwrap();
+            small_style.size = 22.0;
+        }
+        // TODO(finnb): This doesn't work. May have to fork syntax coloring code to fix it.
+        /*
+        {
+            let monospaced_style = style.text_styles.get_mut(&TextStyle::Monospace).unwrap();
+            monospaced_style.size = 22.0;
+        }
+         */
+        ctx.set_style(style);
+
+        // Start fading in title slide now.
+        self.transition_time = ctx.input().time;
+    }
+
+    /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
+    fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
+        // For inspiration and more examples, go to https://emilk.github.io/egui
+
+        /*
+        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
+            egui::menu::bar(ui, |ui| {
+                ui.menu_button("File", |ui| {
+                    if ui.button("Quit").clicked() {
+                        frame.quit();
+                    }
+                });
+            });
+        });
+         */
+
+        // Go forward one slide (don't wait for intra-slide transitions).
+        //
+        // This will wrap around to the beginning.
+        let force_advance =
+            ctx.input().key_pressed(Key::ArrowRight) || ctx.input().key_pressed(Key::D);
+
+        // Play the next intra-slide transition or, if there isn't one, then go forward one slide.
+        //
+        // This will wrap around to the beginning.
+        let advance = force_advance || ctx.input().key_pressed(Key::Space);
+
+        // Go backward one slide (don't wait for intra-slide transitions).
+        //
+        // This will wrap around to the end.
+        let retreat = ctx.input().key_pressed(Key::ArrowLeft) || ctx.input().key_pressed(Key::A);
+
+        if advance || retreat {
+            self.slide_index = if advance {
+                if force_advance || self.slides[self.slide_index].transition(ctx) {
+                    let new = (self.slide_index + 1) % self.slides.len();
+                    if new == 0 {
+                        // We wrapped around to the beginning, so create slides to reset transitions.
+                        self.slides = create_slides();
+                    }
+                    self.transition_time = ctx.input().time;
+                    ctx.request_repaint();
+                    new
+                } else {
+                    self.slide_index
+                }
+            } else {
+                ctx.request_repaint();
+                self.transition_time = ctx.input().time;
+                self.slide_index
+                    .checked_sub(1)
+                    .unwrap_or(self.slides.len() - 1)
+            };
+        }
+
+        egui::CentralPanel::default().show(ctx, |ui| {
+            // The current slide may be fading in.
+            fade_in(ui, self.transition_time, |ui| {
+                // Render the current slide.
+                self.slides[self.slide_index].show(ui);
+            });
+        });
+    }
+}
diff --git a/src/color.rs b/src/color.rs
index 6e037ee..248cc49 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -52,8 +52,6 @@ pub fn set_style_alpha_in_place(style: &mut Style, factor: f32) {
     set_alpha_in_place(&mut style.visuals.extreme_bg_color, factor);
     set_alpha_in_place(&mut style.visuals.code_bg_color, factor);
     set_alpha_in_place(&mut style.visuals.window_shadow.color, factor);
-
-    //style.visuals.widgets.noninteractive.bg_fill = Color32::TRANSPARENT;
 }
 
 /// Render some children with the specified alpha from 0 to 1.
diff --git a/src/component.rs b/src/component.rs
index cf13fbc..6acd3ec 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -1,3 +1,4 @@
-/// See component directory.
+/// See component directory. This file exists to make the individual components accessible to the
+/// rest of the code.
 pub mod code;
 pub mod grid;
diff --git a/src/component/code.rs b/src/component/code.rs
index 056e436..4c6aebe 100644
--- a/src/component/code.rs
+++ b/src/component/code.rs
@@ -2,9 +2,10 @@ use crate::color::{set_alpha_in_place, set_style_alpha};
 use eframe::egui;
 use eframe::egui::{Align2, Color32, Context, Frame, Vec2, Widget, Window};
 
+/// A reusable (pseudo)code-display component.
 #[derive(Clone)]
 pub struct Code {
-    /// Must be unique.
+    /// Must be unique for `egui` reasons.
     pub name: &'static str,
     /// Horizontal offset from middle.
     pub offset_x: f32,
diff --git a/src/component/grid.rs b/src/component/grid.rs
index 499e410..a87c0a9 100644
--- a/src/component/grid.rs
+++ b/src/component/grid.rs
@@ -4,9 +4,10 @@ use eframe::egui::{Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke
 use eframe::emath;
 use eframe::epaint::RectShape;
 
+/// A reusable grid component.
 #[derive(Clone)]
 pub struct Grid {
-    /// Must be unique.
+    /// Must be unique for `egui` reasons.
     pub name: &'static str,
     /// Horizontal offset from middle.
     pub offset_x: f32,
diff --git a/src/fade_in.rs b/src/fade_in.rs
index f028cab..045c19f 100644
--- a/src/fade_in.rs
+++ b/src/fade_in.rs
@@ -12,6 +12,7 @@ pub fn fade_in<R>(
     let since_transition = ui.ctx().input().time - fade_start;
     let alpha = (since_transition * (1.0 / FADE_DURATION)).min(1.0) as f32;
     if alpha < 1.0 {
+        // The fade isn't done yet, so schedule another render.
         ui.ctx().request_repaint();
     }
     with_alpha(ui, alpha, |ui| add_contents(ui))
diff --git a/src/governor.rs b/src/governor.rs
index d8078f6..6d30419 100644
--- a/src/governor.rs
+++ b/src/governor.rs
@@ -8,9 +8,11 @@ impl Governor {
     /// Is the rate limit ready to allow another action?
     pub fn ready(&mut self, time: f64, limit: f64) -> bool {
         if time > self.last + limit {
+            // Enough time passed.
             self.last = time;
             true
         } else {
+            // Keep waiting.
             false
         }
     }
diff --git a/src/image.rs b/src/image.rs
index 35c4819..671258e 100644
--- a/src/image.rs
+++ b/src/image.rs
@@ -19,7 +19,8 @@ macro_rules! ctx_img {
     }};
 }
 
-/// Decodes image data.
+/// Decodes image data. Formats other than PNG may require adding feature flags to the `image` crate
+/// in Cargo.toml.
 pub fn load_image_from_memory(
     image_data: &[u8],
 ) -> Result<eframe::egui::ColorImage, image::ImageError> {
diff --git a/src/main.rs b/src/main.rs
index 13ed9d8..b74843f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,7 @@
 #![feature(derive_default_enum)]
 #![feature(mixed_integer_ops)]
 
+pub mod cartoon;
 pub mod color;
 pub mod component;
 pub mod fade_in;
@@ -8,23 +9,13 @@ pub mod governor;
 pub mod image;
 pub mod slide;
 
-use crate::fade_in::fade_in;
-use crate::slide::s1_title::Title;
-use crate::slide::s2_introduction::Introduction;
-use crate::slide::s3_complexity::Complexity;
-use crate::slide::s4_automata::Automata;
-use crate::slide::s5_fractals::Fractals;
-use crate::slide::s6_computation::Computation;
-use crate::slide::s7_mosaic::Mosaic;
-use crate::slide::s8_conclusion::Conclusion;
-use crate::slide::Slide;
+use crate::cartoon::Cartoon;
+use eframe::egui;
 use eframe::egui::style::Margin;
-use eframe::egui::{Key, Style, TextStyle, Visuals};
-use eframe::epi::{Frame, Storage};
-use eframe::{egui, epi};
 
 // Entry point.
 fn main() {
+    // cartoon.rs is a good place to look next.
     let app = Cartoon::default();
 
     // 16:9 aspect ratio.
@@ -46,145 +37,3 @@ fn main() {
 
     eframe::run_native(Box::new(app), native_options);
 }
-
-/// Top-level state.
-pub struct Cartoon {
-    /// All slides (including their state).
-    slides: Vec<Box<dyn Slide>>,
-    /// Current index into [`slides`].
-    slide_index: usize,
-    /// When we started fading in the current slide, in seconds (with respect to `ctx.input().time`).
-    transition_time: f64,
-}
-
-impl Default for Cartoon {
-    fn default() -> Self {
-        Self {
-            slides: create_slides(),
-            slide_index: 0,
-            transition_time: 0.0,
-        }
-    }
-}
-
-/// Creates all the slides from default values. This will reset any and all animations and
-/// transitions built into the slides.
-///
-/// This is also how the chronology of the slideshow is determined.
-fn create_slides() -> Vec<Box<dyn Slide>> {
-    vec![
-        Box::new(Title::default()) as Box<dyn Slide>,
-        Box::new(Introduction::default()),
-        Box::new(Complexity::default()),
-        Box::new(Automata::default()),
-        Box::new(Fractals::default()),
-        Box::new(Computation::default()),
-        Box::new(Mosaic::default()),
-        Box::new(Conclusion::default()),
-    ]
-}
-
-impl epi::App for Cartoon {
-    /// The title of the window, which is mostly irrelevant.
-    fn name(&self) -> &str {
-        "Generative Art Cartoon"
-    }
-
-    /// Called once at window initialization.
-    fn setup(&mut self, ctx: &egui::Context, _frame: &Frame, _storage: Option<&dyn Storage>) {
-        // Top level style overrides.
-        let mut style = Style::default();
-        style.animation_time = 0.5;
-        style.visuals = Visuals::light();
-        style.spacing.window_margin = Margin::same(24.0);
-
-        // Increase font sizes a lot.
-        {
-            let header_style = style.text_styles.get_mut(&TextStyle::Heading).unwrap();
-            header_style.size = 48.0;
-        }
-        {
-            let body_style = style.text_styles.get_mut(&TextStyle::Body).unwrap();
-            body_style.size = 30.0;
-        }
-        {
-            let small_style = style.text_styles.get_mut(&TextStyle::Small).unwrap();
-            small_style.size = 22.0;
-        }
-        // TODO(finnb): This doesn't work. May have to fork syntax coloring code to fix it.
-        /*
-        {
-            let monospaced_style = style.text_styles.get_mut(&TextStyle::Monospace).unwrap();
-            monospaced_style.size = 22.0;
-        }
-         */
-        ctx.set_style(style);
-
-        // Start fading in title slide now.
-        self.transition_time = ctx.input().time;
-    }
-
-    /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
-    fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
-        // For inspiration and more examples, go to https://emilk.github.io/egui
-
-        /*
-        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
-            egui::menu::bar(ui, |ui| {
-                ui.menu_button("File", |ui| {
-                    if ui.button("Quit").clicked() {
-                        frame.quit();
-                    }
-                });
-            });
-        });
-         */
-
-        // Go forward one slide (don't wait for intra-slide transitions).
-        //
-        // This will wrap around to the beginning.
-        let force_advance =
-            ctx.input().key_pressed(Key::ArrowRight) || ctx.input().key_pressed(Key::D);
-
-        // Play the next intra-slide transition or, if there isn't one, then go forward one slide.
-        //
-        // This will wrap around to the beginning.
-        let advance = force_advance || ctx.input().key_pressed(Key::Space);
-
-        // Go backward one slide (don't wait for intra-slide transitions).
-        //
-        // This will wrap around to the end.
-        let retreat = ctx.input().key_pressed(Key::ArrowLeft) || ctx.input().key_pressed(Key::A);
-
-        if advance || retreat {
-            self.slide_index = if advance {
-                if force_advance || self.slides[self.slide_index].transition(ctx) {
-                    let new = (self.slide_index + 1) % self.slides.len();
-                    if new == 0 {
-                        // We wrapped around to the beginning, so create slides to reset transitions.
-                        self.slides = create_slides();
-                    }
-                    self.transition_time = ctx.input().time;
-                    ctx.request_repaint();
-                    new
-                } else {
-                    self.slide_index
-                }
-            } else {
-                ctx.request_repaint();
-                self.transition_time = ctx.input().time;
-                self.slide_index
-                    .checked_sub(1)
-                    .unwrap_or(self.slides.len() - 1)
-            };
-        }
-
-        egui::CentralPanel::default().show(ctx, |ui| {
-            // The current slide may be fading in.
-            fade_in(ui, self.transition_time, |ui| {
-                // Render the current slide.
-                self.slides[self.slide_index].show(ui);
-            });
-        });
-    }
-}
diff --git a/src/slide.rs b/src/slide.rs
index bc69774..43cfc75 100644
--- a/src/slide.rs
+++ b/src/slide.rs
@@ -1,5 +1,7 @@
 use eframe::egui::{Context, Ui};
 
+/// See slide directory. This file exists to make the individual slides accessible to the
+/// rest of the code.
 pub mod s1_title;
 pub mod s2_introduction;
 pub mod s3_complexity;
@@ -11,7 +13,7 @@ pub mod s8_conclusion;
 
 /// An interface for all slides.
 pub trait Slide {
-    /// Returns whether "done."
+    /// Returns whether "done" i.e. whether should switch to next slide.
     /// Repaint automatically requested.
     fn transition(&mut self, _ctx: &Context) -> bool {
         true
diff --git a/src/slide/s1_title.rs b/src/slide/s1_title.rs
index 8a983ba..ee88a52 100644
--- a/src/slide/s1_title.rs
+++ b/src/slide/s1_title.rs
@@ -1,5 +1,6 @@
 use crate::egui::Align2;
-use crate::{ctx_img, img, Margin, Slide};
+use crate::slide::Slide;
+use crate::{ctx_img, img, Margin};
 use eframe::egui;
 use eframe::egui::{Frame, Ui, Vec2, Window};
 
diff --git a/src/slide/s2_introduction.rs b/src/slide/s2_introduction.rs
index 6c476ea..c24d1a0 100644
--- a/src/slide/s2_introduction.rs
+++ b/src/slide/s2_introduction.rs
@@ -1,4 +1,6 @@
-use crate::{fade_in, Margin, Slide};
+use crate::fade_in::fade_in;
+use crate::slide::Slide;
+use eframe::egui::style::Margin;
 use eframe::egui::{Context, Frame, Ui};
 
 /// A slide that sets some expectations about generative art.
diff --git a/src/slide/s3_complexity.rs b/src/slide/s3_complexity.rs
index 3dd1dfa..794b0ab 100644
--- a/src/slide/s3_complexity.rs
+++ b/src/slide/s3_complexity.rs
@@ -1,5 +1,5 @@
 use crate::egui::{Context, Ui, Window};
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::text_edit::{CCursorRange, TextEditState};
 use eframe::egui::{Align2, Frame, TextEdit, Vec2, Widget};
 use eframe::epaint::text::cursor::CCursor;
diff --git a/src/slide/s4_automata.rs b/src/slide/s4_automata.rs
index 4239c4b..6e83b65 100644
--- a/src/slide/s4_automata.rs
+++ b/src/slide/s4_automata.rs
@@ -1,6 +1,6 @@
 use crate::component::grid::Grid;
 use crate::governor::Governor;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 use eframe::egui::{Color32, Frame, Ui};
 use rand::Rng;
diff --git a/src/slide/s5_fractals.rs b/src/slide/s5_fractals.rs
index adafb70..0d895eb 100644
--- a/src/slide/s5_fractals.rs
+++ b/src/slide/s5_fractals.rs
@@ -8,7 +8,7 @@ use crate::egui::{Color32, Context, Frame, Ui};
 use crate::slide::s5_fractals::circle::circle;
 use crate::slide::s5_fractals::mandelbrot::mandelbrot;
 use crate::slide::s5_fractals::rectangle::rectangle;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 
 /// Mandelbrot, etc.
diff --git a/src/slide/s5_fractals/README.md b/src/slide/s5_fractals/README.md
new file mode 100644
index 0000000..23ea144
--- /dev/null
+++ b/src/slide/s5_fractals/README.md
@@ -0,0 +1,4 @@
+These are linked into the program as both code (`use`) and data (`include_str!`). They should not necessarily obey Rust
+idioms, since they are adapted into pseudocode.
+
+In particular, use `return x;` syntax instead of `x`.
\ No newline at end of file
diff --git a/src/slide/s6_computation.rs b/src/slide/s6_computation.rs
index 68a4a05..660de16 100644
--- a/src/slide/s6_computation.rs
+++ b/src/slide/s6_computation.rs
@@ -1,5 +1,5 @@
 use crate::egui::{Frame, Ui};
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 
 #[derive(Default)]
diff --git a/src/slide/s7_mosaic.rs b/src/slide/s7_mosaic.rs
index e04e522..df7ff68 100644
--- a/src/slide/s7_mosaic.rs
+++ b/src/slide/s7_mosaic.rs
@@ -1,5 +1,5 @@
 use crate::egui::Ui;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 use eframe::egui::Frame;
 
diff --git a/src/slide/s8_conclusion.rs b/src/slide/s8_conclusion.rs
index 169ff0a..6bb55e8 100644
--- a/src/slide/s8_conclusion.rs
+++ b/src/slide/s8_conclusion.rs
@@ -1,5 +1,5 @@
 use crate::egui::Ui;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 use eframe::egui::Frame;
 
-- 
GitLab