fix(board): make Poly' apex generation non-lazy

- insert polygon parts before polygon compound
This commit is contained in:
Ellen Emilia Anna Zscheile 2025-03-24 21:30:55 +01:00
parent 3a2c9deff0
commit 122ff0122f
6 changed files with 158 additions and 165 deletions

View File

@ -77,7 +77,7 @@ impl<M: AccessMesadata> Autorouter<M> {
.node_index() .node_index()
{ {
RatvertexIndex::FixedDot(dot) => dot, RatvertexIndex::FixedDot(dot) => dot,
RatvertexIndex::Poly(poly) => self.board.poly_apex(&mut LayoutEdit::new(), poly), RatvertexIndex::Poly(poly) => self.board.layout().poly(poly).apex(),
}; };
PointrouteExecutionStepper::new(self, origin_dot, point, options) PointrouteExecutionStepper::new(self, origin_dot, point, options)
@ -195,7 +195,7 @@ impl<M: AccessMesadata> Autorouter<M> {
.node_index() .node_index()
{ {
RatvertexIndex::FixedDot(dot) => dot, RatvertexIndex::FixedDot(dot) => dot,
RatvertexIndex::Poly(poly) => self.board.poly_apex(&mut LayoutEdit::new(), poly), RatvertexIndex::Poly(poly) => self.board.layout().poly(poly).apex(),
}; };
let target_dot = match self let target_dot = match self
@ -206,7 +206,7 @@ impl<M: AccessMesadata> Autorouter<M> {
.node_index() .node_index()
{ {
RatvertexIndex::FixedDot(dot) => dot, RatvertexIndex::FixedDot(dot) => dot,
RatvertexIndex::Poly(poly) => self.board.poly_apex(&mut LayoutEdit::new(), poly), RatvertexIndex::Poly(poly) => self.board.layout().poly(poly).apex(),
}; };
(source_dot, target_dot) (source_dot, target_dot)

View File

@ -16,18 +16,14 @@ use crate::{
drawing::{ drawing::{
band::BandUid, band::BandUid,
bend::{BendIndex, BendWeight}, bend::{BendIndex, BendWeight},
dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, GeneralDotWeight}, dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight},
graph::{GetMaybeNet, PrimitiveIndex}, graph::PrimitiveIndex,
seg::{FixedSegIndex, FixedSegWeight, SegIndex, SegWeight}, seg::{FixedSegIndex, FixedSegWeight, SegIndex, SegWeight},
Collect, Collect,
}, },
geometry::{edit::ApplyGeometryEdit, shape::AccessShape, GenericNode, GetLayer}, geometry::{edit::ApplyGeometryEdit, GenericNode, GetLayer},
graph::GenericIndex, graph::GenericIndex,
layout::{ layout::{poly::PolyWeight, CompoundWeight, Layout, LayoutEdit, NodeIndex},
poly::{GetMaybeApex, MakePolygon, PolyWeight},
CompoundWeight, Layout, LayoutEdit, NodeIndex,
},
math::Circle,
}; };
/// Represents a band between two pins. /// Represents a band between two pins.
@ -122,27 +118,6 @@ impl<M: AccessMesadata> Board<M> {
dot dot
} }
/// Adds a fixed segment between two dots with an optional pin name.
///
/// Adds the segment to the layout and maps the pin name to the created segment if provided.
pub fn add_poly_fixed_dot_infringably(
&mut self,
recorder: &mut LayoutEdit,
weight: FixedDotWeight,
poly: GenericIndex<PolyWeight>,
) -> FixedDotIndex {
let dot = self
.layout
.add_poly_fixed_dot_infringably(recorder, weight, poly);
if let Some(pin) = self.node_pinname(&GenericNode::Compound(poly.into())) {
self.node_to_pinname
.insert(GenericNode::Primitive(dot.into()), pin.to_string());
}
dot
}
/// Adds a fixed segment associated with a polygon in the layout. /// Adds a fixed segment associated with a polygon in the layout.
/// ///
/// Adds the segment to the layout and updates the internal mapping if necessary. /// Adds the segment to the layout and updates the internal mapping if necessary.
@ -166,41 +141,27 @@ impl<M: AccessMesadata> Board<M> {
seg seg
} }
/// Adds a fixed segment associated with a polygon in the layout.
///
/// Adds the segment to the layout and updates the internal mapping if necessary.
pub fn add_poly_fixed_seg_infringably(
&mut self,
recorder: &mut LayoutEdit,
from: FixedDotIndex,
to: FixedDotIndex,
weight: FixedSegWeight,
poly: GenericIndex<PolyWeight>,
) -> FixedSegIndex {
let seg = self
.layout
.add_poly_fixed_seg_infringably(recorder, from, to, weight, poly);
if let Some(pin) = self.node_pinname(&GenericNode::Compound(poly.into())) {
self.node_to_pinname
.insert(GenericNode::Primitive(seg.into()), pin.to_string());
}
seg
}
/// Adds a new polygon to the layout with an optional pin name. /// Adds a new polygon to the layout with an optional pin name.
/// ///
/// Inserts the polygon into the layout and, if a pin name is provided, maps it to the created polygon's node. /// Inserts the polygon into the layout and, if a pin name is provided, maps it to the created polygon's node.
pub fn add_poly( pub fn add_poly_with_nodes(
&mut self, &mut self,
recorder: &mut LayoutEdit, recorder: &mut LayoutEdit,
weight: PolyWeight, weight: PolyWeight,
maybe_pin: Option<String>, maybe_pin: Option<String>,
nodes: &[PrimitiveIndex],
) -> GenericIndex<PolyWeight> { ) -> GenericIndex<PolyWeight> {
let poly = self.layout.add_poly(recorder, weight); let (poly, apex) = self.layout.add_poly_with_nodes(recorder, weight, nodes);
if let Some(pin) = maybe_pin { if let Some(pin) = maybe_pin {
for i in nodes {
self.node_to_pinname
.insert(GenericNode::Primitive(*i), pin.clone());
}
self.node_to_pinname
.insert(GenericNode::Primitive(apex.into()), pin.clone());
self.node_to_pinname self.node_to_pinname
.insert(GenericNode::Compound(poly.into()), pin); .insert(GenericNode::Compound(poly.into()), pin);
} }
@ -208,33 +169,6 @@ impl<M: AccessMesadata> Board<M> {
poly poly
} }
/// Retrieves or creates the apex (center point) of a polygon in the layout.
///
/// If the polygon already has an apex, returns it. Otherwise, creates and returns a new fixed dot as the apex.
pub fn poly_apex(
&mut self,
recorder: &mut LayoutEdit,
poly: GenericIndex<PolyWeight>,
) -> FixedDotIndex {
let resolved_poly = self.layout.poly(poly);
if let Some(apex) = resolved_poly.maybe_apex() {
apex
} else {
self.add_poly_fixed_dot_infringably(
recorder,
FixedDotWeight(GeneralDotWeight {
circle: Circle {
pos: resolved_poly.shape().center(),
r: 100.0,
},
layer: resolved_poly.layer(),
maybe_net: resolved_poly.maybe_net(),
}),
poly,
)
}
}
/// Returns the pin name associated with a given node. /// Returns the pin name associated with a given node.
pub fn node_pinname(&self, node: &NodeIndex) -> Option<&String> { pub fn node_pinname(&self, node: &NodeIndex) -> Option<&String> {
self.node_to_pinname.get(node) self.node_to_pinname.get(node)

View File

@ -32,14 +32,14 @@ use crate::{
edit::ApplyGeometryEdit, edit::ApplyGeometryEdit,
primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape}, primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape},
shape::{AccessShape, Shape}, shape::{AccessShape, Shape},
GenericNode, GetSetPos, GenericNode, GetLayer, GetSetPos,
}, },
graph::{GenericIndex, GetPetgraphIndex}, graph::{GenericIndex, GetPetgraphIndex},
layout::{ layout::{
poly::{MakePolygon, Poly, PolyWeight}, poly::{is_apex, MakePolygon, Poly, PolyWeight},
via::{Via, ViaWeight}, via::{Via, ViaWeight},
}, },
math::{LineIntersection, NormalLine}, math::{Circle, LineIntersection, NormalLine},
}; };
/// Represents a weight for various compounds /// Represents a weight for various compounds
@ -271,6 +271,48 @@ impl<R: AccessRules> Layout<R> {
) )
} }
/// insert a polygon based upon the border nodes, and computes + returns the
/// associated apex
pub fn add_poly_with_nodes(
&mut self,
recorder: &mut LayoutEdit,
weight: PolyWeight,
nodes: &[PrimitiveIndex],
) -> (GenericIndex<PolyWeight>, FixedDotIndex) {
let layer = weight.layer();
let maybe_net = weight.maybe_net();
let poly = self.add_poly(recorder, weight);
let poly_compound = poly.into();
for i in nodes {
self.drawing.add_to_compound(
recorder,
GenericIndex::<()>::new(i.petgraph_index()),
poly_compound,
);
}
let shape = self.poly(poly).shape();
let apex = self.add_fixed_dot_infringably(
recorder,
FixedDotWeight(GeneralDotWeight {
circle: Circle {
pos: shape.center(),
r: 100.0,
},
layer,
maybe_net,
}),
);
// maybe this should be a different edge kind
self.drawing.add_to_compound(recorder, apex, poly_compound);
assert!(is_apex(&self.drawing, apex));
(poly, apex)
}
pub fn remove_band( pub fn remove_band(
&mut self, &mut self,
recorder: &mut LayoutEdit, recorder: &mut LayoutEdit,

View File

@ -27,30 +27,47 @@ pub trait MakePolygon {
fn shape(&self) -> Polygon; fn shape(&self) -> Polygon;
} }
#[enum_dispatch]
pub trait GetMaybeApex {
fn maybe_apex(&self) -> Option<FixedDotIndex>;
}
#[derive(Debug)] #[derive(Debug)]
pub struct Poly<'a, R> { pub struct Poly<'a, R> {
pub index: GenericIndex<PolyWeight>, pub index: GenericIndex<PolyWeight>,
drawing: &'a Drawing<CompoundWeight, R>, drawing: &'a Drawing<CompoundWeight, R>,
} }
pub(super) fn is_apex<'a, R: AccessRules>(
drawing: &'a Drawing<CompoundWeight, R>,
dot: FixedDotIndex,
) -> bool {
!drawing
.primitive(dot)
.segs()
.iter()
.any(|seg| matches!(seg, SegIndex::Fixed(..)))
&& drawing.primitive(dot).bends().is_empty()
}
impl<'a, R: AccessRules> Poly<'a, R> { impl<'a, R: AccessRules> Poly<'a, R> {
pub fn new(index: GenericIndex<PolyWeight>, drawing: &'a Drawing<CompoundWeight, R>) -> Self { pub fn new(index: GenericIndex<PolyWeight>, drawing: &'a Drawing<CompoundWeight, R>) -> Self {
Self { index, drawing } Self { index, drawing }
} }
fn is_apex(&self, dot: FixedDotIndex) -> bool { fn is_apex(&self, dot: FixedDotIndex) -> bool {
!self is_apex(self.drawing, dot)
.drawing }
.primitive(dot)
.segs() pub fn apex(&self) -> FixedDotIndex {
.iter() self.drawing
.any(|seg| matches!(seg, SegIndex::Fixed(..))) .geometry()
&& self.drawing.primitive(dot).bends().is_empty() .compound_members(self.index.into())
.find_map(|primitive_node| {
if let PrimitiveIndex::FixedDot(dot) = primitive_node {
if self.is_apex(dot) {
return Some(dot);
}
}
None
})
.unwrap()
} }
} }
@ -95,23 +112,6 @@ impl<R: AccessRules> MakePolygon for Poly<'_, R> {
} }
} }
impl<R: AccessRules> GetMaybeApex for Poly<'_, R> {
fn maybe_apex(&self) -> Option<FixedDotIndex> {
self.drawing
.geometry()
.compound_members(self.index.into())
.find_map(|primitive_node| {
if let PrimitiveIndex::FixedDot(dot) = primitive_node {
if self.is_apex(dot) {
return Some(dot);
}
}
None
})
}
}
#[enum_dispatch(GetLayer, GetMaybeNet)] #[enum_dispatch(GetLayer, GetMaybeNet)]
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum PolyWeight { pub enum PolyWeight {

View File

@ -456,14 +456,8 @@ impl SpecctraDesign {
maybe_net: Option<usize>, maybe_net: Option<usize>,
maybe_pin: Option<String>, maybe_pin: Option<String>,
) { ) {
let poly = board.add_poly(
recorder,
SolidPolyWeight { layer, maybe_net }.into(),
maybe_pin,
);
// Corners. // Corners.
let dot_1_1 = board.add_poly_fixed_dot_infringably( let dot_1_1 = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { circle: Circle {
@ -473,9 +467,9 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
); );
let dot_2_1 = board.add_poly_fixed_dot_infringably( let dot_2_1 = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { circle: Circle {
@ -485,9 +479,9 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
); );
let dot_2_2 = board.add_poly_fixed_dot_infringably( let dot_2_2 = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { circle: Circle {
@ -497,9 +491,9 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
); );
let dot_1_2 = board.add_poly_fixed_dot_infringably( let dot_1_2 = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { circle: Circle {
@ -509,10 +503,10 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
); );
// Sides. // Sides.
board.add_poly_fixed_seg_infringably( let seg1 = board.add_fixed_seg_infringably(
recorder, recorder,
dot_1_1, dot_1_1,
dot_2_1, dot_2_1,
@ -521,9 +515,9 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
); );
board.add_poly_fixed_seg_infringably( let seg2 = board.add_fixed_seg_infringably(
recorder, recorder,
dot_2_1, dot_2_1,
dot_2_2, dot_2_2,
@ -532,9 +526,9 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
); );
board.add_poly_fixed_seg_infringably( let seg3 = board.add_fixed_seg_infringably(
recorder, recorder,
dot_2_2, dot_2_2,
dot_1_2, dot_1_2,
@ -543,9 +537,9 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
); );
board.add_poly_fixed_seg_infringably( let seg4 = board.add_fixed_seg_infringably(
recorder, recorder,
dot_1_2, dot_1_2,
dot_1_1, dot_1_1,
@ -554,7 +548,23 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
poly, None,
);
board.add_poly_with_nodes(
recorder,
SolidPolyWeight { layer, maybe_net }.into(),
maybe_pin,
&[
dot_1_1.into(),
dot_1_2.into(),
dot_2_2.into(),
dot_2_1.into(),
seg1.into(),
seg2.into(),
seg3.into(),
seg4.into(),
],
); );
} }
@ -634,14 +644,10 @@ impl SpecctraDesign {
maybe_net: Option<usize>, maybe_net: Option<usize>,
maybe_pin: Option<String>, maybe_pin: Option<String>,
) { ) {
let poly = board.add_poly( let mut nodes = Vec::with_capacity(coords.len() * 2 - 1);
recorder,
SolidPolyWeight { layer, maybe_net }.into(),
maybe_pin,
);
// add the first coordinate in the wire path as a dot and save its index // add the first coordinate in the wire path as a dot and save its index
let mut prev_index = board.add_poly_fixed_dot_infringably( let mut prev_index = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { circle: Circle {
@ -651,14 +657,13 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
// TODO: This manual retagging shouldn't be necessary, `.into()` should suffice. None,
//GenericIndex::new(poly.petgraph_index()).into(),
poly,
); );
nodes.push(prev_index.into());
// iterate through path coords starting from the second // iterate through path coords starting from the second
for coord in coords.iter().skip(1) { for coord in coords.iter().skip(1) {
let index = board.add_poly_fixed_dot_infringably( let index = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { circle: Circle {
@ -668,26 +673,38 @@ impl SpecctraDesign {
layer, layer,
maybe_net, maybe_net,
}), }),
// TODO: This manual retagging shouldn't be necessary, `.into()` should suffice. None,
poly,
); );
nodes.push(index.into());
// add a seg between the current and previous coords // add a seg between the current and previous coords
let _ = board.add_poly_fixed_seg_infringably( nodes.push(
recorder, board
prev_index, .add_fixed_seg_infringably(
index, recorder,
FixedSegWeight(GeneralSegWeight { prev_index,
width, index,
layer, FixedSegWeight(GeneralSegWeight {
maybe_net, width,
}), layer,
// TODO: This manual retagging shouldn't be necessary, `.into()` should suffice. maybe_net,
poly, }),
None,
)
.into(),
); );
prev_index = index; prev_index = index;
} }
// assumption: the last coord and the first coord are equal
board.add_poly_with_nodes(
recorder,
SolidPolyWeight { layer, maybe_net }.into(),
maybe_pin,
&nodes[..],
);
} }
fn pos(place: PointWithRotation, pin: PointWithRotation, x: f64, y: f64) -> Point { fn pos(place: PointWithRotation, pin: PointWithRotation, x: f64, y: f64) -> Point {

View File

@ -17,7 +17,7 @@ mod common;
#[test] #[test]
fn test_0603_breakout() { fn test_0603_breakout() {
let mut autorouter = common::load_design("tests/single_layer/0603_breakout/0603_breakout.dsn"); let mut autorouter = common::load_design("tests/single_layer/0603_breakout/0603_breakout.dsn");
common::assert_navvertex_count(&mut autorouter, "R1-2", "J1-2", 50); common::assert_navvertex_count(&mut autorouter, "R1-2", "J1-2", 54);
let mut invoker = common::create_invoker_and_assert(autorouter); let mut invoker = common::create_invoker_and_assert(autorouter);
common::replay_and_assert( common::replay_and_assert(
&mut invoker, &mut invoker,
@ -35,7 +35,7 @@ fn test_tht_diode_bridge_rectifier() {
let mut autorouter = common::load_design( let mut autorouter = common::load_design(
"tests/single_layer/tht_diode_bridge_rectifier/tht_diode_bridge_rectifier.dsn", "tests/single_layer/tht_diode_bridge_rectifier/tht_diode_bridge_rectifier.dsn",
); );
common::assert_navvertex_count(&mut autorouter, "J2-2", "D4-2", 56); common::assert_navvertex_count(&mut autorouter, "J2-2", "D4-2", 68);
let mut invoker = common::create_invoker_and_assert(autorouter); let mut invoker = common::create_invoker_and_assert(autorouter);
common::replay_and_assert( common::replay_and_assert(
&mut invoker, &mut invoker,
@ -46,7 +46,7 @@ fn test_tht_diode_bridge_rectifier() {
common::assert_single_layer_groundless_autoroute(&mut autorouter, "F.Cu"); common::assert_single_layer_groundless_autoroute(&mut autorouter, "F.Cu");
//common::assert_number_of_conncomps(&mut autorouter, 4); //common::assert_number_of_conncomps(&mut autorouter, 4);
common::assert_band_length(autorouter.board(), "J2-2", "D4-2", 15900.0, 0.01); common::assert_band_length(autorouter.board(), "J2-2", "D4-2", 15906.760439007436, 0.01);
let mut invoker = Invoker::new(autorouter); let mut invoker = Invoker::new(autorouter);
let result = invoker.execute(Command::PlaceVia(ViaWeight { let result = invoker.execute(Command::PlaceVia(ViaWeight {
@ -71,7 +71,7 @@ fn test_4x_3rd_order_smd_lc_filters() {
let mut autorouter = common::load_design( let mut autorouter = common::load_design(
"tests/single_layer/4x_3rd_order_smd_lc_filters/4x_3rd_order_smd_lc_filters.dsn", "tests/single_layer/4x_3rd_order_smd_lc_filters/4x_3rd_order_smd_lc_filters.dsn",
); );
common::assert_navvertex_count(&mut autorouter, "J1-1", "L1-1", 2026); common::assert_navvertex_count(&mut autorouter, "J1-1", "L1-1", 2062);
let mut invoker = common::create_invoker_and_assert(autorouter); let mut invoker = common::create_invoker_and_assert(autorouter);
common::replay_and_assert( common::replay_and_assert(
&mut invoker, &mut invoker,