From 15a7bc65f1c980922014f59a5c606a4c34f61afb Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Wed, 27 May 2026 00:45:30 +0200 Subject: [PATCH] Implement selection of nets --- topola-egui/src/display.rs | 20 ++++- topola-egui/src/viewport.rs | 16 ++-- .../src/board/interactors/drag_selection.rs | 21 +++++ topola/src/board/locate.rs | 77 +++++++++++++------ topola/src/board/select.rs | 49 +++++++++++- topola/src/layout/locate.rs | 70 ++++++++++++++++- 6 files changed, 215 insertions(+), 38 deletions(-) diff --git a/topola-egui/src/display.rs b/topola-egui/src/display.rs index a24df58..389ebc6 100644 --- a/topola-egui/src/display.rs +++ b/topola-egui/src/display.rs @@ -46,6 +46,8 @@ impl Display { for joint_id in layout.layer_joints(layer) { let joint = layout.joint(joint_id); + let pin_selected = board.pins_contain_joint(&workspace.selection.pins, joint_id); + let net_selected = board.nets_contain_joint(&workspace.selection.nets, joint_id); self.paint_joint( ctx, ui, @@ -54,13 +56,17 @@ impl Display { workspace.appearance_panel.layer_color( ctx, board.layer_desc(joint.spec.layer), - board.pins_contain_joint(&workspace.selection.pins, joint_id), + pin_selected || (joint.spec.pin.is_none() && net_selected), ), ); } for segment_id in layout.layer_segments(layer) { let segment = layout.segment(segment_id); + let pin_selected = + board.pins_contain_segment(&workspace.selection.pins, segment_id); + let net_selected = + board.nets_contain_segment(&workspace.selection.nets, segment_id); self.paint_segment( ctx, ui, @@ -69,13 +75,15 @@ impl Display { workspace.appearance_panel.layer_color( ctx, board.layer_desc(segment.layer), - board.pins_contain_segment(&workspace.selection.pins, segment_id), + pin_selected || (segment.spec.pin.is_none() && net_selected), ), ); } for via_id in layout.layer_vias(layer) { let via = layout.via(via_id); + let pin_selected = board.pins_contain_via(&workspace.selection.pins, via_id); + let net_selected = board.nets_contain_via(&workspace.selection.nets, via_id); self.paint_via( ctx, ui, @@ -84,13 +92,17 @@ impl Display { workspace.appearance_panel.layer_color( ctx, board.layer_desc(layer), - board.pins_contain_via(&workspace.selection.pins, via_id), + pin_selected || (via.spec.pin.is_none() && net_selected), ), ); } for polygon_id in layout.layer_polygons(layer) { let polygon = layout.polygon(polygon_id); + let pin_selected = + board.pins_contain_polygon(&workspace.selection.pins, polygon_id); + let net_selected = + board.nets_contain_polygon(&workspace.selection.nets, polygon_id); self.paint_polygon( ctx, ui, @@ -99,7 +111,7 @@ impl Display { workspace.appearance_panel.layer_color( ctx, board.layer_desc(polygon.layer), - board.pins_contain_polygon(&workspace.selection.pins, polygon_id), + pin_selected || (polygon.pin.is_none() && net_selected), ), ); } diff --git a/topola-egui/src/viewport.rs b/topola-egui/src/viewport.rs index eabc1f7..519ffab 100644 --- a/topola-egui/src/viewport.rs +++ b/topola-egui/src/viewport.rs @@ -81,14 +81,7 @@ impl Viewport { )); } - if let Some(interactor) = self.drag_selection_interactor.as_mut() { - if primary_down || primary_released { - let _ = interactor.update( - workspace.autorouter.router().navmesher_board().board(), - InteractiveInput::new(pointer_scene, false), - ); - } - } else if response.clicked() { + if response.clicked() { if let Some(pin_selector) = workspace .autorouter .router() @@ -102,6 +95,13 @@ impl Viewport { { workspace.selection.pins.xor(std::iter::once(pin_selector)); } + } else if let Some(interactor) = self.drag_selection_interactor.as_mut() { + if primary_down || primary_released { + let _ = interactor.update( + workspace.autorouter.router().navmesher_board().board(), + InteractiveInput::new(pointer_scene, false), + ); + } } } diff --git a/topola/src/board/interactors/drag_selection.rs b/topola/src/board/interactors/drag_selection.rs index 77a084f..96aa893 100644 --- a/topola/src/board/interactors/drag_selection.rs +++ b/topola/src/board/interactors/drag_selection.rs @@ -68,6 +68,17 @@ impl DragSelectionInteractor { } } + match self.options.contain { + SelectionContainMode::Crossing => { + self.selection + .nets + .add(board.locate_nets_intersecting_rect(rect)); + } + SelectionContainMode::Window => { + self.selection.nets.add(board.locate_nets_inside_rect(rect)); + } + } + match self.options.contain { SelectionContainMode::Crossing => { self.selection @@ -85,12 +96,16 @@ impl DragSelectionInteractor { match self.options.combine { SelectionCombineMode::Replace => { combined_selection.components = self.selection.components.clone(); + combined_selection.nets = self.selection.nets.clone(); combined_selection.pins = self.selection.pins.clone(); } SelectionCombineMode::Additive => { combined_selection .pins .add(self.selection.pins.0.iter().cloned()); + combined_selection + .nets + .add(self.selection.nets.0.iter().cloned()); combined_selection .components .add(self.selection.components.0.iter().cloned()); @@ -99,6 +114,9 @@ impl DragSelectionInteractor { combined_selection .pins .sub(self.selection.pins.0.iter().cloned()); + combined_selection + .nets + .sub(self.selection.nets.0.iter().cloned()); combined_selection .components .sub(self.selection.components.0.iter().cloned()); @@ -107,6 +125,9 @@ impl DragSelectionInteractor { combined_selection .components .xor(self.selection.components.0.iter().cloned()); + combined_selection + .nets + .xor(self.selection.nets.0.iter().cloned()); combined_selection .pins .xor(self.selection.pins.0.iter().cloned()); diff --git a/topola/src/board/locate.rs b/topola/src/board/locate.rs index f0c9d05..554ee8c 100644 --- a/topola/src/board/locate.rs +++ b/topola/src/board/locate.rs @@ -2,6 +2,8 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use std::collections::BTreeSet; + use crate::{ Rect3, Vector3, board::Board, @@ -77,6 +79,57 @@ impl Board { ) } + pub fn locate_nets_at_point( + &self, + point: Vector3, + ) -> impl Iterator + '_ { + let mut selectors = BTreeSet::new(); + + for net_id in self.layout.locate_nets_at_point(point) { + let Some(net_name) = self.net_name(net_id) else { + continue; + }; + + selectors.insert(NetSelector::new(net_name.to_string())); + } + + selectors.into_iter() + } + + pub fn locate_nets_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + let mut selectors = BTreeSet::new(); + + for net_id in self.layout.locate_nets_intersecting_rect(rect) { + let Some(net_name) = self.net_name(net_id) else { + continue; + }; + + selectors.insert(NetSelector::new(net_name.to_string())); + } + + selectors.into_iter() + } + + pub fn locate_nets_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + let mut selectors = BTreeSet::new(); + + for net_id in self.layout.locate_nets_inside_rect(rect) { + let Some(net_name) = self.net_name(net_id) else { + continue; + }; + + selectors.insert(NetSelector::new(net_name.to_string())); + } + + selectors.into_iter() + } + pub fn locate_pin_at_point(&self, point: Vector3) -> Option { if let Some(joint_id) = self.layout.locate_joints_at_point(point).next() { return self.joint_pin_selector(joint_id); @@ -144,28 +197,4 @@ impl Board { .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)), ) } - - pub fn locate_nets_at_point( - &self, - _point: Vector3, - ) -> impl Iterator + '_ { - // TODO - std::iter::empty() - } - - pub fn locate_nets_intersecting_rect( - &self, - _rect: Rect3, - ) -> impl Iterator + '_ { - // TODO - std::iter::empty() - } - - pub fn locate_nets_inside_rect( - &self, - _rect: Rect3, - ) -> impl Iterator + '_ { - // TODO - std::iter::empty() - } } diff --git a/topola/src/board/select.rs b/topola/src/board/select.rs index 84dc33a..0c03253 100644 --- a/topola/src/board/select.rs +++ b/topola/src/board/select.rs @@ -5,7 +5,10 @@ use crate::{ board::{ Board, - selections::{ComponentSelection, ComponentSelector, PinSelection, PinSelector}, + selections::{ + ComponentSelection, ComponentSelector, NetSelection, NetSelector, PinSelection, + PinSelector, + }, }, primitives::{JointId, PolygonId, SegmentId, ViaId}, }; @@ -147,6 +150,50 @@ impl Board { }) } + pub fn nets_contain_joint(&self, selection: &NetSelection, id: JointId) -> bool { + let joint = self.layout.joint(id); + let Some(net_name) = self.net_name(joint.spec.net) else { + return false; + }; + + selection + .0 + .contains(&NetSelector::new(net_name.to_string())) + } + + pub fn nets_contain_segment(&self, selection: &NetSelection, id: SegmentId) -> bool { + let segment = self.layout.segment(id); + let Some(net_name) = self.net_name(segment.net) else { + return false; + }; + + selection + .0 + .contains(&NetSelector::new(net_name.to_string())) + } + + pub fn nets_contain_via(&self, selection: &NetSelection, id: ViaId) -> bool { + let via = self.layout.via(id); + let Some(net_name) = self.net_name(via.net) else { + return false; + }; + + selection + .0 + .contains(&NetSelector::new(net_name.to_string())) + } + + pub fn nets_contain_polygon(&self, selection: &NetSelection, id: PolygonId) -> bool { + let polygon = self.layout.polygon(id); + let Some(net_name) = self.net_name(polygon.net) else { + return false; + }; + + selection + .0 + .contains(&NetSelector::new(net_name.to_string())) + } + pub fn pins_contain_joint(&self, selection: &PinSelection, id: JointId) -> bool { let Some(selector) = self.joint_pin_selector(id) else { return false; diff --git a/topola/src/layout/locate.rs b/topola/src/layout/locate.rs index f4a07db..c4e338c 100644 --- a/topola/src/layout/locate.rs +++ b/topola/src/layout/locate.rs @@ -2,9 +2,11 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use std::collections::BTreeSet; + use crate::{ Rect3, Vector3, - layout::{LayerId, Layout}, + layout::{LayerId, Layout, compounds::NetId}, primitives::{JointId, PolygonId, SegmentId, ViaId}, }; @@ -118,4 +120,70 @@ impl Layout { .locate_in_envelope(&rect_aabb) .map(|geom_with_data| geom_with_data.data) } + + pub fn locate_nets_at_point(&self, point: Vector3) -> impl Iterator { + let mut nets = BTreeSet::new(); + + for joint_id in self.locate_joints_at_point(point) { + nets.insert(self.joint(joint_id).spec.net); + } + + for segment_id in self.locate_segments_at_point(point) { + nets.insert(self.segment(segment_id).net); + } + + for via_id in self.locate_vias_at_point(point) { + nets.insert(self.via(via_id).net); + } + + for polygon_id in self.locate_polygons_at_point(point) { + nets.insert(self.polygon(polygon_id).net); + } + + nets.into_iter() + } + + pub fn locate_nets_intersecting_rect(&self, rect: Rect3) -> impl Iterator { + let mut nets = BTreeSet::new(); + + for joint_id in self.locate_joints_intersecting_rect(rect) { + nets.insert(self.joint(joint_id).spec.net); + } + + for segment_id in self.locate_segments_intersecting_rect(rect) { + nets.insert(self.segment(segment_id).net); + } + + for via_id in self.locate_vias_intersecting_rect(rect) { + nets.insert(self.via(via_id).net); + } + + for polygon_id in self.locate_polygons_intersecting_rect(rect) { + nets.insert(self.polygon(polygon_id).net); + } + + nets.into_iter() + } + + pub fn locate_nets_inside_rect(&self, rect: Rect3) -> impl Iterator { + let mut nets = BTreeSet::new(); + + for joint_id in self.locate_joints_inside_rect(rect) { + nets.insert(self.joint(joint_id).spec.net); + } + + for segment_id in self.locate_segments_inside_rect(rect) { + nets.insert(self.segment(segment_id).net); + } + + for via_id in self.locate_vias_inside_rect(rect) { + nets.insert(self.via(via_id).net); + } + + for polygon_id in self.locate_polygons_inside_rect(rect) { + nets.insert(self.polygon(polygon_id).net); + } + + nets.into_iter() + } }