diff --git a/topola/src/board/mod.rs b/topola/src/board/mod.rs index 1531802..44fed60 100644 --- a/topola/src/board/mod.rs +++ b/topola/src/board/mod.rs @@ -22,7 +22,7 @@ use undoredo::{Delta, Recorder}; use crate::{ layout::{ LayerId, Layout, LayoutHalfDelta, - compounds::{ComponentId, NetId, PinId}, + compounds::{ComponentId, NetId, PinId, PinSpec}, }, vector::Vector2, }; @@ -43,7 +43,11 @@ pub struct Board { impl Board { /*pub fn new(boundary: Vec>, layer_count: usize) -> Self { Self { - layout: Layout::new(boundary.into_iter().map(Into::into).collect(), layer_count), + layout: Layout::new( + boundary.into_iter().map(Into::into).collect(), + layer_count, + 0, + ), component_names: Recorder::new(BiBTreeMap::new()), pin_names: Recorder::new(BiBTreeMap::new()), layer_descs: Recorder::new(BiBTreeMap::new()), @@ -60,6 +64,7 @@ impl Board { layout: Layout::new( boundary.into_iter().map(Into::into).collect(), layer_descs.len(), + net_names.len(), ), component_names: Recorder::new(BiBTreeMap::new()), pin_names: Recorder::new(BiBTreeMap::new()), @@ -79,12 +84,12 @@ impl Board { component_id } - pub fn ensure_named_pin(&mut self, pin_name: String, net_id: Option) -> PinId { + pub fn ensure_named_pin(&mut self, pin_name: String, pin_spec: PinSpec) -> PinId { if let Some(pin) = self.pin_names.get_by_right(&pin_name) { return *pin; }; - let pin_id = self.layout.insert_pin(net_id); + let pin_id = self.layout.insert_pin(pin_spec); self.pin_names.insert(pin_id, pin_name); pin_id diff --git a/topola/src/layout/attraction.rs b/topola/src/layout/attraction.rs new file mode 100644 index 0000000..c014082 --- /dev/null +++ b/topola/src/layout/attraction.rs @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2026 Topola contributors +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{ + Layout, Vector2, + layout::compounds::{ComponentId, PinId}, +}; + +impl Layout { + pub fn component_attractions( + &self, + attractee: ComponentId, + ) -> impl Iterator> + '_ { + self.component(attractee) + .pins + .iter() + .flat_map(move |&pin_id| self.pin_attractions(pin_id)) + } + + pub fn pin_attractions(&self, attractee: PinId) -> impl Iterator> + '_ { + self.pin(attractee) + .spec + .net + .into_iter() + .flat_map(move |net_id| { + self.nets[net_id.index()] + .pins + .iter() + .copied() + .filter(move |&attractor| attractor != attractee) + .map(move |attractor| self.pin_pin_attraction(attractee, attractor)) + }) + } + + pub fn pin_pin_attraction(&self, attractee: PinId, attractor: PinId) -> Vector2 { + self.pin_centroid(attractor) - self.pin_centroid(attractee) + } +} diff --git a/topola/src/layout/compounds/component.rs b/topola/src/layout/compounds/component.rs index 186b810..c351765 100644 --- a/topola/src/layout/compounds/component.rs +++ b/topola/src/layout/compounds/component.rs @@ -6,6 +6,7 @@ use derive_more::{Constructor, From}; use serde::{Deserialize, Serialize}; use crate::{ + PinId, layout::primitives::{JointId, PolygonId, SegmentId, ViaId}, primitives::PrimitiveId, }; @@ -35,6 +36,7 @@ impl ComponentId { #[derive(Clone, Debug, Default)] pub struct Component { + pub pins: Vec, pub joints: Vec, pub segments: Vec, pub vias: Vec, diff --git a/topola/src/layout/compounds/pin.rs b/topola/src/layout/compounds/pin.rs index f6d5872..a608374 100644 --- a/topola/src/layout/compounds/pin.rs +++ b/topola/src/layout/compounds/pin.rs @@ -5,7 +5,13 @@ use derive_more::{Constructor, From}; use serde::{Deserialize, Serialize}; -use crate::layout::primitives::{JointId, PolygonId, SegmentId, ViaId}; +use crate::{ + Layout, Vector2, + layout::{ + compounds::{ComponentId, NetId}, + primitives::{JointId, PolygonId, SegmentId, ViaId}, + }, +}; #[derive( Clone, @@ -30,8 +36,15 @@ impl PinId { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Copy, Debug)] +pub struct PinSpec { + pub component: Option, + pub net: Option, +} + +#[derive(Clone, Debug)] pub struct Pin { + pub spec: PinSpec, pub joints: Vec, pub segments: Vec, pub vias: Vec, @@ -39,7 +52,45 @@ pub struct Pin { } impl Pin { - pub fn new() -> Self { - Default::default() + pub fn new(spec: PinSpec) -> Self { + Pin { + spec, + joints: Vec::new(), + segments: Vec::new(), + vias: Vec::new(), + polygons: Vec::new(), + } + } +} + +impl Layout { + pub fn pin_centroid(&self, pin_id: PinId) -> Vector2 { + let pin = self.pin(pin_id); + let mut sum = Vector2::new(0, 0); + let mut count = 0; + + for &joint_id in &pin.joints { + sum = sum + self.joint(joint_id).center(); + count += 1; + } + for &segment_id in &pin.segments { + sum = sum + self.segment(segment_id).center(); + count += 1; + } + for &via_id in &pin.vias { + sum = sum + self.via(via_id).position; + count += 1; + } + for &polygon_id in &pin.polygons { + sum = sum + self.polygon(polygon_id).center(); + count += 1; + } + + if count == 0 { + return Vector2::new(0, 0); + } + + let count = count as i64; + Vector2::new(sum.x / count, sum.y / count) } } diff --git a/topola/src/layout/infringement.rs b/topola/src/layout/infringement.rs index b68050f..93710d0 100644 --- a/topola/src/layout/infringement.rs +++ b/topola/src/layout/infringement.rs @@ -392,7 +392,7 @@ impl Layout { } } - fn primitive_net(&self, primitive: PrimitiveId) -> Option { + pub fn primitive_net(&self, primitive: PrimitiveId) -> Option { match primitive { PrimitiveId::Joint(joint_id) => self.joint(joint_id).spec.net, PrimitiveId::Segment(segment_id) => self.segment(segment_id).net, diff --git a/topola/src/layout/insert.rs b/topola/src/layout/insert.rs index 9e65ebe..fb0aab9 100644 --- a/topola/src/layout/insert.rs +++ b/topola/src/layout/insert.rs @@ -5,10 +5,9 @@ use rstar::primitives::GeomWithData; use crate::{ - Pin, PinId, layout::{ Layout, - compounds::{Component, ComponentId, NetId}, + compounds::{Component, ComponentId, Pin, PinId, PinSpec}, }, primitives::{ Joint, JointId, JointSpec, Polygon, PolygonId, Segment, SegmentId, SegmentSpec, Via, ViaId, @@ -21,10 +20,16 @@ impl Layout { ComponentId::new(self.components.push(Component::new())) } - pub fn insert_pin(&mut self, net_id: Option) -> PinId { - let pin_id = PinId::new(self.pins.push(Pin::new())); + pub fn insert_pin(&mut self, spec: PinSpec) -> PinId { + let pin_id = PinId::new(self.pins.push(Pin::new(spec))); - if let Some(net_id) = net_id { + if let Some(component_id) = spec.component { + self.components.modify(component_id.index(), |component| { + component.pins.push(pin_id) + }); + } + + if let Some(net_id) = spec.net { self.nets .modify(net_id.index(), |net| net.pins.push(pin_id)); } @@ -33,11 +38,7 @@ impl Layout { } pub fn insert_joint(&mut self, spec: JointSpec) -> JointId { - let joint = Joint { - spec, - segments: Vec::new(), - vias: Vec::new(), - }; + let joint = Joint::new(spec); let bbox = joint.bbox(); let component_id = joint.spec.component; let pin_id = joint.spec.pin; diff --git a/topola/src/layout/mod.rs b/topola/src/layout/mod.rs index 5b6a8df..2c03189 100644 --- a/topola/src/layout/mod.rs +++ b/topola/src/layout/mod.rs @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +mod attraction; mod bbox; pub mod compounds; mod delete; @@ -90,14 +91,19 @@ pub struct Layout { } impl Layout { - pub fn new(boundary: Vec<[i64; 2]>, layer_count: usize) -> Self { + pub fn new(boundary: Vec<[i64; 2]>, layer_count: usize, net_count: usize) -> Self { + let mut nets = StableVec::new(); + for _ in 0..net_count { + nets.push(Net::default()); + } + Self { boundary: boundary.clone(), place_boundary: boundary, layer_count, components: Recorder::new(StableVec::new()), - nets: Recorder::new(StableVec::new()), + nets: Recorder::new(nets), pins: Recorder::new(StableVec::new()), joints: Recorder::new(StableVec::new()), diff --git a/topola/src/layout/primitives/joint.rs b/topola/src/layout/primitives/joint.rs index d3fa793..0a1bbd6 100644 --- a/topola/src/layout/primitives/joint.rs +++ b/topola/src/layout/primitives/joint.rs @@ -52,6 +52,14 @@ pub struct Joint { } impl Joint { + pub fn new(spec: JointSpec) -> Self { + Joint { + spec, + segments: Vec::new(), + vias: Vec::new(), + } + } + pub fn center(&self) -> Vector2 { self.spec.position } diff --git a/topola/src/layout/primitives/mod.rs b/topola/src/layout/primitives/mod.rs index e9f7d72..9bc1ea7 100644 --- a/topola/src/layout/primitives/mod.rs +++ b/topola/src/layout/primitives/mod.rs @@ -15,6 +15,8 @@ pub use polygon::*; pub use segment::*; pub use via::*; +use crate::{Layout, PinId}; + #[derive(Clone, Copy, Debug, Deserialize, Eq, From, Ord, PartialEq, PartialOrd, Serialize)] pub enum PrimitiveId { Joint(JointId), @@ -22,3 +24,14 @@ pub enum PrimitiveId { Via(ViaId), Polygon(PolygonId), } + +impl Layout { + pub fn primitive_pin(&self, primitive: PrimitiveId) -> Option { + match primitive { + PrimitiveId::Joint(joint_id) => self.joint(joint_id).spec.pin, + PrimitiveId::Segment(segment_id) => self.segment(segment_id).spec.pin, + PrimitiveId::Via(via_id) => self.via(via_id).spec.pin, + PrimitiveId::Polygon(polygon_id) => self.polygon(polygon_id).pin, + } + } +} diff --git a/topola/src/specctra.rs b/topola/src/specctra.rs index da7de80..d0d03d7 100644 --- a/topola/src/specctra.rs +++ b/topola/src/specctra.rs @@ -12,8 +12,10 @@ use specctra::{ use crate::{ board::{Board, LayerDesc, LayerSide, LayerType}, - layout::LayerId, - layout::compounds::{ComponentId, NetId, PinId}, + layout::{ + LayerId, + compounds::{ComponentId, NetId, PinId, PinSpec}, + }, primitives::{JointSpec, Polygon, Segment, SegmentSpec}, vector::Vector2, }; @@ -161,7 +163,13 @@ impl Board { let pin_name = format!("{}-{}", place.name, pin.id); let net_id = pin_nets.get(&pin_name).copied(); - let pin_id = board.ensure_named_pin(pin_name.clone(), net_id); + let pin_id = board.ensure_named_pin( + pin_name.clone(), + PinSpec { + component: Some(component_id), + net: net_id, + }, + ); let padstack = dsn.pcb.library.find_padstack_by_name(&pin.name).unwrap(); for shape in padstack.shapes.iter() {