From 0239a49c73a230e3ecf184287fe0e4bbb2c95859 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Wed, 11 Sep 2024 16:03:18 +0200 Subject: [PATCH] egui: implement zooming to fit --- src/bin/topola-egui/app.rs | 12 ++---- src/bin/topola-egui/bottom.rs | 4 +- src/bin/topola-egui/top.rs | 10 +++++ src/bin/topola-egui/viewport.rs | 69 ++++++++++++++++++++++++++++++++- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 0f6a9a8..6b4142e 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -177,12 +177,16 @@ impl eframe::App for App { self.history_channel.0.clone(), self.arc_mutex_maybe_invoker.clone(), &mut self.maybe_execute, + &mut self.viewport, &mut self.maybe_overlay, &self.maybe_design, ); self.update_state(ctx.input(|i| i.stable_dt)); + self.bottom + .update(ctx, &self.translator, &self.viewport, &self.maybe_execute); + if let Some(ref mut layers) = self.maybe_layers { if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() { layers.update(ctx, invoker.autorouter().board()); @@ -198,14 +202,6 @@ impl eframe::App for App { &self.maybe_layers, ); - self.bottom.update( - ctx, - &self.translator, - &self.viewport, - &viewport_rect, - &self.maybe_execute, - ); - if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { ctx.send_viewport_cmd(egui::ViewportCommand::Close); } diff --git a/src/bin/topola-egui/bottom.rs b/src/bin/topola-egui/bottom.rs index 0b3dd34..a93fe31 100644 --- a/src/bin/topola-egui/bottom.rs +++ b/src/bin/topola-egui/bottom.rs @@ -14,13 +14,11 @@ impl Bottom { ctx: &egui::Context, tr: &Translator, viewport: &Viewport, - viewport_rect: &egui::Rect, maybe_execute: &Option, ) { egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| { let latest_pos = viewport.transform.inverse() - * ctx.input(|i| i.pointer.latest_pos().unwrap_or_default()) - - viewport_rect.size() / 2.0; + * ctx.input(|i| i.pointer.latest_pos().unwrap_or_default()); let mut message = String::from(""); diff --git a/src/bin/topola-egui/top.rs b/src/bin/topola-egui/top.rs index b959950..dd713f6 100644 --- a/src/bin/topola-egui/top.rs +++ b/src/bin/topola-egui/top.rs @@ -19,6 +19,7 @@ use crate::{ file_sender::FileSender, overlay::Overlay, translator::Translator, + viewport::Viewport, }; pub struct Top { @@ -56,6 +57,7 @@ impl Top { history_sender: Sender, arc_mutex_maybe_invoker: Arc>>>, maybe_execute: &mut Option, + viewport: &mut Viewport, maybe_overlay: &mut Option, maybe_design: &Option, ) -> Result<(), InvokerError> { @@ -145,10 +147,18 @@ impl Top { redo.button(ctx, ui); ui.separator(); + remove_bands.button(ctx, ui); }); ui.menu_button(tr.text("menu-view"), |ui| { + ui.toggle_value( + &mut viewport.scheduled_zoom_to_fit, + tr.text("zoom-to-fit"), + ); + + ui.separator(); + ui.checkbox(&mut self.show_ratsnest, tr.text("show-ratsnest")); ui.checkbox(&mut self.show_navmesh, tr.text("show-navmesh")); ui.checkbox(&mut self.show_bboxes, tr.text("show-bboxes")); diff --git a/src/bin/topola-egui/viewport.rs b/src/bin/topola-egui/viewport.rs index fa0b704..0b6ae08 100644 --- a/src/bin/topola-egui/viewport.rs +++ b/src/bin/topola-egui/viewport.rs @@ -3,7 +3,7 @@ use petgraph::{ data::DataMap, visit::{EdgeRef, IntoEdgeReferences}, }; -use rstar::AABB; +use rstar::{Envelope, AABB}; use topola::{ autorouter::invoker::{ Command, ExecuteWithStatus, GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles, @@ -24,13 +24,14 @@ use crate::{app::execute, layers::Layers, overlay::Overlay, painter::Painter, to pub struct Viewport { pub transform: egui::emath::TSTransform, + pub scheduled_zoom_to_fit: bool, } impl Viewport { pub fn new() -> Self { Self { - //from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), transform: egui::emath::TSTransform::new([0.0, 0.0].into(), 0.01), + scheduled_zoom_to_fit: false, } } @@ -42,6 +43,31 @@ impl Viewport { maybe_execute: &mut Option, maybe_overlay: &mut Option, maybe_layers: &Option, + ) -> egui::Rect { + let viewport_rect = self.paint( + ctx, + top, + maybe_invoker, + maybe_execute, + maybe_overlay, + maybe_layers, + ); + + if self.scheduled_zoom_to_fit { + self.zoom_to_fit(maybe_invoker, &viewport_rect); + } + + viewport_rect + } + + pub fn paint( + &mut self, + ctx: &egui::Context, + top: &Top, + maybe_invoker: &mut Option>, + maybe_execute: &mut Option, + maybe_overlay: &mut Option, + maybe_layers: &Option, ) -> egui::Rect { egui::CentralPanel::default().show(ctx, |ui| { egui::Frame::canvas(ui.style()).show(ui, |ui| { @@ -250,4 +276,43 @@ impl Viewport { }) }).inner.inner } + + fn zoom_to_fit( + &mut self, + maybe_invoker: &mut Option>, + viewport_rect: &egui::Rect, + ) { + if self.scheduled_zoom_to_fit { + if let Some(invoker) = maybe_invoker { + let root_bbox = invoker + .autorouter() + .board() + .layout() + .drawing() + .rtree() + .root() + .envelope(); + + let root_bbox_width = root_bbox.upper()[0] - root_bbox.lower()[0]; + let root_bbox_height = root_bbox.upper()[1] - root_bbox.lower()[1]; + + if root_bbox_width / root_bbox_height + >= (viewport_rect.width() as f64) / (viewport_rect.height() as f64) + { + self.transform.scaling = viewport_rect.width() / root_bbox_width as f32; + } else { + self.transform.scaling = viewport_rect.height() / root_bbox_height as f32; + } + + self.transform.translation = egui::Vec2::new( + viewport_rect.center()[0] as f32, + viewport_rect.center()[1] as f32, + ) - (self.transform.scaling + * egui::Pos2::new(root_bbox.center()[0] as f32, -root_bbox.center()[1] as f32)) + .to_vec2(); + } + } + + self.scheduled_zoom_to_fit = false; + } }