Skip to content

WIP: [kicad-parser] write a KicadBoard #201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 9 additions & 17 deletions crates/kicad-parser/src/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use crate::graphics::{GraphicArc, GraphicCircle, GraphicLine, GraphicRect};

#[derive(Debug, Clone, Default)]
pub struct KicadBoard {
graphic_lines: Vec<GraphicLine>,
graphic_arcs: Vec<GraphicArc>,
graphic_circles: Vec<GraphicCircle>,
graphic_rects: Vec<GraphicRect>,
footprints: Vec<Footprint>,
pub graphic_lines: Vec<GraphicLine>,
pub graphic_arcs: Vec<GraphicArc>,
pub graphic_circles: Vec<GraphicCircle>,
pub graphic_rects: Vec<GraphicRect>,
pub footprints: Vec<Footprint>,
}

impl KicadBoard {
Expand Down Expand Up @@ -107,8 +107,10 @@ impl KicadBoard {
pub struct Footprint {
pub location: (f64, f64),
pub rotation_degrees: f64,
graphic_lines: Vec<GraphicLine>,
graphic_arcs: Vec<GraphicArc>,
// NOTE: The assumption that FootprintLine and GraphicLine are equivalent may not be correct
pub graphic_lines: Vec<GraphicLine>,
// NOTE: The assumption that FootprintArc and GraphicArc are equivalent may not be correct
pub graphic_arcs: Vec<GraphicArc>,
}

impl Footprint {
Expand Down Expand Up @@ -157,16 +159,6 @@ impl Footprint {

Ok(footprint)
}

pub fn lines(&self) -> impl Iterator<Item = &GraphicLine> {
// TODO - map from footprint space to world space
self.graphic_lines.iter()
}

pub fn arcs(&self) -> impl Iterator<Item = &GraphicArc> {
// TODO - map from footprint space to world space
self.graphic_arcs.iter()
}
}

#[derive(Debug, Clone, PartialEq)]
Expand Down
1 change: 1 addition & 0 deletions crates/kicad-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use thiserror::Error;

pub mod board;
pub mod graphics;
pub mod writer;

#[derive(Error, Debug)]
pub enum Error {
Expand Down
146 changes: 146 additions & 0 deletions crates/kicad-parser/src/writer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use sexp::{atom_f, atom_s, Sexp};

use crate::{
board::{BoardLayer, Footprint, KicadBoard},
graphics::{GraphicArc, GraphicCircle, GraphicLine, GraphicRect},
};

pub fn write_board<W: std::io::Write>(
writer: &mut W,
board: &KicadBoard,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this instead be KicadPcb? I think my original plan was for KicadBoard to hold the actual board data like copper, footprints, silkscreens, etc, while KicadPcb would hold a KicadBoard, as well as extra data like the version and other metadata.

) -> 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)
}

fn circle_to_sexp(circle: &GraphicCircle) -> Sexp {
let items = vec![
atom_s("gr_circle"),
cons_s("start", point_to_sexp(circle.center_point)),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cons_s("start", point_to_sexp(circle.center_point)),
cons_s("center", 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)
}

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 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might call this orientation_to_sexp or transform_to_sexp, as I expected position to just mean an XY coordinate.

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],
})
}
33 changes: 18 additions & 15 deletions crates/opencascade/src/kicad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down