From 5f7f8fe14ed1938452969f0d84bc64e7c8294f66 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Mon, 9 Mar 2026 23:04:40 +0100 Subject: [PATCH] feat(topola-egui): Add zooming to fit --- topola-egui/src/app.rs | 1 + topola-egui/src/displayer.rs | 13 +------- topola-egui/src/viewport.rs | 59 ++++++++++++++++++++++++++++++------ 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/topola-egui/src/app.rs b/topola-egui/src/app.rs index 6f1f4ef..17ad969 100644 --- a/topola-egui/src/app.rs +++ b/topola-egui/src/app.rs @@ -54,6 +54,7 @@ impl App { Board::from_specctra(data.unwrap()), &self.translator, )); + self.viewport.scheduled_zoom_to_fit = true; } } diff --git a/topola-egui/src/displayer.rs b/topola-egui/src/displayer.rs index 56c37b5..0414ef7 100644 --- a/topola-egui/src/displayer.rs +++ b/topola-egui/src/displayer.rs @@ -43,18 +43,7 @@ impl Displayer { y: p[1] as f32, }) .collect::>(), - egui::Stroke::new(20.0 / viewport.scale_factor(), egui::Color32::WHITE), + egui::Stroke::new(5.0 / viewport.scale_factor(), egui::Color32::WHITE), ); - ui.painter().line( - vec![ - Pos2::new(0.0, 0.0), - Pos2::new(100.0, 100.0), - Pos2::new(100.0, 500.0), - ], - egui::Stroke::new(2.0, egui::Color32::GOLD), - ); - ui.painter() - .circle_filled(egui::pos2(0.0, 0.0), 2.0, egui::Color32::RED); - //workspace.board.layout().boundary() } } diff --git a/topola-egui/src/viewport.rs b/topola-egui/src/viewport.rs index 242993b..ad65713 100644 --- a/topola-egui/src/viewport.rs +++ b/topola-egui/src/viewport.rs @@ -2,24 +2,22 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use egui::Pos2; + use crate::{displayer::Displayer, workspace::Workspace}; pub struct Viewport { pub scene_rect: egui::Rect, pub ref_scene_rect: egui::Rect, + pub scheduled_zoom_to_fit: bool, } impl Viewport { pub fn new() -> Self { Self { - scene_rect: egui::Rect::from_min_max( - egui::pos2(-10000.0, -10000.0), - egui::pos2(10000.0, 10000.0), - ), - ref_scene_rect: egui::Rect::from_min_max( - egui::pos2(-10000.0, -10000.0), - egui::pos2(10000.0, 10000.0), - ), + scene_rect: egui::Rect::from_min_max(egui::pos2(-1.0, -1.0), egui::pos2(1.0, 1.0)), + ref_scene_rect: egui::Rect::from_min_max(egui::pos2(-1.0, -1.0), egui::pos2(1.0, 1.0)), + scheduled_zoom_to_fit: false, } } @@ -30,16 +28,59 @@ impl Viewport { egui::Scene::new() .zoom_range(0.0001..=10000.0) .show(ui, &mut scene_rect, |ui| { - if let Some(workspace) = workspace { + if let Some(ref workspace) = workspace { let mut displayer = Displayer::new(); displayer.update(ctx, ui, &self, workspace); } }); self.scene_rect = scene_rect; + + if let Some(workspace) = workspace { + self.zoom_to_fit_if_scheduled(workspace); + } }); } + fn zoom_to_fit_if_scheduled(&mut self, workspace: &Workspace) { + if self.scheduled_zoom_to_fit { + self.scene_rect = Self::boundary_bounding_box(workspace); + self.ref_scene_rect = self.scene_rect.clone(); + } + + self.scheduled_zoom_to_fit = false; + } + + fn boundary_bounding_box(workspace: &Workspace) -> egui::Rect { + let first = workspace.board.layout().boundary()[0]; + + let mut min_x = first[0]; + let mut max_x = first[0]; + let mut min_y = first[1]; + let mut max_y = first[1]; + + for point in workspace.board.layout().boundary()[1..].iter() { + if point[0] < min_x { + min_x = point[0]; + } + if point[0] > max_x { + max_x = point[0]; + } + if point[1] < min_y { + min_y = point[1]; + } + if point[1] > max_y { + max_y = point[1]; + } + } + + egui::Rect::from_min_max( + Pos2::new(min_x as f32, min_y as f32), + Pos2::new(max_x as f32, max_y as f32), + ) + .scale_from_center(1.05) + } + pub fn scale_factor(&self) -> f32 { self.ref_scene_rect.width() / self.scene_rect.width() }