From 51005bb97c8d3b53c132092a9220f855a5787989 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Wed, 27 May 2026 00:21:13 +0200 Subject: [PATCH] Make drag selection interactor take selection modes as input --- topola-egui/src/viewport.rs | 40 +++--- .../src/board/interactors/drag_selection.rs | 126 ++++++++++++------ topola/src/board/interactors/mod.rs | 30 ++++- topola/src/board/locate.rs | 97 ++++++++------ topola/src/board/selections/component.rs | 20 ++- topola/src/board/selections/net.rs | 20 ++- topola/src/board/selections/pin.rs | 20 ++- topola/src/layout/locate.rs | 109 +++++---------- topola/src/lib.rs | 7 +- topola/src/math.rs | 66 +++++++++ 10 files changed, 345 insertions(+), 190 deletions(-) diff --git a/topola-egui/src/viewport.rs b/topola-egui/src/viewport.rs index 436ee18..eabc1f7 100644 --- a/topola-egui/src/viewport.rs +++ b/topola-egui/src/viewport.rs @@ -3,7 +3,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use egui::Pos2; -use topola::{CrossingDragSelectionInteractor, InteractiveInput, Vector2}; +use topola::{ + DragSelectionInteractor, InteractiveInput, SelectionCombineMode, SelectionContainMode, + SelectionOptions, Vector2, Vector3, +}; use crate::{display::Display, workspace::Workspace}; @@ -11,7 +14,7 @@ pub struct Viewport { pub scene_rect: egui::Rect, pub ref_scene_rect: egui::Rect, pub scheduled_zoom_to_fit: bool, - crossing_drag_selection_interactor: Option, + drag_selection_interactor: Option, } impl Viewport { @@ -20,7 +23,7 @@ impl Viewport { 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, - crossing_drag_selection_interactor: None, + drag_selection_interactor: None, } } @@ -52,7 +55,7 @@ impl Viewport { if let Some(workspace) = workspace { if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { - self.crossing_drag_selection_interactor = None; + self.drag_selection_interactor = None; } let primary_pressed = @@ -68,15 +71,21 @@ impl Viewport { Vector2::new(pointer_scene_pos.x as i64, pointer_scene_pos.y as i64); if primary_pressed && response.hovered() { - self.crossing_drag_selection_interactor = - Some(CrossingDragSelectionInteractor::new(pointer_scene)); + self.drag_selection_interactor = Some(DragSelectionInteractor::new( + pointer_scene, + workspace.selection.clone(), + SelectionOptions::new( + SelectionCombineMode::Replace, + SelectionContainMode::Crossing, + ), + )); } - if let Some(interactor) = self.crossing_drag_selection_interactor.as_mut() { + if let Some(interactor) = self.drag_selection_interactor.as_mut() { if primary_down || primary_released { - interactor.update( + let _ = interactor.update( workspace.autorouter.router().navmesher_board().board(), - InteractiveInput::new(pointer_scene), + InteractiveInput::new(pointer_scene, false), ); } } else if response.clicked() { @@ -85,18 +94,19 @@ impl Viewport { .router() .navmesher_board() .board() - .locate_pin_at_point( - workspace.appearance_panel.active, - pointer_scene, - ) + .locate_pin_at_point(Vector3::new( + pointer_scene.x, + pointer_scene.y, + workspace.appearance_panel.active.index() as i64, + )) { - workspace.selection.pins.toggle(pin_selector); + workspace.selection.pins.xor(std::iter::once(pin_selector)); } } } if primary_released { - if let Some(interactor) = self.crossing_drag_selection_interactor.take() { + if let Some(interactor) = self.drag_selection_interactor.take() { workspace.selection = interactor.selection().clone(); } } diff --git a/topola/src/board/interactors/drag_selection.rs b/topola/src/board/interactors/drag_selection.rs index 266f134..77a084f 100644 --- a/topola/src/board/interactors/drag_selection.rs +++ b/topola/src/board/interactors/drag_selection.rs @@ -5,73 +5,115 @@ use derive_getters::Getters; use crate::{ - Rect2, Vector2, - board::{Board, interactors::InteractiveInput, selections::PersistableSelection}, - layout::LayerId, + Rect3, Vector2, Vector3, + board::{ + Board, + interactors::{ + InteractiveInput, SelectionCombineMode, SelectionContainMode, SelectionOptions, + }, + selections::PersistableSelection, + }, }; #[derive(Clone, Debug, Eq, Getters, Ord, PartialEq, PartialOrd)] -pub struct CrossingDragSelectionInteractor { +pub struct DragSelectionInteractor { origin: Vector2, + original_selection: PersistableSelection, selection: PersistableSelection, + options: SelectionOptions, } -impl CrossingDragSelectionInteractor { - pub fn new(origin: Vector2) -> Self { +impl DragSelectionInteractor { + pub fn new( + origin: Vector2, + original_selection: PersistableSelection, + options: SelectionOptions, + ) -> Self { Self { origin, + original_selection, selection: PersistableSelection::new(), + options, } } - pub fn update(&mut self, board: &Board, input: InteractiveInput) { - let rect = Rect2::new(self.origin, input.pointer); + pub fn update( + &mut self, + board: &Board, + input: InteractiveInput, + ) -> Option { + if input.cancel { + self.selection = self.original_selection.clone(); + return Some(self.selection.clone()); + } self.selection = PersistableSelection::new(); for layer_index in 0..*board.layout().layer_count() { - let layer = LayerId::new(layer_index); + let rect = Rect3::new( + Vector3::new(self.origin.x, self.origin.y, layer_index as i64), + Vector3::new(input.pointer.x, input.pointer.y, layer_index as i64), + ); - for selector in board.locate_components_intersecting_rect(layer, rect) { - self.selection.components.0.insert(selector); + match self.options.contain { + SelectionContainMode::Crossing => { + self.selection + .components + .add(board.locate_components_intersecting_rect(rect)); + } + SelectionContainMode::Window => { + self.selection + .components + .add(board.locate_components_inside_rect(rect)); + } } - for selector in board.locate_pins_intersecting_rect(layer, rect) { - self.selection.pins.0.insert(selector); + match self.options.contain { + SelectionContainMode::Crossing => { + self.selection + .pins + .add(board.locate_pins_intersecting_rect(rect)); + } + SelectionContainMode::Window => { + self.selection.pins.add(board.locate_pins_inside_rect(rect)); + } } } - } -} -#[derive(Clone, Debug, Eq, Getters, Ord, PartialEq, PartialOrd)] -pub struct WindowDragSelectionInteractor { - origin: Vector2, - selection: PersistableSelection, -} - -impl WindowDragSelectionInteractor { - pub fn new(origin: Vector2) -> Self { - Self { - origin, - selection: PersistableSelection::new(), - } - } - - pub fn update(&mut self, board: &Board, input: InteractiveInput) { - let rect = Rect2::new(self.origin, input.pointer); - - self.selection = PersistableSelection::new(); - - for layer_index in 0..*board.layout().layer_count() { - let layer = LayerId::new(layer_index); - - for selector in board.locate_components_inside_rect(layer, rect) { - self.selection.components.0.insert(selector); + // TODO: There's no need to clone on each update. + let mut combined_selection = self.original_selection.clone(); + match self.options.combine { + SelectionCombineMode::Replace => { + combined_selection.components = self.selection.components.clone(); + combined_selection.pins = self.selection.pins.clone(); } - - for selector in board.locate_pins_inside_rect(layer, rect) { - self.selection.pins.0.insert(selector); + SelectionCombineMode::Additive => { + combined_selection + .pins + .add(self.selection.pins.0.iter().cloned()); + combined_selection + .components + .add(self.selection.components.0.iter().cloned()); + } + SelectionCombineMode::Subtractive => { + combined_selection + .pins + .sub(self.selection.pins.0.iter().cloned()); + combined_selection + .components + .sub(self.selection.components.0.iter().cloned()); + } + SelectionCombineMode::Toggle => { + combined_selection + .components + .xor(self.selection.components.0.iter().cloned()); + combined_selection + .pins + .xor(self.selection.pins.0.iter().cloned()); } } + + self.selection = combined_selection; + Some(self.selection.clone()) } } diff --git a/topola/src/board/interactors/mod.rs b/topola/src/board/interactors/mod.rs index 5077245..81ae521 100644 --- a/topola/src/board/interactors/mod.rs +++ b/topola/src/board/interactors/mod.rs @@ -4,16 +4,40 @@ mod drag_selection; -pub use drag_selection::CrossingDragSelectionInteractor; +use derive_more::Constructor; +pub use drag_selection::DragSelectionInteractor; +use serde::{Deserialize, Serialize}; use crate::Vector2; +#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct InteractiveInput { pointer: Vector2, + cancel: bool, } impl InteractiveInput { - pub fn new(pointer: Vector2) -> Self { - Self { pointer } + pub fn new(pointer: Vector2, cancel: bool) -> Self { + Self { pointer, cancel } } } + +#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub enum SelectionCombineMode { + Replace, + Additive, + Subtractive, + Toggle, +} + +#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub enum SelectionContainMode { + Crossing, + Window, +} + +#[derive(Clone, Constructor, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub struct SelectionOptions { + combine: SelectionCombineMode, + contain: SelectionContainMode, +} diff --git a/topola/src/board/locate.rs b/topola/src/board/locate.rs index b51d949..f0c9d05 100644 --- a/topola/src/board/locate.rs +++ b/topola/src/board/locate.rs @@ -3,31 +3,26 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ - Rect2, Vector2, + Rect3, Vector3, board::Board, - layout::LayerId, - selections::{ComponentSelector, PinSelector}, + selections::{ComponentSelector, NetSelector, PinSelector}, }; impl Board { - pub fn locate_component_at_point( - &self, - layer: LayerId, - point: Vector2, - ) -> Option { - if let Some(joint_id) = self.layout.locate_joints_at_point(layer, point).next() { + pub fn locate_component_at_point(&self, point: Vector3) -> Option { + if let Some(joint_id) = self.layout.locate_joints_at_point(point).next() { return self.joint_component_selector(joint_id); } - if let Some(segment_id) = self.layout.locate_segments_at_point(layer, point).next() { + if let Some(segment_id) = self.layout.locate_segments_at_point(point).next() { return self.segment_component_selector(segment_id); } - if let Some(via_id) = self.layout.locate_vias_at_point(layer, point).next() { + if let Some(via_id) = self.layout.locate_vias_at_point(point).next() { return self.via_component_selector(via_id); } - if let Some(polygon_id) = self.layout.locate_polygons_at_point(layer, point).next() { + if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() { return self.polygon_component_selector(polygon_id); } @@ -36,68 +31,66 @@ impl Board { pub fn locate_components_intersecting_rect( &self, - layer: LayerId, - rect: Rect2, + rect: Rect3, ) -> impl Iterator + '_ { self.layout - .locate_joints_intersecting_rect(layer, rect) + .locate_joints_intersecting_rect(rect) .filter_map(|joint_id| self.joint_component_selector(joint_id)) .chain( self.layout - .locate_segments_intersecting_rect(layer, rect) + .locate_segments_intersecting_rect(rect) .filter_map(|segment_id| self.segment_component_selector(segment_id)), ) .chain( self.layout - .locate_vias_intersecting_rect(layer, rect) + .locate_vias_intersecting_rect(rect) .filter_map(|via_id| self.via_component_selector(via_id)), ) .chain( self.layout - .locate_polygons_intersecting_rect(layer, rect) + .locate_polygons_intersecting_rect(rect) .filter_map(|polygon_id| self.polygon_component_selector(polygon_id)), ) } pub fn locate_components_inside_rect( &self, - layer: LayerId, - rect: Rect2, + rect: Rect3, ) -> impl Iterator + '_ { self.layout - .locate_joints_inside_rect(layer, rect) + .locate_joints_inside_rect(rect) .filter_map(|joint_id| self.joint_component_selector(joint_id)) .chain( self.layout - .locate_segments_inside_rect(layer, rect) + .locate_segments_inside_rect(rect) .filter_map(|segment_id| self.segment_component_selector(segment_id)), ) .chain( self.layout - .locate_vias_inside_rect(layer, rect) + .locate_vias_inside_rect(rect) .filter_map(|via_id| self.via_component_selector(via_id)), ) .chain( self.layout - .locate_polygons_inside_rect(layer, rect) + .locate_polygons_inside_rect(rect) .filter_map(|polygon_id| self.polygon_component_selector(polygon_id)), ) } - pub fn locate_pin_at_point(&self, layer: LayerId, point: Vector2) -> Option { - if let Some(joint_id) = self.layout.locate_joints_at_point(layer, point).next() { + 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); } - if let Some(segment_id) = self.layout.locate_segments_at_point(layer, point).next() { + if let Some(segment_id) = self.layout.locate_segments_at_point(point).next() { return self.segment_pin_selector(segment_id); } - if let Some(via_id) = self.layout.locate_vias_at_point(layer, point).next() { + if let Some(via_id) = self.layout.locate_vias_at_point(point).next() { return self.via_pin_selector(via_id); } - if let Some(polygon_id) = self.layout.locate_polygons_at_point(layer, point).next() { + if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() { return self.polygon_pin_selector(polygon_id); } @@ -106,51 +99,73 @@ impl Board { pub fn locate_pins_intersecting_rect( &self, - layer: LayerId, - rect: Rect2, + rect: Rect3, ) -> impl Iterator + '_ { self.layout - .locate_joints_intersecting_rect(layer, rect) + .locate_joints_intersecting_rect(rect) .filter_map(|joint_id| self.joint_pin_selector(joint_id)) .chain( self.layout - .locate_segments_intersecting_rect(layer, rect) + .locate_segments_intersecting_rect(rect) .filter_map(|segment_id| self.segment_pin_selector(segment_id)), ) .chain( self.layout - .locate_vias_intersecting_rect(layer, rect) + .locate_vias_intersecting_rect(rect) .filter_map(|via_id| self.via_pin_selector(via_id)), ) .chain( self.layout - .locate_polygons_intersecting_rect(layer, rect) + .locate_polygons_intersecting_rect(rect) .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)), ) } pub fn locate_pins_inside_rect( &self, - layer: LayerId, - rect: Rect2, + rect: Rect3, ) -> impl Iterator + '_ { self.layout - .locate_joints_inside_rect(layer, rect) + .locate_joints_inside_rect(rect) .filter_map(|joint_id| self.joint_pin_selector(joint_id)) .chain( self.layout - .locate_segments_inside_rect(layer, rect) + .locate_segments_inside_rect(rect) .filter_map(|segment_id| self.segment_pin_selector(segment_id)), ) .chain( self.layout - .locate_vias_inside_rect(layer, rect) + .locate_vias_inside_rect(rect) .filter_map(|via_id| self.via_pin_selector(via_id)), ) .chain( self.layout - .locate_polygons_inside_rect(layer, rect) + .locate_polygons_inside_rect(rect) .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/selections/component.rs b/topola/src/board/selections/component.rs index 1a59b21..a4f47f1 100644 --- a/topola/src/board/selections/component.rs +++ b/topola/src/board/selections/component.rs @@ -20,11 +20,23 @@ impl ComponentSelection { Default::default() } - pub fn toggle(&mut self, selector: ComponentSelector) { - if self.0.contains(&selector) { + pub fn add(&mut self, selectors: impl IntoIterator) { + self.0.extend(selectors); + } + + pub fn sub(&mut self, selectors: impl IntoIterator) { + for selector in selectors { self.0.remove(&selector); - } else { - self.0.insert(selector); + } + } + + pub fn xor(&mut self, selectors: impl IntoIterator) { + for selector in selectors { + if self.0.contains(&selector) { + self.0.remove(&selector); + } else { + self.0.insert(selector); + } } } } diff --git a/topola/src/board/selections/net.rs b/topola/src/board/selections/net.rs index 5e7912c..74aa80f 100644 --- a/topola/src/board/selections/net.rs +++ b/topola/src/board/selections/net.rs @@ -25,11 +25,23 @@ impl NetSelection { Default::default() } - pub fn toggle(&mut self, selector: NetSelector) { - if self.0.contains(&selector) { + pub fn add(&mut self, selectors: impl IntoIterator) { + self.0.extend(selectors); + } + + pub fn sub(&mut self, selectors: impl IntoIterator) { + for selector in selectors { self.0.remove(&selector); - } else { - self.0.insert(selector); + } + } + + pub fn xor(&mut self, selectors: impl IntoIterator) { + for selector in selectors { + if self.0.contains(&selector) { + self.0.remove(&selector); + } else { + self.0.insert(selector); + } } } } diff --git a/topola/src/board/selections/pin.rs b/topola/src/board/selections/pin.rs index 944d08e..fd9f245 100644 --- a/topola/src/board/selections/pin.rs +++ b/topola/src/board/selections/pin.rs @@ -23,11 +23,23 @@ impl PinSelection { Default::default() } - pub fn toggle(&mut self, selector: PinSelector) { - if self.0.contains(&selector) { + pub fn add(&mut self, selectors: impl IntoIterator) { + self.0.extend(selectors); + } + + pub fn sub(&mut self, selectors: impl IntoIterator) { + for selector in selectors { self.0.remove(&selector); - } else { - self.0.insert(selector); + } + } + + pub fn xor(&mut self, selectors: impl IntoIterator) { + for selector in selectors { + if self.0.contains(&selector) { + self.0.remove(&selector); + } else { + self.0.insert(selector); + } } } } diff --git a/topola/src/layout/locate.rs b/topola/src/layout/locate.rs index 5770880..f4a07db 100644 --- a/topola/src/layout/locate.rs +++ b/topola/src/layout/locate.rs @@ -3,157 +3,116 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ - Rect2, Vector2, + Rect3, Vector3, layout::{LayerId, Layout}, primitives::{JointId, PolygonId, SegmentId, ViaId}, }; impl Layout { - pub fn locate_joints_at_point( - &self, - layer: LayerId, - point: Vector2, - ) -> impl Iterator { + pub fn locate_joints_at_point(&self, point: Vector3) -> impl Iterator { + let point2 = point.xy(); self.joints_rtree .as_ref() - .locate_all_at_point(&[point.x, point.y, layer.index() as i64]) + .locate_all_at_point(&[point.x, point.y, point.z]) .map(|geom_with_data| geom_with_data.data) - .filter(move |&joint_id| self.joints[joint_id.index()].contains_point(point)) + .filter(move |&joint_id| self.joints[joint_id.index()].contains_point(point2)) } pub fn locate_joints_intersecting_rect( &self, - layer: LayerId, - rect: Rect2, + rect: Rect3, ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + let rect_aabb = rect.aabb3(); self.joints_rtree .as_ref() .locate_in_envelope_intersecting(&rect_aabb) .map(|geom_with_data| geom_with_data.data) - .filter(move |&joint_id| self.joint(joint_id).spec.layer == layer) } - pub fn locate_joints_inside_rect( - &self, - layer: LayerId, - rect: Rect2, - ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + pub fn locate_joints_inside_rect(&self, rect: Rect3) -> impl Iterator { + let rect_aabb = rect.aabb3(); self.joints_rtree .as_ref() .locate_in_envelope(&rect_aabb) .map(|geom_with_data| geom_with_data.data) } - pub fn locate_segments_at_point( - &self, - layer: LayerId, - point: Vector2, - ) -> impl Iterator { + pub fn locate_segments_at_point(&self, point: Vector3) -> impl Iterator { + let point2 = point.xy(); self.segments_rtree .as_ref() - .locate_all_at_point(&[point.x, point.y, layer.index() as i64]) + .locate_all_at_point(&[point.x, point.y, point.z]) .map(|geom_with_data| geom_with_data.data) - .filter(move |&segment_id| self.segment(segment_id).contains_point(point)) + .filter(move |&segment_id| self.segment(segment_id).contains_point(point2)) } pub fn locate_segments_intersecting_rect( &self, - layer: LayerId, - rect: Rect2, + rect: Rect3, ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + let rect_aabb = rect.aabb3(); self.segments_rtree .as_ref() .locate_in_envelope_intersecting(&rect_aabb) .map(|geom_with_data| geom_with_data.data) - .filter(move |&segment_id| self.segment(segment_id).layer == layer) } - pub fn locate_segments_inside_rect( - &self, - layer: LayerId, - rect: Rect2, - ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + pub fn locate_segments_inside_rect(&self, rect: Rect3) -> impl Iterator { + let rect_aabb = rect.aabb3(); self.segments_rtree .as_ref() .locate_in_envelope(&rect_aabb) .map(|geom_with_data| geom_with_data.data) } - pub fn locate_vias_at_point( - &self, - layer: LayerId, - point: Vector2, - ) -> impl Iterator { + pub fn locate_vias_at_point(&self, point: Vector3) -> impl Iterator { + let layer = LayerId::new(point.z as usize); + let point2 = point.xy(); self.vias_rtree .as_ref() - .locate_all_at_point(&[point.x, point.y, layer.index() as i64]) + .locate_all_at_point(&[point.x, point.y, point.z]) .map(|geom_with_data| geom_with_data.data) - .filter(move |&via_id| self.vias[via_id.index()].contains_point(layer, point)) + .filter(move |&via_id| self.vias[via_id.index()].contains_point(layer, point2)) } - pub fn locate_vias_intersecting_rect( - &self, - layer: LayerId, - rect: Rect2, - ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + pub fn locate_vias_intersecting_rect(&self, rect: Rect3) -> impl Iterator { + let rect_aabb = rect.aabb3(); self.vias_rtree .as_ref() .locate_in_envelope_intersecting(&rect_aabb) .map(|geom_with_data| geom_with_data.data) - .filter(move |&via_id| { - let via = self.via(via_id); - via.min_layer <= layer && layer <= via.max_layer - }) } - pub fn locate_vias_inside_rect( - &self, - layer: LayerId, - rect: Rect2, - ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + pub fn locate_vias_inside_rect(&self, rect: Rect3) -> impl Iterator { + let rect_aabb = rect.aabb3(); self.vias_rtree .as_ref() .locate_in_envelope(&rect_aabb) .map(|geom_with_data| geom_with_data.data) } - pub fn locate_polygons_at_point( - &self, - layer: LayerId, - point: Vector2, - ) -> impl Iterator { + pub fn locate_polygons_at_point(&self, point: Vector3) -> impl Iterator { + let point2 = point.xy(); self.polygons_rtree .as_ref() - .locate_all_at_point(&[point.x, point.y, layer.index() as i64]) + .locate_all_at_point(&[point.x, point.y, point.z]) .map(|geom_with_data| geom_with_data.data) - .filter(move |&polygon_id| self.polygons[polygon_id.index()].contains_point(point)) + .filter(move |&polygon_id| self.polygons[polygon_id.index()].contains_point(point2)) } pub fn locate_polygons_intersecting_rect( &self, - layer: LayerId, - rect: Rect2, + rect: Rect3, ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + let rect_aabb = rect.aabb3(); self.polygons_rtree .as_ref() .locate_in_envelope_intersecting(&rect_aabb) .map(|geom_with_data| geom_with_data.data) - .filter(move |&polygon_id| self.polygon(polygon_id).layer == layer) } - pub fn locate_polygons_inside_rect( - &self, - layer: LayerId, - rect: Rect2, - ) -> impl Iterator { - let rect_aabb = rect.aabb3(layer.index() as i64); + pub fn locate_polygons_inside_rect(&self, rect: Rect3) -> impl Iterator { + let rect_aabb = rect.aabb3(); self.polygons_rtree .as_ref() .locate_in_envelope(&rect_aabb) diff --git a/topola/src/lib.rs b/topola/src/lib.rs index 11cd4f9..8349da7 100644 --- a/topola/src/lib.rs +++ b/topola/src/lib.rs @@ -18,11 +18,14 @@ pub use crate::board::Board; pub use crate::board::LayerDesc; pub use crate::board::LayerSide; pub use crate::board::LayerType; -pub use crate::board::interactors::{CrossingDragSelectionInteractor, InteractiveInput}; +pub use crate::board::interactors::{ + DragSelectionInteractor, InteractiveInput, SelectionCombineMode, SelectionContainMode, + SelectionOptions, +}; pub use crate::board::selections; pub use crate::layout::LayerId; pub use crate::layout::Layout; pub use crate::layout::compounds::{Pin, PinId}; pub use crate::layout::primitives; -pub use crate::math::{Rect2, Vector2}; +pub use crate::math::{Rect2, Rect3, Vector2, Vector3}; pub use crate::ratsnest::{Ratline, Ratsnest}; diff --git a/topola/src/math.rs b/topola/src/math.rs index 171d1ff..c55aeb9 100644 --- a/topola/src/math.rs +++ b/topola/src/math.rs @@ -31,6 +31,38 @@ impl Rect2 { } } +#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Rect3 { + min: Vector3, + max: Vector3, +} + +impl Rect3 { + pub fn new(from: Vector3, to: Vector3) -> Self { + Self { + min: Vector3::new( + std::cmp::min(from.x, to.x), + std::cmp::min(from.y, to.y), + std::cmp::min(from.z, to.z), + ), + max: Vector3::new( + std::cmp::max(from.x, to.x), + std::cmp::max(from.y, to.y), + std::cmp::max(from.z, to.z), + ), + } + } +} + +impl Rect3 { + pub fn aabb3(self) -> AABB<[i64; 3]> { + AABB::from_corners( + [self.min.x, self.min.y, self.min.z], + [self.max.x, self.max.y, self.max.z], + ) + } +} + #[derive( Add, AddAssign, @@ -58,6 +90,40 @@ pub struct Vector2 { pub y: T, } +#[derive( + Add, + AddAssign, + Clone, + Constructor, + Copy, + Debug, + Deserialize, + Div, + DivAssign, + Eq, + From, + Into, + Mul, + MulAssign, + Ord, + PartialEq, + PartialOrd, + Serialize, + Sub, + SubAssign, +)] +pub struct Vector3 { + pub x: T, + pub y: T, + pub z: T, +} + +impl Vector3 { + pub fn xy(self) -> Vector2 { + Vector2::new(self.x, self.y) + } +} + impl From<[T; 2]> for Vector2 { fn from(from: [T; 2]) -> Self { Self {