From 6fbdc8f738f203556029eb5299cd4d2d21237923 Mon Sep 17 00:00:00 2001 From: Alain Emilia Anna Zscheile Date: Tue, 31 Dec 2024 20:51:24 +0100 Subject: [PATCH] feat: implement bounding box selection backend --- crates/topola-egui/src/overlay.rs | 35 ++++++++++++++++++++++++++++++ crates/topola-egui/src/painter.rs | 6 ++++- crates/topola-egui/src/viewport.rs | 20 +++++++++++++---- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/crates/topola-egui/src/overlay.rs b/crates/topola-egui/src/overlay.rs index 325df9f..8546755 100644 --- a/crates/topola-egui/src/overlay.rs +++ b/crates/topola-egui/src/overlay.rs @@ -25,14 +25,19 @@ use topola::{ pub struct Overlay { ratsnest: Ratsnest, selection: Selection, + pub selected_bbox: AABB<[f64; 2]>, + reselect_bbox: Option>, active_layer: usize, } impl Overlay { pub fn new(board: &Board) -> Result { + const INF: f64 = f64::INFINITY; Ok(Self { ratsnest: Ratsnest::new(board.layout())?, selection: Selection::new(), + selected_bbox: AABB::from_corners([-INF, -INF], [INF, INF]), + reselect_bbox: None, active_layer: 0, }) } @@ -45,7 +50,29 @@ impl Overlay { self.selection = Selection::new(); } + pub fn reset_selected_bbox(&mut self) { + const INF: f64 = f64::INFINITY; + self.selected_bbox = AABB::from_corners([-INF, -INF], [INF, INF]); + } + + pub fn start_bbox_reselect(&mut self) { + self.reselect_bbox = Some(None); + } + pub fn click(&mut self, board: &Board, at: Point) { + if let Some(rsbb) = self.reselect_bbox.take() { + // handle bounding box selection (takes precendence over other interactions) + use rstar::Point; + self.reselect_bbox = match rsbb { + None => Some(Some(at)), + Some(pt) => { + self.selected_bbox = AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()]); + None + } + }; + return; + } + let geoms: Vec<_> = board .layout() .drawing() @@ -121,4 +148,12 @@ impl Overlay { pub fn selection(&self) -> &Selection { &self.selection } + + /// Returns the currently selected bounding box of a bounding-box reselect + pub fn get_bbox_reselect(&self, at: Point) -> Option> { + self.reselect_bbox + .as_ref() + .and_then(|pt| *pt) + .map(|pt| AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()])) + } } diff --git a/crates/topola-egui/src/painter.rs b/crates/topola-egui/src/painter.rs index 8a48ad6..65d0f05 100644 --- a/crates/topola-egui/src/painter.rs +++ b/crates/topola-egui/src/painter.rs @@ -59,6 +59,10 @@ impl<'a> Painter<'a> { } pub fn paint_bbox(&mut self, bbox: AABB<[f64; 2]>) { + self.paint_bbox_with_color(bbox, egui::Color32::GRAY) + } + + pub fn paint_bbox_with_color(&mut self, bbox: AABB<[f64; 2]>, color: egui::Color32) { let rect = egui::epaint::Rect { min: [bbox.lower()[0] as f32, -bbox.upper()[1] as f32].into(), max: [bbox.upper()[0] as f32, -bbox.lower()[1] as f32].into(), @@ -66,7 +70,7 @@ impl<'a> Painter<'a> { self.ui.painter().add(egui::Shape::rect_stroke( self.transform * rect, egui::Rounding::ZERO, - egui::Stroke::new(1.0, egui::Color32::GRAY), + egui::Stroke::new(1.0, color), )); } diff --git a/crates/topola-egui/src/viewport.rs b/crates/topola-egui/src/viewport.rs index 633fa41..3a0ebee 100644 --- a/crates/topola-egui/src/viewport.rs +++ b/crates/topola-egui/src/viewport.rs @@ -63,6 +63,7 @@ impl Viewport { if let Some(workspace) = maybe_workspace { let layers = &mut workspace.appearance_panel; let overlay = &mut workspace.overlay; + let latest_point = point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}; if ctx.input(|i| i.pointer.any_click()) { if menu_bar.is_placing_via { @@ -71,7 +72,7 @@ impl Viewport { from_layer: 0, to_layer: 0, circle: Circle { - pos: point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, + pos: latest_point, r: menu_bar.autorouter_options.router_options.routed_band_width / 2.0, }, maybe_net: Some(1234), @@ -80,9 +81,11 @@ impl Viewport { } else { overlay.click( workspace.interactor.invoker().autorouter().board(), - point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, + latest_point, ); } + } else if let Some(cur_bbox) = overlay.get_bbox_reselect(latest_point) { + painter.paint_bbox_with_color(cur_bbox, egui::Color32::RED); } let board = workspace.interactor.invoker().autorouter().board(); @@ -207,11 +210,20 @@ impl Viewport { if menu_bar.show_bboxes { let root_bbox3d = board.layout().drawing().rtree().root().envelope(); - let root_bbox = AABB::<[f64; 2]>::from_corners([root_bbox3d.lower()[0], root_bbox3d.lower()[1]].into(), [root_bbox3d.upper()[0], root_bbox3d.upper()[1]].into()); + let root_bbox = AABB::<[f64; 2]>::from_corners( + [root_bbox3d.lower()[0], root_bbox3d.lower()[1]].into(), + [root_bbox3d.upper()[0], root_bbox3d.upper()[1]].into(), + ); painter.paint_bbox(root_bbox); + + let selected_bbox = overlay.selected_bbox; + const INF: f64 = f64::INFINITY; + if selected_bbox != AABB::from_corners([-INF, -INF], [INF, INF]) { + painter.paint_bbox_with_color(selected_bbox, egui::Color32::BLUE); + } } - if let Some(activity) = &mut workspace.interactor.maybe_activity() { + if let Some(activity) = workspace.interactor.maybe_activity() { for ghost in activity.ghosts().iter() { painter.paint_primitive(&ghost, egui::Color32::from_rgb(75, 75, 150)); }