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

Fractal WIP.

parent f358b465
No related branches found
No related tags found
No related merge requests found
pub mod code;
pub mod grid; pub mod grid;
use eframe::egui::{Align2, Color32, Context, Vec2, Window};
#[derive(Clone)]
pub struct Code {
/// Must be unique.
pub name: &'static str,
/// Horizontal offset from middle.
pub offset_x: f32,
/// Vertical offset from middle.
pub offset_y: f32,
/// How tall and wide.
pub size_pixels: f32,
/// Background color. Transparent disables background.
pub background: Color32,
/// Code.
pub code: String,
}
impl Default for Code {
fn default() -> Self {
Self {
name: "grid",
offset_x: 0.0,
offset_y: 0.0,
size_pixels: 400.0,
background: Color32::from_rgb(230, 230, 230),
code: String::from(r#"println!("Hello world!");"#),
}
}
}
impl Code {
/// Render to UI.
pub fn show(&self, ctx: &Context) {
Window::new(self.name)
.title_bar(false)
//.frame(Frame::none())
.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));
ui.code_editor(&mut self.code.as_str());
});
}
}
use eframe::egui::{ use eframe::egui::{Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke, Vec2, Window};
Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke, Ui, Vec2, Window,
};
use eframe::emath; use eframe::emath;
use eframe::epaint::RectShape; use eframe::epaint::RectShape;
#[derive(Clone)] #[derive(Clone)]
pub struct Grid { pub struct Grid {
/// Must be unique.
pub name: &'static str, pub name: &'static str,
/// Horizontal offset from middle.
pub offset_x: f32, pub offset_x: f32,
/// Vertical offset from middle.
pub offset_y: f32, pub offset_y: f32,
/// How many rows and columns.
pub size_cells: usize, pub size_cells: usize,
/// How tall and wide.
pub size_pixels: f32, pub size_pixels: f32,
/// Stroke color. Transparent disables stroke.
pub stroke: Color32, pub stroke: Color32,
/// Stroke width. Zero disables stroke.
pub stroke_width: f32, pub stroke_width: f32,
/// Background color. Transparent disables background.
pub background: Color32, pub background: Color32,
/// Fill color of each pixel (use getters and setters).
fill: Vec<Color32>, fill: Vec<Color32>,
} }
...@@ -34,12 +41,14 @@ impl Default for Grid { ...@@ -34,12 +41,14 @@ impl Default for Grid {
} }
impl Grid { impl Grid {
/// X and y coordinate of a cell to its index in [`self.fill`].
fn index(&self, x: usize, y: usize) -> usize { fn index(&self, x: usize, y: usize) -> usize {
assert!(x < self.size_cells); assert!(x < self.size_cells);
assert!(y < self.size_cells); assert!(y < self.size_cells);
x + y * self.size_cells x + y * self.size_cells
} }
/// Gets the fill color at a cell, which defaults to transparent.
pub fn fill(&self, x: usize, y: usize) -> Color32 { pub fn fill(&self, x: usize, y: usize) -> Color32 {
self.fill self.fill
.get(self.index(x, y)) .get(self.index(x, y))
...@@ -47,28 +56,56 @@ impl Grid { ...@@ -47,28 +56,56 @@ impl Grid {
.unwrap_or(Color32::TRANSPARENT) .unwrap_or(Color32::TRANSPARENT)
} }
pub fn set_fill(&mut self, x: usize, y: usize, fill: Color32) { /// Gets mutable reference to the fill color at a cell, which defaults to transparent.
pub fn fill_mut(&mut self, x: usize, y: usize) -> &mut Color32 {
let idx = self.index(x, y); let idx = self.index(x, y);
loop { loop {
match self.fill.get_mut(idx) { match self.fill.get_mut(idx) {
Some(f) => { // TODO: Fix lifetime issue.
*f = fill; Some(_) => break,
return; None => {
self.fill.push(Color32::TRANSPARENT);
} }
None => self.fill.push(Color32::TRANSPARENT),
} }
} }
self.fill.get_mut(idx).unwrap()
} }
/// Sets the fill color at a cell.
pub fn set_fill(&mut self, x: usize, y: usize, fill: Color32) {
*self.fill_mut(x, y) = fill;
}
/// Reset all cell fill colors to transparent.
pub fn clear_fill(&mut self) { pub fn clear_fill(&mut self) {
self.fill.clear(); self.fill.clear();
} }
pub fn iter(&self) -> impl Iterator<Item = (usize, usize, Color32)> + '_ {
(0..self.size_cells)
.flat_map(move |x| (0..self.size_cells).map(move |y| (x, y, self.fill(x, y))))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut Color32)> + '_ {
// Ensure fill is populated.
self.fill_mut(self.size_cells - 1, self.size_cells - 1);
self.fill
.iter_mut()
.take(self.size_cells.pow(2))
.enumerate()
.map(|(i, c)| (i % self.size_cells, i / self.size_cells, c))
}
/// Render to UI.
pub fn show(&self, ctx: &Context) { pub fn show(&self, ctx: &Context) {
Window::new(self.name) Window::new(self.name)
.title_bar(false) .title_bar(false)
//.frame(Frame::none()) //.frame(Frame::none())
.anchor(Align2::CENTER_CENTER, Vec2::ZERO) .anchor(
Align2::CENTER_CENTER,
Vec2::new(self.offset_x, self.offset_y),
)
.fixed_size(Vec2::splat(self.size_pixels)) .fixed_size(Vec2::splat(self.size_pixels))
.show(ctx, |ui| { .show(ctx, |ui| {
let (_id, rect) = ui.allocate_space(Vec2::splat(self.size_pixels)); let (_id, rect) = ui.allocate_space(Vec2::splat(self.size_pixels));
...@@ -93,10 +130,11 @@ impl Grid { ...@@ -93,10 +130,11 @@ impl Grid {
if let Some(fill) = self.fill.get(idx) { if let Some(fill) = self.fill.get(idx) {
if fill.a() > 0 { if fill.a() > 0 {
let x_coord = x as f32 / self.size_cells as f32; let x_coord = x as f32 / self.size_cells as f32;
const TOLERANCE: f32 = 0.001;
ui.painter().add(Shape::Rect(RectShape::filled( ui.painter().add(Shape::Rect(RectShape::filled(
to_screen.transform_rect(Rect::from_x_y_ranges( to_screen.transform_rect(Rect::from_x_y_ranges(
x_coord..=x_coord + cell_width, x_coord - TOLERANCE..=x_coord + cell_width + TOLERANCE,
y_coord..=y_coord + cell_width, y_coord - TOLERANCE..=y_coord + cell_width + TOLERANCE,
)), )),
Rounding::none(), Rounding::none(),
*fill, *fill,
......
...@@ -9,15 +9,13 @@ pub mod slide; ...@@ -9,15 +9,13 @@ pub mod slide;
use crate::slide::s1_title::Title; use crate::slide::s1_title::Title;
use crate::slide::s2_introduction::Introduction; use crate::slide::s2_introduction::Introduction;
use crate::slide::s3_complexity::Complexity; use crate::slide::s3_complexity::Complexity;
use crate::slide::s4_conway::Conway; use crate::slide::s4_automata::Automata;
use crate::slide::s5_fractals::Fractals;
use crate::slide::Slide; use crate::slide::Slide;
use eframe::egui::style::{Margin, Widgets}; use eframe::egui::style::Margin;
use eframe::egui::{ use eframe::egui::{Key, Style, TextStyle, Visuals};
Align, Direction, FontFamily, Key, Layout, Pos2, Rect, Style, TextStyle, Vec2, Visuals,
};
use eframe::epi::{Frame, Storage}; use eframe::epi::{Frame, Storage};
use eframe::{egui, epi}; use eframe::{egui, epi};
use std::collections::VecDeque;
fn main() { fn main() {
let app = Cartoon::default(); let app = Cartoon::default();
...@@ -60,7 +58,8 @@ fn create_slides() -> Vec<Box<dyn Slide>> { ...@@ -60,7 +58,8 @@ fn create_slides() -> Vec<Box<dyn Slide>> {
Box::new(Title::default()) as Box<dyn Slide>, Box::new(Title::default()) as Box<dyn Slide>,
Box::new(Introduction::default()), Box::new(Introduction::default()),
Box::new(Complexity::default()), Box::new(Complexity::default()),
Box::new(Conway::default()), Box::new(Automata::default()),
Box::new(Fractals::default()),
] ]
} }
...@@ -90,7 +89,7 @@ impl epi::App for Cartoon { ...@@ -90,7 +89,7 @@ impl epi::App for Cartoon {
} }
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
fn update(&mut self, ctx: &egui::Context, frame: &epi::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
// For inspiration and more examples, go to https://emilk.github.io/egui // For inspiration and more examples, go to https://emilk.github.io/egui
/* /*
......
use eframe::egui::{Context, Ui}; use eframe::egui::{Context, Ui};
use eframe::{egui, epi};
pub mod s1_title; pub mod s1_title;
pub mod s2_introduction; pub mod s2_introduction;
pub mod s3_complexity; pub mod s3_complexity;
pub mod s4_conway; pub mod s4_automata;
pub mod s5_fractals;
pub trait Slide { pub trait Slide {
fn show(&mut self, ui: &mut Ui, ctx: &Context); fn show(&mut self, ui: &mut Ui, ctx: &Context);
/// Returns whether "done." /// Returns whether "done."
/// Repaint automatically requested. /// Repaint automatically requested.
fn transition(&mut self, ctx: &Context) -> bool { fn transition(&mut self, _ctx: &Context) -> bool {
true true
} }
} }
use crate::egui::{Align2, Context}; use crate::egui::{Align2, Context};
use crate::{ctx_img, img, Margin, Slide}; use crate::{ctx_img, img, Margin, Slide};
use eframe::egui; use eframe::egui;
use eframe::egui::{Align, Color32, Frame, Layout, ScrollArea, Style, Ui, Vec2, Window}; use eframe::egui::{Frame, Ui, Vec2, Window};
use rand::Rng;
#[derive(Default)] #[derive(Default)]
pub struct Title { pub struct Title {
......
use crate::{Margin, Slide}; use crate::{Margin, Slide};
use eframe::egui::{Context, Frame, Ui}; use eframe::egui::{Context, Frame, Ui};
use eframe::{egui, epi};
#[derive(Default)] #[derive(Default)]
pub struct Introduction { pub struct Introduction {
...@@ -8,7 +7,7 @@ pub struct Introduction { ...@@ -8,7 +7,7 @@ pub struct Introduction {
} }
impl Slide for Introduction { impl Slide for Introduction {
fn transition(&mut self, ctx: &Context) -> bool { fn transition(&mut self, _ctx: &Context) -> bool {
let done = self.strengths; let done = self.strengths;
self.strengths = true; self.strengths = true;
done done
......
use crate::egui::{Context, Ui, Window}; use crate::egui::{Context, Ui, Window};
use crate::{Key, Slide}; use crate::Slide;
use eframe::egui::text_edit::{CCursorRange, CursorRange, TextEditState}; use eframe::egui::text_edit::{CCursorRange, TextEditState};
use eframe::egui::{Align, Align2, Frame, Layout, TextEdit, Vec2}; use eframe::egui::{Align2, Frame, TextEdit, Vec2};
use eframe::epaint::text::cursor::{CCursor, Cursor}; use eframe::epaint::text::cursor::CCursor;
use std::ops::RangeInclusive;
#[derive(Default)] #[derive(Default)]
pub struct Complexity { pub struct Complexity {
...@@ -21,7 +20,7 @@ enum ComplexityState { ...@@ -21,7 +20,7 @@ enum ComplexityState {
} }
impl Slide for Complexity { impl Slide for Complexity {
fn transition(&mut self, ctx: &Context) -> bool { fn transition(&mut self, _ctx: &Context) -> bool {
if self.state == ComplexityState::Before { if self.state == ComplexityState::Before {
self.state = ComplexityState::Selecting(0); self.state = ComplexityState::Selecting(0);
false false
...@@ -30,7 +29,7 @@ impl Slide for Complexity { ...@@ -30,7 +29,7 @@ impl Slide for Complexity {
} }
} }
fn show(&mut self, ui: &mut Ui, ctx: &Context) { fn show(&mut self, _ui: &mut Ui, ctx: &Context) {
Window::new("complexity") Window::new("complexity")
.title_bar(false) .title_bar(false)
.resizable(false) .resizable(false)
......
...@@ -2,19 +2,18 @@ use crate::component::grid::Grid; ...@@ -2,19 +2,18 @@ use crate::component::grid::Grid;
use crate::governor::Governor; use crate::governor::Governor;
use crate::Slide; use crate::Slide;
use eframe::egui::style::Margin; use eframe::egui::style::Margin;
use eframe::egui::{Align2, Color32, Context, Frame, Pos2, Rect, Shape, Stroke, Ui, Vec2, Window}; use eframe::egui::{Color32, Context, Frame, Ui};
use eframe::emath;
use rand::Rng; use rand::Rng;
pub struct Conway { pub struct Automata {
life: Grid, life: Grid,
governor: Governor, governor: Governor,
} }
impl Default for Conway { impl Default for Automata {
fn default() -> Self { fn default() -> Self {
let mut life = Grid::default(); let mut life = Grid::default();
life.name = "life";
for y in 0..life.size_cells { for y in 0..life.size_cells {
for x in 0..life.size_cells { for x in 0..life.size_cells {
if rand::thread_rng().gen_bool(0.5) { if rand::thread_rng().gen_bool(0.5) {
...@@ -37,7 +36,7 @@ impl Default for Conway { ...@@ -37,7 +36,7 @@ impl Default for Conway {
} }
} }
impl Slide for Conway { impl Slide for Automata {
fn show(&mut self, ui: &mut Ui, ctx: &Context) { fn show(&mut self, ui: &mut Ui, ctx: &Context) {
Frame::none().margin(Margin::same(20.0)).show(ui, |ui| { Frame::none().margin(Margin::same(20.0)).show(ui, |ui| {
ui.vertical_centered(|ui| { ui.vertical_centered(|ui| {
...@@ -47,13 +46,13 @@ impl Slide for Conway { ...@@ -47,13 +46,13 @@ impl Slide for Conway {
ctx.request_repaint(); ctx.request_repaint();
if self.governor.ready(ctx.input().time, 0.2) { if self.governor.ready(ctx.input().time, 0.2) {
self.life = conway(&self.life); self.life = conways_game_of_life(&self.life);
} }
self.life.show(ctx); self.life.show(ctx);
} }
} }
fn conway(grid: &Grid) -> Grid { fn conways_game_of_life(grid: &Grid) -> Grid {
let mut new = grid.clone(); let mut new = grid.clone();
new.clear_fill(); new.clear_fill();
......
use crate::component::code::Code;
use crate::component::grid::Grid;
use crate::egui::{Color32, Context, Frame, Ui};
use crate::Slide;
use eframe::egui::style::Margin;
pub struct Fractals {
code: Code,
grid: Grid,
}
impl Default for Fractals {
fn default() -> Self {
const HORIZONTAL_OFFSET: f32 = 275.0;
let mut code = Code::default();
code.code = String::from(
r###"
fn mandelbrot(x0, y0) -> color {
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
}
if i == MAX {
return black;
} else {
return white;
}
}
"###,
);
code.offset_x = -HORIZONTAL_OFFSET;
let mut grid = Grid::default();
const RESOLUTION: usize = 256;
grid.name = "fractal";
grid.size_cells = RESOLUTION;
grid.stroke_width = 0.0;
grid.offset_x = HORIZONTAL_OFFSET;
for (gx, gy, c) in 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;
// -4 to 4.
let x = nx * 4.0 - 2.0;
let y = ny * 4.0 - 2.0;
*c = if mandelbrot(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);
}
}
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
}
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