Factored out crates for web version

This commit is contained in:
2025-07-03 09:00:37 +02:00
parent 79267ee5e9
commit df056b7b2c
9 changed files with 105 additions and 67 deletions

24
Cargo.lock generated
View File

@@ -1980,19 +1980,37 @@ dependencies = [
] ]
[[package]] [[package]]
name = "mandelia" name = "mandelia_native"
version = "0.1.0"
dependencies = [
"eframe",
"egui",
"mandelia_ui",
]
[[package]]
name = "mandelia_renderer"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"eframe",
"egui",
"image", "image",
"nalgebra", "nalgebra",
"num", "num",
"rayon", "rayon",
] ]
[[package]]
name = "mandelia_ui"
version = "0.1.0"
dependencies = [
"anyhow",
"eframe",
"egui",
"image",
"mandelia_renderer",
]
[[package]] [[package]]
name = "matrixmultiply" name = "matrixmultiply"
version = "0.3.10" version = "0.3.10"

View File

@@ -1,14 +1,7 @@
[package] [workspace]
name = "mandelia" resolver = "3"
version = "0.1.0" members = [
edition = "2024" "mandelia_renderer",
"mandelia_native",
[dependencies] "mandelia_ui"
image = { version = "0.25.6", features = ["png"] } ]
rayon = "1.10.0"
num = "0.4.3"
nalgebra = "0.33.2"
anyhow = "1.0.98"
clap = { version = "4.5.40", features = ["derive"] }
egui = "0.31"
eframe = { version = "0.31", features = ["wayland", "glow"] }

View File

@@ -0,0 +1,10 @@
[package]
name = "mandelia_native"
version = "0.1.0"
edition = "2024"
[dependencies]
mandelia_ui = { path = "../mandelia_ui" }
eframe = { version = "0.31", features = ["wayland", "glow"] }
egui = "0.31"

View File

@@ -0,0 +1,17 @@
fn main() -> eframe::Result {
// mandelia::main()
let options = eframe::NativeOptions {
..eframe::NativeOptions::default()
};
eframe::run_native(
"Mandelia",
options,
Box::new(|cc| {
Ok(Box::new(mandelia_ui::App::new(cc)))
})
)
}
// this is backend-independent

View File

@@ -0,0 +1,12 @@
[package]
name = "mandelia_renderer"
version = "0.1.0"
edition = "2024"
[dependencies]
image = { version = "0.25.6", features = ["png"] }
rayon = "1.10.0"
num = "0.4.3"
nalgebra = "0.33.2"
anyhow = "1.0.98"
clap = { version = "4.5.40", features = ["derive"] }

View File

@@ -1,10 +1,8 @@
//#![feature(portable_simd)] //#![feature(portable_simd)]
use clap::Parser;
use image::{ImageBuffer, Rgb};
use std::path::PathBuf; use std::path::PathBuf;
use clap::{Arg, Parser};
use image::{GrayImage, ImageBuffer, Luma, Rgb};
use image::buffer::ConvertBuffer;
use rayon::iter::ParallelIterator;
pub mod transform; pub mod transform;
@@ -60,7 +58,6 @@ impl Config {
const ESCAPE: Float = 2.; const ESCAPE: Float = 2.;
const ESCAPE2: Float = ESCAPE * ESCAPE; const ESCAPE2: Float = ESCAPE * ESCAPE;
let mut base = base;
let result = let result =
std::iter::successors(Some(base), |i| Some(*i * *i + delta)) std::iter::successors(Some(base), |i| Some(*i * *i + delta))
.take(self.maxiter) .take(self.maxiter)

View File

@@ -114,7 +114,6 @@ pub fn transform_to_coords(mat: &MTransform) -> super::Transform {
} }
pub fn parse_transforms<'a>(transforms: impl Iterator<Item=impl AsRef<str>>) ->anyhow::Result<super::Transform> { pub fn parse_transforms<'a>(transforms: impl Iterator<Item=impl AsRef<str>>) ->anyhow::Result<super::Transform> {
use super::Point;
let mat = transforms let mat = transforms
.map(|s| parse_step(s.as_ref()) .map(|s| parse_step(s.as_ref())
.ok_or_else(|| anyhow::anyhow!("Invalid step: {:?}", s.as_ref()))) .ok_or_else(|| anyhow::anyhow!("Invalid step: {:?}", s.as_ref())))

11
mandelia_ui/Cargo.toml Normal file
View 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"

View File

@@ -1,44 +1,26 @@
// extern crate mandelia; use eframe::{CreationContext, Frame};
use eframe::{CreationContext, Frame, Result};
use egui::Context; use egui::Context;
use egui::load::SizedTexture;
use image::EncodableLayout; use image::EncodableLayout;
use mandelia::Float; use mandelia_renderer::Float;
fn main() -> eframe::Result {
// mandelia::main()
let options = eframe::NativeOptions {
..eframe::NativeOptions::default()
};
eframe::run_native(
"Mandelia",
options,
Box::new(|cc| {
Ok(Box::new(App::new(cc)))
})
)
}
// this is backend-independent
pub struct App { pub struct App {
tx_mat: nalgebra::Matrix5<mandelia::Float>, tx_mat: mandelia_renderer::transform::MTransform,
transform: mandelia::Transform, transform: mandelia_renderer::Transform,
texture: egui::TextureHandle, texture: egui::TextureHandle,
last_render: RenderData, last_render: RenderData,
} }
#[derive(Copy, Clone, PartialEq, Debug, Default)] #[derive(Copy, Clone, PartialEq, Debug, Default)]
struct RenderData { struct RenderData {
transform: mandelia::Transform, transform: mandelia_renderer::Transform,
render_size: [usize; 2], render_size: [usize; 2],
} }
impl App { impl App {
fn new(cc: &CreationContext) -> Self { pub fn new(cc: &CreationContext) -> Self {
let mut result = Self { let mut result = Self {
transform: Default::default(), transform: Default::default(),
tx_mat: nalgebra::Matrix5::identity(), tx_mat: mandelia_renderer::transform::MTransform::identity(),
texture: cc.egui_ctx.load_texture("rendering", egui::ColorImage::example(), egui::TextureOptions::LINEAR), texture: cc.egui_ctx.load_texture("rendering", egui::ColorImage::example(), egui::TextureOptions::LINEAR),
last_render: Default::default(), last_render: Default::default(),
}; };
@@ -47,12 +29,12 @@ impl App {
} }
pub fn reset_transform(&mut self) { pub fn reset_transform(&mut self) {
self.tx_mat = nalgebra::Matrix5::identity(); self.tx_mat = mandelia_renderer::transform::MTransform::identity();
self.update_transform(); self.update_transform();
} }
fn update_transform(&mut self) { fn update_transform(&mut self) {
use mandelia::Point; use mandelia_renderer::Point;
let mat = &self.tx_mat; let mat = &self.tx_mat;
let bx = Point::new(mat[(0,0)], mat[(0,1)]); let bx = Point::new(mat[(0,0)], mat[(0,1)]);
let by = Point::new(mat[(1,0)], mat[(1,1)]); let by = Point::new(mat[(1,0)], mat[(1,1)]);
@@ -69,12 +51,12 @@ impl App {
] ]
} }
fn add_transform(&mut self, xf: mandelia::transform::Type) { fn add_transform(&mut self, xf: mandelia_renderer::transform::Type) {
self.tx_mat = mandelia::transform::MTransform::from(xf) * self.tx_mat; self.tx_mat = mandelia_renderer::transform::MTransform::from(xf) * self.tx_mat;
self.update_transform(); self.update_transform();
} }
fn mk_adjuster(&mut self, ui: &mut egui::Ui, label: &str, key_dec: egui::Key, key_inc: egui::Key, mut action: impl Fn(Float) -> mandelia::transform::Type) { 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))); 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 { if ui.button(format!("- ({})", key_dec.symbol_or_name())).clicked() || dn {
self.add_transform(action(-1.)); self.add_transform(action(-1.));
@@ -94,7 +76,7 @@ impl App {
}; };
if self.last_render != new_rdata { if self.last_render != new_rdata {
self.last_render = new_rdata; self.last_render = new_rdata;
let config = mandelia::Config { let config = mandelia_renderer::Config {
transform: self.transform, transform: self.transform,
width: size[0] as u32, width: size[0] as u32,
height: size[1] as u32, height: size[1] as u32,
@@ -111,26 +93,26 @@ impl App {
} }
// transform functions // transform functions
fn mk_translation(axis: u8) -> impl Fn(Float) -> mandelia::transform::Type { fn mk_translation(axis: u8) -> impl Fn(Float) -> mandelia_renderer::transform::Type {
move |amt| mandelia::transform::Type::Translation(axis, amt / 100.) move |amt| mandelia_renderer::transform::Type::Translation(axis, amt / 100.)
} }
fn mk_rotation(a0: u8, a1: u8) -> impl Fn(Float) -> mandelia::transform::Type { fn mk_rotation(a0: u8, a1: u8) -> impl Fn(Float) -> mandelia_renderer::transform::Type {
move |amt| mandelia::transform::Type::Rotation(a0, a1, amt) move |amt| mandelia_renderer::transform::Type::Rotation(a0, a1, amt)
} }
fn mk_scale(scale: Float) -> mandelia::transform::Type { fn mk_scale(scale: Float) -> mandelia_renderer::transform::Type {
mandelia::transform::Type::Scale(1. + scale / 4.) mandelia_renderer::transform::Type::Scale(1. + scale / 4.)
} }
enum JumpMode { pub enum JumpMode {
Mandelbrot, Mandelbrot,
Julia, Julia,
Origin, Origin,
} }
impl eframe::App for App { impl eframe::App for App {
fn update(&mut self, ctx: &Context, frame: &mut Frame) { fn update(&mut self, ctx: &Context, _frame: &mut Frame) {
if let Some(jump_mode) = ctx.input(|is| { if let Some(jump_mode) = ctx.input(|is| {
if is.key_pressed(egui::Key::Num0) { if is.key_pressed(egui::Key::Num0) {
Some(JumpMode::Origin) Some(JumpMode::Origin)
@@ -142,7 +124,7 @@ impl eframe::App for App {
None None
} }
}) { }) {
use mandelia::transform::Type; use mandelia_renderer::transform::Type;
match jump_mode { match jump_mode {
JumpMode::Mandelbrot => { JumpMode::Mandelbrot => {
self.reset_transform(); self.reset_transform();
@@ -160,7 +142,6 @@ impl eframe::App for App {
} }
}; };
egui::SidePanel::left("nav").show(ctx, |ui| { egui::SidePanel::left("nav").show(ctx, |ui| {
use mandelia::transform::Type;
use egui::Key; use egui::Key;
egui::Grid::new("nav_grid") egui::Grid::new("nav_grid")
.num_columns(3) .num_columns(3)
@@ -169,9 +150,9 @@ impl eframe::App for App {
self.mk_adjuster(ui, "base y", Key::S, Key::W, mk_translation(1)); 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 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, "delta y", Key::K, Key::I, mk_translation(3));
self.mk_adjuster(ui, "scale", Key::OpenBracket, Key::CloseBracket, mk_scale); 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::Q, Key::E, mk_rotation(0, 1));
self.mk_adjuster(ui, "XY", Key::U, Key::O, mk_rotation(2, 3)); 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, "xX", Key::F, Key::G, mk_rotation(0, 2));
@@ -185,7 +166,7 @@ impl eframe::App for App {
let size = base_size * ui.pixels_per_point(); let size = base_size * ui.pixels_per_point();
let size = [size.x as usize, size.y as usize]; let size = [size.x as usize, size.y as usize];
self.update_texture(size); self.update_texture(size);
ui.add(egui::Image::from_texture(SizedTexture::new(&self.texture, base_size))); ui.add(egui::Image::from_texture(egui::load::SizedTexture::new(&self.texture, base_size)));
}); });
} }
} }