From f068f4efe5000584fa93ca285ca060592e6c52f7 Mon Sep 17 00:00:00 2001 From: Finn Bear <finnbearlabs@gmail.com> Date: Wed, 27 Apr 2022 19:42:11 -0700 Subject: [PATCH] Fade in transition, content WIP. --- src/color.rs | 68 ++++++++++++ src/component/code.rs | 29 ++++-- src/component/grid.rs | 29 ++++-- src/fade_in.rs | 17 +++ src/main.rs | 19 +++- src/slide.rs | 4 +- src/slide/s1_title.rs | 9 +- src/slide/s2_introduction.rs | 46 ++++++--- src/slide/s3_complexity.rs | 12 ++- src/slide/s4_automata.rs | 10 +- src/slide/s5_fractals.rs | 154 ++++++++++++++++++---------- src/slide/s5_fractals/circle.rs | 3 + src/slide/s5_fractals/mandelbrot.rs | 15 +++ src/slide/s5_fractals/rectangle.rs | 3 + src/slide/s6_computation.rs | 4 +- src/slide/s7_mosaic.rs | 4 +- src/slide/s8_conclusion.rs | 4 +- 17 files changed, 318 insertions(+), 112 deletions(-) create mode 100644 src/color.rs create mode 100644 src/fade_in.rs create mode 100644 src/slide/s5_fractals/circle.rs create mode 100644 src/slide/s5_fractals/mandelbrot.rs create mode 100644 src/slide/s5_fractals/rectangle.rs diff --git a/src/color.rs b/src/color.rs new file mode 100644 index 0000000..54b6d31 --- /dev/null +++ b/src/color.rs @@ -0,0 +1,68 @@ +use crate::egui::style::WidgetVisuals; +use eframe::egui::{Color32, InnerResponse, Style, Ui}; + +/// Reduces the alpha of a color (how opaque it is) by a factor. +pub fn set_alpha(color: Color32, factor: f32) -> Color32 { + if factor == 1.0 { + // Fast/common path. + color + } else { + color.linear_multiply(factor) + } +} + +/// Same as [`multiply_alpha`], but operates in place. +pub fn set_alpha_in_place(color: &mut Color32, factor: f32) { + assert!(factor >= 0.0 && factor <= 1.0); + *color = set_alpha(*color, factor); +} + +/// Changes alpha of entire widget style. +fn set_widget_alpha_in_place(widget: &mut WidgetVisuals, factor: f32) { + set_alpha_in_place(&mut widget.bg_fill, factor); + set_alpha_in_place(&mut widget.bg_stroke.color, factor); + set_alpha_in_place(&mut widget.fg_stroke.color, factor); +} + +/// Changes alpha of entire style. +pub fn set_style_alpha(style: &Style, factor: f32) -> Style { + let mut clone = style.clone(); + set_style_alpha_in_place(&mut clone, factor); + clone +} + +/// Changes alpha of entire style, in place. +pub fn set_style_alpha_in_place(style: &mut Style, factor: f32) { + if factor == 1.0 { + // Common/fast path. + return; + } + let widgets = &mut style.visuals.widgets; + for widget in [ + &mut widgets.noninteractive, + &mut widgets.inactive, + &mut widgets.hovered, + &mut widgets.active, + &mut widgets.open, + ] { + set_widget_alpha_in_place(widget, factor); + } + set_alpha_in_place(&mut style.visuals.hyperlink_color, factor); + set_alpha_in_place(&mut style.visuals.faint_bg_color, factor); + 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; +} + +pub fn with_alpha<R>( + ui: &mut Ui, + alpha: f32, + add_contents: impl FnOnce(&mut Ui) -> R, +) -> InnerResponse<R> { + ui.scope(|ui| { + set_style_alpha_in_place(ui.style_mut(), alpha); + add_contents(ui) + }) +} diff --git a/src/component/code.rs b/src/component/code.rs index 2202d1b..dc830e1 100644 --- a/src/component/code.rs +++ b/src/component/code.rs @@ -1,5 +1,6 @@ +use crate::color::{set_alpha_in_place, set_style_alpha}; use eframe::egui; -use eframe::egui::{Align2, Color32, Context, Vec2, Widget, Window}; +use eframe::egui::{Align2, Color32, Context, Frame, Vec2, Widget, Window}; #[derive(Clone)] pub struct Code { @@ -13,6 +14,8 @@ pub struct Code { pub size_pixels: f32, /// Background color. Transparent disables background. pub background: Color32, + /// Alpha (how opaque) entire widget is. + pub alpha: f32, /// Code. pub code: String, } @@ -25,6 +28,7 @@ impl Default for Code { offset_y: 0.0, size_pixels: 400.0, background: Color32::from_rgb(230, 230, 230), + alpha: 1.0, code: String::from(r#"println!("Hello world!");"#), } } @@ -35,17 +39,15 @@ impl Code { pub fn show(&self, ctx: &Context) { Window::new(self.name) .title_bar(false) - //.frame(Frame::none()) + .frame(Frame::window(&set_style_alpha(&ctx.style(), self.alpha))) .anchor( Align2::CENTER_CENTER, Vec2::new(self.offset_x, self.offset_y), ) .fixed_size(Vec2::splat(self.size_pixels)) .show(ctx, |ui| { - //let (_id, _rect) = ui.allocate_space(Vec2::splat(self.size_pixels)); - + // Syntax coloring code adapted from: https://github.com/emilk/egui/blob/master/egui_demo_lib/src/apps/demo/code_editor.rs let theme = egui_demo_lib::syntax_highlighting::CodeTheme::light(); - let mut layouter = |ui: &egui::Ui, string: &str, wrap_width: f32| { let mut layout_job = egui_demo_lib::syntax_highlighting::highlight( ui.ctx(), @@ -53,6 +55,9 @@ impl Code { string, "rs", ); + for section in &mut layout_job.sections { + set_alpha_in_place(&mut section.format.color, self.alpha); + } layout_job.wrap_width = wrap_width; ui.fonts().layout_job(layout_job) }; @@ -60,7 +65,6 @@ impl Code { egui::TextEdit::multiline(&mut self.code.as_str()) .font(egui::TextStyle::Monospace) // Only affects cursor. .code_editor() - //.desired_rows(10) .lock_focus(true) .desired_width(f32::INFINITY) .layouter(&mut layouter) @@ -68,3 +72,16 @@ impl Code { }); } } + +/// Converts Rust code to pseudocode. +/// +/// Note: Must use non-idiomatic `return x;` instead of just `x`. +pub fn pseudocode(code: &str) -> String { + code.replace("pub ", "") + .replace("fn ", "function ") + .replace("let ", "") + .replace("mut ", "") + .replace(": f32", "") + .replace(" -> bool", "") + .replace(";\n", "\n") +} diff --git a/src/component/grid.rs b/src/component/grid.rs index fe1144e..499e410 100644 --- a/src/component/grid.rs +++ b/src/component/grid.rs @@ -1,3 +1,5 @@ +use crate::color::{set_alpha, set_style_alpha}; +use crate::egui::Frame; use eframe::egui::{Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke, Vec2, Window}; use eframe::emath; use eframe::epaint::RectShape; @@ -20,6 +22,8 @@ pub struct Grid { pub stroke_width: f32, /// Background color. Transparent disables background. pub background: Color32, + /// Transparency of entire widget. + pub alpha: f32, /// Fill color of each pixel (use getters and setters). fill: Vec<Color32>, } @@ -35,6 +39,7 @@ impl Default for Grid { stroke: Color32::GRAY, stroke_width: 1.0, background: Color32::from_rgb(230, 230, 230), + alpha: 1.0, fill: Vec::new(), } } @@ -101,7 +106,7 @@ impl Grid { pub fn show(&self, ctx: &Context) { Window::new(self.name) .title_bar(false) - //.frame(Frame::none()) + .frame(Frame::window(&set_style_alpha(&ctx.style(), self.alpha))) .anchor( Align2::CENTER_CENTER, Vec2::new(self.offset_x, self.offset_y), @@ -114,37 +119,41 @@ impl Grid { rect, ); - if self.background.a() > 0 { + let background = set_alpha(self.background, self.alpha); + if background.a() > 0 { ui.painter().add(Shape::Rect(RectShape::filled( to_screen.transform_rect(Rect::from_x_y_ranges(0f32..=1f32, 0f32..=1f32)), Rounding::none(), - self.background, + background, ))); } let cell_width = 1.0 / self.size_cells as f32; + // Make sure neighboring cells overlap without gaps. + let tolerance = 0.001; for y in 0..self.size_cells { let y_coord = y as f32 / self.size_cells as f32; for x in 0..self.size_cells { let idx = self.index(x, y); if let Some(fill) = self.fill.get(idx) { + let fill = set_alpha(*fill, self.alpha); if fill.a() > 0 { let x_coord = x as f32 / self.size_cells as f32; - const TOLERANCE: f32 = 0.001; ui.painter().add(Shape::Rect(RectShape::filled( to_screen.transform_rect(Rect::from_x_y_ranges( - x_coord - TOLERANCE..=x_coord + cell_width + TOLERANCE, - y_coord - TOLERANCE..=y_coord + cell_width + TOLERANCE, + x_coord - tolerance..=x_coord + cell_width + tolerance, + y_coord - tolerance..=y_coord + cell_width + tolerance, )), Rounding::none(), - *fill, + fill, ))); } } } } - if self.stroke.a() > 0 && self.stroke_width > 0.0 { + let stroke = set_alpha(self.stroke, self.alpha); + if stroke.a() > 0 && self.stroke_width > 0.0 { for c in 0..=self.size_cells { let coord = c as f32 / self.size_cells as f32; // Horizontal. @@ -153,7 +162,7 @@ impl Grid { to_screen * Pos2::new(0.0, coord), to_screen * Pos2::new(1.0, coord), ], - stroke: Stroke::new(self.stroke_width, self.stroke), + stroke: Stroke::new(self.stroke_width, stroke), }); // Vertical. ui.painter().add(Shape::LineSegment { @@ -161,7 +170,7 @@ impl Grid { to_screen * Pos2::new(coord, 0.0), to_screen * Pos2::new(coord, 1.0), ], - stroke: Stroke::new(self.stroke_width, self.stroke), + stroke: Stroke::new(self.stroke_width, stroke), }); } } diff --git a/src/fade_in.rs b/src/fade_in.rs new file mode 100644 index 0000000..a515e5c --- /dev/null +++ b/src/fade_in.rs @@ -0,0 +1,17 @@ +use crate::color::with_alpha; +use crate::egui::{InnerResponse, Ui}; + +const FADE_DURATION: f64 = 0.6; + +pub fn fade_in<R>( + ui: &mut Ui, + fade_start: f64, + add_contents: impl FnOnce(&mut Ui) -> R, +) -> InnerResponse<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 { + ui.ctx().request_repaint(); + } + with_alpha(ui, alpha, |ui| add_contents(ui)) +} diff --git a/src/main.rs b/src/main.rs index 8dd7e1c..82c522b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ #![feature(derive_default_enum)] #![feature(mixed_integer_ops)] +pub mod color; pub mod component; +pub mod fade_in; 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; @@ -32,10 +35,10 @@ fn main() { drag_and_drop_support: false, icon_data: None, initial_window_pos: None, - initial_window_size: None, - min_window_size: Some(size), + initial_window_size: Some(size), + min_window_size: None, max_window_size: Some(size), - resizable: false, + resizable: true, transparent: false, }; @@ -45,6 +48,7 @@ fn main() { pub struct Cartoon { slides: Vec<Box<dyn Slide>>, slide_index: usize, + transition_time: f64, } impl Default for Cartoon { @@ -52,6 +56,7 @@ impl Default for Cartoon { Self { slides: create_slides(), slide_index: 0, + transition_time: 0.0, } } } @@ -98,6 +103,8 @@ impl epi::App for Cartoon { } */ ctx.set_style(style); + + self.transition_time = ctx.input().time; } /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. @@ -128,6 +135,7 @@ impl epi::App for Cartoon { if new == 0 { self.slides = create_slides(); } + self.transition_time = ctx.input().time; ctx.request_repaint(); new } else { @@ -135,6 +143,7 @@ impl epi::App for Cartoon { } } else { ctx.request_repaint(); + self.transition_time = ctx.input().time; self.slide_index .checked_sub(1) .unwrap_or(self.slides.len() - 1) @@ -142,7 +151,9 @@ impl epi::App for Cartoon { } egui::CentralPanel::default().show(ctx, |ui| { - self.slides[self.slide_index].show(ui, ctx); + fade_in(ui, self.transition_time, |ui| { + self.slides[self.slide_index].show(ui); + }); }); } } diff --git a/src/slide.rs b/src/slide.rs index 0b69fef..b2f7a81 100644 --- a/src/slide.rs +++ b/src/slide.rs @@ -10,10 +10,12 @@ pub mod s7_mosaic; pub mod s8_conclusion; pub trait Slide { - fn show(&mut self, ui: &mut Ui, ctx: &Context); /// Returns whether "done." /// Repaint automatically requested. fn transition(&mut self, _ctx: &Context) -> bool { true } + + /// Renders slide into UI. + fn show(&mut self, ui: &mut Ui); } diff --git a/src/slide/s1_title.rs b/src/slide/s1_title.rs index acc37a5..947ecd2 100644 --- a/src/slide/s1_title.rs +++ b/src/slide/s1_title.rs @@ -1,4 +1,4 @@ -use crate::egui::{Align2, Context}; +use crate::egui::Align2; use crate::{ctx_img, img, Margin, Slide}; use eframe::egui; use eframe::egui::{Frame, Ui, Vec2, Window}; @@ -9,7 +9,7 @@ pub struct Title { } impl Slide for Title { - fn show(&mut self, ui: &mut Ui, ctx: &Context) { + fn show(&mut self, ui: &mut Ui) { if self.examples.is_empty() { self.examples = vec![ ctx_img!(ui.ctx(), "raymarching0.png"), @@ -28,7 +28,7 @@ impl Slide for Title { .title_bar(false) .resizable(false) .frame(Frame::window(&ui.style()).margin(Margin::same(5.0))) - .show(ctx, |ui| { + .show(ui.ctx(), |ui| { ui.image(example, Vec2::splat(128.0)); }); } @@ -39,7 +39,8 @@ impl Slide for Title { .resizable(false) .anchor(Align2::CENTER_CENTER, Vec2::ZERO) .default_width(400.0) - .show(ctx, |ui| { + .frame(Frame::window(ui.style())) + .show(ui.ctx(), |ui| { ui.vertical_centered(|ui| { ui.heading("Generative Art"); ui.label("By: Finn, Matthew, Nathan, Owen"); diff --git a/src/slide/s2_introduction.rs b/src/slide/s2_introduction.rs index 56753da..fe97817 100644 --- a/src/slide/s2_introduction.rs +++ b/src/slide/s2_introduction.rs @@ -1,19 +1,34 @@ -use crate::{Margin, Slide}; +use crate::{fade_in, Margin, Slide}; use eframe::egui::{Context, Frame, Ui}; #[derive(Default)] pub struct Introduction { - strengths: bool, + state: IntroductionState, +} + +#[derive(Default)] +enum IntroductionState { + #[default] + Weaknesses, + Strengths { + transition_time: f64, + }, } impl Slide for Introduction { - fn transition(&mut self, _ctx: &Context) -> bool { - let done = self.strengths; - self.strengths = true; - done + fn transition(&mut self, ctx: &Context) -> bool { + match self.state { + IntroductionState::Weaknesses => { + self.state = IntroductionState::Strengths { + transition_time: ctx.input().time, + }; + false + } + IntroductionState::Strengths { .. } => true, + } } - fn show(&mut self, ui: &mut Ui, _ctx: &Context) { + fn show(&mut self, ui: &mut Ui) { Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { ui.heading("Introduction to Artistic Algorithms"); ui.add_space(8.0); @@ -21,13 +36,16 @@ impl Slide for Introduction { ui.small(" ✖ Social context"); ui.small(" ✖ Human emotion"); ui.small(" ✖ Political commentary"); - if self.strengths { - ui.add_space(10.0); - ui.label("Strengths"); - ui.small(" ✔ Following rules"); - ui.small(" ✔ Performing computation"); - ui.small(" ✔ Harnessing chaos and randomness"); - ui.small(" ✔ Guided exploration"); + + if let IntroductionState::Strengths { transition_time } = &self.state { + fade_in(ui, *transition_time, |ui| { + ui.add_space(10.0); + ui.label("Strengths"); + ui.small(" ✔ Following rules"); + ui.small(" ✔ Performing computation"); + ui.small(" ✔ Harnessing chaos and randomness"); + ui.small(" ✔ Guided exploration"); + }); } }); } diff --git a/src/slide/s3_complexity.rs b/src/slide/s3_complexity.rs index c208e48..92e09ce 100644 --- a/src/slide/s3_complexity.rs +++ b/src/slide/s3_complexity.rs @@ -1,7 +1,7 @@ use crate::egui::{Context, Ui, Window}; use crate::Slide; use eframe::egui::text_edit::{CCursorRange, TextEditState}; -use eframe::egui::{Align2, Frame, TextEdit, Vec2}; +use eframe::egui::{Align2, Frame, TextEdit, Vec2, Widget}; use eframe::epaint::text::cursor::CCursor; #[derive(Default)] @@ -29,14 +29,14 @@ impl Slide for Complexity { } } - fn show(&mut self, _ui: &mut Ui, ctx: &Context) { + fn show(&mut self, ui: &mut Ui) { Window::new("complexity") .title_bar(false) .resizable(false) .frame(Frame::none()) .anchor(Align2::CENTER_CENTER, Vec2::ZERO) .fixed_size(Vec2::new(440.0, 100.0)) - .show(ctx, |ui| { + .show(ui.ctx(), |ui| { ui.ctx().request_repaint(); let time = ui.input().time; let time_delta = (time - self.last_time).min(1.0); @@ -81,13 +81,15 @@ impl Slide for Complexity { } } - let text_edit = ui.text_edit_singleline(&mut text.as_str()); + let text_edit = TextEdit::singleline(&mut text.as_str()) + .desired_width(f32::INFINITY) + .ui(ui); if state.ccursor_range().is_some() { text_edit.request_focus(); } else { text_edit.surrender_focus(); } - TextEdit::store_state(ctx, text_edit.id, state); + TextEdit::store_state(ui.ctx(), text_edit.id, state); }); }); } diff --git a/src/slide/s4_automata.rs b/src/slide/s4_automata.rs index 67b625c..5ba5385 100644 --- a/src/slide/s4_automata.rs +++ b/src/slide/s4_automata.rs @@ -2,7 +2,7 @@ use crate::component::grid::Grid; use crate::governor::Governor; use crate::Slide; use eframe::egui::style::Margin; -use eframe::egui::{Color32, Context, Frame, Ui}; +use eframe::egui::{Color32, Frame, Ui}; use rand::Rng; pub struct Automata { @@ -37,18 +37,18 @@ impl Default for Automata { } impl Slide for Automata { - fn show(&mut self, ui: &mut Ui, ctx: &Context) { + fn show(&mut self, ui: &mut Ui) { Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { ui.vertical_centered(|ui| { ui.heading("Cellular Automata"); }); }); - ctx.request_repaint(); - if self.governor.ready(ctx.input().time, 0.2) { + ui.ctx().request_repaint(); + if self.governor.ready(ui.ctx().input().time, 0.2) { self.life = conways_game_of_life(&self.life); } - self.life.show(ctx); + self.life.show(ui.ctx()); } } diff --git a/src/slide/s5_fractals.rs b/src/slide/s5_fractals.rs index cc58e7c..037baa4 100644 --- a/src/slide/s5_fractals.rs +++ b/src/slide/s5_fractals.rs @@ -1,52 +1,120 @@ -use crate::component::code::Code; +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::slide::s5_fractals::circle::circle; +use crate::slide::s5_fractals::mandelbrot::mandelbrot; +use crate::slide::s5_fractals::rectangle::rectangle; use crate::Slide; use eframe::egui::style::Margin; pub struct Fractals { + state: FractalsState, code: Code, grid: Grid, } +#[derive(Default)] +#[allow(unused)] +enum FractalsState { + #[default] + Before, + Grid { + fade_start: f64, + }, + Rectangle { + pixel: usize, + }, + Circle { + pixel: usize, + }, + Mandelbrot { + pixel: usize, + }, +} + impl Default for Fractals { fn default() -> Self { const HORIZONTAL_OFFSET: f32 = 275.0; let mut code = Code::default(); + code.name = "fractal_code"; + code.offset_x = -HORIZONTAL_OFFSET; - code.code = String::from( - r###" -fn mandelbrot(x0, y0) -> bool { - MAX = 512 - x = 0.0 - y = 0.0 - i = 0 - - while x^2 + y^2 <= 4 and i < MAX { - x_temp = x^2 - y^2 + x0 - y = 2.0 * x * y + y0 - x = x_temp - i += 1 - } + let mut grid = Grid::default(); + grid.name = "fractal_grid"; + grid.offset_x = HORIZONTAL_OFFSET; - return i == MAX + grid.size_cells = 256; + grid.stroke_width = 0.0; + + Self { + state: FractalsState::default(), + code, + grid, + } + } } -"###, - ); - code.offset_x = -HORIZONTAL_OFFSET; +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 { .. } => 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, + } + false + } - let mut grid = Grid::default(); + fn show(&mut self, ui: &mut Ui) { + Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { + ui.vertical_centered(|ui| { + ui.heading("Fractals"); + }); + }); - const RESOLUTION: usize = 256; + ui.ctx().request_repaint(); + + let algo: Box<dyn Fn(f32, f32) -> bool>; + + match self.state { + FractalsState::Before => { + return; + } + FractalsState::Grid { fade_start } => { + let elapsed = ui.ctx().input().time - fade_start; + let alpha = (elapsed * 2.0).min(1.0) as f32; + self.code.alpha = alpha; + self.grid.alpha = alpha; + algo = Box::new(|_, _| false); + } + FractalsState::Rectangle { .. } => { + self.code.code = pseudocode(include_str!("s5_fractals/rectangle.rs")); + algo = Box::new(rectangle); + } + FractalsState::Circle { .. } => { + self.code.code = pseudocode(include_str!("s5_fractals/circle.rs")); + algo = Box::new(circle); + } + FractalsState::Mandelbrot { .. } => { + self.code.code = pseudocode(include_str!("s5_fractals/mandelbrot.rs")); + algo = Box::new(mandelbrot); + } + } - grid.name = "fractal"; - grid.size_cells = RESOLUTION; - grid.stroke_width = 0.0; - grid.offset_x = HORIZONTAL_OFFSET; + const RESOLUTION: usize = 256; - for (gx, gy, c) in grid.iter_mut() { + for (gx, gy, c) in self.grid.iter_mut() { // 0 to 1. let nx = (gx as f32 + 0.5) / RESOLUTION as f32; let ny = (gy as f32 + 0.5) / RESOLUTION as f32; @@ -55,42 +123,14 @@ fn mandelbrot(x0, y0) -> bool { let x = nx * 4.0 - 2.0; let y = ny * 4.0 - 2.0; - *c = if mandelbrot(x, y) { + *c = if algo(x, y) { Color32::BLACK } else { Color32::TRANSPARENT }; } - Self { code, grid } - } -} - -impl Slide for Fractals { - fn show(&mut self, ui: &mut Ui, ctx: &Context) { - Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { - ui.vertical_centered(|ui| { - ui.heading("Fractals"); - }); - }); - - self.code.show(ctx); - self.grid.show(ctx); + self.code.show(ui.ctx()); + self.grid.show(ui.ctx()); } } - -fn mandelbrot(x0: f32, y0: f32) -> bool { - const MAX_ITERATIONS: usize = 512; - - let mut x = 0f32; - let mut y = 0f32; - let mut i = 0usize; - while x.powi(2) + y.powi(2) <= 2f32.powi(2) && i < MAX_ITERATIONS { - let x_tmp = x.powi(2) - y.powi(2) + x0; - y = 2.0 * x * y + y0; - x = x_tmp; - i += 1; - } - - i == MAX_ITERATIONS -} diff --git a/src/slide/s5_fractals/circle.rs b/src/slide/s5_fractals/circle.rs new file mode 100644 index 0000000..2ede0c0 --- /dev/null +++ b/src/slide/s5_fractals/circle.rs @@ -0,0 +1,3 @@ +pub fn circle(x: f32, y: f32) -> bool { + return x * x + y * y <= 3.0; +} diff --git a/src/slide/s5_fractals/mandelbrot.rs b/src/slide/s5_fractals/mandelbrot.rs new file mode 100644 index 0000000..ffa1715 --- /dev/null +++ b/src/slide/s5_fractals/mandelbrot.rs @@ -0,0 +1,15 @@ +pub fn mandelbrot(x: f32, y: f32) -> bool { + let max = 512; + let mut x1 = 0.0; + let mut y1 = 0.0; + let mut i = 0; + + while x1 * x1 + y1 * y1 <= 4.0 && i < max { + let x_tmp = x1 * x1 - y1 * y1 + x; + y1 = 2.0 * x1 * y1 + y; + x1 = x_tmp; + i += 1; + } + + return i == max; +} diff --git a/src/slide/s5_fractals/rectangle.rs b/src/slide/s5_fractals/rectangle.rs new file mode 100644 index 0000000..8c6978f --- /dev/null +++ b/src/slide/s5_fractals/rectangle.rs @@ -0,0 +1,3 @@ +pub fn rectangle(x: f32, y: f32) -> bool { + return -1.5 < x && x < 1.5 && -1.0 < y && y < 1.0; +} diff --git a/src/slide/s6_computation.rs b/src/slide/s6_computation.rs index 8336531..68a4a05 100644 --- a/src/slide/s6_computation.rs +++ b/src/slide/s6_computation.rs @@ -1,4 +1,4 @@ -use crate::egui::{Context, Frame, Ui}; +use crate::egui::{Frame, Ui}; use crate::Slide; use eframe::egui::style::Margin; @@ -6,7 +6,7 @@ use eframe::egui::style::Margin; pub struct Computation {} impl Slide for Computation { - fn show(&mut self, ui: &mut Ui, _ctx: &Context) { + fn show(&mut self, ui: &mut Ui) { Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { ui.heading("More Computation-based Art"); ui.add_space(8.0); diff --git a/src/slide/s7_mosaic.rs b/src/slide/s7_mosaic.rs index d34e839..36241f1 100644 --- a/src/slide/s7_mosaic.rs +++ b/src/slide/s7_mosaic.rs @@ -1,4 +1,4 @@ -use crate::egui::{Context, Ui}; +use crate::egui::Ui; use crate::Slide; use eframe::egui::style::Margin; use eframe::egui::Frame; @@ -7,7 +7,7 @@ use eframe::egui::Frame; pub struct Mosaic {} impl Slide for Mosaic { - fn show(&mut self, ui: &mut Ui, _ctx: &Context) { + fn show(&mut self, ui: &mut Ui) { Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { ui.heading("Going Further"); ui.add_space(8.0); diff --git a/src/slide/s8_conclusion.rs b/src/slide/s8_conclusion.rs index ea44b7d..169ff0a 100644 --- a/src/slide/s8_conclusion.rs +++ b/src/slide/s8_conclusion.rs @@ -1,4 +1,4 @@ -use crate::egui::{Context, Ui}; +use crate::egui::Ui; use crate::Slide; use eframe::egui::style::Margin; use eframe::egui::Frame; @@ -7,7 +7,7 @@ use eframe::egui::Frame; pub struct Conclusion {} impl Slide for Conclusion { - fn show(&mut self, ui: &mut Ui, _ctx: &Context) { + fn show(&mut self, ui: &mut Ui) { Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { ui.heading("Conclusion"); ui.add_space(8.0); -- GitLab