mirror of https://codeberg.org/topola/topola.git
refactor(specctra/design): turn SpecctraDesign into a trait
This commit is contained in:
parent
3c807b5078
commit
f1225ba52e
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Result<SpecctraDesign, SpecctraLoadingError>>,
|
||||
Receiver<Result<SpecctraDesign, SpecctraLoadingError>>,
|
||||
Sender<Result<DsnFile, SpecctraLoadingError>>,
|
||||
Receiver<Result<DsnFile, SpecctraLoadingError>>,
|
||||
),
|
||||
|
||||
viewport: Viewport,
|
||||
|
|
|
|||
|
|
@ -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<Result<SpecctraDesign, SpecctraLoadingError>>,
|
||||
content_sender: Sender<Result<DsnFile, SpecctraLoadingError>>,
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SpecctraMesadata>,
|
||||
|
|
@ -33,7 +36,7 @@ pub struct 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 appearance_panel = AppearancePanel::new(&board);
|
||||
let overlay = Overlay::new(&board).map_err(|err| {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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<SpecctraDesign, ParseErrorContext> {
|
||||
let mut list_reader = ListTokenizer::new(reader);
|
||||
let dsn = list_reader.read_value::<DsnFile>()?;
|
||||
|
||||
Ok(Self { pcb: dsn.pcb })
|
||||
}
|
||||
fn load(reader: impl std::io::BufRead) -> Result<Self, ParseErrorContext>;
|
||||
|
||||
/// 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<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,
|
||||
board: &Board<SpecctraMesadata>,
|
||||
writer: impl std::io::Write,
|
||||
|
|
@ -173,13 +186,7 @@ impl SpecctraDesign {
|
|||
ListWriter::new(writer).write_value(&ses)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn make_board(&self, recorder: &mut LayoutEdit) -> Board<SpecctraMesadata> {
|
||||
fn make_board(&self, recorder: &mut LayoutEdit) -> Board<SpecctraMesadata> {
|
||||
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<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 {
|
||||
|
|
@ -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<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 {
|
||||
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<SpecctraMesadata>,
|
||||
layers: &[Layer],
|
||||
layername: &str,
|
||||
front: bool,
|
||||
) -> usize {
|
||||
let image_layer = board
|
||||
.layout()
|
||||
.drawing()
|
||||
.rules()
|
||||
.layername_layer(layername)
|
||||
.unwrap();
|
||||
fn layer(board: &Board<SpecctraMesadata>, 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<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(
|
||||
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(
|
||||
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<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,
|
||||
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<SpecctraMesadata>,
|
||||
place: PointWithRotation,
|
||||
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 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<SpecctraMesadata>,
|
||||
place: PointWithRotation,
|
||||
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);
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SpecctraMesadata> {
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue