1use std::io::Write;
4
5use crate::errors::GerberResult;
6use crate::traits::PartialGerberCode;
7use crate::MacroDecimal;
8use strum_macros;
9use strum_macros::{IntoStaticStr, VariantArray, VariantNames};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IntoStaticStr, VariantNames, VariantArray)]
14#[strum(serialize_all = "UPPERCASE")]
15pub enum Unit {
16 #[strum(serialize = "IN")]
17 Inches,
18 #[strum(serialize = "MM")]
19 Millimeters,
20}
21
22impl_partial_gerber_code_via_strum!(Unit);
23
24#[derive(Debug, Clone, PartialEq)]
27pub struct ApertureDefinition {
28 pub code: i32,
29 pub aperture: Aperture,
30}
31
32impl ApertureDefinition {
33 pub fn new(code: i32, aperture: Aperture) -> Self {
34 ApertureDefinition { code, aperture }
35 }
36}
37
38impl<W: Write> PartialGerberCode<W> for ApertureDefinition {
39 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
40 write!(writer, "{}", self.code)?;
41 self.aperture.serialize_partial(writer)?;
42 Ok(())
43 }
44}
45
46#[derive(Debug, Clone, PartialEq)]
49pub enum Aperture {
50 Circle(Circle),
51 Rectangle(Rectangular),
52 Obround(Rectangular),
53 Polygon(Polygon),
54
55 Macro(String, Option<Vec<MacroDecimal>>),
63}
64
65impl<W: Write> PartialGerberCode<W> for Aperture {
66 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
67 match *self {
68 Aperture::Circle(ref circle) => {
69 write!(writer, "C,")?;
70 circle.serialize_partial(writer)?;
71 }
72 Aperture::Rectangle(ref rectangular) => {
73 write!(writer, "R,")?;
74 rectangular.serialize_partial(writer)?;
75 }
76 Aperture::Obround(ref rectangular) => {
77 write!(writer, "O,")?;
78 rectangular.serialize_partial(writer)?;
79 }
80 Aperture::Polygon(ref polygon) => {
81 write!(writer, "P,")?;
82 polygon.serialize_partial(writer)?;
83 }
84 Aperture::Macro(ref string, ref args) => {
85 write!(writer, "{}", string)?;
86 if let Some(ref args) = *args {
87 write!(writer, ",")?;
88 for (index, arg) in args.iter().enumerate() {
89 if index > 0 {
90 write!(writer, "X")?;
91 }
92 arg.serialize_partial(writer)?;
93 }
94 }
95 }
96 };
97 Ok(())
98 }
99}
100
101#[derive(Debug, Clone, PartialEq)]
104pub struct Circle {
105 pub diameter: f64,
106 pub hole_diameter: Option<f64>,
107}
108
109impl Circle {
110 pub fn new(diameter: f64) -> Self {
111 Circle {
112 diameter,
113 hole_diameter: None,
114 }
115 }
116
117 pub fn with_hole(diameter: f64, hole_diameter: f64) -> Self {
118 Circle {
119 diameter,
120 hole_diameter: Some(hole_diameter),
121 }
122 }
123}
124
125impl<W: Write> PartialGerberCode<W> for Circle {
126 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
127 match self.hole_diameter {
128 Some(hole_diameter) => {
129 write!(writer, "{}X{}", self.diameter, hole_diameter)?;
130 }
131 None => write!(writer, "{}", self.diameter)?,
132 };
133 Ok(())
134 }
135}
136
137#[derive(Debug, Clone, PartialEq)]
140pub struct Rectangular {
141 pub x: f64,
142 pub y: f64,
143 pub hole_diameter: Option<f64>,
144}
145
146impl Rectangular {
147 pub fn new(x: f64, y: f64) -> Self {
148 Rectangular {
149 x,
150 y,
151 hole_diameter: None,
152 }
153 }
154
155 pub fn with_hole(x: f64, y: f64, hole_diameter: f64) -> Self {
156 Rectangular {
157 x,
158 y,
159 hole_diameter: Some(hole_diameter),
160 }
161 }
162}
163
164impl<W: Write> PartialGerberCode<W> for Rectangular {
165 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
166 match self.hole_diameter {
167 Some(hole_diameter) => write!(writer, "{}X{}X{}", self.x, self.y, hole_diameter)?,
168 None => write!(writer, "{}X{}", self.x, self.y)?,
169 };
170 Ok(())
171 }
172}
173
174#[derive(Debug, Clone, PartialEq)]
177pub struct Polygon {
178 pub diameter: f64,
179 pub vertices: u8, pub rotation: Option<f64>,
181 pub hole_diameter: Option<f64>,
182}
183
184impl Polygon {
185 pub fn new(diameter: f64, vertices: u8) -> Self {
186 Polygon {
187 diameter,
188 vertices,
189 rotation: None,
190 hole_diameter: None,
191 }
192 }
193
194 pub fn with_rotation(mut self, angle: f64) -> Self {
195 self.rotation = Some(angle);
196 self
197 }
198
199 pub fn with_diameter(mut self, diameter: f64) -> Self {
200 self.diameter = diameter;
201 self
202 }
203}
204
205impl<W: Write> PartialGerberCode<W> for Polygon {
206 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
207 match (self.rotation, self.hole_diameter) {
208 (Some(rot), Some(hd)) => {
209 write!(writer, "{}X{}X{}X{}", self.diameter, self.vertices, rot, hd)?
210 }
211 (Some(rot), None) => write!(writer, "{}X{}X{}", self.diameter, self.vertices, rot)?,
212 (None, Some(hd)) => write!(writer, "{}X{}X0X{}", self.diameter, self.vertices, hd)?,
213 (None, None) => write!(writer, "{}X{}", self.diameter, self.vertices)?,
214 };
215 Ok(())
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, VariantNames, VariantArray)]
222#[strum(serialize_all = "UPPERCASE")]
223pub enum Polarity {
224 #[strum(serialize = "C")]
225 Clear,
226 #[strum(serialize = "D")]
227 Dark,
228}
229
230impl_partial_gerber_code_via_strum!(Polarity);
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, VariantNames, VariantArray)]
235#[strum(serialize_all = "UPPERCASE")]
236pub enum Mirroring {
237 #[strum(serialize = "N")]
238 None,
239 X,
240 Y,
241 XY,
242}
243
244impl_partial_gerber_code_via_strum!(Mirroring);
245
246#[derive(Debug, Clone, Copy, PartialEq)]
249pub struct Scaling {
250 pub scale: f64,
251}
252
253impl<W: Write> PartialGerberCode<W> for Scaling {
254 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
255 write!(writer, "{}", self.scale)?;
256 Ok(())
257 }
258}
259
260#[derive(Debug, Clone, Copy, PartialEq)]
263pub struct Rotation {
264 pub rotation: f64,
266}
267
268impl<W: Write> PartialGerberCode<W> for Rotation {
269 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
270 write!(writer, "{}", self.rotation)?;
271 Ok(())
272 }
273}
274
275#[derive(Debug, Clone, PartialEq)]
278pub enum StepAndRepeat {
279 Open {
280 repeat_x: u32,
281 repeat_y: u32,
282 distance_x: f64,
283 distance_y: f64,
284 },
285 Close,
286}
287
288impl<W: Write> PartialGerberCode<W> for StepAndRepeat {
289 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
290 match *self {
291 StepAndRepeat::Open {
292 repeat_x: rx,
293 repeat_y: ry,
294 distance_x: dx,
295 distance_y: dy,
296 } => write!(writer, "X{}Y{}I{}J{}", rx, ry, dx, dy)?,
297 StepAndRepeat::Close => {}
298 };
299 Ok(())
300 }
301}
302
303#[derive(Debug, Clone, PartialEq)]
306pub enum ApertureBlock {
307 Open { code: i32 },
308 Close,
309}
310
311impl<W: Write> PartialGerberCode<W> for ApertureBlock {
312 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
313 match *self {
314 ApertureBlock::Open { code } => write!(writer, "{}", code)?,
315 ApertureBlock::Close => {}
316 };
317 Ok(())
318 }
319}
320
321#[cfg(test)]
322mod test {
323 use super::*;
324
325 #[test]
326 fn test_aperture_definition_new() {
327 let ad1 = ApertureDefinition::new(10, Aperture::Circle(Circle::new(3.0)));
328 let ad2 = ApertureDefinition {
329 code: 10,
330 aperture: Aperture::Circle(Circle::new(3.0)),
331 };
332 assert_eq!(ad1, ad2);
333 }
334
335 #[test]
336 fn test_rectangular_new() {
337 let r1 = Rectangular::new(2.0, 3.0);
338 let r2 = Rectangular {
339 x: 2.0,
340 y: 3.0,
341 hole_diameter: None,
342 };
343 assert_eq!(r1, r2);
344 }
345
346 #[test]
347 fn test_rectangular_with_hole() {
348 let r1 = Rectangular::with_hole(3.0, 2.0, 1.0);
349 let r2 = Rectangular {
350 x: 3.0,
351 y: 2.0,
352 hole_diameter: Some(1.0),
353 };
354 assert_eq!(r1, r2);
355 }
356
357 #[test]
358 fn test_circle_new() {
359 let c1 = Circle::new(3.0);
360 let c2 = Circle {
361 diameter: 3.0,
362 hole_diameter: None,
363 };
364 assert_eq!(c1, c2);
365 }
366
367 #[test]
368 fn test_circle_with_hole() {
369 let c1 = Circle::with_hole(3.0, 1.0);
370 let c2 = Circle {
371 diameter: 3.0,
372 hole_diameter: Some(1.0),
373 };
374 assert_eq!(c1, c2);
375 }
376
377 #[test]
378 fn test_polygon_new() {
379 let p1 = Polygon::new(3.0, 4).with_rotation(45.0);
380 let p2 = Polygon {
381 diameter: 3.0,
382 vertices: 4,
383 rotation: Some(45.0),
384 hole_diameter: None,
385 };
386 assert_eq!(p1, p2);
387 }
388
389 #[test]
391 fn unit_in_hashmap() {
392 let mut map = std::collections::HashMap::new();
393 map.insert(Unit::Inches, ());
394 map.insert(Unit::Millimeters, ());
395
396 assert_eq!(map.len(), 2);
397 }
398}
399
400#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, VariantNames, VariantArray)]
404pub enum ImageMirroring {
405 #[strum(serialize = "")]
406 None,
407 #[strum(serialize = "A1")]
408 A,
409 #[strum(serialize = "B1")]
410 B,
411 #[strum(serialize = "A1B1")]
412 AB,
413}
414
415impl_partial_gerber_code_via_strum!(ImageMirroring);
416
417impl Default for ImageMirroring {
418 fn default() -> Self {
419 ImageMirroring::None
420 }
421}
422
423#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, VariantNames, VariantArray)]
427#[allow(non_camel_case_types)]
428pub enum ImageRotation {
429 #[strum(serialize = "0")]
430 None,
431 #[strum(serialize = "90")]
432 CCW_90,
433 #[strum(serialize = "180")]
434 CCW_180,
435 #[strum(serialize = "270")]
436 CCW_270,
437}
438
439impl_partial_gerber_code_via_strum!(ImageRotation);
440
441impl Default for ImageRotation {
442 fn default() -> Self {
443 ImageRotation::None
444 }
445}
446#[derive(Debug, Clone, Copy, PartialEq)]
451pub struct ImageScaling {
452 pub a: f64,
454 pub b: f64,
456}
457
458impl<W: Write> PartialGerberCode<W> for ImageScaling {
459 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
460 if self.a != 0.0 {
461 write!(writer, "A{}", self.a)?;
462 }
463 if self.b != 0.0 {
464 write!(writer, "B{}", self.b)?;
465 }
466 Ok(())
467 }
468}
469
470impl Default for ImageScaling {
471 fn default() -> Self {
472 ImageScaling { a: 1.0, b: 1.0 }
473 }
474}
475
476#[derive(Debug, Clone, Copy, PartialEq, Default)]
481pub struct ImageOffset {
482 pub a: f64,
484 pub b: f64,
486}
487
488impl<W: Write> PartialGerberCode<W> for ImageOffset {
489 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
490 if self.a != 0.0 {
491 write!(writer, "A{}", self.a)?;
492 }
493 if self.b != 0.0 {
494 write!(writer, "B{}", self.b)?;
495 }
496 Ok(())
497 }
498}
499
500#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, VariantNames, VariantArray)]
503#[strum(serialize_all = "UPPERCASE")]
504pub enum AxisSelect {
505 AXBY,
506 AYBX,
507}
508
509impl_partial_gerber_code_via_strum!(AxisSelect);
510
511impl Default for AxisSelect {
512 fn default() -> Self {
513 AxisSelect::AXBY
514 }
515}
516
517#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoStaticStr, VariantNames, VariantArray)]
520pub enum ImagePolarity {
521 #[strum(serialize = "POS")]
522 Positive,
523 #[strum(serialize = "NEG")]
524 Negative,
525}
526
527impl_partial_gerber_code_via_strum!(ImagePolarity);
528
529impl Default for ImagePolarity {
530 fn default() -> Self {
531 ImagePolarity::Positive
532 }
533}
534
535#[derive(Debug, Clone, PartialEq)]
538pub struct ImageName {
539 pub name: String,
540}
541
542impl<W: Write> PartialGerberCode<W> for ImageName {
543 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
544 write!(writer, "{}", self.name)?;
545 Ok(())
546 }
547}