transforms appear to work correctly
This commit is contained in:
5
TODO.adoc
Normal file
5
TODO.adoc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
= TODOs
|
||||||
|
|
||||||
|
- Improve deep zoom precision
|
||||||
|
* https://mathr.co.uk/blog/2021-05-14_deep_zoom_theory_and_practice.html)
|
||||||
|
|
||||||
10
src/lib.rs
10
src/lib.rs
@@ -24,7 +24,7 @@ pub struct Args {
|
|||||||
pub transform: Vec<String>
|
pub transform: Vec<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Float = f32;
|
pub type Float = f64;
|
||||||
pub type Point = Complex<Float>;
|
pub type Point = Complex<Float>;
|
||||||
use num::complex::Complex;
|
use num::complex::Complex;
|
||||||
pub type Transform = [(Point, Point); 3];
|
pub type Transform = [(Point, Point); 3];
|
||||||
@@ -56,7 +56,7 @@ pub struct Config {
|
|||||||
|
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
fn render_point(&self, (base, delta): (Point, Point)) -> f32 {
|
fn render_point(&self, (base, delta): (Point, Point)) -> Float {
|
||||||
const ESCAPE: Float = 2.;
|
const ESCAPE: Float = 2.;
|
||||||
const ESCAPE2: Float = ESCAPE * ESCAPE;
|
const ESCAPE2: Float = ESCAPE * ESCAPE;
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ impl Config {
|
|||||||
|(iter, val)|
|
|(iter, val)|
|
||||||
iter as Float + 1. - val.norm().ln().ln() / ESCAPE.ln()
|
iter as Float + 1. - val.norm().ln().ln() / ESCAPE.ln()
|
||||||
);
|
);
|
||||||
result as f32
|
result as Float
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_image(&self) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
|
pub fn render_image(&self) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
|
||||||
@@ -86,8 +86,8 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_palette(val: f32) -> image::Rgb<u8> {
|
pub fn map_palette(val: Float) -> image::Rgb<u8> {
|
||||||
let magnitude = f32::clamp(val * 255., 0., 255.) as u8;
|
let magnitude = Float::clamp(val * 255., 0., 255.) as u8;
|
||||||
image::Rgb([magnitude, magnitude, magnitude])
|
image::Rgb([magnitude, magnitude, magnitude])
|
||||||
}
|
}
|
||||||
pub fn main() -> anyhow::Result<()> {
|
pub fn main() -> anyhow::Result<()> {
|
||||||
|
|||||||
86
src/main.rs
86
src/main.rs
@@ -1,8 +1,9 @@
|
|||||||
// extern crate mandelia;
|
// extern crate mandelia;
|
||||||
use eframe::{CreationContext, Frame, Result};
|
use eframe::{CreationContext, Frame, Result};
|
||||||
use egui::Context;
|
use egui::Context;
|
||||||
|
use egui::load::SizedTexture;
|
||||||
use image::EncodableLayout;
|
use image::EncodableLayout;
|
||||||
|
use mandelia::Float;
|
||||||
fn main() -> eframe::Result {
|
fn main() -> eframe::Result {
|
||||||
// mandelia::main()
|
// mandelia::main()
|
||||||
|
|
||||||
@@ -69,17 +70,18 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_transform(&mut self, xf: mandelia::transform::Type) {
|
fn add_transform(&mut self, xf: mandelia::transform::Type) {
|
||||||
self.tx_mat *= mandelia::transform::MTransform::from(xf);
|
self.tx_mat = mandelia::transform::MTransform::from(xf) * self.tx_mat;
|
||||||
self.update_transform();
|
self.update_transform();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_adjuster(&mut self, ui: &mut egui::Ui, label: &str, mut action: impl FnMut(f32) -> mandelia::transform::Type) {
|
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) {
|
||||||
if ui.button("-").clicked() {
|
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.));
|
self.add_transform(action(-1.));
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.centered_and_justified(|ui| {ui.label(label);});
|
ui.centered_and_justified(|ui| {ui.label(label);});
|
||||||
if ui.button("+").clicked() {
|
if ui.button(format!("+ ({})", key_inc.symbol_or_name())).clicked() || up {
|
||||||
self.add_transform(action(1.));
|
self.add_transform(action(1.));
|
||||||
}
|
}
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
@@ -108,34 +110,82 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transform functions
|
||||||
|
fn mk_translation(axis: u8) -> impl Fn(Float) -> mandelia::transform::Type {
|
||||||
|
move |amt| mandelia::transform::Type::Translation(axis, amt / 100.)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_rotation(a0: u8, a1: u8) -> impl Fn(Float) -> mandelia::transform::Type {
|
||||||
|
move |amt| mandelia::transform::Type::Rotation(a0, a1, amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_scale(scale: Float) -> mandelia::transform::Type {
|
||||||
|
mandelia::transform::Type::Scale(1. + scale / 4.)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum JumpMode {
|
||||||
|
Mandelbrot,
|
||||||
|
Julia,
|
||||||
|
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 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::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| {
|
egui::SidePanel::left("nav").show(ctx, |ui| {
|
||||||
use mandelia::transform::Type;
|
use mandelia::transform::Type;
|
||||||
|
use egui::Key;
|
||||||
egui::Grid::new("nav_grid")
|
egui::Grid::new("nav_grid")
|
||||||
.num_columns(3)
|
.num_columns(3)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
self.mk_adjuster(ui, "base x", |delta| Type::Translation(0, delta));
|
self.mk_adjuster(ui, "base x", Key::A, Key::D, mk_translation(0));
|
||||||
self.mk_adjuster(ui, "base y", |delta| Type::Translation(1, delta));
|
self.mk_adjuster(ui, "base y", Key::S, Key::W, mk_translation(1));
|
||||||
self.mk_adjuster(ui, "delta x", |delta| Type::Translation(2, delta));
|
self.mk_adjuster(ui, "delta x", Key::J, Key::L, mk_translation(2));
|
||||||
self.mk_adjuster(ui, "delta y", |delta| Type::Translation(3, delta));
|
self.mk_adjuster(ui, "delta y", Key::K, Key::I, mk_translation(3));
|
||||||
|
|
||||||
self.mk_adjuster(ui, "scale", |scale| Type::Scale(scale));
|
self.mk_adjuster(ui, "scale", Key::OpenBracket, Key::CloseBracket, mk_scale);
|
||||||
|
|
||||||
self.mk_adjuster(ui, "xy", |delta| Type::Rotation(0, 1, delta));
|
self.mk_adjuster(ui, "xy", Key::Q, Key::E, mk_rotation(0, 1));
|
||||||
self.mk_adjuster(ui, "XY", |delta| Type::Rotation(2, 3, delta));
|
self.mk_adjuster(ui, "XY", Key::U, Key::O, mk_rotation(2, 3));
|
||||||
self.mk_adjuster(ui, "xX", |delta| Type::Rotation(0, 2, delta));
|
self.mk_adjuster(ui, "xX", Key::F, Key::G, mk_rotation(0, 2));
|
||||||
self.mk_adjuster(ui, "yY", |delta| Type::Rotation(1, 3, delta));
|
self.mk_adjuster(ui, "yY", Key::R, Key::T, mk_rotation(1, 3));
|
||||||
self.mk_adjuster(ui, "xY", |delta| Type::Rotation(0, 3, delta));
|
self.mk_adjuster(ui, "xY", Key::Z, Key::X, mk_rotation(0, 3));
|
||||||
self.mk_adjuster(ui, "Xy", |delta| Type::Rotation(1, 2, delta));
|
self.mk_adjuster(ui, "Xy", Key::N, Key::M, mk_rotation(1, 2));
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
let size = ui.available_size();
|
let base_size = ui.available_size();
|
||||||
|
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(&self.texture));
|
ui.add(egui::Image::from_texture(SizedTexture::new(&self.texture, base_size)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,10 @@ pub type MTransform = nalgebra::Matrix5<Float>;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Rotation(u8, u8, f32),
|
Rotation(u8, u8, Float),
|
||||||
Translation(u8, f32),
|
Translation(u8, Float),
|
||||||
CoordSwap(u8, u8),
|
CoordSwap(u8, u8),
|
||||||
Scale(f32),
|
Scale(Float),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Type> for MTransform {
|
impl From<Type> for MTransform {
|
||||||
@@ -43,7 +43,9 @@ impl From<Type> for MTransform {
|
|||||||
m
|
m
|
||||||
}
|
}
|
||||||
Type::Scale(delta) => {
|
Type::Scale(delta) => {
|
||||||
MTransform::identity() / delta
|
let mut xf = MTransform::identity() / delta;
|
||||||
|
xf[(4,4)] = 1.;
|
||||||
|
xf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +68,7 @@ fn parse_axis(c: char) -> Option<u8> {
|
|||||||
/// TODO: replace this with a nom parser
|
/// TODO: replace this with a nom parser
|
||||||
pub fn parse_step(s: &str) -> Option<Type> {
|
pub fn parse_step(s: &str) -> Option<Type> {
|
||||||
if let Some(r) = s.strip_prefix("z") {
|
if let Some(r) = s.strip_prefix("z") {
|
||||||
return f32::from_str_radix(r, 10).ok().map(Type::Scale);
|
return Float::from_str_radix(r, 10).ok().map(Type::Scale);
|
||||||
}
|
}
|
||||||
if s.len() < 2 {
|
if s.len() < 2 {
|
||||||
return None;
|
return None;
|
||||||
@@ -85,11 +87,11 @@ pub fn parse_step(s: &str) -> Option<Type> {
|
|||||||
if axis0 == axis1 {
|
if axis0 == axis1 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let val = f32::from_str_radix(chars.as_str().trim(), 10).ok()?;
|
let val = Float::from_str_radix(chars.as_str().trim(), 10).ok()?;
|
||||||
Type::Rotation(axis0, axis1, val)
|
Type::Rotation(axis0, axis1, val)
|
||||||
}
|
}
|
||||||
'0' ..= '9' => {
|
'0' ..= '9' => {
|
||||||
let val = f32::from_str_radix(chars.as_str().trim(), 10).ok()?;
|
let val = Float::from_str_radix(chars.as_str().trim(), 10).ok()?;
|
||||||
Type::Translation(axis0, val)
|
Type::Translation(axis0, val)
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
|
|||||||
Reference in New Issue
Block a user