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)]
|
#[serde(skip)]
|
||||||
maybe_layers: Option<Layers>,
|
maybe_layers: Option<Layers>,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
maybe_design: Option<SpecctraDesign>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
update_counter: f32,
|
update_counter: f32,
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +95,7 @@ impl Default for App {
|
||||||
top: Top::new(),
|
top: Top::new(),
|
||||||
bottom: Bottom::new(),
|
bottom: Bottom::new(),
|
||||||
maybe_layers: None,
|
maybe_layers: None,
|
||||||
|
maybe_design: None,
|
||||||
update_counter: 0.0,
|
update_counter: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,6 +128,7 @@ impl App {
|
||||||
let board = design.make_board();
|
let board = design.make_board();
|
||||||
self.maybe_overlay = Some(Overlay::new(&board).unwrap());
|
self.maybe_overlay = Some(Overlay::new(&board).unwrap());
|
||||||
self.maybe_layers = Some(Layers::new(&board));
|
self.maybe_layers = Some(Layers::new(&board));
|
||||||
|
self.maybe_design = Some(design);
|
||||||
self.arc_mutex_maybe_invoker = Arc::new(Mutex::new(Some(Invoker::new(
|
self.arc_mutex_maybe_invoker = Arc::new(Mutex::new(Some(Invoker::new(
|
||||||
Autorouter::new(board).unwrap(),
|
Autorouter::new(board).unwrap(),
|
||||||
))))
|
))))
|
||||||
|
|
@ -165,6 +170,7 @@ impl eframe::App for App {
|
||||||
self.arc_mutex_maybe_invoker.clone(),
|
self.arc_mutex_maybe_invoker.clone(),
|
||||||
&mut self.maybe_execute,
|
&mut self.maybe_execute,
|
||||||
&mut self.maybe_overlay,
|
&mut self.maybe_overlay,
|
||||||
|
&self.maybe_design,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ref mut layers) = self.maybe_layers {
|
if let Some(ref mut layers) = self.maybe_layers {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
sync::{mpsc::Sender, Arc, Mutex},
|
sync::{mpsc::Sender, Arc, Mutex},
|
||||||
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use topola::{
|
use topola::{
|
||||||
autorouter::invoker::{
|
autorouter::invoker::{
|
||||||
Command, Execute, ExecuteWithStatus, Invoker, InvokerError, InvokerStatus,
|
Command, Execute, ExecuteWithStatus, Invoker, InvokerError, InvokerStatus,
|
||||||
},
|
},
|
||||||
specctra::mesadata::SpecctraMesadata,
|
specctra::{mesadata::SpecctraMesadata, design::SpecctraDesign},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -40,9 +41,12 @@ impl Top {
|
||||||
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
|
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
|
||||||
maybe_execute: &mut Option<ExecuteWithStatus>,
|
maybe_execute: &mut Option<ExecuteWithStatus>,
|
||||||
maybe_overlay: &mut Option<Overlay>,
|
maybe_overlay: &mut Option<Overlay>,
|
||||||
|
maybe_design: &Option<SpecctraDesign>,
|
||||||
) -> Result<(), InvokerError> {
|
) -> Result<(), InvokerError> {
|
||||||
let mut open_design =
|
let mut open_design =
|
||||||
Trigger::new(Action::new("Open", egui::Modifiers::CTRL, egui::Key::O));
|
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(
|
let mut import_history = Trigger::new(Action::new(
|
||||||
"Import history",
|
"Import history",
|
||||||
egui::Modifiers::CTRL,
|
egui::Modifiers::CTRL,
|
||||||
|
|
@ -72,6 +76,7 @@ impl Top {
|
||||||
egui::menu::bar(ui, |ui| {
|
egui::menu::bar(ui, |ui| {
|
||||||
ui.menu_button("File", |ui| {
|
ui.menu_button("File", |ui| {
|
||||||
open_design.button(ctx, ui);
|
open_design.button(ctx, ui);
|
||||||
|
export_session.button(ctx, ui);
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
|
@ -119,6 +124,37 @@ impl Top {
|
||||||
ctx.request_repaint();
|
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) {
|
} else if import_history.consume_key_triggered(ctx, ui) {
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
let task = rfd::AsyncFileDialog::new().pick_file();
|
let task = rfd::AsyncFileDialog::new().pick_file();
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,23 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
board::{mesadata::AccessMesadata, Board},
|
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},
|
layout::{poly::SolidPolyWeight, Layout},
|
||||||
math::Circle,
|
math::Circle,
|
||||||
specctra::{
|
specctra::{
|
||||||
mesadata::SpecctraMesadata,
|
mesadata::SpecctraMesadata,
|
||||||
read::{self, ListTokenizer},
|
read::{self, ListTokenizer},
|
||||||
|
write::{self, ListWriter},
|
||||||
structure::{self, DsnFile, Layer, Pcb, Shape},
|
structure::{self, DsnFile, Layer, Pcb, Shape},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -36,6 +47,80 @@ impl SpecctraDesign {
|
||||||
Ok(Self { pcb: dsn.pcb })
|
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> {
|
pub fn make_board(&self) -> 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(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue