use crate::InnerAtom;
use conv::TryFrom;
use geo::{Point, Polygon};
use gerber_types::*;
use std::collections::HashMap;
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, Copy)]
enum ApertureType {
Circle(f64),
Rect(f64, f64),
}
fn gerber_prelude<'a>(
cf: CoordinateFormat,
ff: Option<FileFunction>,
apertures: impl Iterator<Item = &'a (i32, ApertureType)>,
) -> Vec<Command> {
let mut commands =
vec![
FunctionCode::GCode(GCode::Comment("Autogenerated Maker Panel".to_string())).into(),
ExtendedCode::CoordinateFormat(cf).into(),
ExtendedCode::Unit(Unit::Millimeters).into(),
ExtendedCode::FileAttribute(FileAttribute::GenerationSoftware(
GenerationSoftware::new("Maker Panel", "maker-panel", Some(VERSION)),
))
.into(),
ExtendedCode::FileAttribute(FileAttribute::Part(Part::Single)).into(),
if let Some(ff) = ff {
ExtendedCode::FileAttribute(FileAttribute::FileFunction(ff)).into()
} else {
FunctionCode::GCode(GCode::Comment("".to_string())).into()
},
ExtendedCode::LoadPolarity(Polarity::Dark).into(),
FunctionCode::GCode(GCode::InterpolationMode(InterpolationMode::Linear)).into(),
];
for (code, shape) in apertures {
commands.push(
ExtendedCode::ApertureDefinition(ApertureDefinition {
code: *code,
aperture: match shape {
ApertureType::Circle(diameter) => Aperture::Circle(Circle {
diameter: *diameter,
hole_diameter: None,
}),
ApertureType::Rect(x, y) => Aperture::Rectangle(Rectangular {
x: *x,
y: *y,
hole_diameter: None,
}),
},
})
.into(),
);
}
commands
}
fn emit_poly<I: Iterator<Item = geo::Point<f64>>>(commands: &mut Vec<Command>, points: I) {
let cf = CoordinateFormat::new(4, 6);
let mut last: Option<Point<f64>> = None;
for point in points {
if let Some(cmd) = match last {
None => FunctionCode::DCode(DCode::Operation(Operation::Move(Coordinates::new(
CoordinateNumber::try_from(point.x()).unwrap(),
CoordinateNumber::try_from(point.y()).unwrap(),
cf,
))))
.into(),
Some(last) => {
let x = CoordinateNumber::try_from(point.x()).unwrap();
let y = CoordinateNumber::try_from(point.y()).unwrap();
let (dx, dy) = (point.x() - last.x(), point.y() - last.y());
match (dx < 1.0E-7 && dx > -1.0E-7, dy < 1.0E-7 && dy > -1.0E-7) {
(true, true) => None,
(_, true) => Some(
FunctionCode::DCode(DCode::Operation(Operation::Interpolate(
Coordinates::at_x(x, cf),
None,
)))
.into(),
),
(true, _) => Some(
FunctionCode::DCode(DCode::Operation(Operation::Interpolate(
Coordinates::at_y(y, cf),
None,
)))
.into(),
),
(_, _) => Some(
FunctionCode::DCode(DCode::Operation(Operation::Interpolate(
Coordinates::new(x, y, cf),
None,
)))
.into(),
),
}
}
} {
commands.push(gerber_types::Command::FunctionCode(cmd));
}
last = Some(point.clone());
}
}
pub fn serialize_edge(poly: Polygon<f64>) -> Result<Vec<Command>, ()> {
let cf = CoordinateFormat::new(4, 6);
let mut commands = gerber_prelude(
cf,
Some(FileFunction::Profile(Profile::NonPlated)),
[(10, ApertureType::Circle(0.01))].iter(),
);
commands.push(FunctionCode::DCode(DCode::SelectAperture(10)).into());
emit_poly(&mut commands, poly.exterior().points_iter());
for poly in poly.interiors() {
emit_poly(&mut commands, poly.points_iter());
}
commands.push(FunctionCode::MCode(MCode::EndOfFile).into());
Ok(commands)
}
#[derive(Debug, Copy, Clone)]
struct FloatBits(f64);
impl FloatBits {
fn key(&self) -> u64 {
unsafe { std::mem::transmute(self.0) }
}
}
impl std::hash::Hash for FloatBits {
fn hash<H>(&self, state: &mut H)
where
H: std::hash::Hasher,
{
self.key().hash(state)
}
}
impl PartialEq for FloatBits {
fn eq(&self, other: &FloatBits) -> bool {
self.key() == other.key()
}
}
impl Eq for FloatBits {}
pub fn serialize_layer(
out_layer: super::Layer,
features: Vec<InnerAtom>,
bounds: geo::Rect<f64>,
) -> Result<Vec<Command>, ()> {
let cf = CoordinateFormat::new(4, 6);
let mut dias = HashMap::new();
let mut rects = HashMap::new();
for feature in &features {
match feature {
InnerAtom::Circle { radius, layer, .. } => {
if out_layer == *layer {
dias.insert(FloatBits(*radius * 2.0), ());
}
}
InnerAtom::Rect { rect, layer } => {
if out_layer == *layer {
rects.insert((FloatBits(rect.width()), FloatBits(rect.height())), ());
}
}
InnerAtom::Drill { .. } => (), InnerAtom::VScoreH(_) | InnerAtom::VScoreV(_) => {
if out_layer == super::Layer::FabricationInstructions {
dias.insert(FloatBits(0.18), ());
}
}
}
}
let apertures: Vec<(i32, ApertureType)> = dias
.keys()
.map(|fb| ApertureType::Circle(fb.0))
.chain(
rects
.keys()
.map(|(xfb, yfb)| ApertureType::Rect(xfb.0, yfb.0)),
)
.enumerate()
.map(|(i, f)| (10 + i as i32, f))
.collect();
let mut commands = gerber_prelude(
cf,
match out_layer {
super::Layer::FrontCopper => Some(FileFunction::Copper {
layer: 1,
pos: ExtendedPosition::Top,
copper_type: None,
}),
super::Layer::BackCopper => Some(FileFunction::Copper {
layer: 2,
pos: ExtendedPosition::Bottom,
copper_type: None,
}),
super::Layer::FrontMask => Some(FileFunction::Soldermask {
pos: Position::Top,
index: None,
}),
super::Layer::BackMask => Some(FileFunction::Soldermask {
pos: Position::Bottom,
index: None,
}),
super::Layer::FrontLegend => Some(FileFunction::Legend {
pos: Position::Top,
index: None,
}),
super::Layer::BackLegend => Some(FileFunction::Legend {
pos: Position::Bottom,
index: None,
}),
super::Layer::FabricationInstructions => None,
},
apertures.iter(),
);
let mut last_aperture: Option<i32> = None;
for feature in &features {
match feature {
InnerAtom::Circle {
center,
radius,
layer,
..
} => {
if out_layer == *layer {
let code = apertures.iter().find(|&(_, f)| matches!(f, ApertureType::Circle(f) if *f == (*radius * 2.0))).unwrap().0;
if last_aperture != Some(code) {
commands.push(FunctionCode::DCode(DCode::SelectAperture(code)).into());
last_aperture = Some(code);
}
let x = CoordinateNumber::try_from(center.x).unwrap();
let y = CoordinateNumber::try_from(center.y).unwrap();
commands.push(gerber_types::Command::FunctionCode(
FunctionCode::DCode(DCode::Operation(Operation::Flash(Coordinates::new(
x, y, cf,
))))
.into(),
));
}
}
InnerAtom::Rect { rect, layer } => {
if out_layer == *layer {
let code = apertures.iter().find(|&(_, f)| matches!(f, ApertureType::Rect(x, y) if *x == rect.width() && *y == rect.height())).unwrap().0;
if last_aperture != Some(code) {
commands.push(FunctionCode::DCode(DCode::SelectAperture(code)).into());
last_aperture = Some(code);
}
let x = CoordinateNumber::try_from(rect.center().x).unwrap();
let y = CoordinateNumber::try_from(rect.center().y).unwrap();
commands.push(gerber_types::Command::FunctionCode(
FunctionCode::DCode(DCode::Operation(Operation::Flash(Coordinates::new(
x, y, cf,
))))
.into(),
));
}
}
InnerAtom::Drill { .. } => (),
InnerAtom::VScoreH(y) => {
if out_layer == super::Layer::FabricationInstructions {
let code = apertures
.iter()
.find(|&(_, f)| matches!(f, ApertureType::Circle(f) if *f == 0.18))
.unwrap()
.0;
if last_aperture != Some(code) {
commands.push(FunctionCode::DCode(DCode::SelectAperture(code)).into());
last_aperture = Some(code);
}
commands.push(
FunctionCode::DCode(DCode::Operation(Operation::Move(Coordinates::new(
CoordinateNumber::try_from(bounds.min().x - 3.).unwrap(),
CoordinateNumber::try_from(*y).unwrap(),
cf,
))))
.into(),
);
commands.push(
FunctionCode::DCode(DCode::Operation(Operation::Interpolate(
Coordinates::new(
CoordinateNumber::try_from(bounds.max().x + 3.).unwrap(),
CoordinateNumber::try_from(*y).unwrap(),
cf,
),
None,
)))
.into(),
);
flash_text(
"V-SCORE",
bounds.max().x + 0.5,
*y + 1.,
code,
cf,
&mut commands,
);
}
}
InnerAtom::VScoreV(x) => {
if out_layer == super::Layer::FabricationInstructions {
let code = apertures
.iter()
.find(|&(_, f)| matches!(f, ApertureType::Circle(f) if *f == 0.18))
.unwrap()
.0;
if last_aperture != Some(code) {
commands.push(FunctionCode::DCode(DCode::SelectAperture(code)).into());
last_aperture = Some(code);
}
commands.push(
FunctionCode::DCode(DCode::Operation(Operation::Move(Coordinates::new(
CoordinateNumber::try_from(*x).unwrap(),
CoordinateNumber::try_from(bounds.min().y - 3.).unwrap(),
cf,
))))
.into(),
);
commands.push(
FunctionCode::DCode(DCode::Operation(Operation::Interpolate(
Coordinates::new(
CoordinateNumber::try_from(*x).unwrap(),
CoordinateNumber::try_from(bounds.max().y + 3.).unwrap(),
cf,
),
None,
)))
.into(),
);
}
}
}
}
commands.push(FunctionCode::MCode(MCode::EndOfFile).into());
Ok(commands)
}
#[cfg(feature = "text")]
fn flash_text(
text: &str,
lx: f64,
ly: f64,
d_code: i32,
cf: CoordinateFormat,
commands: &mut Vec<Command>,
) {
commands.push(FunctionCode::DCode(DCode::SelectAperture(d_code)).into());
for y in 0..8 {
for x in 0..6 * text.len() {
let is_set =
super::text::character_pixel(text.as_bytes()[x / 6] as char, (x % 6) as u32, y);
if is_set {
let x2 = CoordinateNumber::try_from(lx + (x as f64 * 0.11)).unwrap();
let y2 = CoordinateNumber::try_from(ly - (y as f64 * 0.11)).unwrap();
commands.push(gerber_types::Command::FunctionCode(
FunctionCode::DCode(DCode::Operation(Operation::Flash(Coordinates::new(
x2, y2, cf,
))))
.into(),
));
}
}
}
}
#[cfg(not(feature = "text"))]
fn flash_text(
text: &str,
lx: f64,
ly: f64,
d_code: i32,
cf: CoordinateFormat,
commands: &mut Vec<Command>,
) {
}