use std::collections::HashMap; use gerber_types::{ Aperture, ApertureMacro, Command, Coordinates, DCode, FunctionCode, GCode, InterpolationMode, MCode, MacroContent, Operation, Unit, }; use tracing::{error, info}; use crate::{ geometry::{ elements::{ circle::Circle, linepath::LinePath, obround::Obround, rectangle::Rectangle, Element, }, point::Point, }, gerber::doc::GerberDoc, }; use super::{ elements::{composite::Composite, polygon::Polygon}, union::{union_lines, union_with_apertures}, Geometry, }; impl From for Geometry { fn from(gerber: GerberDoc) -> Self { // working variables let mut selected_aperture = None; let mut selected_interpolation_mode = InterpolationMode::Linear; let mut current_position = Point::new(0., 0.); let mut active_path: LinePath = LinePath::new(); let mut path_container: Vec = Vec::new(); let mut added_apertures: Vec = Vec::new(); // create geometries by applying all gerber commands for command in gerber.commands { match command { Command::FunctionCode(f) => { match f { FunctionCode::DCode(code) => { match code { DCode::Operation(op) => match op { Operation::Interpolate(coordinates, offset) => { // create line by interpolating from current position to new position if selected_interpolation_mode == InterpolationMode::Linear { // add current position as starting position if active path is empty (=> start new one) if active_path.is_empty() { active_path.add(current_position); } // add point (from gerber coordinates) to active path match Point::try_from(&coordinates) { Ok(point) => { active_path.add(point); } Err(e) => error!("{e:?}"), } } else { // TODO // self.add_arc_segment(coord, offset.as_ref().expect(format!("No offset coord with 'Circular' state\r\n{:#?}", c).as_str())) } // move current coordinates to new position Self::move_position(&coordinates, &mut current_position); } // move current coordinates to new position Operation::Move(m) => { // check if a aperture is selected and if it's circular if let Some(Aperture::Circle(c)) = selected_aperture.as_ref() { // check if a path is currently active if !active_path.is_empty() { // finish active path if there is an active one active_path.finalize(c.diameter); path_container.push(active_path); } } // create new active path and move position tp current position active_path = LinePath::new(); Self::move_position(&m, &mut current_position); } // add selected Aperture Operation::Flash(f) => { Self::add_geometry( &mut added_apertures, &gerber.aperture_macros, ¤t_position, &f, &selected_aperture, ); Self::move_position(&f, &mut current_position); } }, // select an aperture DCode::SelectAperture(ap) => { // check if a aperture is selected and if it's circular if let Some(Aperture::Circle(c)) = selected_aperture.as_ref() { // check if a path is currently active if !active_path.is_empty() { // finish active path if there is an active one before selecting new aperture active_path.finalize(c.diameter); path_container.push(active_path); // create new active path active_path = LinePath::new(); } } selected_aperture = Some( gerber .apertures .get(&ap) .unwrap_or_else(|| { panic!("Unknown aperture id '{}'", ap) }) .clone(), ) } } } FunctionCode::GCode(code) => match code { GCode::InterpolationMode(im) => selected_interpolation_mode = im, GCode::Comment(c) => info!("[COMMENT] \"{}\"", c), _ => error!("Unsupported GCode:\r\n{:#?}", code), }, FunctionCode::MCode(m) => { // check for end of file if m == MCode::EndOfFile && !active_path.is_empty() { // finish current path if one is present if let Some(Aperture::Circle(c)) = selected_aperture.as_ref() { active_path.finalize(c.diameter); path_container.push(active_path); break; // finish executing commands } } } } } Command::ExtendedCode(_) => {} } } // union all drawn lines into nets of conductors let conductor_net = union_lines(&path_container); // union conductors with apertures let united_nets = union_with_apertures(&added_apertures, conductor_net).unwrap(); Self { united_nets, apertures: added_apertures, paths: path_container, units: gerber.units.unwrap_or(Unit::Millimeters).into(), } } } impl Geometry { fn move_position(coord: &Coordinates, position: &mut Point) { if let Ok(pos) = Point::try_from(coord) { *position = pos; }; } fn add_geometry( geometries: &mut Vec, aperture_macros: &HashMap, position: &Point, coordinates: &Coordinates, aperture: &Option, ) { let target = match Point::try_from(coordinates) { Ok(point) => point, Err(_) => *position, }; match aperture.as_ref().expect("No aperture selected") { Aperture::Circle(c) => { geometries.push(Element::Circle(Circle::from_aperture_circle(c, target))); } Aperture::Rectangle(r) => { geometries.push(Element::Rectangle(Rectangle::from_aperture_rectangular( r, target, ))); } Aperture::Obround(o) => { geometries.push(Element::Obround(Obround::from_aperture_obround(o, target))); } Aperture::Polygon(p) => { // TODO add polygon error!("Unsupported Polygon aperture:\r\n{:#?}", p); } Aperture::Other(o) => { // split at '/' -> name/arguments if let Some((name, args)) = o.split_once("/") { // parse variables from args let variables: Vec = args .split(",") .map(|s| s.parse::().unwrap_or(0.)) .collect(); if let Some(ap_macro) = aperture_macros.get(name) { let mut composite = Composite::new(); // add elements from macro for aperture in &ap_macro.content { match aperture { MacroContent::Circle(c) => composite.add(Element::Circle( Circle::from_macro(c, &variables, target), )), MacroContent::VectorLine(vl) => composite.add(Element::Rectangle( Rectangle::from_macro_vector_line(vl, &variables, target), )), MacroContent::CenterLine(cl) => composite.add(Element::Rectangle( Rectangle::from_macro_center_line(cl, &variables, target), )), MacroContent::Outline(ol) => composite.add(Element::Polygon( Polygon::from_macro(ol, &variables, target), )), MacroContent::Polygon(p) => { error!("Polygon Not implemented yet") } MacroContent::Moire(m) => { error!("Moire Not implemented yet") } MacroContent::Thermal(t) => { error!("Thermal Not implemented yet") } MacroContent::VariableDefinition(_) => {} MacroContent::Comment(_) => {} }; } composite.finalize(); geometries.push(Element::Composite(composite)); } else { error!("Unsupported Other aperture:\r\n{:#?}", o); } } else { error!("Unsupported Other aperture:\r\n{:#?}", o); } } } } }