Newer
Older
mod circle;
mod mandelbrot;
mod rectangle;
use crate::component::code::{pseudocode, Code};
use crate::component::grid::Grid;
use crate::egui::{Color32, Context, Frame, Ui};
use crate::fade_in::{fade_in_manual, fade_out_manual};
use crate::slide::s5_fractals::circle::circle;
use crate::slide::s5_fractals::mandelbrot::mandelbrot;
use crate::slide::s5_fractals::rectangle::rectangle;
#[derive(Default)]
#[allow(unused)]
enum FractalsState {
/// Erasing the automata cells.
Erase {
/// When we started fading out the dots.
fade_start: f64,
},
impl Default for Fractals {
fn default() -> Self {
const HORIZONTAL_OFFSET: f32 = 275.0;
code.name = "fractal_code";
code.offset_x = -HORIZONTAL_OFFSET;
let mut grid = Grid::default();
grid.name = "fractal_grid";
grid.offset_x = HORIZONTAL_OFFSET;
// Grid cell stroke aliases too much at this resolution.
grid.stroke_width = 0.0;
Self {
state: FractalsState::default(),
impl Slide for Fractals {
fn transition(&mut self, ctx: &Context) -> bool {
// Increment state by one.
match self.state {
FractalsState::Before => {
self.state = FractalsState::Grid {
fade_start: ctx.input().time,
}
}
FractalsState::Grid { .. } => {
// Make sure we finished fading in.
self.code.alpha = 1.0;
self.grid.alpha = 1.0;
self.state = FractalsState::Erase {
fade_start: ctx.input().time,
};
FractalsState::Erase { .. } => self.state = FractalsState::Rectangle { pixels: 0 },
FractalsState::Rectangle { .. } => self.state = FractalsState::Circle { pixels: 0 },
FractalsState::Circle { .. } => self.state = FractalsState::Mandelbrot { pixels: 0 },
FractalsState::Mandelbrot { .. } => return true,
}
false
}
fn show(&mut self, ui: &mut Ui) {
Frame::none().margin(Margin::same(20.0)).show(ui, |ui| {
ui.vertical_centered(|ui| {
ui.heading("Fractals");
});
});
//
// It is dynamically typed (so that multiple distinct functions can be used).
//
// Note that we use indirection via a reference but not a heap-allocated [`Box`]. We can get
// away with that since the function doesn't leave our stack frame.
// Limit to this many pixels. If [`None`], will render all pixels.
// We use a mutable reference so we can increment in one place.
let mut limit_pixels: Option<&mut usize> = None;
match &mut self.state {
fade_in_manual(ui, fade_start, |_, alpha| {
self.code.alpha = alpha;
self.grid.alpha = alpha;
});
algo = None;
}
&mut FractalsState::Erase { fade_start } => {
fade_out_manual(ui, fade_start, |_, alpha| {
// Fade out the automata cells.
for (_, _, cell) in self.grid.iter_mut() {
if cell.a() > 0 {
*cell = set_alpha(Color32::BLACK, alpha);
}
}
});
algo = None;
self.code.code = pseudocode(include_str!("s5_fractals/rectangle.rs"));
self.code.code = pseudocode(include_str!("s5_fractals/circle.rs"));
self.code.code = pseudocode(include_str!("s5_fractals/mandelbrot.rs"));
limit_pixels = Some(pixels);
}
}
// Double mutable borrow to avoid moving limit_pixels.
if let Some(limit_pixels) = &mut limit_pixels {
if **limit_pixels < self.grid.size_cells.pow(2)
&& self.governor.ready(ui.ctx().input().time, 0.02)
{
**limit_pixels += self.grid.size_cells.pow(2) * 33 / 32 / 32;
// Paint the grid.
if let Some(algo) = algo {
// Effective limit for pixels.
let effective_limit = limit_pixels.map(|l| *l).unwrap_or(usize::MAX);
// Store the resolution before mutably borrowing self.grid.
let size_cells = self.grid.size_cells;
for (i, (gx, gy, c)) in self.grid.iter_mut().enumerate() {
if i >= effective_limit {
*c = Color32::TRANSPARENT;
continue;
}
// 0 to 1.
let nx = (gx as f32 + 0.5) / size_cells as f32;
let ny = (gy as f32 + 0.5) / size_cells as f32;
// -4 to 4.
let x = nx * 4.0 - 2.0;
let y = ny * 4.0 - 2.0;
*c = if algo(x, y) {
Color32::BLACK
} else {
Color32::WHITE
};
}
self.code.show(ui.ctx());
self.grid.show(ui.ctx());