Make drag selection interactor take selection modes as input

This commit is contained in:
Mikolaj Wielgus 2026-05-27 00:21:13 +02:00
parent 739bdafea2
commit 51005bb97c
10 changed files with 345 additions and 190 deletions

View File

@ -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<CrossingDragSelectionInteractor>,
drag_selection_interactor: Option<DragSelectionInteractor>,
}
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();
}
}

View File

@ -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<i64>,
original_selection: PersistableSelection,
selection: PersistableSelection,
options: SelectionOptions,
}
impl CrossingDragSelectionInteractor {
pub fn new(origin: Vector2<i64>) -> Self {
impl DragSelectionInteractor {
pub fn new(
origin: Vector2<i64>,
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<PersistableSelection> {
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<i64>,
selection: PersistableSelection,
}
impl WindowDragSelectionInteractor {
pub fn new(origin: Vector2<i64>) -> 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())
}
}

View File

@ -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<i64>,
cancel: bool,
}
impl InteractiveInput {
pub fn new(pointer: Vector2<i64>) -> Self {
Self { pointer }
pub fn new(pointer: Vector2<i64>, 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,
}

View File

@ -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<i64>,
) -> Option<ComponentSelector> {
if let Some(joint_id) = self.layout.locate_joints_at_point(layer, point).next() {
pub fn locate_component_at_point(&self, point: Vector3<i64>) -> Option<ComponentSelector> {
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<i64>,
rect: Rect3<i64>,
) -> impl Iterator<Item = ComponentSelector> + '_ {
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<i64>,
rect: Rect3<i64>,
) -> impl Iterator<Item = ComponentSelector> + '_ {
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<i64>) -> Option<PinSelector> {
if let Some(joint_id) = self.layout.locate_joints_at_point(layer, point).next() {
pub fn locate_pin_at_point(&self, point: Vector3<i64>) -> Option<PinSelector> {
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<i64>,
rect: Rect3<i64>,
) -> impl Iterator<Item = PinSelector> + '_ {
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<i64>,
rect: Rect3<i64>,
) -> impl Iterator<Item = PinSelector> + '_ {
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<i64>,
) -> impl Iterator<Item = NetSelector> + '_ {
// TODO
std::iter::empty()
}
pub fn locate_nets_intersecting_rect(
&self,
_rect: Rect3<i64>,
) -> impl Iterator<Item = NetSelector> + '_ {
// TODO
std::iter::empty()
}
pub fn locate_nets_inside_rect(
&self,
_rect: Rect3<i64>,
) -> impl Iterator<Item = NetSelector> + '_ {
// TODO
std::iter::empty()
}
}

View File

@ -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<Item = ComponentSelector>) {
self.0.extend(selectors);
}
pub fn sub(&mut self, selectors: impl IntoIterator<Item = ComponentSelector>) {
for selector in selectors {
self.0.remove(&selector);
} else {
self.0.insert(selector);
}
}
pub fn xor(&mut self, selectors: impl IntoIterator<Item = ComponentSelector>) {
for selector in selectors {
if self.0.contains(&selector) {
self.0.remove(&selector);
} else {
self.0.insert(selector);
}
}
}
}

View File

@ -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<Item = NetSelector>) {
self.0.extend(selectors);
}
pub fn sub(&mut self, selectors: impl IntoIterator<Item = NetSelector>) {
for selector in selectors {
self.0.remove(&selector);
} else {
self.0.insert(selector);
}
}
pub fn xor(&mut self, selectors: impl IntoIterator<Item = NetSelector>) {
for selector in selectors {
if self.0.contains(&selector) {
self.0.remove(&selector);
} else {
self.0.insert(selector);
}
}
}
}

View File

@ -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<Item = PinSelector>) {
self.0.extend(selectors);
}
pub fn sub(&mut self, selectors: impl IntoIterator<Item = PinSelector>) {
for selector in selectors {
self.0.remove(&selector);
} else {
self.0.insert(selector);
}
}
pub fn xor(&mut self, selectors: impl IntoIterator<Item = PinSelector>) {
for selector in selectors {
if self.0.contains(&selector) {
self.0.remove(&selector);
} else {
self.0.insert(selector);
}
}
}
}

View File

@ -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<i64>,
) -> impl Iterator<Item = JointId> {
pub fn locate_joints_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = JointId> {
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<i64>,
rect: Rect3<i64>,
) -> impl Iterator<Item = JointId> {
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<i64>,
) -> impl Iterator<Item = JointId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
pub fn locate_joints_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = JointId> {
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<i64>,
) -> impl Iterator<Item = SegmentId> {
pub fn locate_segments_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = SegmentId> {
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<i64>,
rect: Rect3<i64>,
) -> impl Iterator<Item = SegmentId> {
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<i64>,
) -> impl Iterator<Item = SegmentId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
pub fn locate_segments_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = SegmentId> {
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<i64>,
) -> impl Iterator<Item = ViaId> {
pub fn locate_vias_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = ViaId> {
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<i64>,
) -> impl Iterator<Item = ViaId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
pub fn locate_vias_intersecting_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = ViaId> {
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<i64>,
) -> impl Iterator<Item = ViaId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
pub fn locate_vias_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = ViaId> {
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<i64>,
) -> impl Iterator<Item = PolygonId> {
pub fn locate_polygons_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = PolygonId> {
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<i64>,
rect: Rect3<i64>,
) -> impl Iterator<Item = PolygonId> {
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<i64>,
) -> impl Iterator<Item = PolygonId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
pub fn locate_polygons_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = PolygonId> {
let rect_aabb = rect.aabb3();
self.polygons_rtree
.as_ref()
.locate_in_envelope(&rect_aabb)

View File

@ -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};

View File

@ -31,6 +31,38 @@ impl Rect2<i64> {
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Rect3<T> {
min: Vector3<T>,
max: Vector3<T>,
}
impl<T: Ord + Copy> Rect3<T> {
pub fn new(from: Vector3<T>, to: Vector3<T>) -> 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<i64> {
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<T> {
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<T> {
pub x: T,
pub y: T,
pub z: T,
}
impl<T: Copy> Vector3<T> {
pub fn xy(self) -> Vector2<T> {
Vector2::new(self.x, self.y)
}
}
impl<T: Copy> From<[T; 2]> for Vector2<T> {
fn from(from: [T; 2]) -> Self {
Self {