Factored out crates for web version
This commit is contained in:
11
mandelia_ui/Cargo.toml
Normal file
11
mandelia_ui/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "mandelia_ui"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
mandelia_renderer = {path = "../mandelia_renderer"}
|
||||
anyhow = "1.0.98"
|
||||
egui = "0.31"
|
||||
eframe = { version = "0.31" }
|
||||
image = "0.25.6"
|
||||
172
mandelia_ui/src/lib.rs
Normal file
172
mandelia_ui/src/lib.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
use eframe::{CreationContext, Frame};
|
||||
use egui::Context;
|
||||
use image::EncodableLayout;
|
||||
use mandelia_renderer::Float;
|
||||
|
||||
pub struct App {
|
||||
tx_mat: mandelia_renderer::transform::MTransform,
|
||||
transform: mandelia_renderer::Transform,
|
||||
texture: egui::TextureHandle,
|
||||
last_render: RenderData,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Default)]
|
||||
struct RenderData {
|
||||
transform: mandelia_renderer::Transform,
|
||||
render_size: [usize; 2],
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(cc: &CreationContext) -> Self {
|
||||
let mut result = Self {
|
||||
transform: Default::default(),
|
||||
tx_mat: mandelia_renderer::transform::MTransform::identity(),
|
||||
texture: cc.egui_ctx.load_texture("rendering", egui::ColorImage::example(), egui::TextureOptions::LINEAR),
|
||||
last_render: Default::default(),
|
||||
};
|
||||
result.reset_transform();
|
||||
result
|
||||
}
|
||||
|
||||
pub fn reset_transform(&mut self) {
|
||||
self.tx_mat = mandelia_renderer::transform::MTransform::identity();
|
||||
self.update_transform();
|
||||
}
|
||||
|
||||
fn update_transform(&mut self) {
|
||||
use mandelia_renderer::Point;
|
||||
let mat = &self.tx_mat;
|
||||
let bx = Point::new(mat[(0,0)], mat[(0,1)]);
|
||||
let by = Point::new(mat[(1,0)], mat[(1,1)]);
|
||||
let bd = Point::new(mat[(4,0)], mat[(4,1)]);
|
||||
|
||||
let cx = Point::new(mat[(0,2)], mat[(0,3)]);
|
||||
let cy = Point::new(mat[(1,2)], mat[(1,3)]);
|
||||
let cd = Point::new(mat[(4,2)], mat[(4,3)]);
|
||||
|
||||
self.transform = [
|
||||
(bx, cx),
|
||||
(by, cy),
|
||||
(bd, cd),
|
||||
]
|
||||
}
|
||||
|
||||
fn add_transform(&mut self, xf: mandelia_renderer::transform::Type) {
|
||||
self.tx_mat = mandelia_renderer::transform::MTransform::from(xf) * self.tx_mat;
|
||||
self.update_transform();
|
||||
}
|
||||
|
||||
fn mk_adjuster(&mut self, ui: &mut egui::Ui, label: &str, key_dec: egui::Key, key_inc: egui::Key, action: impl Fn(Float) -> mandelia_renderer::transform::Type) {
|
||||
let (dn, up) = ui.ctx().input(|is| (is.key_pressed(key_dec), is.key_pressed(key_inc)));
|
||||
if ui.button(format!("- ({})", key_dec.symbol_or_name())).clicked() || dn {
|
||||
self.add_transform(action(-1.));
|
||||
}
|
||||
|
||||
ui.centered_and_justified(|ui| {ui.label(label);});
|
||||
if ui.button(format!("+ ({})", key_inc.symbol_or_name())).clicked() || up {
|
||||
self.add_transform(action(1.));
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
fn update_texture(&mut self, size: [usize; 2]) {
|
||||
let new_rdata = RenderData {
|
||||
transform: self.transform,
|
||||
render_size: size,
|
||||
};
|
||||
if self.last_render != new_rdata {
|
||||
self.last_render = new_rdata;
|
||||
let config = mandelia_renderer::Config {
|
||||
transform: self.transform,
|
||||
width: size[0] as u32,
|
||||
height: size[1] as u32,
|
||||
maxiter: 100,
|
||||
};
|
||||
|
||||
let img = config.render_image();
|
||||
|
||||
let image_data = egui::ColorImage::from_rgb([config.width as usize, config.height as usize], img.as_bytes());
|
||||
self.texture.set(image_data, egui::TextureOptions::LINEAR);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// transform functions
|
||||
fn mk_translation(axis: u8) -> impl Fn(Float) -> mandelia_renderer::transform::Type {
|
||||
move |amt| mandelia_renderer::transform::Type::Translation(axis, amt / 100.)
|
||||
}
|
||||
|
||||
fn mk_rotation(a0: u8, a1: u8) -> impl Fn(Float) -> mandelia_renderer::transform::Type {
|
||||
move |amt| mandelia_renderer::transform::Type::Rotation(a0, a1, amt)
|
||||
}
|
||||
|
||||
fn mk_scale(scale: Float) -> mandelia_renderer::transform::Type {
|
||||
mandelia_renderer::transform::Type::Scale(1. + scale / 4.)
|
||||
}
|
||||
|
||||
pub enum JumpMode {
|
||||
Mandelbrot,
|
||||
Julia,
|
||||
Origin,
|
||||
}
|
||||
|
||||
impl eframe::App for App {
|
||||
fn update(&mut self, ctx: &Context, _frame: &mut Frame) {
|
||||
if let Some(jump_mode) = ctx.input(|is| {
|
||||
if is.key_pressed(egui::Key::Num0) {
|
||||
Some(JumpMode::Origin)
|
||||
} else if is.key_pressed(egui::Key::Num1) {
|
||||
Some(JumpMode::Mandelbrot)
|
||||
} else if is.key_pressed(egui::Key::Num2) {
|
||||
Some(JumpMode::Julia)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
use mandelia_renderer::transform::Type;
|
||||
match jump_mode {
|
||||
JumpMode::Mandelbrot => {
|
||||
self.reset_transform();
|
||||
self.add_transform(Type::CoordSwap(0, 2));
|
||||
self.add_transform(Type::CoordSwap(1, 3));
|
||||
},
|
||||
JumpMode::Julia => {
|
||||
self.reset_transform();
|
||||
self.add_transform(Type::Translation(0, 0.31));
|
||||
self.add_transform(Type::Translation(1, 0.31));
|
||||
}
|
||||
JumpMode::Origin => {
|
||||
self.reset_transform();
|
||||
}
|
||||
}
|
||||
};
|
||||
egui::SidePanel::left("nav").show(ctx, |ui| {
|
||||
use egui::Key;
|
||||
egui::Grid::new("nav_grid")
|
||||
.num_columns(3)
|
||||
.show(ui, |ui| {
|
||||
self.mk_adjuster(ui, "base x", Key::A, Key::D, mk_translation(0));
|
||||
self.mk_adjuster(ui, "base y", Key::S, Key::W, mk_translation(1));
|
||||
self.mk_adjuster(ui, "delta x", Key::J, Key::L, mk_translation(2));
|
||||
self.mk_adjuster(ui, "delta y", Key::K, Key::I, mk_translation(3));
|
||||
|
||||
self.mk_adjuster(ui, "scale", Key::OpenBracket, Key::CloseBracket, mk_scale);
|
||||
|
||||
self.mk_adjuster(ui, "xy", Key::Q, Key::E, mk_rotation(0, 1));
|
||||
self.mk_adjuster(ui, "XY", Key::U, Key::O, mk_rotation(2, 3));
|
||||
self.mk_adjuster(ui, "xX", Key::F, Key::G, mk_rotation(0, 2));
|
||||
self.mk_adjuster(ui, "yY", Key::R, Key::T, mk_rotation(1, 3));
|
||||
self.mk_adjuster(ui, "xY", Key::Z, Key::X, mk_rotation(0, 3));
|
||||
self.mk_adjuster(ui, "Xy", Key::N, Key::M, mk_rotation(1, 2));
|
||||
})
|
||||
});
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let base_size = ui.available_size();
|
||||
let size = base_size * ui.pixels_per_point();
|
||||
let size = [size.x as usize, size.y as usize];
|
||||
self.update_texture(size);
|
||||
ui.add(egui::Image::from_texture(egui::load::SizedTexture::new(&self.texture, base_size)));
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user