Add location methods for inside rect and intersecting rect

I did some renames while at it.
This commit is contained in:
Mikolaj Wielgus 2026-05-26 01:27:06 +02:00
parent d9a4c7a11f
commit 1b4bb49a89
10 changed files with 265 additions and 72 deletions

View File

@ -54,7 +54,7 @@ impl Display {
workspace.appearance_panel.layer_color(
ctx,
board.layer_desc(joint.spec.layer),
board.pin_selection_contains_joint(&workspace.selection.pins, joint_id),
board.pins_contain_joint(&workspace.selection.pins, joint_id),
),
);
}
@ -69,7 +69,7 @@ impl Display {
workspace.appearance_panel.layer_color(
ctx,
board.layer_desc(segment.layer),
board.pin_selection_contains_segment(&workspace.selection.pins, segment_id),
board.pins_contain_segment(&workspace.selection.pins, segment_id),
),
);
}
@ -84,7 +84,7 @@ impl Display {
workspace.appearance_panel.layer_color(
ctx,
board.layer_desc(layer),
board.pin_selection_contains_via(&workspace.selection.pins, via_id),
board.pins_contain_via(&workspace.selection.pins, via_id),
),
);
}
@ -99,7 +99,7 @@ impl Display {
workspace.appearance_panel.layer_color(
ctx,
board.layer_desc(polygon.layer),
board.pin_selection_contains_polygon(&workspace.selection.pins, polygon_id),
board.pins_contain_polygon(&workspace.selection.pins, polygon_id),
),
);
}

View File

@ -6,7 +6,7 @@ use std::collections::BTreeMap;
use egui::{Context, Grid, ScrollArea, SidePanel, widget_text::WidgetText};
use serde::{Deserialize, Serialize};
use topola::{Board, LayerDesc, LayerId, LayerTier, LayerType};
use topola::{Board, LayerDesc, LayerId, LayerSide, LayerType};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Colors {
@ -59,8 +59,8 @@ impl LayersPanel {
};
let color = match layer_desc.typ {
LayerType::Copper => match layer_desc.tier {
LayerTier::Top => Some((
LayerType::Copper => match layer_desc.side {
LayerSide::Top => Some((
LayerColors {
normal: egui::Color32::from_rgb(255, 52, 52),
highlighted: egui::Color32::from_rgb(255, 100, 100),
@ -70,7 +70,7 @@ impl LayersPanel {
highlighted: egui::Color32::from_rgb(255, 52, 52),
},
)),
LayerTier::Bottom => Some((
LayerSide::Bottom => Some((
LayerColors {
normal: egui::Color32::from_rgb(52, 52, 255),
highlighted: egui::Color32::from_rgb(100, 100, 255),
@ -80,7 +80,7 @@ impl LayersPanel {
highlighted: egui::Color32::from_rgb(52, 52, 255),
},
)),
LayerTier::Inner => (layer_desc.index % 2 == 0)
LayerSide::Inner => (layer_desc.index % 2 == 0)
.then_some((
LayerColors {
normal: egui::Color32::from_rgb(127, 200, 127),

View File

@ -14,7 +14,7 @@ pub enum LayerType {
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum LayerTier {
pub enum LayerSide {
Top,
Inner,
Bottom,
@ -23,22 +23,22 @@ pub enum LayerTier {
#[derive(Clone, Constructor, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct LayerDesc {
pub typ: LayerType,
pub tier: LayerTier,
pub side: LayerSide,
pub index: usize,
}
impl Display for LayerDesc {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.typ {
LayerType::Copper => match self.tier {
LayerTier::Top => write!(f, "F.Cu"),
LayerTier::Bottom => write!(f, "B.Cu"),
LayerTier::Inner => write!(f, "In{}.Cu", self.index.saturating_sub(1)),
LayerType::Copper => match self.side {
LayerSide::Top => write!(f, "F.Cu"),
LayerSide::Bottom => write!(f, "B.Cu"),
LayerSide::Inner => write!(f, "In{}.Cu", self.index.saturating_sub(1)),
},
LayerType::Outline => match self.tier {
LayerTier::Top => write!(f, "outlines.top"),
LayerTier::Bottom => write!(f, "outlines.bottom"),
LayerTier::Inner => write!(f, "outlines.{}", self.index),
LayerType::Outline => match self.side {
LayerSide::Top => write!(f, "F.Outl"),
LayerSide::Bottom => write!(f, "B.Outl"),
LayerSide::Inner => write!(f, "In{}.Outl", self.index),
},
}
}

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{
Vector2,
Rect2, Vector2,
board::Board,
layout::LayerId,
selections::{ComponentSelector, PinSelector},
@ -34,6 +34,56 @@ impl Board {
None
}
pub fn locate_component_intersecting_rect(
&self,
layer: LayerId,
rect: Rect2<i64>,
) -> impl Iterator<Item = ComponentSelector> + '_ {
self.layout
.locate_joints_intersecting_rect(layer, rect)
.filter_map(|joint_id| self.joint_component_selector(joint_id))
.chain(
self.layout
.locate_segments_intersecting_rect(layer, rect)
.filter_map(|segment_id| self.segment_component_selector(segment_id)),
)
.chain(
self.layout
.locate_vias_intersecting_rect(layer, rect)
.filter_map(|via_id| self.via_component_selector(via_id)),
)
.chain(
self.layout
.locate_polygons_intersecting_rect(layer, rect)
.filter_map(|polygon_id| self.polygon_component_selector(polygon_id)),
)
}
pub fn locate_component_inside_rect(
&self,
layer: LayerId,
rect: Rect2<i64>,
) -> impl Iterator<Item = ComponentSelector> + '_ {
self.layout
.locate_joints_inside_rect(layer, rect)
.filter_map(|joint_id| self.joint_component_selector(joint_id))
.chain(
self.layout
.locate_segments_inside_rect(layer, rect)
.filter_map(|segment_id| self.segment_component_selector(segment_id)),
)
.chain(
self.layout
.locate_vias_inside_rect(layer, rect)
.filter_map(|via_id| self.via_component_selector(via_id)),
)
.chain(
self.layout
.locate_polygons_inside_rect(layer, 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() {
return self.joint_pin_selector(joint_id);
@ -53,4 +103,54 @@ impl Board {
None
}
pub fn locate_pin_intersecting_rect(
&self,
layer: LayerId,
rect: Rect2<i64>,
) -> impl Iterator<Item = PinSelector> + '_ {
self.layout
.locate_joints_intersecting_rect(layer, rect)
.filter_map(|joint_id| self.joint_pin_selector(joint_id))
.chain(
self.layout
.locate_segments_intersecting_rect(layer, rect)
.filter_map(|segment_id| self.segment_pin_selector(segment_id)),
)
.chain(
self.layout
.locate_vias_intersecting_rect(layer, rect)
.filter_map(|via_id| self.via_pin_selector(via_id)),
)
.chain(
self.layout
.locate_polygons_intersecting_rect(layer, rect)
.filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)),
)
}
pub fn locate_pin_inside_rect(
&self,
layer: LayerId,
rect: Rect2<i64>,
) -> impl Iterator<Item = PinSelector> + '_ {
self.layout
.locate_joints_inside_rect(layer, rect)
.filter_map(|joint_id| self.joint_pin_selector(joint_id))
.chain(
self.layout
.locate_segments_inside_rect(layer, rect)
.filter_map(|segment_id| self.segment_pin_selector(segment_id)),
)
.chain(
self.layout
.locate_vias_inside_rect(layer, rect)
.filter_map(|via_id| self.via_pin_selector(via_id)),
)
.chain(
self.layout
.locate_polygons_inside_rect(layer, rect)
.filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)),
)
}
}

View File

@ -9,7 +9,7 @@ mod select;
pub mod selections;
mod transforms;
pub use crate::board::layer::{LayerDesc, LayerTier, LayerType};
pub use crate::board::layer::{LayerDesc, LayerSide, LayerType};
use bidimap::BiBTreeMap;
use derive_getters::Getters;

View File

@ -7,16 +7,11 @@ use crate::{
Board,
selections::{ComponentSelection, ComponentSelector, PinSelection, PinSelector},
},
layout::LayerId,
math::Vector2,
primitives::{JointId, PolygonId, SegmentId, ViaId},
};
impl Board {
pub fn pin_selection_to_component_selection(
&mut self,
pin_selection: PinSelection,
) -> ComponentSelection {
pub fn pins_to_components(&mut self, pin_selection: PinSelection) -> ComponentSelection {
let mut component_selection = ComponentSelection::new();
for selector in pin_selection.0 {
@ -80,11 +75,7 @@ impl Board {
component_selection
}
pub fn component_selection_contains_joint(
&self,
selection: &ComponentSelection,
id: JointId,
) -> bool {
pub fn components_contain_joint(&self, selection: &ComponentSelection, id: JointId) -> bool {
let Some(selector) = self.joint_component_selector(id) else {
return false;
};
@ -92,7 +83,7 @@ impl Board {
selection.0.contains(&selector)
}
pub fn component_selection_contains_segment(
pub fn components_contain_segment(
&self,
selection: &ComponentSelection,
id: SegmentId,
@ -104,11 +95,7 @@ impl Board {
selection.0.contains(&selector)
}
pub fn component_selection_contains_via(
&self,
selection: &ComponentSelection,
id: ViaId,
) -> bool {
pub fn components_contain_via(&self, selection: &ComponentSelection, id: ViaId) -> bool {
let Some(selector) = self.via_component_selector(id) else {
return false;
};
@ -116,7 +103,7 @@ impl Board {
selection.0.contains(&selector)
}
pub fn component_selection_contains_polygon(
pub fn components_contain_polygon(
&self,
selection: &ComponentSelection,
id: PolygonId,
@ -160,7 +147,7 @@ impl Board {
})
}
pub fn pin_selection_contains_joint(&self, selection: &PinSelection, id: JointId) -> bool {
pub fn pins_contain_joint(&self, selection: &PinSelection, id: JointId) -> bool {
let Some(selector) = self.joint_pin_selector(id) else {
return false;
};
@ -168,7 +155,7 @@ impl Board {
selection.0.contains(&selector)
}
pub fn pin_selection_contains_segment(&self, selection: &PinSelection, id: SegmentId) -> bool {
pub fn pins_contain_segment(&self, selection: &PinSelection, id: SegmentId) -> bool {
let Some(selector) = self.segment_pin_selector(id) else {
return false;
};
@ -176,7 +163,7 @@ impl Board {
selection.0.contains(&selector)
}
pub fn pin_selection_contains_via(&self, selection: &PinSelection, id: ViaId) -> bool {
pub fn pins_contain_via(&self, selection: &PinSelection, id: ViaId) -> bool {
let Some(selector) = self.via_pin_selector(id) else {
return false;
};
@ -184,7 +171,7 @@ impl Board {
selection.0.contains(&selector)
}
pub fn pin_selection_contains_polygon(&self, selection: &PinSelection, id: PolygonId) -> bool {
pub fn pins_contain_polygon(&self, selection: &PinSelection, id: PolygonId) -> bool {
let Some(selector) = self.polygon_pin_selector(id) else {
return false;
};
@ -192,26 +179,6 @@ impl Board {
selection.0.contains(&selector)
}
pub fn point_pin_selector(&self, layer: LayerId, point: Vector2<i64>) -> Option<PinSelector> {
if let Some(joint_id) = self.layout.locate_joints_at_point(layer, point).next() {
return self.joint_pin_selector(joint_id);
}
if let Some(segment_id) = self.layout.locate_segments_at_point(layer, point).next() {
return self.segment_pin_selector(segment_id);
}
if let Some(via_id) = self.layout.locate_vias_at_point(layer, point).next() {
return self.via_pin_selector(via_id);
}
if let Some(polygon_id) = self.layout.locate_polygons_at_point(layer, point).next() {
return self.polygon_pin_selector(polygon_id);
}
None
}
pub fn joint_pin_selector(&self, id: JointId) -> Option<PinSelector> {
let joint = self.layout.joint(id);

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{
Vector2,
Rect2, Vector2,
layout::{LayerId, Layout},
primitives::{JointId, PolygonId, SegmentId, ViaId},
};
@ -21,6 +21,31 @@ impl Layout {
.filter(move |&joint_id| self.joints[joint_id.index()].contains_point(point))
}
pub fn locate_joints_intersecting_rect(
&self,
layer: LayerId,
rect: Rect2<i64>,
) -> impl Iterator<Item = JointId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
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);
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,
@ -33,6 +58,31 @@ impl Layout {
.filter(move |&segment_id| self.segment(segment_id).contains_point(point))
}
pub fn locate_segments_intersecting_rect(
&self,
layer: LayerId,
rect: Rect2<i64>,
) -> impl Iterator<Item = SegmentId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
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);
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,
@ -45,6 +95,34 @@ impl Layout {
.filter(move |&via_id| self.vias[via_id.index()].contains_point(layer, point))
}
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);
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);
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,
@ -56,4 +134,29 @@ impl Layout {
.map(|geom_with_data| geom_with_data.data)
.filter(move |&polygon_id| self.polygons[polygon_id.index()].contains_point(point))
}
pub fn locate_polygons_intersecting_rect(
&self,
layer: LayerId,
rect: Rect2<i64>,
) -> impl Iterator<Item = PolygonId> {
let rect_aabb = rect.aabb3(layer.index() as i64);
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);
self.polygons_rtree
.as_ref()
.locate_in_envelope(&rect_aabb)
.map(|geom_with_data| geom_with_data.data)
}
}

View File

@ -16,12 +16,12 @@ mod specctra;
pub use crate::autorouter::Autorouter;
pub use crate::board::Board;
pub use crate::board::LayerDesc;
pub use crate::board::LayerTier;
pub use crate::board::LayerSide;
pub use crate::board::LayerType;
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::Vector2;
pub use crate::math::{Rect2, Vector2};
pub use crate::ratsnest::{Ratline, Ratsnest};

View File

@ -2,12 +2,35 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use derive_getters::Getters;
use derive_more::{
Add, AddAssign, Constructor, Div, DivAssign, From, Into, Mul, MulAssign, Sub, SubAssign,
};
use polygon_unionfind::UnionFind;
use rstar::AABB;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Rect2<T> {
min: Vector2<T>,
max: Vector2<T>,
}
impl<T: Ord + Copy> Rect2<T> {
pub fn new(from: Vector2<T>, to: Vector2<T>) -> Self {
Self {
min: Vector2::new(std::cmp::min(from.x, to.x), std::cmp::min(from.y, to.y)),
max: Vector2::new(std::cmp::max(from.x, to.x), std::cmp::max(from.y, to.y)),
}
}
}
impl Rect2<i64> {
pub fn aabb3(self, z: i64) -> AABB<[i64; 3]> {
AABB::from_corners([self.min.x, self.min.y, z], [self.max.x, self.max.y, z])
}
}
#[derive(
Add,
AddAssign,

View File

@ -11,7 +11,7 @@ use specctra::{
};
use crate::{
board::{Board, LayerDesc, LayerTier, LayerType},
board::{Board, LayerDesc, LayerSide, LayerType},
layout::LayerId,
layout::compounds::{ComponentId, NetId, PinId},
math::Vector2,
@ -31,11 +31,11 @@ impl Board {
BiBTreeMap::from_iter(dsn.pcb.structure.layers.iter().enumerate().map(
|(index, _layer)| {
let tier = if index == 0 {
LayerTier::Top
LayerSide::Top
} else if index + 1 == dsn.pcb.structure.layers.len() {
LayerTier::Bottom
LayerSide::Bottom
} else {
LayerTier::Inner
LayerSide::Inner
};
(
LayerId::new(index + pcb_layer_offset),
@ -48,7 +48,7 @@ impl Board {
top_outline_layer_id,
LayerDesc::new(
LayerType::Outline,
LayerTier::Top,
LayerSide::Top,
top_outline_layer_id.index(),
),
);
@ -56,7 +56,7 @@ impl Board {
bottom_outline_layer_id,
LayerDesc::new(
LayerType::Outline,
LayerTier::Bottom,
LayerSide::Bottom,
bottom_outline_layer_id.index(),
),
);