From 12fb0fa9d489e7bb667b277a78bccb5df3905df9 Mon Sep 17 00:00:00 2001 From: Mate Kovacs Date: Sun, 16 Mar 2025 17:26:12 +0900 Subject: [PATCH 1/2] flesh out external interface --- crates/kicad-parser/src/lib.rs | 1 + crates/kicad-parser/src/writer.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 crates/kicad-parser/src/writer.rs diff --git a/crates/kicad-parser/src/lib.rs b/crates/kicad-parser/src/lib.rs index d6bf29f..00431a0 100644 --- a/crates/kicad-parser/src/lib.rs +++ b/crates/kicad-parser/src/lib.rs @@ -3,6 +3,7 @@ use thiserror::Error; pub mod board; pub mod graphics; +pub mod writer; #[derive(Error, Debug)] pub enum Error { diff --git a/crates/kicad-parser/src/writer.rs b/crates/kicad-parser/src/writer.rs new file mode 100644 index 0000000..9384547 --- /dev/null +++ b/crates/kicad-parser/src/writer.rs @@ -0,0 +1,17 @@ +use crate::board::KicadBoard; + +pub struct Writer<'a, W: std::io::Write> { + writer: &'a mut W, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("IO Error: {0}")] + IoError(#[from] std::io::Error), +} + +impl<'a, W: std::io::Write> Writer<'a, W> { + pub fn write_board(board: &KicadBoard) -> Result<(), Error> { + todo!() + } +} From 90c4c6facc90ba01907bf489639f95d1e03fd8f3 Mon Sep 17 00:00:00 2001 From: Mate Kovacs Date: Sun, 16 Mar 2025 21:12:47 +0900 Subject: [PATCH 2/2] checkpoint --- crates/kicad-parser/src/board.rs | 26 ++---- crates/kicad-parser/src/writer.rs | 149 ++++++++++++++++++++++++++++-- crates/opencascade/src/kicad.rs | 33 ++++--- 3 files changed, 166 insertions(+), 42 deletions(-) diff --git a/crates/kicad-parser/src/board.rs b/crates/kicad-parser/src/board.rs index 14509dc..eb14e2b 100644 --- a/crates/kicad-parser/src/board.rs +++ b/crates/kicad-parser/src/board.rs @@ -6,11 +6,11 @@ use crate::graphics::{GraphicArc, GraphicCircle, GraphicLine, GraphicRect}; #[derive(Debug, Clone, Default)] pub struct KicadBoard { - graphic_lines: Vec, - graphic_arcs: Vec, - graphic_circles: Vec, - graphic_rects: Vec, - footprints: Vec, + pub graphic_lines: Vec, + pub graphic_arcs: Vec, + pub graphic_circles: Vec, + pub graphic_rects: Vec, + pub footprints: Vec, } impl KicadBoard { @@ -107,8 +107,10 @@ impl KicadBoard { pub struct Footprint { pub location: (f64, f64), pub rotation_degrees: f64, - graphic_lines: Vec, - graphic_arcs: Vec, + // NOTE: The assumption that FootprintLine and GraphicLine are equivalent may not be correct + pub graphic_lines: Vec, + // NOTE: The assumption that FootprintArc and GraphicArc are equivalent may not be correct + pub graphic_arcs: Vec, } impl Footprint { @@ -157,16 +159,6 @@ impl Footprint { Ok(footprint) } - - pub fn lines(&self) -> impl Iterator { - // TODO - map from footprint space to world space - self.graphic_lines.iter() - } - - pub fn arcs(&self) -> impl Iterator { - // TODO - map from footprint space to world space - self.graphic_arcs.iter() - } } #[derive(Debug, Clone, PartialEq)] diff --git a/crates/kicad-parser/src/writer.rs b/crates/kicad-parser/src/writer.rs index 9384547..a3b6e80 100644 --- a/crates/kicad-parser/src/writer.rs +++ b/crates/kicad-parser/src/writer.rs @@ -1,17 +1,146 @@ -use crate::board::KicadBoard; +use sexp::{atom_f, atom_s, Sexp}; -pub struct Writer<'a, W: std::io::Write> { - writer: &'a mut W, +use crate::{ + board::{BoardLayer, Footprint, KicadBoard}, + graphics::{GraphicArc, GraphicCircle, GraphicLine, GraphicRect}, +}; + +pub fn write_board( + writer: &mut W, + board: &KicadBoard, +) -> Result<(), std::io::Error> { + let sexp = board_to_sexp(board); + write!(writer, "{}", sexp) +} + +fn board_to_sexp(board: &KicadBoard) -> Sexp { + let mut items = vec![atom_s("kicad_pcb")]; + + for line in &board.graphic_lines { + items.push(line_to_sexp(line)); + } + + for arc in &board.graphic_arcs { + items.push(arc_to_sexp(arc)); + } + + for circle in &board.graphic_circles { + items.push(circle_to_sexp(circle)); + } + + for rect in &board.graphic_rects { + items.push(rect_to_sexp(rect)); + } + + for footprint in &board.footprints { + items.push(footprint_to_sexp(footprint)); + } + + Sexp::List(items) +} + +fn line_to_sexp(line: &GraphicLine) -> Sexp { + let items = vec![ + atom_s("gr_line"), + cons_s("start", point_to_sexp(line.start_point)), + cons_s("end", point_to_sexp(line.end_point)), + cons_s("layer", layer_to_sexp(&line.layer)), + ]; + Sexp::List(items) +} + +fn arc_to_sexp(arc: &GraphicArc) -> Sexp { + let items = vec![ + atom_s("gr_arc"), + cons_s("start", point_to_sexp(arc.start_point)), + cons_s("mid", point_to_sexp(arc.mid_point)), + cons_s("end", point_to_sexp(arc.end_point)), + cons_s("layer", layer_to_sexp(&arc.layer)), + ]; + Sexp::List(items) } -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("IO Error: {0}")] - IoError(#[from] std::io::Error), +fn circle_to_sexp(circle: &GraphicCircle) -> Sexp { + let items = vec![ + atom_s("gr_circle"), + cons_s("start", point_to_sexp(circle.center_point)), + cons_s("end", point_to_sexp(circle.end_point)), + cons_s("layer", layer_to_sexp(&circle.layer)), + ]; + Sexp::List(items) } -impl<'a, W: std::io::Write> Writer<'a, W> { - pub fn write_board(board: &KicadBoard) -> Result<(), Error> { - todo!() +fn rect_to_sexp(rect: &GraphicRect) -> Sexp { + let items = vec![ + atom_s("gr_rect"), + cons_s("start", point_to_sexp(rect.start_point)), + cons_s("end", point_to_sexp(rect.end_point)), + cons_s("layer", layer_to_sexp(&rect.layer)), + ]; + Sexp::List(items) +} + +fn footprint_to_sexp(footprint: &Footprint) -> Sexp { + let mut items = vec![ + atom_s("footprint"), + //cons_s("layer", layer_to_sexp(&footprint.layer)), + cons_s("at", position_to_sexp(footprint.location, footprint.rotation_degrees)), + ]; + + for line in &footprint.graphic_lines { + items.push(fp_line_to_sexp(line)); + } + + for arc in &footprint.graphic_arcs { + items.push(fp_arc_to_sexp(arc)); } + + Sexp::List(items) +} + +// NOTE: The assumption that FootprintLine and GraphicLine are equivalent may not be correct +fn fp_line_to_sexp(line: &GraphicLine) -> Sexp { + let items = vec![ + atom_s("fp_line"), + cons_s("start", point_to_sexp(line.start_point)), + cons_s("end", point_to_sexp(line.end_point)), + cons_s("layer", layer_to_sexp(&line.layer)), + ]; + Sexp::List(items) +} + +// NOTE: The assumption that FootprintArc and GraphicArc are equivalent may not be correct +fn fp_arc_to_sexp(arc: &GraphicArc) -> Sexp { + let items = vec![ + atom_s("fp_arc"), + cons_s("start", point_to_sexp(arc.start_point)), + cons_s("mid", point_to_sexp(arc.mid_point)), + cons_s("end", point_to_sexp(arc.end_point)), + cons_s("layer", layer_to_sexp(&arc.layer)), + ]; + Sexp::List(items) +} + +fn point_to_sexp(point: (f64, f64)) -> Sexp { + Sexp::List(vec![atom_f(point.0), atom_f(point.1)]) +} + +fn layer_to_sexp(layer: &BoardLayer) -> Sexp { + let layer_str: &str = layer.into(); + atom_s(layer_str) +} + +fn position_to_sexp(location: (f64, f64), rotation_degrees: f64) -> Sexp { + Sexp::List(vec![atom_f(location.0), atom_f(location.1), atom_f(rotation_degrees)]) +} + +fn cons_s(head: &str, tail: Sexp) -> Sexp { + Sexp::List(match tail { + Sexp::List(sexps) => { + let mut items = vec![atom_s(head)]; + items.extend(sexps); + items + }, + atom => vec![atom_s(head), atom], + }) } diff --git a/crates/opencascade/src/kicad.rs b/crates/opencascade/src/kicad.rs index 9692664..0d8dc0e 100644 --- a/crates/opencascade/src/kicad.rs +++ b/crates/opencascade/src/kicad.rs @@ -74,7 +74,8 @@ impl KicadPcb { let translate = DVec2::from(footprint.location); footprint - .lines() + .graphic_lines + .iter() .filter(|line| line.layer == *layer) .map(move |line| { let start = line.start_point; @@ -87,20 +88,22 @@ impl KicadPcb { Edge::segment(start.extend(0.0), end.extend(0.0)) }) - .chain(footprint.arcs().filter(|arc| arc.layer == *layer).map(move |arc| { - let start = arc.start_point; - let mid = arc.mid_point; - let end = arc.end_point; - let start = DVec2::from(start); - let mid = DVec2::from(mid); - let end = DVec2::from(end); - - let start = translate + angle_vec.rotate(start); - let mid = translate + angle_vec.rotate(mid); - let end = translate + angle_vec.rotate(end); - - Edge::arc(start.extend(0.0), mid.extend(0.0), end.extend(0.0)) - })) + .chain(footprint.graphic_arcs.iter().filter(|arc| arc.layer == *layer).map( + move |arc| { + let start = arc.start_point; + let mid = arc.mid_point; + let end = arc.end_point; + let start = DVec2::from(start); + let mid = DVec2::from(mid); + let end = DVec2::from(end); + + let start = translate + angle_vec.rotate(start); + let mid = translate + angle_vec.rotate(mid); + let end = translate + angle_vec.rotate(end); + + Edge::arc(start.extend(0.0), mid.extend(0.0), end.extend(0.0)) + }, + )) }); self.board