pedalkernel_layout/
kicad.rs1use crate::types::*;
9use pedalkernel::compiler::Component;
10use pedalkernel::dsl::PedalDef;
11use std::fmt::Write;
12
13const SCALE: f32 = 5.0;
15
16pub fn export_kicad_schematic(layout: &Layout, pedal: &PedalDef) -> String {
18 let mut out = String::with_capacity(8192);
19
20 write_header(&mut out, layout);
21 write_components(&mut out, layout, pedal);
22 write_wires(&mut out, layout);
23 write_power_symbols(&mut out, layout);
24 write_footer(&mut out);
25
26 out
27}
28
29fn write_header(out: &mut String, layout: &Layout) {
30 let w = layout.bounds.width * SCALE;
31 let h = layout.bounds.height * SCALE;
32
33 writeln!(
34 out,
35 "(kicad_sch (version 20230121) (generator pedalkernel-layout)"
36 )
37 .unwrap();
38 writeln!(out).unwrap();
39 writeln!(out, " (uuid \"00000000-0000-0000-0000-000000000001\")").unwrap();
40 writeln!(out, " (paper \"A3\")").unwrap();
41 writeln!(out).unwrap();
42 writeln!(
43 out,
44 " (title_block\n (title \"{name}\")\n (comment 1 \"Generated by PedalKernel Layout Engine\")\n (comment 2 \"Canvas: {w:.0} x {h:.0} mils\")\n )",
45 name = "PedalKernel Circuit",
46 )
47 .unwrap();
48 writeln!(out).unwrap();
49}
50
51fn write_components(out: &mut String, layout: &Layout, pedal: &PedalDef) {
52 let comp_map: std::collections::BTreeMap<&str, &pedalkernel::dsl::ComponentDef> = pedal
54 .components
55 .iter()
56 .map(|c| (c.id.as_str(), c))
57 .collect();
58
59 for (idx, placed) in layout.components.iter().enumerate() {
60 let comp_def = comp_map.get(placed.name.as_str());
61 let (lib_symbol, ref_prefix) = comp_def
62 .map(|c| kicad_symbol_ref(c.kind.as_ref()))
63 .unwrap_or(("Device:R", "X"));
64
65 let x = placed.x * SCALE;
66 let y = placed.y * SCALE;
67 let angle = placed.orientation as f32;
68
69 writeln!(
70 out,
71 " (symbol (lib_id \"{lib_symbol}\") (at {x:.1} {y:.1} {angle:.0})"
72 )
73 .unwrap();
74 writeln!(
75 out,
76 " (uuid \"{:08x}-0000-0000-0000-{:012x}\")",
77 idx + 1,
78 idx + 1
79 )
80 .unwrap();
81 writeln!(
82 out,
83 " (property \"Reference\" \"{ref_prefix}{name}\" (at {x:.1} {y_ref:.1} 0)",
84 name = placed.name,
85 y_ref = y - 15.0
86 )
87 .unwrap();
88 writeln!(out, " (effects (font (size 1.27 1.27)))").unwrap();
89 writeln!(out, " )").unwrap();
90
91 if let Some(label) = &placed.label {
92 writeln!(
93 out,
94 " (property \"Value\" \"{label}\" (at {x:.1} {y_val:.1} 0)",
95 y_val = y + 15.0
96 )
97 .unwrap();
98 writeln!(out, " (effects (font (size 1.27 1.27)))").unwrap();
99 writeln!(out, " )").unwrap();
100 }
101
102 writeln!(out, " )").unwrap();
103 writeln!(out).unwrap();
104 }
105}
106
107fn write_wires(out: &mut String, layout: &Layout) {
108 for wire in &layout.wires {
109 for seg in wire.points.windows(2) {
110 let x1 = seg[0][0] * SCALE;
111 let y1 = seg[0][1] * SCALE;
112 let x2 = seg[1][0] * SCALE;
113 let y2 = seg[1][1] * SCALE;
114
115 writeln!(
116 out,
117 " (wire (pts (xy {x1:.1} {y1:.1}) (xy {x2:.1} {y2:.1})))"
118 )
119 .unwrap();
120 }
121 }
122 writeln!(out).unwrap();
123}
124
125fn write_power_symbols(out: &mut String, layout: &Layout) {
126 for rail in &layout.supply_rails {
127 let y = rail.y * SCALE;
128 let symbol = if rail.name == "vcc" {
129 "power:VCC"
130 } else {
131 "power:GND"
132 };
133 let label = rail.name.to_uppercase();
134
135 writeln!(out, " (symbol (lib_id \"{symbol}\") (at 50.0 {y:.1} 0)").unwrap();
136 writeln!(out, " (uuid \"rail-{name}\")", name = rail.name).unwrap();
137 writeln!(
138 out,
139 " (property \"Reference\" \"#{label}\" (at 50.0 {y_ref:.1} 0)",
140 y_ref = y - 10.0
141 )
142 .unwrap();
143 writeln!(out, " (effects (font (size 1.27 1.27)) hide)").unwrap();
144 writeln!(out, " )").unwrap();
145 writeln!(out, " )").unwrap();
146 writeln!(out).unwrap();
147 }
148}
149
150fn write_footer(out: &mut String) {
151 writeln!(out, ")").unwrap();
152}
153
154fn kicad_symbol_ref(kind: &dyn Component) -> (&'static str, &'static str) {
156 kind.footprint_ref()
157}