use std::collections::HashMap;
use std::io::Write;
use crate::cad::svg_reader::{SvgElement, parse_svg_elements};
use crate::error::Result;
pub fn write_svg_to_gerber<W: Write>(svg_content: &str, mut writer: W) -> Result<()> {
let doc = parse_svg_elements(svg_content)?;
let view_height = doc.height;
let mut flashes: Vec<(f64, f64, f64)> = Vec::new();
let mut traces: Vec<(f64, f64, f64, f64)> = Vec::new();
let mut polygons: Vec<Vec<(f64, f64)>> = Vec::new();
for elem in doc.elements {
match elem {
SvgElement::Line { p1, p2, .. } => {
if p1.x.is_finite() && p1.y.is_finite() && p2.x.is_finite() && p2.y.is_finite() {
traces.push((p1.x, view_height - p1.y, p2.x, view_height - p2.y));
}
}
SvgElement::Circle { center, radius, .. } => {
if center.x.is_finite()
&& center.y.is_finite()
&& radius.is_finite()
&& radius > 0.0
{
flashes.push((center.x, view_height - center.y, radius * 2.0));
}
}
SvgElement::Rect {
x,
y,
width,
height,
..
} => {
if x.is_finite()
&& y.is_finite()
&& width.is_finite()
&& height.is_finite()
&& width > 0.0
&& height > 0.0
{
let y0 = view_height - y;
let y1 = view_height - (y + height);
polygons.push(vec![
(x, y0),
(x + width, y0),
(x + width, y1),
(x, y1),
(x, y0),
]);
}
}
SvgElement::Polyline {
points, is_closed, ..
} => {
let valid_pts: Vec<_> = points
.into_iter()
.filter(|p| p.x.is_finite() && p.y.is_finite())
.collect();
if valid_pts.len() >= 2 {
if is_closed && valid_pts.len() >= 3 {
let mut poly: Vec<(f64, f64)> =
valid_pts.iter().map(|p| (p.x, view_height - p.y)).collect();
if let Some(&first) = poly.first() {
poly.push(first);
}
polygons.push(poly);
} else {
for w in valid_pts.windows(2) {
traces.push((
w[0].x,
view_height - w[0].y,
w[1].x,
view_height - w[1].y,
));
}
}
}
}
SvgElement::Text { .. } => {}
}
}
let mut diam_to_aperture: HashMap<u64, usize> = HashMap::new();
let mut next_aperture = 11;
for (_, _, diam) in &flashes {
let key = (diam * 1000.0).round() as u64;
diam_to_aperture.entry(key).or_insert_with(|| {
let ap = next_aperture;
next_aperture += 1;
ap
});
}
writeln!(writer, "G04 Gerber RS-274X generated by document-svg*")?;
writeln!(writer, "%FSLAX25Y25*%")?; writeln!(writer, "%MOMM*%")?; writeln!(writer, "%LPD*%")?; writeln!(writer, "%ADD10C,0.20000*%")?;
for (key, d_code) in &diam_to_aperture {
let diam_mm = (*key as f64) / 1000.0;
writeln!(writer, "%ADD{}C,{:.5}*%", d_code, diam_mm)?;
}
let fmt_g = |val: f64| -> i64 { (val * 100_000.0).round() as i64 };
if !traces.is_empty() {
writeln!(writer, "D10*")?;
for (x1, y1, x2, y2) in traces {
writeln!(writer, "X{}Y{}D02*", fmt_g(x1), fmt_g(y1))?;
writeln!(writer, "X{}Y{}D01*", fmt_g(x2), fmt_g(y2))?;
}
}
for (x, y, diam) in flashes {
let key = (diam * 1000.0).round() as u64;
if let Some(ap) = diam_to_aperture.get(&key) {
writeln!(writer, "D{}*", ap)?;
writeln!(writer, "X{}Y{}D03*", fmt_g(x), fmt_g(y))?;
}
}
for poly in polygons {
if poly.len() < 3 {
continue;
}
writeln!(writer, "G36*")?; writeln!(writer, "X{}Y{}D02*", fmt_g(poly[0].0), fmt_g(poly[0].1))?;
for pt in &poly[1..] {
writeln!(writer, "X{}Y{}D01*", fmt_g(pt.0), fmt_g(pt.1))?;
}
writeln!(writer, "G37*")?; }
writeln!(writer, "M02*")?; Ok(())
}