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;
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::{
Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke, Ui, Vec2, Window,
};
use eframe::egui::{Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke, Vec2, Window};
use eframe::emath;
use eframe::epaint::RectShape;
#[derive(Clone)]
pub struct Grid {
/// Must be unique.
pub name: &'static str,
/// Horizontal offset from middle.
pub offset_x: f32,
/// Vertical offset from middle.
pub offset_y: f32,
/// How many rows and columns.
pub size_cells: usize,
/// How tall and wide.
pub size_pixels: f32,
/// Stroke color. Transparent disables stroke.
pub stroke: Color32,
/// Stroke width. Zero disables stroke.
pub stroke_width: f32,
/// Background color. Transparent disables background.
pub background: Color32,
/// Fill color of each pixel (use getters and setters).
fill: Vec<Color32>,
}
......@@ -34,12 +41,14 @@ impl Default for Grid {
}
impl Grid {
/// X and y coordinate of a cell to its index in [`self.fill`].
fn index(&self, x: usize, y: usize) -> usize {
assert!(x < self.size_cells);
assert!(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 {
self.fill
.get(self.index(x, y))
......@@ -47,28 +56,56 @@ impl Grid {
.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);
loop {
match self.fill.get_mut(idx) {
Some(f) => {
*f = fill;
return;
// TODO: Fix lifetime issue.
Some(_) => break,
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) {
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) {
Window::new(self.name)
.title_bar(false)
//.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))
.show(ctx, |ui| {
let (_id, rect) = ui.allocate_space(Vec2::splat(self.size_pixels));
......@@ -93,10 +130,11 @@ impl Grid {
if let Some(fill) = self.fill.get(idx) {
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..=x_coord + cell_width,
y_coord..=y_coord + cell_width,
x_coord - TOLERANCE..=x_coord + cell_width + TOLERANCE,
y_coord - TOLERANCE..=y_coord + cell_width + TOLERANCE,
)),
Rounding::none(),
*fill,
......
......@@ -9,15 +9,13 @@ pub mod slide;
use crate::slide::s1_title::Title;
use crate::slide::s2_introduction::Introduction;
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 eframe::egui::style::{Margin, Widgets};
use eframe::egui::{
Align, Direction, FontFamily, Key, Layout, Pos2, Rect, Style, TextStyle, Vec2, Visuals,
};
use eframe::egui::style::Margin;
use eframe::egui::{Key, Style, TextStyle, Visuals};
use eframe::epi::{Frame, Storage};
use eframe::{egui, epi};
use std::collections::VecDeque;
fn main() {
let app = Cartoon::default();
......@@ -60,7 +58,8 @@ fn create_slides() -> Vec<Box<dyn Slide>> {
Box::new(Title::default()) as Box<dyn Slide>,
Box::new(Introduction::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 {
}
/// 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
/*
......
use eframe::egui::{Context, Ui};
use eframe::{egui, epi};
pub mod s1_title;
pub mod s2_introduction;
pub mod s3_complexity;
pub mod s4_conway;
pub mod s4_automata;
pub mod s5_fractals;
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 {
fn transition(&mut self, _ctx: &Context) -> bool {
true
}
}
use crate::egui::{Align2, Context};
use crate::{ctx_img, img, Margin, Slide};
use eframe::egui;
use eframe::egui::{Align, Color32, Frame, Layout, ScrollArea, Style, Ui, Vec2, Window};
use rand::Rng;
use eframe::egui::{Frame, Ui, Vec2, Window};
#[derive(Default)]
pub struct Title {
......
use crate::{Margin, Slide};
use eframe::egui::{Context, Frame, Ui};
use eframe::{egui, epi};
#[derive(Default)]
pub struct Introduction {
......@@ -8,7 +7,7 @@ pub struct Introduction {
}
impl Slide for Introduction {
fn transition(&mut self, ctx: &Context) -> bool {
fn transition(&mut self, _ctx: &Context) -> bool {
let done = self.strengths;
self.strengths = true;
done
......
use crate::egui::{Context, Ui, Window};
use crate::{Key, Slide};
use eframe::egui::text_edit::{CCursorRange, CursorRange, TextEditState};
use eframe::egui::{Align, Align2, Frame, Layout, TextEdit, Vec2};
use eframe::epaint::text::cursor::{CCursor, Cursor};
use std::ops::RangeInclusive;
use crate::Slide;
use eframe::egui::text_edit::{CCursorRange, TextEditState};
use eframe::egui::{Align2, Frame, TextEdit, Vec2};
use eframe::epaint::text::cursor::CCursor;
#[derive(Default)]
pub struct Complexity {
......@@ -21,7 +20,7 @@ enum ComplexityState {
}
impl Slide for Complexity {
fn transition(&mut self, ctx: &Context) -> bool {
fn transition(&mut self, _ctx: &Context) -> bool {
if self.state == ComplexityState::Before {
self.state = ComplexityState::Selecting(0);
false
......@@ -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")
.title_bar(false)
.resizable(false)
......
......@@ -2,19 +2,18 @@ use crate::component::grid::Grid;
use crate::governor::Governor;
use crate::Slide;
use eframe::egui::style::Margin;
use eframe::egui::{Align2, Color32, Context, Frame, Pos2, Rect, Shape, Stroke, Ui, Vec2, Window};
use eframe::emath;
use eframe::egui::{Color32, Context, Frame, Ui};
use rand::Rng;
pub struct Conway {
pub struct Automata {
life: Grid,
governor: Governor,
}
impl Default for Conway {
impl Default for Automata {
fn default() -> Self {
let mut life = Grid::default();
life.name = "life";
for y in 0..life.size_cells {
for x in 0..life.size_cells {
if rand::thread_rng().gen_bool(0.5) {
......@@ -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) {
Frame::none().margin(Margin::same(20.0)).show(ui, |ui| {
ui.vertical_centered(|ui| {
......@@ -47,13 +46,13 @@ impl Slide for Conway {
ctx.request_repaint();
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);
}
}
fn conway(grid: &Grid) -> Grid {
fn conways_game_of_life(grid: &Grid) -> Grid {
let mut new = grid.clone();
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