egui: implement zooming to fit

This commit is contained in:
Mikolaj Wielgus 2024-09-11 16:03:18 +02:00
parent 89717f2b6e
commit 0239a49c73
4 changed files with 82 additions and 13 deletions

View File

@ -177,12 +177,16 @@ impl eframe::App for App {
self.history_channel.0.clone(), self.history_channel.0.clone(),
self.arc_mutex_maybe_invoker.clone(), self.arc_mutex_maybe_invoker.clone(),
&mut self.maybe_execute, &mut self.maybe_execute,
&mut self.viewport,
&mut self.maybe_overlay, &mut self.maybe_overlay,
&self.maybe_design, &self.maybe_design,
); );
self.update_state(ctx.input(|i| i.stable_dt)); 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(ref mut layers) = self.maybe_layers {
if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() { if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() {
layers.update(ctx, invoker.autorouter().board()); layers.update(ctx, invoker.autorouter().board());
@ -198,14 +202,6 @@ impl eframe::App for App {
&self.maybe_layers, &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)) { if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
ctx.send_viewport_cmd(egui::ViewportCommand::Close); ctx.send_viewport_cmd(egui::ViewportCommand::Close);
} }

View File

@ -14,13 +14,11 @@ impl Bottom {
ctx: &egui::Context, ctx: &egui::Context,
tr: &Translator, tr: &Translator,
viewport: &Viewport, viewport: &Viewport,
viewport_rect: &egui::Rect,
maybe_execute: &Option<ExecuteWithStatus>, maybe_execute: &Option<ExecuteWithStatus>,
) { ) {
egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| { egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
let latest_pos = viewport.transform.inverse() let latest_pos = viewport.transform.inverse()
* ctx.input(|i| i.pointer.latest_pos().unwrap_or_default()) * ctx.input(|i| i.pointer.latest_pos().unwrap_or_default());
- viewport_rect.size() / 2.0;
let mut message = String::from(""); let mut message = String::from("");

View File

@ -19,6 +19,7 @@ use crate::{
file_sender::FileSender, file_sender::FileSender,
overlay::Overlay, overlay::Overlay,
translator::Translator, translator::Translator,
viewport::Viewport,
}; };
pub struct Top { pub struct Top {
@ -56,6 +57,7 @@ impl Top {
history_sender: Sender<String>, history_sender: Sender<String>,
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>, arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
maybe_execute: &mut Option<ExecuteWithStatus>, maybe_execute: &mut Option<ExecuteWithStatus>,
viewport: &mut Viewport,
maybe_overlay: &mut Option<Overlay>, maybe_overlay: &mut Option<Overlay>,
maybe_design: &Option<SpecctraDesign>, maybe_design: &Option<SpecctraDesign>,
) -> Result<(), InvokerError> { ) -> Result<(), InvokerError> {
@ -145,10 +147,18 @@ impl Top {
redo.button(ctx, ui); redo.button(ctx, ui);
ui.separator(); ui.separator();
remove_bands.button(ctx, ui); remove_bands.button(ctx, ui);
}); });
ui.menu_button(tr.text("menu-view"), |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_ratsnest, tr.text("show-ratsnest"));
ui.checkbox(&mut self.show_navmesh, tr.text("show-navmesh")); ui.checkbox(&mut self.show_navmesh, tr.text("show-navmesh"));
ui.checkbox(&mut self.show_bboxes, tr.text("show-bboxes")); ui.checkbox(&mut self.show_bboxes, tr.text("show-bboxes"));

View File

@ -3,7 +3,7 @@ use petgraph::{
data::DataMap, data::DataMap,
visit::{EdgeRef, IntoEdgeReferences}, visit::{EdgeRef, IntoEdgeReferences},
}; };
use rstar::AABB; use rstar::{Envelope, AABB};
use topola::{ use topola::{
autorouter::invoker::{ autorouter::invoker::{
Command, ExecuteWithStatus, GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles, 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 struct Viewport {
pub transform: egui::emath::TSTransform, pub transform: egui::emath::TSTransform,
pub scheduled_zoom_to_fit: bool,
} }
impl Viewport { impl Viewport {
pub fn new() -> Self { pub fn new() -> Self {
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), 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<ExecuteWithStatus>, maybe_execute: &mut Option<ExecuteWithStatus>,
maybe_overlay: &mut Option<Overlay>, maybe_overlay: &mut Option<Overlay>,
maybe_layers: &Option<Layers>, maybe_layers: &Option<Layers>,
) -> 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<Invoker<SpecctraMesadata>>,
maybe_execute: &mut Option<ExecuteWithStatus>,
maybe_overlay: &mut Option<Overlay>,
maybe_layers: &Option<Layers>,
) -> egui::Rect { ) -> egui::Rect {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
egui::Frame::canvas(ui.style()).show(ui, |ui| { egui::Frame::canvas(ui.style()).show(ui, |ui| {
@ -250,4 +276,43 @@ impl Viewport {
}) })
}).inner.inner }).inner.inner
} }
fn zoom_to_fit(
&mut self,
maybe_invoker: &mut Option<Invoker<SpecctraMesadata>>,
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;
}
} }