mirror of https://codeberg.org/topola/topola.git
egui: implement zooming to fit
This commit is contained in:
parent
89717f2b6e
commit
0239a49c73
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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("");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"));
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue