Skip to content
Snippets Groups Projects
Commit c7349784 authored by Finn Bear's avatar Finn Bear
Browse files

Document the slides.

parent 2b1c00ea
No related branches found
No related tags found
No related merge requests found
......@@ -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)
......
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,
},
}
......
......@@ -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);
......
......@@ -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();
......
......@@ -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;
......
......@@ -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);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment