mirror of https://codeberg.org/topola/topola.git
feat(specctra-core): Add basic via SES export (constant size and only two layers for now)
This commit is contained in:
parent
8b0adec8fe
commit
00e3bb87bf
|
|
@ -688,7 +688,6 @@ pub struct Via {
|
|||
#[anon]
|
||||
pub y: f64,
|
||||
pub net: String,
|
||||
pub r#type: String,
|
||||
}
|
||||
|
||||
#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -21,20 +21,23 @@ pub struct ConnectedComponents {
|
|||
|
||||
impl ConnectedComponents {
|
||||
pub fn new(board: &Board<impl AccessMesadata>) -> Self {
|
||||
let mut unionfind = UnionFind::new(board.layout().drawing().geometry().dot_index_bound());
|
||||
let mut dot_unionfind =
|
||||
UnionFind::new(board.layout().drawing().geometry().dot_index_bound());
|
||||
|
||||
for node in board.layout().drawing().primitive_nodes() {
|
||||
Self::unionize_primitive_endpoint_dots(board, &mut unionfind, node);
|
||||
Self::unionize_primitive_endpoint_dots(board, &mut dot_unionfind, node);
|
||||
}
|
||||
|
||||
// Pins can have padstacks that span multiple layers. To account for
|
||||
// that, we have another loop to go over all the pins and connect all
|
||||
// their primitives.
|
||||
for pinname in board.pinnames() {
|
||||
Self::unionize_pin(board, &mut unionfind, pinname);
|
||||
Self::unionize_pin(board, &mut dot_unionfind, pinname);
|
||||
}
|
||||
|
||||
Self { unionfind }
|
||||
Self {
|
||||
unionfind: dot_unionfind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_principal_layer(
|
||||
|
|
@ -70,33 +73,33 @@ impl ConnectedComponents {
|
|||
|
||||
fn unionize_primitive_endpoint_dots(
|
||||
board: &Board<impl AccessMesadata>,
|
||||
unionfind: &mut UnionFind<usize>,
|
||||
dot_unionfind: &mut UnionFind<usize>,
|
||||
primitive: PrimitiveIndex,
|
||||
) {
|
||||
match primitive {
|
||||
PrimitiveIndex::FixedSeg(seg) => {
|
||||
let joints = board.layout().drawing().primitive(seg).joints();
|
||||
unionfind.union(joints.0.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
||||
dot_unionfind.union(joints.0.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||
}
|
||||
PrimitiveIndex::LoneLooseSeg(seg) => {
|
||||
let joints = board.layout().drawing().primitive(seg).joints();
|
||||
unionfind.union(joints.0.index(), joints.1.index());
|
||||
dot_unionfind.union(joints.0.index(), joints.1.index());
|
||||
}
|
||||
PrimitiveIndex::SeqLooseSeg(seg) => {
|
||||
let joints = board.layout().drawing().primitive(seg).joints();
|
||||
unionfind.union(joints.0.index(), joints.1.index());
|
||||
dot_unionfind.union(joints.0.index(), joints.1.index());
|
||||
}
|
||||
PrimitiveIndex::FixedBend(bend) => {
|
||||
let joints = board.layout().drawing().primitive(bend).joints();
|
||||
unionfind.union(joints.0.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
||||
dot_unionfind.union(joints.0.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||
}
|
||||
PrimitiveIndex::LooseBend(bend) => {
|
||||
let joints = board.layout().drawing().primitive(bend).joints();
|
||||
unionfind.union(joints.0.index(), joints.1.index());
|
||||
dot_unionfind.union(joints.0.index(), joints.1.index());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -104,7 +107,7 @@ impl ConnectedComponents {
|
|||
|
||||
fn unionize_pin(
|
||||
board: &Board<impl AccessMesadata>,
|
||||
unionfind: &mut UnionFind<usize>,
|
||||
dot_unionfind: &mut UnionFind<usize>,
|
||||
pinname: &str,
|
||||
) {
|
||||
let mut iter = board.pinname_nodes(pinname);
|
||||
|
|
@ -120,53 +123,53 @@ impl ConnectedComponents {
|
|||
|
||||
for node in board.pinname_nodes(pinname) {
|
||||
if let GenericNode::Primitive(primitive) = node {
|
||||
Self::unionize_to_common(board, unionfind, primitive, first_fixed_dot);
|
||||
Self::unionize_to_common(board, dot_unionfind, primitive, first_fixed_dot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unionize_to_common(
|
||||
board: &Board<impl AccessMesadata>,
|
||||
unionfind: &mut UnionFind<usize>,
|
||||
dot_unionfind: &mut UnionFind<usize>,
|
||||
primitive: PrimitiveIndex,
|
||||
common: FixedDotIndex,
|
||||
) {
|
||||
match primitive {
|
||||
PrimitiveIndex::FixedDot(dot) => {
|
||||
unionfind.union(common.index(), dot.index());
|
||||
Self::unionize_fixed_dot_via(board, unionfind, dot);
|
||||
dot_unionfind.union(common.index(), dot.index());
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, dot);
|
||||
}
|
||||
PrimitiveIndex::LooseDot(dot) => {
|
||||
unionfind.union(common.index(), dot.index());
|
||||
dot_unionfind.union(common.index(), dot.index());
|
||||
}
|
||||
PrimitiveIndex::FixedSeg(seg) => {
|
||||
let joints = board.layout().drawing().primitive(seg).joints();
|
||||
unionfind.union(common.index(), joints.0.index());
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
||||
unionfind.union(common.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
||||
dot_unionfind.union(common.index(), joints.0.index());
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||
dot_unionfind.union(common.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||
}
|
||||
PrimitiveIndex::LoneLooseSeg(seg) => {
|
||||
let joints = board.layout().drawing().primitive(seg).joints();
|
||||
unionfind.union(common.index(), joints.0.index());
|
||||
unionfind.union(common.index(), joints.1.index());
|
||||
dot_unionfind.union(common.index(), joints.0.index());
|
||||
dot_unionfind.union(common.index(), joints.1.index());
|
||||
}
|
||||
PrimitiveIndex::SeqLooseSeg(seg) => {
|
||||
let joints = board.layout().drawing().primitive(seg).joints();
|
||||
unionfind.union(common.index(), joints.0.index());
|
||||
unionfind.union(common.index(), joints.1.index());
|
||||
dot_unionfind.union(common.index(), joints.0.index());
|
||||
dot_unionfind.union(common.index(), joints.1.index());
|
||||
}
|
||||
PrimitiveIndex::FixedBend(bend) => {
|
||||
let joints = board.layout().drawing().primitive(bend).joints();
|
||||
unionfind.union(common.index(), joints.0.index());
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
||||
unionfind.union(common.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
||||
dot_unionfind.union(common.index(), joints.0.index());
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||
dot_unionfind.union(common.index(), joints.1.index());
|
||||
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||
}
|
||||
PrimitiveIndex::LooseBend(bend) => {
|
||||
let joints = board.layout().drawing().primitive(bend).joints();
|
||||
unionfind.union(common.index(), joints.0.index());
|
||||
unionfind.union(common.index(), joints.1.index());
|
||||
dot_unionfind.union(common.index(), joints.0.index());
|
||||
dot_unionfind.union(common.index(), joints.1.index());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -174,12 +177,12 @@ impl ConnectedComponents {
|
|||
|
||||
fn unionize_fixed_dot_via(
|
||||
board: &Board<impl AccessMesadata>,
|
||||
unionfind: &mut UnionFind<usize>,
|
||||
dot_unionfind: &mut UnionFind<usize>,
|
||||
dot: FixedDotIndex,
|
||||
) {
|
||||
if let Some(via) = board.layout().fixed_dot_via(dot) {
|
||||
for via_dot in board.layout().via(via).dots() {
|
||||
unionfind.union(dot.index(), via_dot.index());
|
||||
for via_dot in board.layout().via_ref(via).dots() {
|
||||
dot_unionfind.union(dot.index(), via_dot.index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ use crate::{
|
|||
graph::{GenericIndex, GetIndex, MakeRef},
|
||||
layout::{
|
||||
poly::{add_poly_with_nodes_intern, MakePolygon, PolyWeight},
|
||||
via::{Via, ViaWeight},
|
||||
via::{ViaRef, ViaWeight},
|
||||
},
|
||||
math::RotationSense,
|
||||
};
|
||||
|
|
@ -476,8 +476,8 @@ impl<R: AccessRules> Layout<R> {
|
|||
self.drawing.rules_mut()
|
||||
}
|
||||
|
||||
pub fn via(&self, index: GenericIndex<ViaWeight>) -> Via<'_, R> {
|
||||
Via::new(index, self.drawing())
|
||||
pub fn via_ref(&self, index: GenericIndex<ViaWeight>) -> ViaRef<'_, R> {
|
||||
ViaRef::new(index, self.drawing())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ use crate::{
|
|||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Via<'a, R> {
|
||||
pub struct ViaRef<'a, R> {
|
||||
pub index: GenericIndex<ViaWeight>,
|
||||
drawing: &'a Drawing<CompoundWeight, CompoundEntryLabel, R>,
|
||||
}
|
||||
|
||||
impl<'a, R> Via<'a, R> {
|
||||
impl<'a, R> ViaRef<'a, R> {
|
||||
pub fn new(
|
||||
index: GenericIndex<ViaWeight>,
|
||||
drawing: &'a Drawing<CompoundWeight, CompoundEntryLabel, R>,
|
||||
|
|
@ -45,13 +45,13 @@ impl<'a, R> Via<'a, R> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: AccessRules> GetMaybeNet for Via<'_, R> {
|
||||
impl<R: AccessRules> GetMaybeNet for ViaRef<'_, R> {
|
||||
fn maybe_net(&self) -> Option<usize> {
|
||||
self.drawing.compound_weight(self.index.into()).maybe_net()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AccessRules> MakePrimitiveShape for Via<'_, R> {
|
||||
impl<R: AccessRules> MakePrimitiveShape for ViaRef<'_, R> {
|
||||
fn shape(&self) -> PrimitiveShape {
|
||||
if let CompoundWeight::Via(weight) = self.drawing.compound_weight(self.index.into()) {
|
||||
weight.shape()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//! Design DSN file, creating the [`Board`] object from the file, as well as
|
||||
//! exporting the session file
|
||||
|
||||
use std::collections::{btree_map::Entry as BTreeMapEntry, BTreeMap};
|
||||
use std::collections::{btree_map::Entry as BTreeMapEntry, BTreeMap, BTreeSet};
|
||||
|
||||
use geo::{Euclidean, Length, Line, Point, Rotate};
|
||||
use itertools::Itertools;
|
||||
|
|
@ -16,13 +16,14 @@ use crate::{
|
|||
board::{edit::BoardEdit, AccessMesadata, Board},
|
||||
drawing::{
|
||||
dot::{FixedDotIndex, FixedDotWeight, GeneralDotWeight},
|
||||
graph::{GetMaybeNet, MakePrimitiveRef},
|
||||
graph::{GetMaybeNet, MakePrimitiveRef, PrimitiveIndex},
|
||||
primitive::MakePrimitiveShape,
|
||||
seg::{FixedSegWeight, GeneralSegWeight},
|
||||
Drawing,
|
||||
},
|
||||
geometry::{primitive::PrimitiveShape, GetLayer, GetWidth},
|
||||
layout::{poly::SolidPolyWeight, Layout},
|
||||
geometry::{primitive::PrimitiveShape, shape::AccessShape, GetLayer, GetWidth},
|
||||
graph::GenericIndex,
|
||||
layout::{poly::SolidPolyWeight, via::ViaWeight, Layout},
|
||||
math::{self, Circle},
|
||||
specctra::{
|
||||
mesadata::SpecctraMesadata,
|
||||
|
|
@ -77,10 +78,32 @@ impl SpecctraDesign {
|
|||
let drawing = board.layout().drawing();
|
||||
|
||||
let mut net_outs = BTreeMap::<usize, structure::NetOut>::new();
|
||||
|
||||
// Since we iterate over primitives, keep track of added vias to prevent
|
||||
// creation of duplicates.
|
||||
let mut visited_vias: BTreeSet<GenericIndex<ViaWeight>> = BTreeSet::new();
|
||||
|
||||
for index in drawing.primitive_nodes() {
|
||||
let primitive = index.primitive_ref(drawing);
|
||||
|
||||
if let Some(net) = primitive.maybe_net() {
|
||||
let net_out = match net_outs.entry(net) {
|
||||
BTreeMapEntry::Occupied(occ) => occ.into_mut(),
|
||||
BTreeMapEntry::Vacant(vac) => vac.insert(structure::NetOut {
|
||||
name: mesadata
|
||||
.net_netname(net)
|
||||
.ok_or_else(|| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!("tried to reference invalid net ID {}", net),
|
||||
)
|
||||
})?
|
||||
.to_owned(),
|
||||
wire: Vec::new(),
|
||||
via: Vec::new(),
|
||||
}),
|
||||
};
|
||||
|
||||
let coords = match primitive.shape() {
|
||||
PrimitiveShape::Seg(seg) => {
|
||||
vec![
|
||||
|
|
@ -106,12 +129,37 @@ impl SpecctraDesign {
|
|||
.collect()
|
||||
}
|
||||
|
||||
// Intentionally skipped for now.
|
||||
// Topola stores trace segments and dots joining them
|
||||
// as separate objects, but the Specctra formats and KiCad
|
||||
// appear to consider them implicit.
|
||||
// TODO: Vias
|
||||
PrimitiveShape::Dot(_) => continue,
|
||||
PrimitiveShape::Dot(dot_shape) => {
|
||||
let PrimitiveIndex::FixedDot(dot) = index else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(via) = board.layout().fixed_dot_via(dot) {
|
||||
if !visited_vias.contains(&via) {
|
||||
net_out.via.push(structure::Via {
|
||||
name: "__Via".to_string(),
|
||||
x: dot_shape.center().x(),
|
||||
y: dot_shape.center().y(),
|
||||
net: mesadata
|
||||
.net_netname(net)
|
||||
.ok_or_else(|| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"tried to reference invalid net ID {}",
|
||||
net
|
||||
),
|
||||
)
|
||||
})?
|
||||
.to_owned(),
|
||||
});
|
||||
|
||||
visited_vias.insert(via);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let wire = structure::WireOut {
|
||||
|
|
@ -133,22 +181,6 @@ impl SpecctraDesign {
|
|||
},
|
||||
};
|
||||
|
||||
let net_out = match net_outs.entry(net) {
|
||||
BTreeMapEntry::Occupied(occ) => occ.into_mut(),
|
||||
BTreeMapEntry::Vacant(vac) => vac.insert(structure::NetOut {
|
||||
name: mesadata
|
||||
.net_netname(net)
|
||||
.ok_or_else(|| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!("tried to reference invalid net ID {}", net),
|
||||
)
|
||||
})?
|
||||
.to_owned(),
|
||||
wire: Vec::new(),
|
||||
via: Vec::new(),
|
||||
}),
|
||||
};
|
||||
net_out.wire.push(wire);
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +195,23 @@ impl SpecctraDesign {
|
|||
},
|
||||
library_out: structure::Library {
|
||||
images: Vec::new(),
|
||||
padstacks: Vec::new(),
|
||||
padstacks: vec![structure::Padstack {
|
||||
name: "__Via".to_string(),
|
||||
// TODO: Use correct sizes and have all layers.
|
||||
shapes: vec![
|
||||
structure::Shape::Circle(structure::Circle {
|
||||
layer: "F.Cu".to_string(),
|
||||
diameter: 500.0,
|
||||
offset: None,
|
||||
}),
|
||||
structure::Shape::Circle(structure::Circle {
|
||||
layer: "B.Cu".to_string(),
|
||||
diameter: 500.0,
|
||||
offset: None,
|
||||
}),
|
||||
],
|
||||
attach: None,
|
||||
}],
|
||||
},
|
||||
network_out: structure::NetworkOut {
|
||||
net: net_outs.into_values().collect(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue