From f1225ba52eef4a00d7f2c62047ace7d4a5516a43 Mon Sep 17 00:00:00 2001 From: Ellen Emilia Anna Zscheile Date: Sat, 7 Jun 2025 10:39:39 +0200 Subject: [PATCH] refactor(specctra/design): turn SpecctraDesign into a trait --- crates/topola-cli/src/main.rs | 8 +- crates/topola-egui/src/app.rs | 9 +- crates/topola-egui/src/menu_bar.rs | 9 +- crates/topola-egui/src/workspace.rs | 9 +- examples/specctra.rs | 4 +- src/specctra/design.rs | 642 ++++++++++++++-------------- tests/common/mod.rs | 7 +- 7 files changed, 350 insertions(+), 338 deletions(-) diff --git a/crates/topola-cli/src/main.rs b/crates/topola-cli/src/main.rs index 8b660fd..4628a84 100644 --- a/crates/topola-cli/src/main.rs +++ b/crates/topola-cli/src/main.rs @@ -9,11 +9,10 @@ use topola::autorouter::execution::Command; use topola::autorouter::history::History; use topola::autorouter::invoker::Invoker; use topola::autorouter::selection::PinSelection; -use topola::autorouter::Autorouter; -use topola::autorouter::AutorouterOptions; +use topola::autorouter::{Autorouter, AutorouterOptions}; use topola::layout::LayoutEdit; use topola::router::RouterOptions; -use topola::specctra::design::SpecctraDesign; +use topola::specctra::design::{DsnFile, SpecctraDesign as _}; pub mod cli; use cli::Cli; @@ -23,8 +22,7 @@ fn main() -> Result<(), std::io::Error> { let design_file = File::open(&args.input)?; let design_bufread = BufReader::new(design_file); - let design = - SpecctraDesign::load(design_bufread).expect("File failed to parse as Specctra DSN"); + let design = DsnFile::load(design_bufread).expect("File failed to parse as Specctra DSN"); let board = design.make_board(&mut LayoutEdit::new()); diff --git a/crates/topola-egui/src/app.rs b/crates/topola-egui/src/app.rs index c99361a..b1664aa 100644 --- a/crates/topola-egui/src/app.rs +++ b/crates/topola-egui/src/app.rs @@ -14,7 +14,10 @@ use unic_langid::{langid, LanguageIdentifier}; use topola::{ interactor::activity::InteractiveInput, - specctra::{design::SpecctraDesign, ParseErrorContext as SpecctraLoadingError}, + specctra::{ + design::{DsnFile, SpecctraDesign}, + ParseErrorContext as SpecctraLoadingError, + }, }; use crate::{ @@ -27,8 +30,8 @@ pub struct App { translator: Translator, content_channel: ( - Sender>, - Receiver>, + Sender>, + Receiver>, ), viewport: Viewport, diff --git a/crates/topola-egui/src/menu_bar.rs b/crates/topola-egui/src/menu_bar.rs index 93f21ac..835959b 100644 --- a/crates/topola-egui/src/menu_bar.rs +++ b/crates/topola-egui/src/menu_bar.rs @@ -10,7 +10,10 @@ use topola::{ }, board::AccessMesadata, router::RouterOptions, - specctra::{design::SpecctraDesign, ParseError, ParseErrorContext as SpecctraLoadingError}, + specctra::{ + design::{DsnFile, SpecctraDesign as _}, + ParseError, ParseErrorContext as SpecctraLoadingError, + }, }; use crate::{ @@ -59,7 +62,7 @@ impl MenuBar { &mut self, ctx: &egui::Context, tr: &mut Translator, - content_sender: Sender>, + content_sender: Sender>, viewport: &mut Viewport, maybe_workspace: Option<&mut Workspace>, ) -> Result<(), InvokerError> { @@ -174,7 +177,7 @@ impl MenuBar { let data = handle_file(&file_handle) .await .map_err(|e| ParseError::from(e).add_context((0, 0))) - .and_then(SpecctraDesign::load); + .and_then(DsnFile::load); content_sender.send(data); ctx.request_repaint(); } diff --git a/crates/topola-egui/src/workspace.rs b/crates/topola-egui/src/workspace.rs index 0020760..96db268 100644 --- a/crates/topola-egui/src/workspace.rs +++ b/crates/topola-egui/src/workspace.rs @@ -11,7 +11,10 @@ use topola::{ autorouter::history::History, interactor::{activity::InteractiveInput, Interactor}, layout::LayoutEdit, - specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, + specctra::{ + design::{DsnFile, SpecctraDesign as _}, + mesadata::SpecctraMesadata, + }, }; use crate::{ @@ -21,7 +24,7 @@ use crate::{ /// A loaded design and associated structures. pub struct Workspace { - pub design: SpecctraDesign, + pub design: DsnFile, pub appearance_panel: AppearancePanel, pub overlay: Overlay, pub interactor: Interactor, @@ -33,7 +36,7 @@ pub struct Workspace { } impl Workspace { - pub fn new(design: SpecctraDesign, tr: &Translator) -> Result { + pub fn new(design: DsnFile, tr: &Translator) -> Result { let board = design.make_board(&mut LayoutEdit::new()); let appearance_panel = AppearancePanel::new(&board); let overlay = Overlay::new(&board).map_err(|err| { diff --git a/examples/specctra.rs b/examples/specctra.rs index 7a060d9..b83d9ec 100644 --- a/examples/specctra.rs +++ b/examples/specctra.rs @@ -7,13 +7,13 @@ use std::io::BufReader; use topola::autorouter::invoker::Invoker; use topola::autorouter::Autorouter; use topola::layout::LayoutEdit; -use topola::specctra::design::SpecctraDesign; +use topola::specctra::design::{DsnFile, SpecctraDesign as _}; fn main() -> Result<(), std::io::Error> { let design_file = File::open("example.dsn")?; let design_bufread = BufReader::new(design_file); - let design = SpecctraDesign::load(design_bufread).unwrap(); + let design = DsnFile::load(design_bufread).unwrap(); let board = design.make_board(&mut LayoutEdit::new()); let invoker = Invoker::new(Autorouter::new(board).unwrap()); diff --git a/src/specctra/design.rs b/src/specctra/design.rs index dbe31bb..2f86bcb 100644 --- a/src/specctra/design.rs +++ b/src/specctra/design.rs @@ -25,48 +25,61 @@ use crate::{ specctra::{ mesadata::SpecctraMesadata, read::ListTokenizer, - structure::{self, DsnFile, Layer, Pcb, Shape}, + structure::{self, Layer, Shape}, write::ListWriter, }, }; -pub use specctra_core::error::ParseErrorContext; +pub use specctra_core::{error::ParseErrorContext, structure::DsnFile}; -/// This struct is responsible for managing the various Specctra components of a PCB design, +/// This trait is responsible for managing the various Specctra components of a PCB design, /// including parsing the DSN file, handling the resolution, unit of measurement, /// and organizing the PCB's structure, placement, library, network, and wiring. /// It provides functionality for reading from a DSN file and writing Specctra's .SES session files. -#[derive(Debug)] -pub struct SpecctraDesign { - pcb: Pcb, -} - -impl SpecctraDesign { +pub trait SpecctraDesign: Sized { /// Loads a [`SpecctraDesign`] structure instance from a buffered reader. /// /// This function reads the Specctra Design data from an input stream. /// Later the data is parsed and loaded into a [`SpecctraDesign`] structure, /// allowing further operations such as rule validation, routing, or netlist management. - pub fn load(reader: impl std::io::BufRead) -> Result { - let mut list_reader = ListTokenizer::new(reader); - let dsn = list_reader.read_value::()?; - - Ok(Self { pcb: dsn.pcb }) - } + fn load(reader: impl std::io::BufRead) -> Result; /// Function to get name of the DSN file /// /// This function returns the name of the `Pcb` objects - pub fn get_name(&self) -> &str { - &self.pcb.name - } + fn get_name(&self) -> &str; /// Writes the Specctra Session (.ses) file format using the current board layout and mesadata. /// /// This function generates a Specctra SES session file that represents the board's net routing and /// writes it to the provided output stream. The session data includes routed nets, wires, /// layers, and other essential information for routing management. - pub fn write_ses( + fn write_ses( + &self, + board: &Board, + writer: impl std::io::Write, + ) -> Result<(), std::io::Error>; + + /// Generates a [`Board`] from the current PCB data. + /// + /// This function takes the internal `Pcb` structure and transforms it into a [`Board`] object, + /// which is used for layout and routing operations. The board is initialized with [`SpecctraMesadata`], + /// which includes layer and net mappings, and is populated with components, pins, vias, and wires + /// from the PCB definition. + fn make_board(&self, recorder: &mut LayoutEdit) -> Board; +} + +impl SpecctraDesign for DsnFile { + fn load(reader: impl std::io::BufRead) -> Result { + let mut list_reader = ListTokenizer::new(reader); + list_reader.read_value::() + } + + fn get_name(&self) -> &str { + &self.pcb.name + } + + fn write_ses( &self, board: &Board, writer: impl std::io::Write, @@ -173,13 +186,7 @@ impl SpecctraDesign { ListWriter::new(writer).write_value(&ses) } - /// Generates a [`Board`] from the current PCB data. - /// - /// This function takes the internal `Pcb` structure and transforms it into a [`Board`] object, - /// which is used for layout and routing operations. The board is initialized with [`SpecctraMesadata`], - /// which includes layer and net mappings, and is populated with components, pins, vias, and wires - /// from the PCB definition. - pub fn make_board(&self, recorder: &mut LayoutEdit) -> Board { + fn make_board(&self, recorder: &mut LayoutEdit) -> Board { let mesadata = SpecctraMesadata::from_pcb(&self.pcb); let mut board = Board::new(Layout::new(Drawing::new( mesadata, @@ -224,7 +231,7 @@ impl SpecctraDesign { for place in &component.places { let place_side_is_front = place.side == "front"; let get_layer = |board: &Board, name: &str| { - Self::layer(board, &self.pcb.structure.layers, name, place_side_is_front) + layer(board, &self.pcb.structure.layers, name, place_side_is_front) }; for pin in &image.pins { @@ -237,7 +244,7 @@ impl SpecctraDesign { match shape { Shape::Circle(circle) => { let layer = get_layer(&board, &circle.layer); - Self::add_circle( + add_circle( recorder, &mut board, place.point_with_rotation(), @@ -250,7 +257,7 @@ impl SpecctraDesign { } Shape::Rect(rect) => { let layer = get_layer(&board, &rect.layer); - Self::add_rect( + add_rect( recorder, &mut board, place.point_with_rotation(), @@ -266,7 +273,7 @@ impl SpecctraDesign { } Shape::Path(path) => { let layer = get_layer(&board, &path.layer); - Self::add_path( + add_path( recorder, &mut board, place.point_with_rotation(), @@ -280,7 +287,7 @@ impl SpecctraDesign { } Shape::Polygon(polygon) => { let layer = get_layer(&board, &polygon.layer); - Self::add_polygon( + add_polygon( recorder, &mut board, place.point_with_rotation(), @@ -304,14 +311,14 @@ impl SpecctraDesign { let padstack = self.pcb.library.find_padstack_by_name(&via.name).unwrap(); let get_layer = |board: &Board, name: &str| { - Self::layer(board, &self.pcb.structure.layers, name, true) + layer(board, &self.pcb.structure.layers, name, true) }; for shape in &padstack.shapes { match shape { Shape::Circle(circle) => { let layer = get_layer(&board, &circle.layer); - Self::add_circle( + add_circle( recorder, &mut board, // TODO: refactor? @@ -326,7 +333,7 @@ impl SpecctraDesign { } Shape::Rect(rect) => { let layer = get_layer(&board, &rect.layer); - Self::add_rect( + add_rect( recorder, &mut board, PointWithRotation::from_xy(via.x, via.y), @@ -342,7 +349,7 @@ impl SpecctraDesign { } Shape::Path(path) => { let layer = get_layer(&board, &path.layer); - Self::add_path( + add_path( recorder, &mut board, PointWithRotation::from_xy(via.x, via.y), @@ -356,7 +363,7 @@ impl SpecctraDesign { } Shape::Polygon(polygon) => { let layer = get_layer(&board, &polygon.layer); - Self::add_polygon( + add_polygon( recorder, &mut board, PointWithRotation::from_xy(via.x, via.y), @@ -381,7 +388,7 @@ impl SpecctraDesign { .unwrap(); let net = board.layout().drawing().rules().netname_net(&wire.net); - Self::add_path( + add_path( recorder, &mut board, PointWithRotation::default(), @@ -396,196 +403,213 @@ impl SpecctraDesign { board } +} - fn layer( - board: &Board, - layers: &[Layer], - layername: &str, - front: bool, - ) -> usize { - let image_layer = board - .layout() - .drawing() - .rules() - .layername_layer(layername) - .unwrap(); +fn layer(board: &Board, layers: &[Layer], layername: &str, front: bool) -> usize { + let image_layer = board + .layout() + .drawing() + .rules() + .layername_layer(layername) + .unwrap(); - if front { - image_layer - } else { - layers.len() - image_layer - 1 + if front { + image_layer + } else { + layers.len() - image_layer - 1 + } +} + +fn add_circle( + recorder: &mut LayoutEdit, + board: &mut Board, + place: PointWithRotation, + pin: PointWithRotation, + r: f64, + layer: usize, + maybe_net: Option, + maybe_pin: Option, +) { + let circle = Circle { + pos: pos(place, pin, 0.0, 0.0), + r, + }; + + board.add_fixed_dot_infringably( + recorder, + FixedDotWeight(GeneralDotWeight { + circle, + layer, + maybe_net, + }), + maybe_pin, + ); +} + +fn add_rect( + recorder: &mut LayoutEdit, + board: &mut Board, + place: PointWithRotation, + pin: PointWithRotation, + x1: f64, + y1: f64, + x2: f64, + y2: f64, + layer: usize, + maybe_net: Option, + maybe_pin: Option, +) { + // Corners. + let dot_1_1 = board.add_fixed_dot_infringably( + recorder, + FixedDotWeight(GeneralDotWeight { + circle: Circle { + pos: pos(place, pin, x1, y1), + r: 0.5, + }, + layer, + maybe_net, + }), + None, + ); + let dot_2_1 = board.add_fixed_dot_infringably( + recorder, + FixedDotWeight(GeneralDotWeight { + circle: Circle { + pos: pos(place, pin, x2, y1), + r: 0.5, + }, + layer, + maybe_net, + }), + None, + ); + let dot_2_2 = board.add_fixed_dot_infringably( + recorder, + FixedDotWeight(GeneralDotWeight { + circle: Circle { + pos: pos(place, pin, x2, y2), + r: 0.5, + }, + layer, + maybe_net, + }), + None, + ); + let dot_1_2 = board.add_fixed_dot_infringably( + recorder, + FixedDotWeight(GeneralDotWeight { + circle: Circle { + pos: pos(place, pin, x1, y2), + r: 0.5, + }, + layer, + maybe_net, + }), + None, + ); + // Sides. + let seg1 = board.add_fixed_seg_infringably( + recorder, + dot_1_1, + dot_2_1, + FixedSegWeight(GeneralSegWeight { + width: 1.0, + layer, + maybe_net, + }), + None, + ); + let seg2 = board.add_fixed_seg_infringably( + recorder, + dot_2_1, + dot_2_2, + FixedSegWeight(GeneralSegWeight { + width: 1.0, + layer, + maybe_net, + }), + None, + ); + let seg3 = board.add_fixed_seg_infringably( + recorder, + dot_2_2, + dot_1_2, + FixedSegWeight(GeneralSegWeight { + width: 1.0, + layer, + maybe_net, + }), + None, + ); + let seg4 = board.add_fixed_seg_infringably( + recorder, + dot_1_2, + dot_1_1, + FixedSegWeight(GeneralSegWeight { + width: 1.0, + layer, + maybe_net, + }), + 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(), + ], + ); +} + +fn add_path( + recorder: &mut LayoutEdit, + board: &mut Board, + place: PointWithRotation, + pin: PointWithRotation, + coords: &[structure::Point], + width: f64, + layer: usize, + maybe_net: Option, + maybe_pin: Option, +) { + // add the first coordinate in the wire path as a dot and save its index + let mut prev_pos = pos(place, pin, coords[0].x, coords[0].y); + let mut prev_index = board.add_fixed_dot_infringably( + recorder, + FixedDotWeight(GeneralDotWeight { + circle: Circle { + pos: prev_pos, + r: width / 2.0, + }, + layer, + maybe_net, + }), + maybe_pin.clone(), + ); + + // iterate through path coords starting from the second + for coord in coords.iter().skip(1) { + let pos = pos(place, pin, coord.x, coord.y); + + if pos == prev_pos { + continue; } - } - fn add_circle( - recorder: &mut LayoutEdit, - board: &mut Board, - place: PointWithRotation, - pin: PointWithRotation, - r: f64, - layer: usize, - maybe_net: Option, - maybe_pin: Option, - ) { - let circle = Circle { - pos: Self::pos(place, pin, 0.0, 0.0), - r, - }; - - board.add_fixed_dot_infringably( - recorder, - FixedDotWeight(GeneralDotWeight { - circle, - layer, - maybe_net, - }), - maybe_pin, - ); - } - - fn add_rect( - recorder: &mut LayoutEdit, - board: &mut Board, - place: PointWithRotation, - pin: PointWithRotation, - x1: f64, - y1: f64, - x2: f64, - y2: f64, - layer: usize, - maybe_net: Option, - maybe_pin: Option, - ) { - // Corners. - let dot_1_1 = board.add_fixed_dot_infringably( + let index = board.add_fixed_dot_infringably( recorder, FixedDotWeight(GeneralDotWeight { circle: Circle { - pos: Self::pos(place, pin, x1, y1), - r: 0.5, - }, - layer, - maybe_net, - }), - None, - ); - let dot_2_1 = board.add_fixed_dot_infringably( - recorder, - FixedDotWeight(GeneralDotWeight { - circle: Circle { - pos: Self::pos(place, pin, x2, y1), - r: 0.5, - }, - layer, - maybe_net, - }), - None, - ); - let dot_2_2 = board.add_fixed_dot_infringably( - recorder, - FixedDotWeight(GeneralDotWeight { - circle: Circle { - pos: Self::pos(place, pin, x2, y2), - r: 0.5, - }, - layer, - maybe_net, - }), - None, - ); - let dot_1_2 = board.add_fixed_dot_infringably( - recorder, - FixedDotWeight(GeneralDotWeight { - circle: Circle { - pos: Self::pos(place, pin, x1, y2), - r: 0.5, - }, - layer, - maybe_net, - }), - None, - ); - // Sides. - let seg1 = board.add_fixed_seg_infringably( - recorder, - dot_1_1, - dot_2_1, - FixedSegWeight(GeneralSegWeight { - width: 1.0, - layer, - maybe_net, - }), - None, - ); - let seg2 = board.add_fixed_seg_infringably( - recorder, - dot_2_1, - dot_2_2, - FixedSegWeight(GeneralSegWeight { - width: 1.0, - layer, - maybe_net, - }), - None, - ); - let seg3 = board.add_fixed_seg_infringably( - recorder, - dot_2_2, - dot_1_2, - FixedSegWeight(GeneralSegWeight { - width: 1.0, - layer, - maybe_net, - }), - None, - ); - let seg4 = board.add_fixed_seg_infringably( - recorder, - dot_1_2, - dot_1_1, - FixedSegWeight(GeneralSegWeight { - width: 1.0, - layer, - maybe_net, - }), - 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(), - ], - ); - } - - fn add_path( - recorder: &mut LayoutEdit, - board: &mut Board, - place: PointWithRotation, - pin: PointWithRotation, - coords: &[structure::Point], - width: f64, - layer: usize, - maybe_net: Option, - maybe_pin: Option, - ) { - // add the first coordinate in the wire path as a dot and save its index - let mut prev_pos = Self::pos(place, pin, coords[0].x, coords[0].y); - let mut prev_index = board.add_fixed_dot_infringably( - recorder, - FixedDotWeight(GeneralDotWeight { - circle: Circle { - pos: prev_pos, + pos, r: width / 2.0, }, layer, @@ -594,64 +618,72 @@ impl SpecctraDesign { maybe_pin.clone(), ); - // iterate through path coords starting from the second - for coord in coords.iter().skip(1) { - let pos = Self::pos(place, pin, coord.x, coord.y); + // add a seg between the current and previous coords + let _ = board.add_fixed_seg_infringably( + recorder, + prev_index, + index, + FixedSegWeight(GeneralSegWeight { + width, + layer, + maybe_net, + }), + maybe_pin.clone(), + ); - if pos == prev_pos { - continue; - } + prev_index = index; + prev_pos = pos; + } +} - let index = board.add_fixed_dot_infringably( - recorder, - FixedDotWeight(GeneralDotWeight { - circle: Circle { - pos, - r: width / 2.0, - }, - layer, - maybe_net, - }), - maybe_pin.clone(), - ); +fn add_polygon( + recorder: &mut LayoutEdit, + board: &mut Board, + place: PointWithRotation, + pin: PointWithRotation, + mut coords: &[structure::Point], + width: f64, + layer: usize, + maybe_net: Option, + maybe_pin: Option, +) { + let mut nodes = Vec::with_capacity(coords.len() * 2 - 1); - // add a seg between the current and previous coords - let _ = board.add_fixed_seg_infringably( - recorder, - prev_index, - index, - FixedSegWeight(GeneralSegWeight { - width, - layer, - maybe_net, - }), - maybe_pin.clone(), - ); + // add the first coordinate in the wire path as a dot and save its index + let first_index = board.add_fixed_dot_infringably( + recorder, + FixedDotWeight(GeneralDotWeight { + circle: Circle { + pos: pos(place, pin, coords[0].x, coords[0].y), + r: width / 2.0, + }, + layer, + maybe_net, + }), + None, + ); + nodes.push(first_index.into()); + let mut prev_index = first_index; - prev_index = index; - prev_pos = pos; - } + if approx::abs_diff_eq!(coords[0].x, coords.last().unwrap().x) + && approx::abs_diff_eq!(coords[0].y, coords.last().unwrap().y) + { + coords = &coords[..coords.len() - 1]; } - fn add_polygon( - recorder: &mut LayoutEdit, - board: &mut Board, - place: PointWithRotation, - pin: PointWithRotation, - mut coords: &[structure::Point], - width: f64, - layer: usize, - maybe_net: Option, - maybe_pin: Option, - ) { - let mut nodes = Vec::with_capacity(coords.len() * 2 - 1); + let seg_weight = FixedSegWeight(GeneralSegWeight { + width, + layer, + maybe_net, + }); - // add the first coordinate in the wire path as a dot and save its index - let first_index = board.add_fixed_dot_infringably( + // iterate through path coords starting from the second + for coord in &coords[1..] { + let index = board.add_fixed_dot_infringably( recorder, FixedDotWeight(GeneralDotWeight { circle: Circle { - pos: Self::pos(place, pin, coords[0].x, coords[0].y), + pos: pos(place, pin, coord.x, coord.y), r: width / 2.0, }, layer, @@ -659,64 +691,34 @@ impl SpecctraDesign { }), None, ); - nodes.push(first_index.into()); - let mut prev_index = first_index; + nodes.push(index.into()); - if approx::abs_diff_eq!(coords[0].x, coords.last().unwrap().x) - && approx::abs_diff_eq!(coords[0].y, coords.last().unwrap().y) - { - coords = &coords[..coords.len() - 1]; - } - - let seg_weight = FixedSegWeight(GeneralSegWeight { - width, - layer, - maybe_net, - }); - - // iterate through path coords starting from the second - for coord in &coords[1..] { - let index = board.add_fixed_dot_infringably( - recorder, - FixedDotWeight(GeneralDotWeight { - circle: Circle { - pos: Self::pos(place, pin, coord.x, coord.y), - r: width / 2.0, - }, - layer, - maybe_net, - }), - None, - ); - nodes.push(index.into()); - - // add a seg between the current and previous coords - nodes.push( - board - .add_fixed_seg_infringably(recorder, prev_index, index, seg_weight, None) - .into(), - ); - - prev_index = index; - } - - // add a seg between the last and first coords + // add a seg between the current and previous coords nodes.push( board - .add_fixed_seg_infringably(recorder, prev_index, first_index, seg_weight, None) + .add_fixed_seg_infringably(recorder, prev_index, index, seg_weight, None) .into(), ); - board.add_poly_with_nodes( - recorder, - SolidPolyWeight { layer, maybe_net }.into(), - maybe_pin, - &nodes[..], - ); + prev_index = index; } - fn pos(place: PointWithRotation, pin: PointWithRotation, x: f64, y: f64) -> Point { - let pos = (point! {x: x, y: y} + pin.pos).rotate_around_point(pin.rot, pin.pos); - (pos + place.pos).rotate_around_point(place.rot, place.pos) - } + // add a seg between the last and first coords + nodes.push( + board + .add_fixed_seg_infringably(recorder, prev_index, first_index, seg_weight, None) + .into(), + ); + + 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 { + let pos = (point! {x: x, y: y} + pin.pos).rotate_around_point(pin.rot, pin.pos); + (pos + place.pos).rotate_around_point(place.rot, place.pos) } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 8e21180..e726129 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -17,13 +17,16 @@ use topola::{ graph::{GetPetgraphIndex, MakeRef}, layout::LayoutEdit, router::{navmesh::Navmesh, RouterOptions}, - specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, + specctra::{ + design::{DsnFile, SpecctraDesign as _}, + mesadata::SpecctraMesadata, + }, }; pub fn load_design(filename: &str) -> Autorouter { let design_file = File::open(filename).unwrap(); let design_bufread = BufReader::new(design_file); - let design = SpecctraDesign::load(design_bufread).unwrap(); + let design = DsnFile::load(design_bufread).unwrap(); Autorouter::new(design.make_board(&mut LayoutEdit::new())).unwrap() }