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

Docs and modularity.

parent c7349784
No related branches found
No related tags found
No related merge requests found
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);
});
});
}
}
......@@ -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.
......
/// 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;
......@@ -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,
......
......@@ -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,
......
......@@ -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))
......
......@@ -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
}
}
......
......@@ -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> {
......
#![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);
});
});
}
}
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
......
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};
......
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.
......
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;
......
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;
......
......@@ -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.
......
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
use crate::egui::{Frame, Ui};
use crate::Slide;
use crate::slide::Slide;
use eframe::egui::style::Margin;
#[derive(Default)]
......
use crate::egui::Ui;
use crate::Slide;
use crate::slide::Slide;
use eframe::egui::style::Margin;
use eframe::egui::Frame;
......
use crate::egui::Ui;
use crate::Slide;
use crate::slide::Slide;
use eframe::egui::style::Margin;
use eframe::egui::Frame;
......
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