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]
|
#[anon]
|
||||||
pub y: f64,
|
pub y: f64,
|
||||||
pub net: String,
|
pub net: String,
|
||||||
pub r#type: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)]
|
#[derive(ReadDsn, WriteSes, Debug, Clone, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -21,20 +21,23 @@ pub struct ConnectedComponents {
|
||||||
|
|
||||||
impl ConnectedComponents {
|
impl ConnectedComponents {
|
||||||
pub fn new(board: &Board<impl AccessMesadata>) -> Self {
|
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() {
|
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
|
// 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
|
// that, we have another loop to go over all the pins and connect all
|
||||||
// their primitives.
|
// their primitives.
|
||||||
for pinname in board.pinnames() {
|
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(
|
pub fn new_with_principal_layer(
|
||||||
|
|
@ -70,33 +73,33 @@ impl ConnectedComponents {
|
||||||
|
|
||||||
fn unionize_primitive_endpoint_dots(
|
fn unionize_primitive_endpoint_dots(
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
unionfind: &mut UnionFind<usize>,
|
dot_unionfind: &mut UnionFind<usize>,
|
||||||
primitive: PrimitiveIndex,
|
primitive: PrimitiveIndex,
|
||||||
) {
|
) {
|
||||||
match primitive {
|
match primitive {
|
||||||
PrimitiveIndex::FixedSeg(seg) => {
|
PrimitiveIndex::FixedSeg(seg) => {
|
||||||
let joints = board.layout().drawing().primitive(seg).joints();
|
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());
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||||
}
|
}
|
||||||
PrimitiveIndex::LoneLooseSeg(seg) => {
|
PrimitiveIndex::LoneLooseSeg(seg) => {
|
||||||
let joints = board.layout().drawing().primitive(seg).joints();
|
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) => {
|
PrimitiveIndex::SeqLooseSeg(seg) => {
|
||||||
let joints = board.layout().drawing().primitive(seg).joints();
|
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) => {
|
PrimitiveIndex::FixedBend(bend) => {
|
||||||
let joints = board.layout().drawing().primitive(bend).joints();
|
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());
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||||
}
|
}
|
||||||
PrimitiveIndex::LooseBend(bend) => {
|
PrimitiveIndex::LooseBend(bend) => {
|
||||||
let joints = board.layout().drawing().primitive(bend).joints();
|
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(
|
fn unionize_pin(
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
unionfind: &mut UnionFind<usize>,
|
dot_unionfind: &mut UnionFind<usize>,
|
||||||
pinname: &str,
|
pinname: &str,
|
||||||
) {
|
) {
|
||||||
let mut iter = board.pinname_nodes(pinname);
|
let mut iter = board.pinname_nodes(pinname);
|
||||||
|
|
@ -120,53 +123,53 @@ impl ConnectedComponents {
|
||||||
|
|
||||||
for node in board.pinname_nodes(pinname) {
|
for node in board.pinname_nodes(pinname) {
|
||||||
if let GenericNode::Primitive(primitive) = node {
|
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(
|
fn unionize_to_common(
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
unionfind: &mut UnionFind<usize>,
|
dot_unionfind: &mut UnionFind<usize>,
|
||||||
primitive: PrimitiveIndex,
|
primitive: PrimitiveIndex,
|
||||||
common: FixedDotIndex,
|
common: FixedDotIndex,
|
||||||
) {
|
) {
|
||||||
match primitive {
|
match primitive {
|
||||||
PrimitiveIndex::FixedDot(dot) => {
|
PrimitiveIndex::FixedDot(dot) => {
|
||||||
unionfind.union(common.index(), dot.index());
|
dot_unionfind.union(common.index(), dot.index());
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, dot);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, dot);
|
||||||
}
|
}
|
||||||
PrimitiveIndex::LooseDot(dot) => {
|
PrimitiveIndex::LooseDot(dot) => {
|
||||||
unionfind.union(common.index(), dot.index());
|
dot_unionfind.union(common.index(), dot.index());
|
||||||
}
|
}
|
||||||
PrimitiveIndex::FixedSeg(seg) => {
|
PrimitiveIndex::FixedSeg(seg) => {
|
||||||
let joints = board.layout().drawing().primitive(seg).joints();
|
let joints = board.layout().drawing().primitive(seg).joints();
|
||||||
unionfind.union(common.index(), joints.0.index());
|
dot_unionfind.union(common.index(), joints.0.index());
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||||
unionfind.union(common.index(), joints.1.index());
|
dot_unionfind.union(common.index(), joints.1.index());
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||||
}
|
}
|
||||||
PrimitiveIndex::LoneLooseSeg(seg) => {
|
PrimitiveIndex::LoneLooseSeg(seg) => {
|
||||||
let joints = board.layout().drawing().primitive(seg).joints();
|
let joints = board.layout().drawing().primitive(seg).joints();
|
||||||
unionfind.union(common.index(), joints.0.index());
|
dot_unionfind.union(common.index(), joints.0.index());
|
||||||
unionfind.union(common.index(), joints.1.index());
|
dot_unionfind.union(common.index(), joints.1.index());
|
||||||
}
|
}
|
||||||
PrimitiveIndex::SeqLooseSeg(seg) => {
|
PrimitiveIndex::SeqLooseSeg(seg) => {
|
||||||
let joints = board.layout().drawing().primitive(seg).joints();
|
let joints = board.layout().drawing().primitive(seg).joints();
|
||||||
unionfind.union(common.index(), joints.0.index());
|
dot_unionfind.union(common.index(), joints.0.index());
|
||||||
unionfind.union(common.index(), joints.1.index());
|
dot_unionfind.union(common.index(), joints.1.index());
|
||||||
}
|
}
|
||||||
PrimitiveIndex::FixedBend(bend) => {
|
PrimitiveIndex::FixedBend(bend) => {
|
||||||
let joints = board.layout().drawing().primitive(bend).joints();
|
let joints = board.layout().drawing().primitive(bend).joints();
|
||||||
unionfind.union(common.index(), joints.0.index());
|
dot_unionfind.union(common.index(), joints.0.index());
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.0);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.0);
|
||||||
unionfind.union(common.index(), joints.1.index());
|
dot_unionfind.union(common.index(), joints.1.index());
|
||||||
Self::unionize_fixed_dot_via(board, unionfind, joints.1);
|
Self::unionize_fixed_dot_via(board, dot_unionfind, joints.1);
|
||||||
}
|
}
|
||||||
PrimitiveIndex::LooseBend(bend) => {
|
PrimitiveIndex::LooseBend(bend) => {
|
||||||
let joints = board.layout().drawing().primitive(bend).joints();
|
let joints = board.layout().drawing().primitive(bend).joints();
|
||||||
unionfind.union(common.index(), joints.0.index());
|
dot_unionfind.union(common.index(), joints.0.index());
|
||||||
unionfind.union(common.index(), joints.1.index());
|
dot_unionfind.union(common.index(), joints.1.index());
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
@ -174,12 +177,12 @@ impl ConnectedComponents {
|
||||||
|
|
||||||
fn unionize_fixed_dot_via(
|
fn unionize_fixed_dot_via(
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
unionfind: &mut UnionFind<usize>,
|
dot_unionfind: &mut UnionFind<usize>,
|
||||||
dot: FixedDotIndex,
|
dot: FixedDotIndex,
|
||||||
) {
|
) {
|
||||||
if let Some(via) = board.layout().fixed_dot_via(dot) {
|
if let Some(via) = board.layout().fixed_dot_via(dot) {
|
||||||
for via_dot in board.layout().via(via).dots() {
|
for via_dot in board.layout().via_ref(via).dots() {
|
||||||
unionfind.union(dot.index(), via_dot.index());
|
dot_unionfind.union(dot.index(), via_dot.index());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ use crate::{
|
||||||
graph::{GenericIndex, GetIndex, MakeRef},
|
graph::{GenericIndex, GetIndex, MakeRef},
|
||||||
layout::{
|
layout::{
|
||||||
poly::{add_poly_with_nodes_intern, MakePolygon, PolyWeight},
|
poly::{add_poly_with_nodes_intern, MakePolygon, PolyWeight},
|
||||||
via::{Via, ViaWeight},
|
via::{ViaRef, ViaWeight},
|
||||||
},
|
},
|
||||||
math::RotationSense,
|
math::RotationSense,
|
||||||
};
|
};
|
||||||
|
|
@ -476,8 +476,8 @@ impl<R: AccessRules> Layout<R> {
|
||||||
self.drawing.rules_mut()
|
self.drawing.rules_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn via(&self, index: GenericIndex<ViaWeight>) -> Via<'_, R> {
|
pub fn via_ref(&self, index: GenericIndex<ViaWeight>) -> ViaRef<'_, R> {
|
||||||
Via::new(index, self.drawing())
|
ViaRef::new(index, self.drawing())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,12 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Via<'a, R> {
|
pub struct ViaRef<'a, R> {
|
||||||
pub index: GenericIndex<ViaWeight>,
|
pub index: GenericIndex<ViaWeight>,
|
||||||
drawing: &'a Drawing<CompoundWeight, CompoundEntryLabel, R>,
|
drawing: &'a Drawing<CompoundWeight, CompoundEntryLabel, R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, R> Via<'a, R> {
|
impl<'a, R> ViaRef<'a, R> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
index: GenericIndex<ViaWeight>,
|
index: GenericIndex<ViaWeight>,
|
||||||
drawing: &'a Drawing<CompoundWeight, CompoundEntryLabel, R>,
|
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> {
|
fn maybe_net(&self) -> Option<usize> {
|
||||||
self.drawing.compound_weight(self.index.into()).maybe_net()
|
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 {
|
fn shape(&self) -> PrimitiveShape {
|
||||||
if let CompoundWeight::Via(weight) = self.drawing.compound_weight(self.index.into()) {
|
if let CompoundWeight::Via(weight) = self.drawing.compound_weight(self.index.into()) {
|
||||||
weight.shape()
|
weight.shape()
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
//! Design DSN file, creating the [`Board`] object from the file, as well as
|
//! Design DSN file, creating the [`Board`] object from the file, as well as
|
||||||
//! exporting the session file
|
//! 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 geo::{Euclidean, Length, Line, Point, Rotate};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
@ -16,13 +16,14 @@ use crate::{
|
||||||
board::{edit::BoardEdit, AccessMesadata, Board},
|
board::{edit::BoardEdit, AccessMesadata, Board},
|
||||||
drawing::{
|
drawing::{
|
||||||
dot::{FixedDotIndex, FixedDotWeight, GeneralDotWeight},
|
dot::{FixedDotIndex, FixedDotWeight, GeneralDotWeight},
|
||||||
graph::{GetMaybeNet, MakePrimitiveRef},
|
graph::{GetMaybeNet, MakePrimitiveRef, PrimitiveIndex},
|
||||||
primitive::MakePrimitiveShape,
|
primitive::MakePrimitiveShape,
|
||||||
seg::{FixedSegWeight, GeneralSegWeight},
|
seg::{FixedSegWeight, GeneralSegWeight},
|
||||||
Drawing,
|
Drawing,
|
||||||
},
|
},
|
||||||
geometry::{primitive::PrimitiveShape, GetLayer, GetWidth},
|
geometry::{primitive::PrimitiveShape, shape::AccessShape, GetLayer, GetWidth},
|
||||||
layout::{poly::SolidPolyWeight, Layout},
|
graph::GenericIndex,
|
||||||
|
layout::{poly::SolidPolyWeight, via::ViaWeight, Layout},
|
||||||
math::{self, Circle},
|
math::{self, Circle},
|
||||||
specctra::{
|
specctra::{
|
||||||
mesadata::SpecctraMesadata,
|
mesadata::SpecctraMesadata,
|
||||||
|
|
@ -77,10 +78,32 @@ impl SpecctraDesign {
|
||||||
let drawing = board.layout().drawing();
|
let drawing = board.layout().drawing();
|
||||||
|
|
||||||
let mut net_outs = BTreeMap::<usize, structure::NetOut>::new();
|
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() {
|
for index in drawing.primitive_nodes() {
|
||||||
let primitive = index.primitive_ref(drawing);
|
let primitive = index.primitive_ref(drawing);
|
||||||
|
|
||||||
if let Some(net) = primitive.maybe_net() {
|
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() {
|
let coords = match primitive.shape() {
|
||||||
PrimitiveShape::Seg(seg) => {
|
PrimitiveShape::Seg(seg) => {
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -106,12 +129,37 @@ impl SpecctraDesign {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally skipped for now.
|
PrimitiveShape::Dot(dot_shape) => {
|
||||||
// Topola stores trace segments and dots joining them
|
let PrimitiveIndex::FixedDot(dot) = index else {
|
||||||
// as separate objects, but the Specctra formats and KiCad
|
continue;
|
||||||
// appear to consider them implicit.
|
};
|
||||||
// TODO: Vias
|
|
||||||
PrimitiveShape::Dot(_) => 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 {
|
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);
|
net_out.wire.push(wire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +195,23 @@ impl SpecctraDesign {
|
||||||
},
|
},
|
||||||
library_out: structure::Library {
|
library_out: structure::Library {
|
||||||
images: Vec::new(),
|
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 {
|
network_out: structure::NetworkOut {
|
||||||
net: net_outs.into_values().collect(),
|
net: net_outs.into_values().collect(),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue