From 122ff0122fc1c62b7d1774b3d42bba6088caf80f Mon Sep 17 00:00:00 2001 From: Ellen Emilia Anna Zscheile Date: Mon, 24 Mar 2025 21:30:55 +0100 Subject: [PATCH] fix(board): make Poly' apex generation non-lazy - insert polygon parts before polygon compound --- src/autorouter/autorouter.rs | 6 +- src/board/mod.rs | 96 +++++-------------------------- src/layout/layout.rs | 48 +++++++++++++++- src/layout/poly.rs | 58 +++++++++---------- src/specctra/design.rs | 107 ++++++++++++++++++++--------------- tests/single_layer.rs | 8 +-- 6 files changed, 158 insertions(+), 165 deletions(-) diff --git a/src/autorouter/autorouter.rs b/src/autorouter/autorouter.rs index e07c15f..70eaf41 100644 --- a/src/autorouter/autorouter.rs +++ b/src/autorouter/autorouter.rs @@ -77,7 +77,7 @@ impl Autorouter { .node_index() { 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) @@ -195,7 +195,7 @@ impl Autorouter { .node_index() { 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 @@ -206,7 +206,7 @@ impl Autorouter { .node_index() { 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) diff --git a/src/board/mod.rs b/src/board/mod.rs index a7bde6b..90b3199 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -16,18 +16,14 @@ use crate::{ drawing::{ band::BandUid, bend::{BendIndex, BendWeight}, - dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, GeneralDotWeight}, - graph::{GetMaybeNet, PrimitiveIndex}, + dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight}, + graph::PrimitiveIndex, seg::{FixedSegIndex, FixedSegWeight, SegIndex, SegWeight}, Collect, }, - geometry::{edit::ApplyGeometryEdit, shape::AccessShape, GenericNode, GetLayer}, + geometry::{edit::ApplyGeometryEdit, GenericNode, GetLayer}, graph::GenericIndex, - layout::{ - poly::{GetMaybeApex, MakePolygon, PolyWeight}, - CompoundWeight, Layout, LayoutEdit, NodeIndex, - }, - math::Circle, + layout::{poly::PolyWeight, CompoundWeight, Layout, LayoutEdit, NodeIndex}, }; /// Represents a band between two pins. @@ -122,27 +118,6 @@ impl Board { 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, - ) -> 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 the segment to the layout and updates the internal mapping if necessary. @@ -166,41 +141,27 @@ impl Board { 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, - ) -> 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. /// /// 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, recorder: &mut LayoutEdit, weight: PolyWeight, maybe_pin: Option, + nodes: &[PrimitiveIndex], ) -> GenericIndex { - 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 { + 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 .insert(GenericNode::Compound(poly.into()), pin); } @@ -208,33 +169,6 @@ impl Board { 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, - ) -> 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. pub fn node_pinname(&self, node: &NodeIndex) -> Option<&String> { self.node_to_pinname.get(node) diff --git a/src/layout/layout.rs b/src/layout/layout.rs index 4620bb1..2718aeb 100644 --- a/src/layout/layout.rs +++ b/src/layout/layout.rs @@ -32,14 +32,14 @@ use crate::{ edit::ApplyGeometryEdit, primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape}, shape::{AccessShape, Shape}, - GenericNode, GetSetPos, + GenericNode, GetLayer, GetSetPos, }, graph::{GenericIndex, GetPetgraphIndex}, layout::{ - poly::{MakePolygon, Poly, PolyWeight}, + poly::{is_apex, MakePolygon, Poly, PolyWeight}, via::{Via, ViaWeight}, }, - math::{LineIntersection, NormalLine}, + math::{Circle, LineIntersection, NormalLine}, }; /// Represents a weight for various compounds @@ -271,6 +271,48 @@ impl Layout { ) } + /// 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, 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( &mut self, recorder: &mut LayoutEdit, diff --git a/src/layout/poly.rs b/src/layout/poly.rs index 2f61b20..2e5c4e1 100644 --- a/src/layout/poly.rs +++ b/src/layout/poly.rs @@ -27,30 +27,47 @@ pub trait MakePolygon { fn shape(&self) -> Polygon; } -#[enum_dispatch] -pub trait GetMaybeApex { - fn maybe_apex(&self) -> Option; -} - #[derive(Debug)] pub struct Poly<'a, R> { pub index: GenericIndex, drawing: &'a Drawing, } +pub(super) fn is_apex<'a, R: AccessRules>( + drawing: &'a Drawing, + 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> { pub fn new(index: GenericIndex, drawing: &'a Drawing) -> Self { Self { index, drawing } } fn is_apex(&self, dot: FixedDotIndex) -> bool { - !self - .drawing - .primitive(dot) - .segs() - .iter() - .any(|seg| matches!(seg, SegIndex::Fixed(..))) - && self.drawing.primitive(dot).bends().is_empty() + is_apex(self.drawing, dot) + } + + pub fn apex(&self) -> 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 + }) + .unwrap() } } @@ -95,23 +112,6 @@ impl MakePolygon for Poly<'_, R> { } } -impl GetMaybeApex for Poly<'_, R> { - fn maybe_apex(&self) -> Option { - 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)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum PolyWeight { diff --git a/src/specctra/design.rs b/src/specctra/design.rs index 7d9fbda..5375388 100644 --- a/src/specctra/design.rs +++ b/src/specctra/design.rs @@ -456,14 +456,8 @@ impl SpecctraDesign { maybe_net: Option, maybe_pin: Option, ) { - let poly = board.add_poly( - recorder, - SolidPolyWeight { layer, maybe_net }.into(), - maybe_pin, - ); - // Corners. - let dot_1_1 = board.add_poly_fixed_dot_infringably( + let dot_1_1 = board.add_fixed_dot_infringably( recorder, FixedDotWeight(GeneralDotWeight { circle: Circle { @@ -473,9 +467,9 @@ impl SpecctraDesign { layer, maybe_net, }), - poly, + None, ); - let dot_2_1 = board.add_poly_fixed_dot_infringably( + let dot_2_1 = board.add_fixed_dot_infringably( recorder, FixedDotWeight(GeneralDotWeight { circle: Circle { @@ -485,9 +479,9 @@ impl SpecctraDesign { layer, maybe_net, }), - poly, + None, ); - let dot_2_2 = board.add_poly_fixed_dot_infringably( + let dot_2_2 = board.add_fixed_dot_infringably( recorder, FixedDotWeight(GeneralDotWeight { circle: Circle { @@ -497,9 +491,9 @@ impl SpecctraDesign { layer, maybe_net, }), - poly, + None, ); - let dot_1_2 = board.add_poly_fixed_dot_infringably( + let dot_1_2 = board.add_fixed_dot_infringably( recorder, FixedDotWeight(GeneralDotWeight { circle: Circle { @@ -509,10 +503,10 @@ impl SpecctraDesign { layer, maybe_net, }), - poly, + None, ); // Sides. - board.add_poly_fixed_seg_infringably( + let seg1 = board.add_fixed_seg_infringably( recorder, dot_1_1, dot_2_1, @@ -521,9 +515,9 @@ impl SpecctraDesign { layer, maybe_net, }), - poly, + None, ); - board.add_poly_fixed_seg_infringably( + let seg2 = board.add_fixed_seg_infringably( recorder, dot_2_1, dot_2_2, @@ -532,9 +526,9 @@ impl SpecctraDesign { layer, maybe_net, }), - poly, + None, ); - board.add_poly_fixed_seg_infringably( + let seg3 = board.add_fixed_seg_infringably( recorder, dot_2_2, dot_1_2, @@ -543,9 +537,9 @@ impl SpecctraDesign { layer, maybe_net, }), - poly, + None, ); - board.add_poly_fixed_seg_infringably( + let seg4 = board.add_fixed_seg_infringably( recorder, dot_1_2, dot_1_1, @@ -554,7 +548,23 @@ impl SpecctraDesign { layer, 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, maybe_pin: Option, ) { - let poly = board.add_poly( - recorder, - SolidPolyWeight { layer, maybe_net }.into(), - maybe_pin, - ); + let mut nodes = Vec::with_capacity(coords.len() * 2 - 1); // 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, FixedDotWeight(GeneralDotWeight { circle: Circle { @@ -651,14 +657,13 @@ impl SpecctraDesign { layer, maybe_net, }), - // TODO: This manual retagging shouldn't be necessary, `.into()` should suffice. - //GenericIndex::new(poly.petgraph_index()).into(), - poly, + None, ); + nodes.push(prev_index.into()); // iterate through path coords starting from the second for coord in coords.iter().skip(1) { - let index = board.add_poly_fixed_dot_infringably( + let index = board.add_fixed_dot_infringably( recorder, FixedDotWeight(GeneralDotWeight { circle: Circle { @@ -668,26 +673,38 @@ impl SpecctraDesign { layer, maybe_net, }), - // TODO: This manual retagging shouldn't be necessary, `.into()` should suffice. - poly, + None, ); + nodes.push(index.into()); // add a seg between the current and previous coords - let _ = board.add_poly_fixed_seg_infringably( - recorder, - prev_index, - index, - FixedSegWeight(GeneralSegWeight { - width, - layer, - maybe_net, - }), - // TODO: This manual retagging shouldn't be necessary, `.into()` should suffice. - poly, + nodes.push( + board + .add_fixed_seg_infringably( + recorder, + prev_index, + index, + FixedSegWeight(GeneralSegWeight { + width, + layer, + maybe_net, + }), + None, + ) + .into(), ); 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 { diff --git a/tests/single_layer.rs b/tests/single_layer.rs index a9b97ef..87c7bb6 100644 --- a/tests/single_layer.rs +++ b/tests/single_layer.rs @@ -17,7 +17,7 @@ mod common; #[test] fn test_0603_breakout() { 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); common::replay_and_assert( &mut invoker, @@ -35,7 +35,7 @@ fn test_tht_diode_bridge_rectifier() { let mut autorouter = common::load_design( "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); common::replay_and_assert( &mut invoker, @@ -46,7 +46,7 @@ fn test_tht_diode_bridge_rectifier() { common::assert_single_layer_groundless_autoroute(&mut autorouter, "F.Cu"); //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 result = invoker.execute(Command::PlaceVia(ViaWeight { @@ -71,7 +71,7 @@ fn test_4x_3rd_order_smd_lc_filters() { let mut autorouter = common::load_design( "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); common::replay_and_assert( &mut invoker,