mirror of https://codeberg.org/topola/topola.git
specctra, egui: add menu option for specctra session export
This commit is contained in:
parent
299d11b05f
commit
8c3a3f1e72
|
|
@ -76,6 +76,9 @@ pub struct App {
|
|||
#[serde(skip)]
|
||||
maybe_layers: Option<Layers>,
|
||||
|
||||
#[serde(skip)]
|
||||
maybe_design: Option<SpecctraDesign>,
|
||||
|
||||
#[serde(skip)]
|
||||
update_counter: f32,
|
||||
}
|
||||
|
|
@ -92,6 +95,7 @@ impl Default for App {
|
|||
top: Top::new(),
|
||||
bottom: Bottom::new(),
|
||||
maybe_layers: None,
|
||||
maybe_design: None,
|
||||
update_counter: 0.0,
|
||||
}
|
||||
}
|
||||
|
|
@ -124,6 +128,7 @@ impl App {
|
|||
let board = design.make_board();
|
||||
self.maybe_overlay = Some(Overlay::new(&board).unwrap());
|
||||
self.maybe_layers = Some(Layers::new(&board));
|
||||
self.maybe_design = Some(design);
|
||||
self.arc_mutex_maybe_invoker = Arc::new(Mutex::new(Some(Invoker::new(
|
||||
Autorouter::new(board).unwrap(),
|
||||
))))
|
||||
|
|
@ -165,6 +170,7 @@ impl eframe::App for App {
|
|||
self.arc_mutex_maybe_invoker.clone(),
|
||||
&mut self.maybe_execute,
|
||||
&mut self.maybe_overlay,
|
||||
&self.maybe_design,
|
||||
);
|
||||
|
||||
if let Some(ref mut layers) = self.maybe_layers {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
sync::{mpsc::Sender, Arc, Mutex},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use topola::{
|
||||
autorouter::invoker::{
|
||||
Command, Execute, ExecuteWithStatus, Invoker, InvokerError, InvokerStatus,
|
||||
},
|
||||
specctra::mesadata::SpecctraMesadata,
|
||||
specctra::{mesadata::SpecctraMesadata, design::SpecctraDesign},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -40,9 +41,12 @@ impl Top {
|
|||
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
|
||||
maybe_execute: &mut Option<ExecuteWithStatus>,
|
||||
maybe_overlay: &mut Option<Overlay>,
|
||||
maybe_design: &Option<SpecctraDesign>,
|
||||
) -> Result<(), InvokerError> {
|
||||
let mut open_design =
|
||||
Trigger::new(Action::new("Open", egui::Modifiers::CTRL, egui::Key::O));
|
||||
let mut export_session =
|
||||
Trigger::new(Action::new("Export session file", egui::Modifiers::CTRL, egui::Key::S));
|
||||
let mut import_history = Trigger::new(Action::new(
|
||||
"Import history",
|
||||
egui::Modifiers::CTRL,
|
||||
|
|
@ -72,6 +76,7 @@ impl Top {
|
|||
egui::menu::bar(ui, |ui| {
|
||||
ui.menu_button("File", |ui| {
|
||||
open_design.button(ctx, ui);
|
||||
export_session.button(ctx, ui);
|
||||
|
||||
ui.separator();
|
||||
|
||||
|
|
@ -119,6 +124,37 @@ impl Top {
|
|||
ctx.request_repaint();
|
||||
}
|
||||
});
|
||||
} else if export_session.consume_key_triggered(ctx, ui) {
|
||||
if let Some(design) = maybe_design {
|
||||
if let Some(invoker) =
|
||||
arc_mutex_maybe_invoker.clone().lock().unwrap().as_ref()
|
||||
{
|
||||
let ctx = ui.ctx().clone();
|
||||
let board = invoker.autorouter().board();
|
||||
|
||||
// FIXME: I don't know how to avoid buffering the entire exported file
|
||||
let mut writebuf = vec![];
|
||||
|
||||
design.write_ses(board, &mut writebuf);
|
||||
|
||||
let mut dialog = rfd::AsyncFileDialog::new();
|
||||
if let Some(filename) = Path::new(design.get_name()).file_stem() {
|
||||
if let Some(filename) = filename.to_str() {
|
||||
dialog = dialog.set_file_name(filename);
|
||||
}
|
||||
}
|
||||
let task = dialog
|
||||
.add_filter("Specctra session file", &["ses"])
|
||||
.save_file();
|
||||
|
||||
execute(async move {
|
||||
if let Some(file_handle) = task.await {
|
||||
file_handle.write(&writebuf).await;
|
||||
ctx.request_repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if import_history.consume_key_triggered(ctx, ui) {
|
||||
let ctx = ctx.clone();
|
||||
let task = rfd::AsyncFileDialog::new().pick_file();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,23 @@ use thiserror::Error;
|
|||
|
||||
use crate::{
|
||||
board::{mesadata::AccessMesadata, Board},
|
||||
drawing::{dot::FixedDotWeight, seg::FixedSegWeight, Drawing},
|
||||
drawing::{
|
||||
dot::FixedDotWeight,
|
||||
seg::FixedSegWeight,
|
||||
Drawing,
|
||||
graph::{GetMaybeNet, GetLayer, MakePrimitive},
|
||||
primitive::MakePrimitiveShape
|
||||
},
|
||||
geometry::{
|
||||
primitive::{PrimitiveShape},
|
||||
GetWidth,
|
||||
},
|
||||
layout::{poly::SolidPolyWeight, Layout},
|
||||
math::Circle,
|
||||
specctra::{
|
||||
mesadata::SpecctraMesadata,
|
||||
read::{self, ListTokenizer},
|
||||
write::{self, ListWriter},
|
||||
structure::{self, DsnFile, Layer, Pcb, Shape},
|
||||
},
|
||||
};
|
||||
|
|
@ -36,6 +47,80 @@ impl SpecctraDesign {
|
|||
Ok(Self { pcb: dsn.pcb })
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.pcb.name
|
||||
}
|
||||
|
||||
pub fn write_ses(
|
||||
&self,
|
||||
board: &Board<SpecctraMesadata>,
|
||||
writer: impl std::io::Write,
|
||||
) -> Result<(), std::io::Error> {
|
||||
let mesadata = board.mesadata();
|
||||
let drawing = board.layout().drawing();
|
||||
//dbg!(&geometry);
|
||||
|
||||
let mut net_outs = HashMap::<usize, structure::NetOut>::new();
|
||||
for index in drawing.primitive_nodes() {
|
||||
let primitive = index.primitive(drawing);
|
||||
match primitive.shape() {
|
||||
PrimitiveShape::Seg(seg) => {
|
||||
if let Some(net) = primitive.maybe_net() {
|
||||
let net_name = mesadata.net_netname(net).unwrap().to_owned();
|
||||
|
||||
let wire = structure::Wire {
|
||||
path: structure::Path {
|
||||
layer: mesadata.layer_layername(primitive.layer()).unwrap().to_owned(),
|
||||
width: primitive.width(),
|
||||
coords: vec![
|
||||
structure::Point { x: seg.from.x(), y: seg.from.y() },
|
||||
structure::Point { x: seg.to.x(), y: seg.to.y() },
|
||||
],
|
||||
},
|
||||
net: net_name.clone(),
|
||||
r#type: "route".to_owned(),
|
||||
};
|
||||
|
||||
if let Some(net) = net_outs.get_mut(&net) {
|
||||
net.wire.push(wire);
|
||||
} else {
|
||||
net_outs.insert(
|
||||
net,
|
||||
structure::NetOut {
|
||||
name: net_name.clone(),
|
||||
wire: vec![wire],
|
||||
via: Vec::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let ses = structure::SesFile {
|
||||
session: structure::Session {
|
||||
id: "ID".to_string(),
|
||||
routes: structure::Routes {
|
||||
resolution: structure::Resolution {
|
||||
unit: "um".into(),
|
||||
value: 1.0,
|
||||
},
|
||||
library_out: structure::Library {
|
||||
images: Vec::new(),
|
||||
padstacks: Vec::new(),
|
||||
},
|
||||
network_out: structure::NetworkOut {
|
||||
net: net_outs.into_values().collect(),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
ListWriter::new(writer).write_value(&ses)
|
||||
}
|
||||
|
||||
pub fn make_board(&self) -> Board<SpecctraMesadata> {
|
||||
let mesadata = SpecctraMesadata::from_pcb(&self.pcb);
|
||||
let mut board = Board::new(Layout::new(Drawing::new(
|
||||
|
|
|
|||
Loading…
Reference in New Issue