Compare commits

...

6 Commits

Author SHA1 Message Date
Mikolaj Wielgus 76bad7a2a0 Import and keep track of component names 2026-05-19 15:08:30 +02:00
Mikolaj Wielgus 25efbd1627 Rearrange and rename selections and compounds 2026-05-19 14:05:40 +02:00
Mikolaj Wielgus e536e94039 Add new type of compound, component 2026-05-18 23:29:59 +02:00
Mikolaj Wielgus bc5e205b90 Move nets and pins to separate directory, `compounds` 2026-05-18 22:41:57 +02:00
Mikolaj Wielgus b05c31f767 Use `Index` instead of `Get` with unwrap 2026-05-18 21:52:26 +02:00
Mikolaj Wielgus 79f859f8cb Add (not yet used) private interface to modify joints without segment, via updates 2026-05-18 21:09:47 +02:00
22 changed files with 484 additions and 167 deletions

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{viewport::Viewport, workspace::Workspace};
use topola::{Joint, Polygon, Segment};
use topola::primitives::{Joint, Polygon, Segment};
pub struct Display {}
@ -53,7 +53,10 @@ impl Display {
workspace.appearance_panel.layer_color(
ctx,
board.layer_name(joint.spec.layer),
board.pin_selection_contains_joint(&workspace.pin_selection, joint_id),
board.pin_with_layer_selection_contains_joint(
&workspace.selection.pins,
joint_id,
),
),
);
}
@ -68,7 +71,10 @@ impl Display {
workspace.appearance_panel.layer_color(
ctx,
board.layer_name(segment.layer),
board.pin_selection_contains_segment(&workspace.pin_selection, segment_id),
board.pin_with_layer_selection_contains_segment(
&workspace.selection.pins,
segment_id,
),
),
);
}
@ -85,7 +91,10 @@ impl Display {
workspace.appearance_panel.layer_color(
ctx,
board.layer_name(polygon.layer),
board.pin_selection_contains_polygon(&workspace.pin_selection, polygon_id),
board.pin_with_layer_selection_contains_polygon(
&workspace.selection.pins,
polygon_id,
),
),
);
}

View File

@ -58,7 +58,7 @@ impl Viewport {
.router()
.navmesher_board()
.board()
.point_pin_selector(
.point_pin_with_layer_selector(
0,
Vector2::new(
pointer_scene_pos.x as i64,
@ -66,7 +66,7 @@ impl Viewport {
),
)
{
workspace.pin_selection.toggle(pin_selector);
workspace.selection.pins.toggle(pin_selector);
}
}
}

View File

@ -2,14 +2,14 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use topola::{Autorouter, Board, PinSelection};
use topola::{Autorouter, Board, selections::PersistableSelection};
use crate::{appearance_panel::AppearancePanel, translator::Translator};
pub struct Workspace {
pub autorouter: Autorouter,
pub appearance_panel: AppearancePanel,
pub pin_selection: PinSelection,
pub selection: PersistableSelection,
}
impl Workspace {
@ -19,7 +19,7 @@ impl Workspace {
Self {
autorouter: Autorouter::new(board),
appearance_panel,
pin_selection: PinSelection::new(),
selection: PersistableSelection::new(),
}
}

View File

@ -7,19 +7,24 @@ use derive_getters::{Dissolve, Getters};
use undoredo::{ApplyDelta, Delta, FlushDelta};
use crate::{
layout::{Layout, LayoutHalfDelta, NetId, PinId},
compounds::{ComponentId, NetId, PinId},
layout::{Layout, LayoutHalfDelta},
math::Vector2,
primitives::{
JointId, JointSpec, Polygon, PolygonId, Segment, SegmentId, SegmentSpec, Via, ViaId,
ViaSpec,
},
selection::{PinSelection, PinSelector},
selections::{
ComponentSelection, ComponentSelector, PinWithLayerSelection, PinWithLayerSelector,
},
};
#[derive(Clone, Debug, Getters)]
pub struct Board {
layout: Layout,
#[getter(skip)]
component_names: BiBTreeMap<ComponentId, String>,
#[getter(skip)]
pin_names: BiBTreeMap<PinId, String>,
#[getter(skip)]
layer_names: BiBTreeMap<usize, String>,
@ -31,6 +36,7 @@ impl Board {
pub fn new(boundary: Vec<Vector2<i64>>, layer_count: usize) -> Self {
Self {
layout: Layout::new(boundary.into_iter().map(Into::into).collect(), layer_count),
component_names: BiBTreeMap::new(),
pin_names: BiBTreeMap::new(),
layer_names: BiBTreeMap::new(),
net_names: BiBTreeMap::new(),
@ -45,13 +51,25 @@ impl Board {
) -> Self {
Self {
layout: Layout::new(boundary.into_iter().map(Into::into).collect(), layer_count),
component_names: BiBTreeMap::new(),
pin_names: BiBTreeMap::new(),
layer_names,
net_names,
}
}
pub fn ensure_pin(&mut self, pin_name: String) -> PinId {
pub fn ensure_named_component(&mut self, component_name: String) -> ComponentId {
if let Some(component) = self.component_names.get_by_right(&component_name) {
return *component;
};
let component_id = self.layout.add_component();
self.component_names.insert(component_id, component_name);
component_id
}
pub fn ensure_named_pin(&mut self, pin_name: String) -> PinId {
if let Some(pin) = self.pin_names.get_by_right(&pin_name) {
return *pin;
};
@ -62,6 +80,10 @@ impl Board {
pin_id
}
pub fn add_component(&mut self) -> ComponentId {
self.layout.add_component()
}
pub fn add_joint(&mut self, spec: JointSpec) -> JointId {
self.layout.add_joint(spec)
}
@ -86,19 +108,105 @@ impl Board {
self.layout.add_polygon(polygon)
}
pub fn joint_pin_selector(&self, joint_id: JointId) -> Option<PinSelector> {
let joint = self.layout.joint(joint_id);
pub fn joint_component_selector(&self, id: JointId) -> Option<ComponentSelector> {
let joint = self.layout.joint(id);
Some(PinSelector {
Some(ComponentSelector {
component: self.component_name(joint.spec.component?)?.to_string(),
})
}
pub fn segment_component_selector(&self, id: SegmentId) -> Option<ComponentSelector> {
let segment = self.layout.segment(id);
Some(ComponentSelector {
component: self.component_name(segment.spec.component?)?.to_string(),
})
}
// TODO: Vias.
pub fn polygon_component_selector(&self, id: PolygonId) -> Option<ComponentSelector> {
let polygon = self.layout.polygon(id);
Some(ComponentSelector {
component: self.component_name(polygon.component?)?.to_string(),
})
}
pub fn point_component_selector(
&self,
layer: usize,
point: Vector2<i64>,
) -> Option<ComponentSelector> {
if let Some(joint_id) = self.layout.locate_joints_at_point(layer, point).next() {
return self.joint_component_selector(joint_id);
}
if let Some(segment_id) = self.layout.locate_segments_at_point(layer, point).next() {
return self.segment_component_selector(segment_id);
}
// TODO: Vias.
if let Some(polygon_id) = self.layout.locate_polygons_at_point(layer, point).next() {
return self.polygon_component_selector(polygon_id);
}
None
}
pub fn component_selection_contains_joint(
&self,
selection: &ComponentSelection,
id: JointId,
) -> bool {
let Some(selector) = self.joint_component_selector(id) else {
return false;
};
selection.0.contains(&selector)
}
pub fn component_selection_contains_segment(
&self,
selection: &ComponentSelection,
id: SegmentId,
) -> bool {
let Some(selector) = self.segment_component_selector(id) else {
return false;
};
selection.0.contains(&selector)
}
// TODO: Vias.
pub fn component_selection_contains_polygon(
&self,
selection: &ComponentSelection,
id: PolygonId,
) -> bool {
let Some(selector) = self.polygon_component_selector(id) else {
return false;
};
selection.0.contains(&selector)
}
pub fn joint_pin_with_layer_selector(&self, id: JointId) -> Option<PinWithLayerSelector> {
let joint = self.layout.joint(id);
Some(PinWithLayerSelector {
pin: self.pin_name(joint.spec.pin?)?.to_string(),
layer: self.layer_name(joint.spec.layer)?.to_string(),
})
}
pub fn segment_pin_selector(&self, segment_id: SegmentId) -> Option<PinSelector> {
let segment = self.layout.segment(segment_id);
pub fn segment_pin_with_layer_selector(&self, id: SegmentId) -> Option<PinWithLayerSelector> {
let segment = self.layout.segment(id);
Some(PinSelector {
Some(PinWithLayerSelector {
pin: self.pin_name(segment.spec.pin?)?.to_string(),
layer: self.layer_name(segment.layer)?.to_string(),
})
@ -106,73 +214,85 @@ impl Board {
// TODO: Vias.
pub fn polygon_pin_selector(&self, polygon_id: PolygonId) -> Option<PinSelector> {
let polygon = self.layout.polygon(polygon_id);
pub fn polygon_pin_with_layer_selector(&self, id: PolygonId) -> Option<PinWithLayerSelector> {
let polygon = self.layout.polygon(id);
Some(PinSelector {
Some(PinWithLayerSelector {
pin: self.pin_name(polygon.pin?)?.to_string(),
layer: self.layer_name(polygon.layer)?.to_string(),
})
}
pub fn point_pin_selector(&self, layer: usize, point: Vector2<i64>) -> Option<PinSelector> {
pub fn point_pin_with_layer_selector(
&self,
layer: usize,
point: Vector2<i64>,
) -> Option<PinWithLayerSelector> {
if let Some(joint_id) = self.layout.locate_joints_at_point(layer, point).next() {
return self.joint_pin_selector(joint_id);
return self.joint_pin_with_layer_selector(joint_id);
}
if let Some(segment_id) = self.layout.locate_segments_at_point(layer, point).next() {
return self.segment_pin_selector(segment_id);
return self.segment_pin_with_layer_selector(segment_id);
}
// TODO: Vias.
if let Some(polygon_id) = self.layout.locate_polygons_at_point(layer, point).next() {
return self.polygon_pin_selector(polygon_id);
return self.polygon_pin_with_layer_selector(polygon_id);
}
None
}
pub fn pin_selection_contains_joint(
pub fn pin_with_layer_selection_contains_joint(
&self,
pin_selection: &PinSelection,
joint_id: JointId,
selection: &PinWithLayerSelection,
id: JointId,
) -> bool {
let Some(pin_selector) = self.joint_pin_selector(joint_id) else {
let Some(selector) = self.joint_pin_with_layer_selector(id) else {
return false;
};
pin_selection.0.contains(&pin_selector)
selection.0.contains(&selector)
}
pub fn pin_selection_contains_segment(
pub fn pin_with_layer_selection_contains_segment(
&self,
pin_selection: &PinSelection,
segment_id: SegmentId,
selection: &PinWithLayerSelection,
id: SegmentId,
) -> bool {
let Some(pin_selector) = self.segment_pin_selector(segment_id) else {
let Some(selector) = self.segment_pin_with_layer_selector(id) else {
return false;
};
pin_selection.0.contains(&pin_selector)
selection.0.contains(&selector)
}
// TODO: Vias.
pub fn pin_selection_contains_polygon(
pub fn pin_with_layer_selection_contains_polygon(
&self,
pin_selection: &PinSelection,
polygon_id: PolygonId,
selection: &PinWithLayerSelection,
id: PolygonId,
) -> bool {
let Some(pin_selector) = self.polygon_pin_selector(polygon_id) else {
let Some(selector) = self.polygon_pin_with_layer_selector(id) else {
return false;
};
pin_selection.0.contains(&pin_selector)
selection.0.contains(&selector)
}
pub fn pin_name(&self, pin: PinId) -> Option<&str> {
self.pin_names.get_by_left(&pin).map(String::as_str)
pub fn component_name(&self, id: ComponentId) -> Option<&str> {
self.component_names.get_by_left(&id).map(String::as_str)
}
pub fn component_id(&self, component_name: &str) -> Option<ComponentId> {
self.component_names.get_by_right(component_name).copied()
}
pub fn pin_name(&self, id: PinId) -> Option<&str> {
self.pin_names.get_by_left(&id).map(String::as_str)
}
pub fn pin_id(&self, pin_name: &str) -> Option<PinId> {
@ -187,8 +307,8 @@ impl Board {
self.layer_names.get_by_right(layer_name).copied()
}
pub fn net_name(&self, net: NetId) -> Option<&str> {
self.net_names.get_by_left(&net).map(String::as_str)
pub fn net_name(&self, id: NetId) -> Option<&str> {
self.net_names.get_by_left(&id).map(String::as_str)
}
pub fn net_id(&self, net_name: &str) -> Option<NetId> {

View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use derive_more::Constructor;
use serde::{Deserialize, Serialize};
use crate::primitives::{JointId, PolygonId, SegmentId, ViaId};
#[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct ComponentId(usize);
impl ComponentId {
#[inline]
pub fn index(self) -> usize {
self.0
}
}
#[derive(Clone, Debug)]
pub struct Component {
pub joints: Vec<JointId>,
pub segments: Vec<SegmentId>,
pub vias: Vec<ViaId>,
pub polygons: Vec<PolygonId>,
}
impl Component {
pub fn new() -> Self {
Self {
joints: Vec::new(),
segments: Vec::new(),
vias: Vec::new(),
polygons: Vec::new(),
}
}
}

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
mod component;
mod net;
mod pin;
pub use component::*;
pub use net::*;
pub use pin::*;

View File

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use derive_more::Constructor;
use serde::{Deserialize, Serialize};
#[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct NetId(usize);
impl NetId {
#[inline]
pub fn index(self) -> usize {
self.0
}
}

View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use derive_more::Constructor;
use serde::{Deserialize, Serialize};
use crate::primitives::{JointId, PolygonId, SegmentId, ViaId};
#[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct PinId(usize);
impl PinId {
#[inline]
pub fn index(self) -> usize {
self.0
}
}
#[derive(Clone, Debug)]
pub struct Pin {
pub joints: Vec<JointId>,
pub segments: Vec<SegmentId>,
pub vias: Vec<ViaId>,
pub polygons: Vec<PolygonId>,
}
impl Pin {
pub fn new() -> Self {
Self {
joints: Vec::new(),
segments: Vec::new(),
vias: Vec::new(),
polygons: Vec::new(),
}
}
}

View File

@ -3,66 +3,23 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use derive_getters::Getters;
use derive_more::Constructor;
use rstar::{
AABB, RTree,
primitives::{GeomWithData, Rectangle},
};
use serde::{Deserialize, Serialize};
use stable_vec::StableVec;
use undoredo::aliases::RTreeHalfDelta;
use undoredo::{Delta, Recorder};
use crate::{
Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2, Via, ViaId,
primitives::{JointSpec, SegmentSpec, ViaSpec},
compounds::{Component, ComponentId, Pin, PinId},
math::Vector2,
primitives::{
Joint, JointId, JointSpec, Polygon, PolygonId, Segment, SegmentId, SegmentSpec, Via, ViaId,
ViaSpec,
},
};
#[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct PinId(usize);
impl PinId {
/// Returns the underlying index.
#[inline]
pub fn index(self) -> usize {
self.0
}
}
#[derive(Clone, Debug)]
pub struct Pin {
joints: Vec<JointId>,
segments: Vec<SegmentId>,
vias: Vec<ViaId>,
polygons: Vec<PolygonId>,
}
impl Pin {
pub fn new() -> Self {
Self {
joints: Vec::new(),
segments: Vec::new(),
vias: Vec::new(),
polygons: Vec::new(),
}
}
}
#[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct NetId(usize);
impl NetId {
/// Returns the underlying index.
#[inline]
pub fn index(self) -> usize {
self.0
}
}
#[derive(Delta, Clone, Debug, Getters)]
pub struct Layout {
#[undoredo(skip)]
@ -72,8 +29,8 @@ pub struct Layout {
#[undoredo(skip)]
layer_count: usize,
#[undoredo(skip)]
pins: StableVec<Pin>,
components: Recorder<StableVec<Component>>,
pins: Recorder<StableVec<Pin>>,
joints: Recorder<StableVec<Joint>>,
segments: Recorder<StableVec<Segment>>,
@ -105,7 +62,8 @@ impl Layout {
place_boundary: boundary,
layer_count,
pins: StableVec::new(),
components: Recorder::new(StableVec::new()),
pins: Recorder::new(StableVec::new()),
joints: Recorder::new(StableVec::new()),
segments: Recorder::new(StableVec::new()),
@ -119,6 +77,10 @@ impl Layout {
}
}
pub fn add_component(&mut self) -> ComponentId {
ComponentId::new(self.components.push(Component::new()))
}
pub fn add_pin(&mut self) -> PinId {
PinId::new(self.pins.push(Pin::new()))
}
@ -130,14 +92,22 @@ impl Layout {
vias: Vec::new(),
};
let bbox = joint.bbox();
let component_id = joint.spec.component;
let pin_id = joint.spec.pin;
let joint_id = JointId::new(self.joints.push(joint));
self.joints_rtree
.insert(GeomWithData::new(bbox, joint_id), ());
if let Some(component_id) = component_id {
self.components.modify(component_id.index(), |component| {
component.joints.push(joint_id)
});
}
if let Some(pin_id) = pin_id {
self.pins[pin_id.index()].joints.push(joint_id);
self.pins
.modify(pin_id.index(), |pin| pin.joints.push(joint_id));
}
joint_id
@ -147,15 +117,8 @@ impl Layout {
where
F: FnOnce(&mut JointSpec),
{
let old_joint = self.joints.get(&id.index()).unwrap();
self.joints_rtree
.remove(&GeomWithData::new(old_joint.bbox(), id));
self.joints.modify(id.index(), |joint| f(&mut joint.spec));
let new_joint = self.joints.get(&id.index()).unwrap().clone();
self.joints_rtree
.insert(GeomWithData::new(new_joint.bbox(), id), ());
self.modify_joint_raw(id, |joint| f(&mut joint.spec));
let new_joint = self.joints[id.index()].clone();
for &segment in &new_joint.segments {
self.update_segment(segment);
@ -166,6 +129,21 @@ impl Layout {
}
}
fn modify_joint_raw<F>(&mut self, id: JointId, f: F)
where
F: FnOnce(&mut Joint),
{
let old_joint = &self.joints[id.index()];
self.joints_rtree
.remove(&GeomWithData::new(old_joint.bbox(), id));
self.joints.modify(id.index(), |joint| f(joint));
let new_joint = self.joints[id.index()].clone();
self.joints_rtree
.insert(GeomWithData::new(new_joint.bbox(), id), ());
}
pub fn add_segment(&mut self, spec: SegmentSpec) -> SegmentId {
self.add_segment_raw(Segment {
spec,
@ -179,6 +157,7 @@ impl Layout {
}
pub fn add_segment_raw(&mut self, segment: Segment) -> SegmentId {
let component_id = segment.spec.component;
let pin_id = segment.spec.pin;
let bbox = segment.bbox();
let segment_id = SegmentId::new(self.segments.push(segment));
@ -195,8 +174,15 @@ impl Layout {
self.segments_rtree
.insert(GeomWithData::new(bbox, segment_id), ());
if let Some(component_id) = component_id {
self.components.modify(component_id.index(), |component| {
component.segments.push(segment_id)
});
}
if let Some(pin_id) = pin_id {
self.pins[pin_id.index()].segments.push(segment_id);
self.pins
.modify(pin_id.index(), |pin| pin.segments.push(segment_id));
}
segment_id
@ -206,27 +192,27 @@ impl Layout {
where
F: FnOnce(&mut SegmentSpec),
{
let old_segment = self.segments.get(&id.index()).unwrap();
let old_segment = &self.segments[id.index()];
self.segments_rtree
.remove(&GeomWithData::new(old_segment.bbox(), id));
self.segments
.modify(id.index(), |segment| f(&mut segment.spec));
let new_segment = self.segments.get(&id.index()).unwrap();
let new_segment = &self.segments[id.index()];
self.segments_rtree
.insert(GeomWithData::new(new_segment.bbox(), id), ());
}
fn update_segment(&mut self, id: SegmentId) {
let old_segment = self.segments.get(&id.index()).unwrap();
let old_segment = &self.segments[id.index()];
self.segments_rtree
.remove(&GeomWithData::new(old_segment.bbox(), id));
let endjoint_ids = old_segment.spec.endjoints;
let endjoint_specs = [
self.joints.get(&endjoint_ids[0].index()).unwrap().spec,
self.joints.get(&endjoint_ids[1].index()).unwrap().spec,
self.joints[endjoint_ids[0].index()].spec,
self.joints[endjoint_ids[1].index()].spec,
];
self.segments.modify(id.index(), |segment| {
segment.endpoints = [endjoint_specs[0].position, endjoint_specs[1].position];
@ -234,7 +220,7 @@ impl Layout {
segment.net = endjoint_specs[0].net;
});
let new_segment = self.segments.get(&id.index()).unwrap();
let new_segment = &self.segments[id.index()];
self.segments_rtree
.insert(GeomWithData::new(new_segment.bbox(), id), ());
}
@ -253,6 +239,7 @@ impl Layout {
pub fn add_via_raw(&mut self, via: Via) -> ViaId {
let bbox = via.bbox();
let component_id = via.spec.component;
let pin_id = via.spec.pin;
let via_id = ViaId::new(self.vias.push(via));
@ -265,8 +252,15 @@ impl Layout {
self.vias_rtree.insert(GeomWithData::new(bbox, via_id), ());
if let Some(component_id) = component_id {
self.components.modify(component_id.index(), |component| {
component.vias.push(via_id)
});
}
if let Some(pin_id) = pin_id {
self.pins[pin_id.index()].vias.push(via_id);
self.pins
.modify(pin_id.index(), |pin| pin.vias.push(via_id));
}
via_id
@ -276,26 +270,26 @@ impl Layout {
where
F: FnOnce(&mut ViaSpec),
{
let old_via = self.vias.get(&id.index()).unwrap();
let old_via = &self.vias[id.index()];
self.vias_rtree
.remove(&GeomWithData::new(old_via.bbox(), id));
self.vias.modify(id.index(), |via| f(&mut via.spec));
let new_via = self.vias.get(&id.index()).unwrap();
let new_via = &self.vias[id.index()];
self.vias_rtree
.insert(GeomWithData::new(new_via.bbox(), id), ());
}
fn update_via(&mut self, id: ViaId) {
let old_via = self.vias.get(&id.index()).unwrap();
let old_via = &self.vias[id.index()];
self.vias_rtree
.remove(&GeomWithData::new(old_via.bbox(), id));
let endjoint_ids = old_via.spec.endjoints;
let endjoint_specs = [
self.joints.get(&endjoint_ids[0].index()).unwrap().spec,
self.joints.get(&endjoint_ids[1].index()).unwrap().spec,
self.joints[endjoint_ids[0].index()].spec,
self.joints[endjoint_ids[1].index()].spec,
];
self.vias.modify(id.index(), |via| {
via.position = endjoint_specs[0].position;
@ -304,21 +298,29 @@ impl Layout {
via.net = endjoint_specs[0].net;
});
let new_via = self.vias.get(&id.index()).unwrap();
let new_via = &self.vias[id.index()];
self.vias_rtree
.insert(GeomWithData::new(new_via.bbox(), id), ());
}
pub fn add_polygon(&mut self, polygon: Polygon) -> PolygonId {
let bbox = polygon.bbox();
let component_id = polygon.component;
let pin_id = polygon.pin;
let polygon_id = PolygonId::new(self.polygons.push(polygon));
self.polygons_rtree
.insert(GeomWithData::new(bbox, polygon_id), ());
if let Some(component_id) = component_id {
self.components.modify(component_id.index(), |component| {
component.polygons.push(polygon_id)
});
}
if let Some(pin_id) = pin_id {
self.pins[pin_id.index()].polygons.push(polygon_id);
self.pins
.modify(pin_id.index(), |pin| pin.polygons.push(polygon_id));
}
polygon_id
@ -328,13 +330,13 @@ impl Layout {
where
F: FnOnce(&mut Polygon),
{
let old_polygon = self.polygons.get(&id.index()).unwrap();
let old_polygon = &self.polygons[id.index()];
self.polygons_rtree
.remove(&GeomWithData::new(old_polygon.bbox(), id));
self.polygons.modify(id.index(), |polygon| f(polygon));
let new_polygon = self.polygons.get(&id.index()).unwrap();
let new_polygon = &self.polygons[id.index()];
self.polygons_rtree
.insert(GeomWithData::new(new_polygon.bbox(), id), ());
}
@ -348,12 +350,7 @@ impl Layout {
.as_ref()
.locate_all_at_point(&[point.x, point.y, layer as i64])
.map(|geom_with_data| geom_with_data.data)
.filter(move |&joint_id| {
self.joints
.get(&joint_id.index())
.unwrap()
.contains_point(point)
})
.filter(move |&joint_id| self.joints[joint_id.index()].contains_point(point))
}
pub fn locate_segments_at_point(
@ -379,12 +376,7 @@ impl Layout {
.as_ref()
.locate_all_at_point(&[point.x, point.y, layer as i64])
.map(|geom_with_data| geom_with_data.data)
.filter(move |&polygon_id| {
self.polygons
.get(&polygon_id.index())
.unwrap()
.contains_point(point)
})
.filter(move |&polygon_id| self.polygons[polygon_id.index()].contains_point(point))
}
pub fn layer_joints(&self, layer: usize) -> impl Iterator<Item = JointId> + '_ {
@ -422,15 +414,15 @@ impl Layout {
}
pub fn joint(&self, joint_id: JointId) -> &Joint {
self.joints.get(&joint_id.index()).unwrap()
&self.joints[joint_id.index()]
}
pub fn segment(&self, segment_id: SegmentId) -> &Segment {
self.segments.get(&segment_id.index()).unwrap()
&self.segments[segment_id.index()]
}
pub fn polygon(&self, polygon_id: PolygonId) -> &Polygon {
self.polygons.get(&polygon_id.index()).unwrap()
&self.polygons[polygon_id.index()]
}
pub fn pin(&self, pin_id: PinId) -> &Pin {

View File

@ -4,23 +4,21 @@
mod autorouter;
mod board;
mod compounds;
mod drawer;
mod layout;
mod math;
mod navmesher;
mod pathfinder;
mod primitives;
pub mod primitives;
mod ratsnest;
mod router;
mod selection;
pub mod selections;
mod specctra;
pub use crate::autorouter::Autorouter;
pub use crate::board::Board;
pub use crate::compounds::{Pin, PinId};
pub use crate::layout::Layout;
pub use crate::math::Vector2;
pub use crate::primitives::{
Joint, JointId, Polygon, PolygonId, PrimitiveId, Segment, SegmentId, Via, ViaId,
};
pub use crate::ratsnest::{Ratline, Ratsnest};
pub use crate::selection::{PinSelection, PinSelector};

View File

@ -10,7 +10,9 @@ use stable_vec::StableVec;
use undoredo::Recorder;
use crate::{
Board, Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2, primitives::JointSpec,
Board,
math::Vector2,
primitives::{Joint, JointId, JointSpec, Polygon, PolygonId, Segment, SegmentId},
};
#[derive(

View File

@ -6,7 +6,7 @@ use derive_more::Constructor;
use rstar::{AABB, primitives::Rectangle};
use serde::{Deserialize, Serialize};
use crate::layout::{NetId, PinId};
use crate::compounds::{ComponentId, NetId, PinId};
use crate::math::Vector2;
use crate::primitives::{SegmentId, ViaId};
@ -29,6 +29,7 @@ pub struct JointSpec {
pub layer: usize,
pub radius: u64,
pub net: NetId,
pub component: Option<ComponentId>,
pub pin: Option<PinId>,
}

View File

@ -9,10 +9,10 @@ mod polygon;
mod segment;
mod via;
pub use joint::{Joint, JointId, JointSpec};
pub use polygon::{Polygon, PolygonId};
pub use segment::{Segment, SegmentId, SegmentSpec};
pub use via::{Via, ViaId, ViaSpec};
pub use joint::*;
pub use polygon::*;
pub use segment::*;
pub use via::*;
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum PrimitiveId {

View File

@ -6,7 +6,7 @@ use derive_more::Constructor;
use rstar::{AABB, Envelope, primitives::Rectangle};
use serde::{Deserialize, Serialize};
use crate::layout::{NetId, PinId};
use crate::compounds::{ComponentId, NetId, PinId};
use crate::math::Vector2;
#[derive(
@ -27,6 +27,7 @@ pub struct Polygon {
pub vertices: Vec<Vector2<i64>>,
pub layer: usize,
pub net: NetId,
pub component: Option<ComponentId>,
pub pin: Option<PinId>,
}

View File

@ -6,7 +6,7 @@ use derive_more::Constructor;
use rstar::primitives::Rectangle;
use serde::{Deserialize, Serialize};
use crate::layout::{NetId, PinId};
use crate::compounds::{ComponentId, NetId, PinId};
use crate::math::Vector2;
use crate::primitives::JointId;
@ -27,6 +27,7 @@ impl SegmentId {
pub struct SegmentSpec {
pub endjoints: [JointId; 2],
pub half_width: u64,
pub component: Option<ComponentId>,
pub pin: Option<PinId>,
}

View File

@ -6,10 +6,9 @@ use derive_more::Constructor;
use rstar::{AABB, primitives::Rectangle};
use serde::{Deserialize, Serialize};
use crate::layout::{NetId, PinId};
use crate::compounds::{ComponentId, NetId, PinId};
use crate::math::Vector2;
use super::joint::JointId;
use crate::primitives::JointId;
#[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
@ -28,6 +27,7 @@ impl ViaId {
pub struct ViaSpec {
pub endjoints: [JointId; 2],
pub radius: u64,
pub component: Option<ComponentId>,
pub pin: Option<PinId>,
}

View File

@ -9,7 +9,10 @@ use serde::{Deserialize, Serialize};
use spade::{DelaunayTriangulation, HasPosition, Triangulation, handles::FixedVertexHandle};
use crate::{
Board, JointId, PolygonId, SegmentId, Vector2, layout::NetId, primitives::PrimitiveId,
Board,
compounds::NetId,
math::Vector2,
primitives::{JointId, PolygonId, PrimitiveId, SegmentId},
};
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]

View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct ComponentSelector {
pub component: String,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentSelection(pub BTreeSet<ComponentSelector>);
impl ComponentSelection {
pub fn new() -> Self {
Self(BTreeSet::new())
}
pub fn toggle(&mut self, selector: ComponentSelector) {
if self.0.contains(&selector) {
self.0.remove(&selector);
} else {
self.0.insert(selector);
}
}
}

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
mod component;
mod persistable;
mod pin_with_layer;
pub use component::*;
pub use persistable::*;
pub use pin_with_layer::*;

View File

@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::{Deserialize, Serialize};
use crate::selections::{ComponentSelection, PinWithLayerSelection};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PersistableSelection {
pub components: ComponentSelection,
pub pins: PinWithLayerSelection,
}
impl PersistableSelection {
pub fn new() -> Self {
Self {
components: ComponentSelection::new(),
pins: PinWithLayerSelection::new(),
}
}
}

View File

@ -7,24 +7,24 @@ use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct PinSelector {
pub struct PinWithLayerSelector {
pub pin: String,
pub layer: String,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PinSelection(pub BTreeSet<PinSelector>);
pub struct PinWithLayerSelection(pub BTreeSet<PinWithLayerSelector>);
impl PinSelection {
impl PinWithLayerSelection {
pub fn new() -> Self {
Self(BTreeSet::new())
}
pub fn toggle(&mut self, pin_selector: PinSelector) {
if self.0.contains(&pin_selector) {
self.0.remove(&pin_selector);
pub fn toggle(&mut self, selector: PinWithLayerSelector) {
if self.0.contains(&selector) {
self.0.remove(&selector);
} else {
self.0.insert(pin_selector);
self.0.insert(selector);
}
}
}

View File

@ -11,10 +11,10 @@ use specctra::{
};
use crate::{
Segment, Vector2,
board::Board,
layout::{NetId, PinId},
primitives::{JointSpec, Polygon, SegmentSpec},
compounds::{ComponentId, NetId, PinId},
math::Vector2,
primitives::{JointSpec, Polygon, Segment, SegmentSpec},
};
impl Board {
@ -97,6 +97,8 @@ impl Board {
.unwrap();
for place in &component.places {
let component_id = board.ensure_named_component(place.name.clone());
let place_side_is_front = place.side == "front";
let get_layer = |board: &Board, name: &str| {
Self::layer(board, &dsn.pcb.structure.layers, name, place_side_is_front)
@ -109,7 +111,7 @@ impl Board {
continue;
};
let pin_id = board.ensure_pin(pin_name.clone());
let pin_id = board.ensure_named_pin(pin_name.clone());
let padstack = dsn.pcb.library.find_padstack_by_name(&pin.name).unwrap();
for shape in padstack.shapes.iter() {
@ -123,6 +125,7 @@ impl Board {
(circle.diameter / 2.0) as u64,
layer,
net,
Some(component_id),
Some(pin_id),
!place_side_is_front,
)
@ -139,6 +142,7 @@ impl Board {
rect.y2,
layer,
net,
Some(component_id),
Some(pin_id),
!place_side_is_front,
)
@ -153,6 +157,7 @@ impl Board {
path.width,
layer,
net,
Some(component_id),
Some(pin_id),
!place_side_is_front,
)
@ -167,6 +172,7 @@ impl Board {
polygon.width,
layer,
net,
Some(component_id),
Some(pin_id),
!place_side_is_front,
)
@ -197,6 +203,7 @@ impl Board {
layer,
net,
None,
None,
false,
)
}
@ -213,6 +220,7 @@ impl Board {
layer,
net,
None,
None,
false,
)
}
@ -227,6 +235,7 @@ impl Board {
layer,
net,
None,
None,
false,
)
}
@ -241,6 +250,7 @@ impl Board {
layer,
net,
None,
None,
false,
)
}
@ -261,6 +271,7 @@ impl Board {
layer,
net,
None,
None,
false,
);
}
@ -275,6 +286,7 @@ impl Board {
radius: u64,
layer: usize,
net: NetId,
component: Option<ComponentId>,
pin: Option<PinId>,
flip: bool,
) {
@ -282,6 +294,7 @@ impl Board {
position: Self::pos(place, pin_pos, 0.0, 0.0, flip),
layer,
net,
component,
pin,
radius,
});
@ -297,6 +310,7 @@ impl Board {
y2: f64,
layer: usize,
net: NetId,
component: Option<ComponentId>,
pin: Option<PinId>,
flip: bool,
) {
@ -309,6 +323,7 @@ impl Board {
],
layer,
net,
component,
pin,
});
}
@ -321,6 +336,7 @@ impl Board {
width: f64,
layer: usize,
net: NetId,
component: Option<ComponentId>,
pin: Option<PinId>,
flip: bool,
) {
@ -331,6 +347,7 @@ impl Board {
layer,
radius: (width / 2.0) as u64,
net,
component,
pin,
});
@ -347,6 +364,7 @@ impl Board {
layer,
radius: (width / 2.0) as u64,
net,
component,
pin,
});
@ -355,6 +373,7 @@ impl Board {
spec: SegmentSpec {
endjoints: [prev_joint, joint],
half_width: (width / 2.0) as u64,
component,
pin,
},
endpoints: [prev_pos, pos],
@ -375,6 +394,7 @@ impl Board {
_width: f64,
layer: usize,
net: NetId,
component: Option<ComponentId>,
pin: Option<PinId>,
flip: bool,
) {
@ -386,6 +406,7 @@ impl Board {
vertices,
layer,
net,
component,
pin,
});
}