refactor(specctra/design): turn SpecctraDesign into a trait

This commit is contained in:
Ellen Emilia Anna Zscheile 2025-06-07 10:39:39 +02:00
parent 3c807b5078
commit f1225ba52e
7 changed files with 350 additions and 338 deletions

View File

@ -9,11 +9,10 @@ use topola::autorouter::execution::Command;
use topola::autorouter::history::History; use topola::autorouter::history::History;
use topola::autorouter::invoker::Invoker; use topola::autorouter::invoker::Invoker;
use topola::autorouter::selection::PinSelection; use topola::autorouter::selection::PinSelection;
use topola::autorouter::Autorouter; use topola::autorouter::{Autorouter, AutorouterOptions};
use topola::autorouter::AutorouterOptions;
use topola::layout::LayoutEdit; use topola::layout::LayoutEdit;
use topola::router::RouterOptions; use topola::router::RouterOptions;
use topola::specctra::design::SpecctraDesign; use topola::specctra::design::{DsnFile, SpecctraDesign as _};
pub mod cli; pub mod cli;
use cli::Cli; use cli::Cli;
@ -23,8 +22,7 @@ fn main() -> Result<(), std::io::Error> {
let design_file = File::open(&args.input)?; let design_file = File::open(&args.input)?;
let design_bufread = BufReader::new(design_file); let design_bufread = BufReader::new(design_file);
let design = let design = DsnFile::load(design_bufread).expect("File failed to parse as Specctra DSN");
SpecctraDesign::load(design_bufread).expect("File failed to parse as Specctra DSN");
let board = design.make_board(&mut LayoutEdit::new()); let board = design.make_board(&mut LayoutEdit::new());

View File

@ -14,7 +14,10 @@ use unic_langid::{langid, LanguageIdentifier};
use topola::{ use topola::{
interactor::activity::InteractiveInput, interactor::activity::InteractiveInput,
specctra::{design::SpecctraDesign, ParseErrorContext as SpecctraLoadingError}, specctra::{
design::{DsnFile, SpecctraDesign},
ParseErrorContext as SpecctraLoadingError,
},
}; };
use crate::{ use crate::{
@ -27,8 +30,8 @@ pub struct App {
translator: Translator, translator: Translator,
content_channel: ( content_channel: (
Sender<Result<SpecctraDesign, SpecctraLoadingError>>, Sender<Result<DsnFile, SpecctraLoadingError>>,
Receiver<Result<SpecctraDesign, SpecctraLoadingError>>, Receiver<Result<DsnFile, SpecctraLoadingError>>,
), ),
viewport: Viewport, viewport: Viewport,

View File

@ -10,7 +10,10 @@ use topola::{
}, },
board::AccessMesadata, board::AccessMesadata,
router::RouterOptions, router::RouterOptions,
specctra::{design::SpecctraDesign, ParseError, ParseErrorContext as SpecctraLoadingError}, specctra::{
design::{DsnFile, SpecctraDesign as _},
ParseError, ParseErrorContext as SpecctraLoadingError,
},
}; };
use crate::{ use crate::{
@ -59,7 +62,7 @@ impl MenuBar {
&mut self, &mut self,
ctx: &egui::Context, ctx: &egui::Context,
tr: &mut Translator, tr: &mut Translator,
content_sender: Sender<Result<SpecctraDesign, SpecctraLoadingError>>, content_sender: Sender<Result<DsnFile, SpecctraLoadingError>>,
viewport: &mut Viewport, viewport: &mut Viewport,
maybe_workspace: Option<&mut Workspace>, maybe_workspace: Option<&mut Workspace>,
) -> Result<(), InvokerError> { ) -> Result<(), InvokerError> {
@ -174,7 +177,7 @@ impl MenuBar {
let data = handle_file(&file_handle) let data = handle_file(&file_handle)
.await .await
.map_err(|e| ParseError::from(e).add_context((0, 0))) .map_err(|e| ParseError::from(e).add_context((0, 0)))
.and_then(SpecctraDesign::load); .and_then(DsnFile::load);
content_sender.send(data); content_sender.send(data);
ctx.request_repaint(); ctx.request_repaint();
} }

View File

@ -11,7 +11,10 @@ use topola::{
autorouter::history::History, autorouter::history::History,
interactor::{activity::InteractiveInput, Interactor}, interactor::{activity::InteractiveInput, Interactor},
layout::LayoutEdit, layout::LayoutEdit,
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, specctra::{
design::{DsnFile, SpecctraDesign as _},
mesadata::SpecctraMesadata,
},
}; };
use crate::{ use crate::{
@ -21,7 +24,7 @@ use crate::{
/// A loaded design and associated structures. /// A loaded design and associated structures.
pub struct Workspace { pub struct Workspace {
pub design: SpecctraDesign, pub design: DsnFile,
pub appearance_panel: AppearancePanel, pub appearance_panel: AppearancePanel,
pub overlay: Overlay, pub overlay: Overlay,
pub interactor: Interactor<SpecctraMesadata>, pub interactor: Interactor<SpecctraMesadata>,
@ -33,7 +36,7 @@ pub struct Workspace {
} }
impl Workspace { impl Workspace {
pub fn new(design: SpecctraDesign, tr: &Translator) -> Result<Self, String> { pub fn new(design: DsnFile, tr: &Translator) -> Result<Self, String> {
let board = design.make_board(&mut LayoutEdit::new()); let board = design.make_board(&mut LayoutEdit::new());
let appearance_panel = AppearancePanel::new(&board); let appearance_panel = AppearancePanel::new(&board);
let overlay = Overlay::new(&board).map_err(|err| { let overlay = Overlay::new(&board).map_err(|err| {

View File

@ -7,13 +7,13 @@ use std::io::BufReader;
use topola::autorouter::invoker::Invoker; use topola::autorouter::invoker::Invoker;
use topola::autorouter::Autorouter; use topola::autorouter::Autorouter;
use topola::layout::LayoutEdit; use topola::layout::LayoutEdit;
use topola::specctra::design::SpecctraDesign; use topola::specctra::design::{DsnFile, SpecctraDesign as _};
fn main() -> Result<(), std::io::Error> { fn main() -> Result<(), std::io::Error> {
let design_file = File::open("example.dsn")?; let design_file = File::open("example.dsn")?;
let design_bufread = BufReader::new(design_file); 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 board = design.make_board(&mut LayoutEdit::new());
let invoker = Invoker::new(Autorouter::new(board).unwrap()); let invoker = Invoker::new(Autorouter::new(board).unwrap());

View File

@ -25,48 +25,61 @@ use crate::{
specctra::{ specctra::{
mesadata::SpecctraMesadata, mesadata::SpecctraMesadata,
read::ListTokenizer, read::ListTokenizer,
structure::{self, DsnFile, Layer, Pcb, Shape}, structure::{self, Layer, Shape},
write::ListWriter, 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, /// including parsing the DSN file, handling the resolution, unit of measurement,
/// and organizing the PCB's structure, placement, library, network, and wiring. /// 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. /// It provides functionality for reading from a DSN file and writing Specctra's .SES session files.
#[derive(Debug)] pub trait SpecctraDesign: Sized {
pub struct SpecctraDesign {
pcb: Pcb,
}
impl SpecctraDesign {
/// Loads a [`SpecctraDesign`] structure instance from a buffered reader. /// Loads a [`SpecctraDesign`] structure instance from a buffered reader.
/// ///
/// This function reads the Specctra Design data from an input stream. /// This function reads the Specctra Design data from an input stream.
/// Later the data is parsed and loaded into a [`SpecctraDesign`] structure, /// Later the data is parsed and loaded into a [`SpecctraDesign`] structure,
/// allowing further operations such as rule validation, routing, or netlist management. /// allowing further operations such as rule validation, routing, or netlist management.
pub fn load(reader: impl std::io::BufRead) -> Result<SpecctraDesign, ParseErrorContext> { fn load(reader: impl std::io::BufRead) -> Result<Self, ParseErrorContext>;
let mut list_reader = ListTokenizer::new(reader);
let dsn = list_reader.read_value::<DsnFile>()?;
Ok(Self { pcb: dsn.pcb })
}
/// Function to get name of the DSN file /// Function to get name of the DSN file
/// ///
/// This function returns the name of the `Pcb` objects /// This function returns the name of the `Pcb` objects
pub fn get_name(&self) -> &str { fn get_name(&self) -> &str;
&self.pcb.name
}
/// Writes the Specctra Session (.ses) file format using the current board layout and mesadata. /// 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 /// 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, /// writes it to the provided output stream. The session data includes routed nets, wires,
/// layers, and other essential information for routing management. /// layers, and other essential information for routing management.
pub fn write_ses( fn write_ses(
&self,
board: &Board<SpecctraMesadata>,
writer: impl std::io::Write,
) -> Result<(), std::io::Error>;
/// Generates a [`Board<SpecctraMesadata>`] 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<SpecctraMesadata>;
}
impl SpecctraDesign for DsnFile {
fn load(reader: impl std::io::BufRead) -> Result<Self, ParseErrorContext> {
let mut list_reader = ListTokenizer::new(reader);
list_reader.read_value::<DsnFile>()
}
fn get_name(&self) -> &str {
&self.pcb.name
}
fn write_ses(
&self, &self,
board: &Board<SpecctraMesadata>, board: &Board<SpecctraMesadata>,
writer: impl std::io::Write, writer: impl std::io::Write,
@ -173,13 +186,7 @@ impl SpecctraDesign {
ListWriter::new(writer).write_value(&ses) ListWriter::new(writer).write_value(&ses)
} }
/// Generates a [`Board<SpecctraMesadata>`] from the current PCB data. fn make_board(&self, recorder: &mut LayoutEdit) -> Board<SpecctraMesadata> {
///
/// 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<SpecctraMesadata> {
let mesadata = SpecctraMesadata::from_pcb(&self.pcb); let mesadata = SpecctraMesadata::from_pcb(&self.pcb);
let mut board = Board::new(Layout::new(Drawing::new( let mut board = Board::new(Layout::new(Drawing::new(
mesadata, mesadata,
@ -224,7 +231,7 @@ impl SpecctraDesign {
for place in &component.places { for place in &component.places {
let place_side_is_front = place.side == "front"; let place_side_is_front = place.side == "front";
let get_layer = |board: &Board<SpecctraMesadata>, name: &str| { let get_layer = |board: &Board<SpecctraMesadata>, 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 { for pin in &image.pins {
@ -237,7 +244,7 @@ impl SpecctraDesign {
match shape { match shape {
Shape::Circle(circle) => { Shape::Circle(circle) => {
let layer = get_layer(&board, &circle.layer); let layer = get_layer(&board, &circle.layer);
Self::add_circle( add_circle(
recorder, recorder,
&mut board, &mut board,
place.point_with_rotation(), place.point_with_rotation(),
@ -250,7 +257,7 @@ impl SpecctraDesign {
} }
Shape::Rect(rect) => { Shape::Rect(rect) => {
let layer = get_layer(&board, &rect.layer); let layer = get_layer(&board, &rect.layer);
Self::add_rect( add_rect(
recorder, recorder,
&mut board, &mut board,
place.point_with_rotation(), place.point_with_rotation(),
@ -266,7 +273,7 @@ impl SpecctraDesign {
} }
Shape::Path(path) => { Shape::Path(path) => {
let layer = get_layer(&board, &path.layer); let layer = get_layer(&board, &path.layer);
Self::add_path( add_path(
recorder, recorder,
&mut board, &mut board,
place.point_with_rotation(), place.point_with_rotation(),
@ -280,7 +287,7 @@ impl SpecctraDesign {
} }
Shape::Polygon(polygon) => { Shape::Polygon(polygon) => {
let layer = get_layer(&board, &polygon.layer); let layer = get_layer(&board, &polygon.layer);
Self::add_polygon( add_polygon(
recorder, recorder,
&mut board, &mut board,
place.point_with_rotation(), place.point_with_rotation(),
@ -304,14 +311,14 @@ impl SpecctraDesign {
let padstack = self.pcb.library.find_padstack_by_name(&via.name).unwrap(); let padstack = self.pcb.library.find_padstack_by_name(&via.name).unwrap();
let get_layer = |board: &Board<SpecctraMesadata>, name: &str| { let get_layer = |board: &Board<SpecctraMesadata>, name: &str| {
Self::layer(board, &self.pcb.structure.layers, name, true) layer(board, &self.pcb.structure.layers, name, true)
}; };
for shape in &padstack.shapes { for shape in &padstack.shapes {
match shape { match shape {
Shape::Circle(circle) => { Shape::Circle(circle) => {
let layer = get_layer(&board, &circle.layer); let layer = get_layer(&board, &circle.layer);
Self::add_circle( add_circle(
recorder, recorder,
&mut board, &mut board,
// TODO: refactor? // TODO: refactor?
@ -326,7 +333,7 @@ impl SpecctraDesign {
} }
Shape::Rect(rect) => { Shape::Rect(rect) => {
let layer = get_layer(&board, &rect.layer); let layer = get_layer(&board, &rect.layer);
Self::add_rect( add_rect(
recorder, recorder,
&mut board, &mut board,
PointWithRotation::from_xy(via.x, via.y), PointWithRotation::from_xy(via.x, via.y),
@ -342,7 +349,7 @@ impl SpecctraDesign {
} }
Shape::Path(path) => { Shape::Path(path) => {
let layer = get_layer(&board, &path.layer); let layer = get_layer(&board, &path.layer);
Self::add_path( add_path(
recorder, recorder,
&mut board, &mut board,
PointWithRotation::from_xy(via.x, via.y), PointWithRotation::from_xy(via.x, via.y),
@ -356,7 +363,7 @@ impl SpecctraDesign {
} }
Shape::Polygon(polygon) => { Shape::Polygon(polygon) => {
let layer = get_layer(&board, &polygon.layer); let layer = get_layer(&board, &polygon.layer);
Self::add_polygon( add_polygon(
recorder, recorder,
&mut board, &mut board,
PointWithRotation::from_xy(via.x, via.y), PointWithRotation::from_xy(via.x, via.y),
@ -381,7 +388,7 @@ impl SpecctraDesign {
.unwrap(); .unwrap();
let net = board.layout().drawing().rules().netname_net(&wire.net); let net = board.layout().drawing().rules().netname_net(&wire.net);
Self::add_path( add_path(
recorder, recorder,
&mut board, &mut board,
PointWithRotation::default(), PointWithRotation::default(),
@ -396,196 +403,213 @@ impl SpecctraDesign {
board board
} }
}
fn layer( fn layer(board: &Board<SpecctraMesadata>, layers: &[Layer], layername: &str, front: bool) -> usize {
board: &Board<SpecctraMesadata>, let image_layer = board
layers: &[Layer], .layout()
layername: &str, .drawing()
front: bool, .rules()
) -> usize { .layername_layer(layername)
let image_layer = board .unwrap();
.layout()
.drawing()
.rules()
.layername_layer(layername)
.unwrap();
if front { if front {
image_layer image_layer
} else { } else {
layers.len() - image_layer - 1 layers.len() - image_layer - 1
}
}
fn add_circle(
recorder: &mut LayoutEdit,
board: &mut Board<SpecctraMesadata>,
place: PointWithRotation,
pin: PointWithRotation,
r: f64,
layer: usize,
maybe_net: Option<usize>,
maybe_pin: Option<String>,
) {
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<SpecctraMesadata>,
place: PointWithRotation,
pin: PointWithRotation,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
layer: usize,
maybe_net: Option<usize>,
maybe_pin: Option<String>,
) {
// 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<SpecctraMesadata>,
place: PointWithRotation,
pin: PointWithRotation,
coords: &[structure::Point],
width: f64,
layer: usize,
maybe_net: Option<usize>,
maybe_pin: Option<String>,
) {
// 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( let index = board.add_fixed_dot_infringably(
recorder: &mut LayoutEdit,
board: &mut Board<SpecctraMesadata>,
place: PointWithRotation,
pin: PointWithRotation,
r: f64,
layer: usize,
maybe_net: Option<usize>,
maybe_pin: Option<String>,
) {
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<SpecctraMesadata>,
place: PointWithRotation,
pin: PointWithRotation,
x1: f64,
y1: f64,
x2: f64,
y2: f64,
layer: usize,
maybe_net: Option<usize>,
maybe_pin: Option<String>,
) {
// Corners.
let dot_1_1 = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { circle: Circle {
pos: Self::pos(place, pin, x1, y1), pos,
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<SpecctraMesadata>,
place: PointWithRotation,
pin: PointWithRotation,
coords: &[structure::Point],
width: f64,
layer: usize,
maybe_net: Option<usize>,
maybe_pin: Option<String>,
) {
// 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,
r: width / 2.0, r: width / 2.0,
}, },
layer, layer,
@ -594,64 +618,72 @@ impl SpecctraDesign {
maybe_pin.clone(), maybe_pin.clone(),
); );
// iterate through path coords starting from the second // add a seg between the current and previous coords
for coord in coords.iter().skip(1) { let _ = board.add_fixed_seg_infringably(
let pos = Self::pos(place, pin, coord.x, coord.y); recorder,
prev_index,
index,
FixedSegWeight(GeneralSegWeight {
width,
layer,
maybe_net,
}),
maybe_pin.clone(),
);
if pos == prev_pos { prev_index = index;
continue; prev_pos = pos;
} }
}
let index = board.add_fixed_dot_infringably( fn add_polygon(
recorder, recorder: &mut LayoutEdit,
FixedDotWeight(GeneralDotWeight { board: &mut Board<SpecctraMesadata>,
circle: Circle { place: PointWithRotation,
pos, pin: PointWithRotation,
r: width / 2.0, mut coords: &[structure::Point],
}, width: f64,
layer, layer: usize,
maybe_net, maybe_net: Option<usize>,
}), maybe_pin: Option<String>,
maybe_pin.clone(), ) {
); let mut nodes = Vec::with_capacity(coords.len() * 2 - 1);
// add a seg between the current and previous coords // add the first coordinate in the wire path as a dot and save its index
let _ = board.add_fixed_seg_infringably( let first_index = board.add_fixed_dot_infringably(
recorder, recorder,
prev_index, FixedDotWeight(GeneralDotWeight {
index, circle: Circle {
FixedSegWeight(GeneralSegWeight { pos: pos(place, pin, coords[0].x, coords[0].y),
width, r: width / 2.0,
layer, },
maybe_net, layer,
}), maybe_net,
maybe_pin.clone(), }),
); None,
);
nodes.push(first_index.into());
let mut prev_index = first_index;
prev_index = index; if approx::abs_diff_eq!(coords[0].x, coords.last().unwrap().x)
prev_pos = pos; && approx::abs_diff_eq!(coords[0].y, coords.last().unwrap().y)
} {
coords = &coords[..coords.len() - 1];
} }
fn add_polygon( let seg_weight = FixedSegWeight(GeneralSegWeight {
recorder: &mut LayoutEdit, width,
board: &mut Board<SpecctraMesadata>, layer,
place: PointWithRotation, maybe_net,
pin: PointWithRotation, });
mut coords: &[structure::Point],
width: f64,
layer: usize,
maybe_net: Option<usize>,
maybe_pin: Option<String>,
) {
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 // iterate through path coords starting from the second
let first_index = board.add_fixed_dot_infringably( for coord in &coords[1..] {
let index = board.add_fixed_dot_infringably(
recorder, recorder,
FixedDotWeight(GeneralDotWeight { FixedDotWeight(GeneralDotWeight {
circle: Circle { 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, r: width / 2.0,
}, },
layer, layer,
@ -659,64 +691,34 @@ impl SpecctraDesign {
}), }),
None, None,
); );
nodes.push(first_index.into()); nodes.push(index.into());
let mut prev_index = first_index;
if approx::abs_diff_eq!(coords[0].x, coords.last().unwrap().x) // add a seg between the current and previous coords
&& 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
nodes.push( nodes.push(
board 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(), .into(),
); );
board.add_poly_with_nodes( prev_index = index;
recorder,
SolidPolyWeight { layer, maybe_net }.into(),
maybe_pin,
&nodes[..],
);
} }
fn pos(place: PointWithRotation, pin: PointWithRotation, x: f64, y: f64) -> Point { // add a seg between the last and first coords
let pos = (point! {x: x, y: y} + pin.pos).rotate_around_point(pin.rot, pin.pos); nodes.push(
(pos + place.pos).rotate_around_point(place.rot, place.pos) 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)
} }

View File

@ -17,13 +17,16 @@ use topola::{
graph::{GetPetgraphIndex, MakeRef}, graph::{GetPetgraphIndex, MakeRef},
layout::LayoutEdit, layout::LayoutEdit,
router::{navmesh::Navmesh, RouterOptions}, router::{navmesh::Navmesh, RouterOptions},
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, specctra::{
design::{DsnFile, SpecctraDesign as _},
mesadata::SpecctraMesadata,
},
}; };
pub fn load_design(filename: &str) -> Autorouter<SpecctraMesadata> { pub fn load_design(filename: &str) -> Autorouter<SpecctraMesadata> {
let design_file = File::open(filename).unwrap(); let design_file = File::open(filename).unwrap();
let design_bufread = BufReader::new(design_file); 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() Autorouter::new(design.make_board(&mut LayoutEdit::new())).unwrap()
} }