1use std::convert::From;
4use std::io::Write;
5
6use crate::errors::{GerberError, GerberResult};
7use crate::traits::PartialGerberCode;
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct ApertureMacro {
11 pub name: String,
12 pub content: Vec<MacroContent>,
13}
14
15impl ApertureMacro {
16 pub fn new<S: Into<String>>(name: S) -> Self {
17 ApertureMacro {
18 name: name.into(),
19 content: Vec::new(),
20 }
21 }
22
23 pub fn add_content<C>(mut self, c: C) -> Self
24 where
25 C: Into<MacroContent>,
26 {
27 self.content.push(c.into());
28 self
29 }
30
31 pub fn add_content_mut<C>(&mut self, c: C)
32 where
33 C: Into<MacroContent>,
34 {
35 self.content.push(c.into());
36 }
37}
38
39impl<W: Write> PartialGerberCode<W> for ApertureMacro {
40 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
41 if self.content.is_empty() {
42 return Err(GerberError::MissingDataError(
43 "There must be at least 1 content element in an aperture macro".into(),
44 ));
45 }
46 writeln!(writer, "AM{}*", self.name)?;
47 let mut first = true;
48 for content in &self.content {
49 if first {
50 first = false;
51 } else {
52 writeln!(writer)?;
53 }
54 content.serialize_partial(writer)?;
55 }
56 Ok(())
57 }
58}
59
60#[derive(Debug, Clone, PartialEq)]
61pub enum MacroDecimal {
63 Value(f64),
65 Variable(u32),
67}
68
69impl MacroDecimal {
70 fn is_negative(&self) -> bool {
71 match *self {
72 MacroDecimal::Value(v) => v < 0.0,
73 MacroDecimal::Variable(_) => false,
74 }
75 }
76}
77
78impl From<f32> for MacroDecimal {
79 fn from(val: f32) -> Self {
80 MacroDecimal::Value(val as f64)
81 }
82}
83
84impl From<f64> for MacroDecimal {
85 fn from(val: f64) -> Self {
86 MacroDecimal::Value(val)
87 }
88}
89
90impl<W: Write> PartialGerberCode<W> for MacroDecimal {
91 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
92 match *self {
93 MacroDecimal::Value(ref v) => write!(writer, "{}", v)?,
94 MacroDecimal::Variable(ref v) => write!(writer, "${}", v)?,
95 };
96 Ok(())
97 }
98}
99
100#[derive(Debug, Clone, PartialEq)]
101pub enum MacroContent {
102 Circle(CirclePrimitive),
104 VectorLine(VectorLinePrimitive),
105 CenterLine(CenterLinePrimitive),
106 Outline(OutlinePrimitive),
107 Polygon(PolygonPrimitive),
108 Moire(MoirePrimitive),
109 Thermal(ThermalPrimitive),
110
111 VariableDefinition(VariableDefinition),
113
114 Comment(String),
116}
117
118impl<W: Write> PartialGerberCode<W> for MacroContent {
119 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
120 match *self {
121 MacroContent::Circle(ref c) => c.serialize_partial(writer)?,
122 MacroContent::VectorLine(ref vl) => vl.serialize_partial(writer)?,
123 MacroContent::CenterLine(ref cl) => cl.serialize_partial(writer)?,
124 MacroContent::Outline(ref o) => o.serialize_partial(writer)?,
125 MacroContent::Polygon(ref p) => p.serialize_partial(writer)?,
126 MacroContent::Moire(ref m) => m.serialize_partial(writer)?,
127 MacroContent::Thermal(ref t) => t.serialize_partial(writer)?,
128 MacroContent::Comment(ref s) => write!(writer, "0 {}*", &s)?,
129 MacroContent::VariableDefinition(ref v) => v.serialize_partial(writer)?,
130 };
131 Ok(())
132 }
133}
134
135macro_rules! impl_into {
136 ($target:ty, $from:ty, $choice:expr) => {
137 impl From<$from> for $target {
138 fn from(val: $from) -> $target {
139 $choice(val)
140 }
141 }
142 };
143}
144
145impl_into!(MacroContent, CirclePrimitive, MacroContent::Circle);
146impl_into!(MacroContent, VectorLinePrimitive, MacroContent::VectorLine);
147impl_into!(MacroContent, CenterLinePrimitive, MacroContent::CenterLine);
148impl_into!(MacroContent, OutlinePrimitive, MacroContent::Outline);
149impl_into!(MacroContent, PolygonPrimitive, MacroContent::Polygon);
150impl_into!(MacroContent, MoirePrimitive, MacroContent::Moire);
151impl_into!(MacroContent, ThermalPrimitive, MacroContent::Thermal);
152impl_into!(
153 MacroContent,
154 VariableDefinition,
155 MacroContent::VariableDefinition
156);
157
158impl<T: Into<String>> From<T> for MacroContent {
159 fn from(val: T) -> Self {
160 MacroContent::Comment(val.into())
161 }
162}
163
164#[derive(Debug, Clone, PartialEq)]
165pub struct CirclePrimitive {
166 pub exposure: bool,
168
169 pub diameter: MacroDecimal,
171
172 pub center: (MacroDecimal, MacroDecimal),
174
175 pub angle: Option<MacroDecimal>,
184}
185
186impl CirclePrimitive {
187 pub fn new(diameter: MacroDecimal) -> Self {
188 CirclePrimitive {
189 exposure: true,
190 diameter,
191 center: (MacroDecimal::Value(0.0), MacroDecimal::Value(0.0)),
192 angle: None,
193 }
194 }
195
196 pub fn centered_at(mut self, center: (MacroDecimal, MacroDecimal)) -> Self {
197 self.center = center;
198 self
199 }
200
201 pub fn exposure_on(mut self, exposure: bool) -> Self {
202 self.exposure = exposure;
203 self
204 }
205
206 pub fn with_angle(mut self, angle: MacroDecimal) -> Self {
207 self.angle = Some(angle);
208 self
209 }
210}
211
212impl<W: Write> PartialGerberCode<W> for CirclePrimitive {
213 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
214 write!(writer, "1,")?;
215 self.exposure.serialize_partial(writer)?;
216 write!(writer, ",")?;
217 self.diameter.serialize_partial(writer)?;
218 write!(writer, ",")?;
219 self.center.0.serialize_partial(writer)?;
220 write!(writer, ",")?;
221 self.center.1.serialize_partial(writer)?;
222 if let Some(ref a) = self.angle {
223 write!(writer, ",")?;
224 a.serialize_partial(writer)?;
225 }
226 write!(writer, "*")?;
227 Ok(())
228 }
229}
230
231#[derive(Debug, Clone, PartialEq)]
232pub struct VectorLinePrimitive {
233 pub exposure: bool,
235
236 pub width: MacroDecimal,
238
239 pub start: (MacroDecimal, MacroDecimal),
241
242 pub end: (MacroDecimal, MacroDecimal),
244
245 pub angle: MacroDecimal,
251}
252
253impl VectorLinePrimitive {
254 pub fn new(start: (MacroDecimal, MacroDecimal), end: (MacroDecimal, MacroDecimal)) -> Self {
255 VectorLinePrimitive {
256 exposure: true,
257 width: MacroDecimal::Value(0.0),
258 start,
259 end,
260 angle: MacroDecimal::Value(0.0),
261 }
262 }
263
264 pub fn exposure_on(mut self, exposure: bool) -> Self {
265 self.exposure = exposure;
266 self
267 }
268
269 pub fn with_width(mut self, width: MacroDecimal) -> Self {
270 self.width = width;
271 self
272 }
273
274 pub fn with_angle(mut self, angle: MacroDecimal) -> Self {
275 self.angle = angle;
276 self
277 }
278}
279
280impl<W: Write> PartialGerberCode<W> for VectorLinePrimitive {
281 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
282 write!(writer, "20,")?;
283 self.exposure.serialize_partial(writer)?;
284 write!(writer, ",")?;
285 self.width.serialize_partial(writer)?;
286 write!(writer, ",")?;
287 self.start.0.serialize_partial(writer)?;
288 write!(writer, ",")?;
289 self.start.1.serialize_partial(writer)?;
290 write!(writer, ",")?;
291 self.end.0.serialize_partial(writer)?;
292 write!(writer, ",")?;
293 self.end.1.serialize_partial(writer)?;
294 write!(writer, ",")?;
295 self.angle.serialize_partial(writer)?;
296 write!(writer, "*")?;
297 Ok(())
298 }
299}
300
301#[derive(Debug, Clone, PartialEq)]
302pub struct CenterLinePrimitive {
303 pub exposure: bool,
305
306 pub dimensions: (MacroDecimal, MacroDecimal),
308
309 pub center: (MacroDecimal, MacroDecimal),
311
312 pub angle: MacroDecimal,
318}
319
320impl CenterLinePrimitive {
321 pub fn new(dimensions: (MacroDecimal, MacroDecimal)) -> Self {
322 CenterLinePrimitive {
323 exposure: true,
324 dimensions,
325 center: (MacroDecimal::Value(0.0), MacroDecimal::Value(0.0)),
326 angle: MacroDecimal::Value(0.0),
327 }
328 }
329
330 pub fn exposure_on(mut self, exposure: bool) -> Self {
331 self.exposure = exposure;
332 self
333 }
334
335 pub fn centered_at(mut self, center: (MacroDecimal, MacroDecimal)) -> Self {
336 self.center = center;
337 self
338 }
339
340 pub fn with_angle(mut self, angle: MacroDecimal) -> Self {
341 self.angle = angle;
342 self
343 }
344}
345
346impl<W: Write> PartialGerberCode<W> for CenterLinePrimitive {
347 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
348 write!(writer, "21,")?;
349 self.exposure.serialize_partial(writer)?;
350 write!(writer, ",")?;
351 self.dimensions.0.serialize_partial(writer)?;
352 write!(writer, ",")?;
353 self.dimensions.1.serialize_partial(writer)?;
354 write!(writer, ",")?;
355 self.center.0.serialize_partial(writer)?;
356 write!(writer, ",")?;
357 self.center.1.serialize_partial(writer)?;
358 write!(writer, ",")?;
359 self.angle.serialize_partial(writer)?;
360 write!(writer, "*")?;
361 Ok(())
362 }
363}
364
365#[derive(Debug, Clone, PartialEq)]
366pub struct OutlinePrimitive {
367 pub exposure: bool,
369
370 pub points: Vec<(MacroDecimal, MacroDecimal)>,
374
375 pub angle: MacroDecimal,
381}
382
383impl OutlinePrimitive {
384 pub fn new() -> Self {
385 OutlinePrimitive {
386 exposure: true,
387 points: Vec::new(),
388 angle: MacroDecimal::Value(0.0),
389 }
390 }
391
392 pub fn from_points(points: Vec<(MacroDecimal, MacroDecimal)>) -> Self {
393 let mut outline_prim = Self::new();
394 outline_prim.points = points;
395 outline_prim
396 }
397
398 pub fn add_point(mut self, point: (MacroDecimal, MacroDecimal)) -> Self {
399 self.points.push(point);
400 self
401 }
402
403 pub fn with_angle(mut self, angle: MacroDecimal) -> Self {
404 self.angle = angle;
405 self
406 }
407}
408
409impl<W: Write> PartialGerberCode<W> for OutlinePrimitive {
410 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
411 if self.points.len() < 2 {
413 return Err(GerberError::MissingDataError(
414 "There must be at least 1 subsequent point in an outline".into(),
415 ));
416 }
417 if self.points.len() > 5001 {
418 return Err(GerberError::RangeError(
419 "The maximum number of subsequent points in an outline is 5000".into(),
420 ));
421 }
422 if self.points[0] != self.points[self.points.len() - 1] {
423 return Err(GerberError::RangeError(
424 "The last point must be equal to the first point".into(),
425 ));
426 }
427
428 write!(writer, "4,")?;
429 self.exposure.serialize_partial(writer)?;
430 writeln!(writer, ",{},", self.points.len() - 1)?;
431
432 for &(ref x, ref y) in &self.points {
433 x.serialize_partial(writer)?;
434 write!(writer, ",")?;
435 y.serialize_partial(writer)?;
436 writeln!(writer, ",")?;
437 }
438 self.angle.serialize_partial(writer)?;
439 write!(writer, "*")?;
440 Ok(())
441 }
442}
443
444#[derive(Debug, Clone, PartialEq)]
445pub struct PolygonPrimitive {
448 pub exposure: bool,
450
451 pub vertices: u8,
453
454 pub center: (MacroDecimal, MacroDecimal),
456
457 pub diameter: MacroDecimal,
459
460 pub angle: MacroDecimal,
470}
471
472impl PolygonPrimitive {
473 pub fn new(vertices: u8) -> Self {
474 PolygonPrimitive {
475 exposure: true,
476 vertices,
477 center: (MacroDecimal::Value(0.0), MacroDecimal::Value(0.0)),
478 diameter: MacroDecimal::Value(0.0),
479 angle: MacroDecimal::Value(0.0),
480 }
481 }
482
483 pub fn exposure_on(mut self, exposure: bool) -> Self {
484 self.exposure = exposure;
485 self
486 }
487
488 pub fn centered_at(mut self, center: (MacroDecimal, MacroDecimal)) -> Self {
489 self.center = center;
490 self
491 }
492
493 pub fn with_diameter(mut self, diameter: MacroDecimal) -> Self {
494 self.diameter = diameter;
495 self
496 }
497
498 pub fn with_angle(mut self, angle: MacroDecimal) -> Self {
499 self.angle = angle;
500 self
501 }
502}
503
504impl<W: Write> PartialGerberCode<W> for PolygonPrimitive {
505 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
506 if self.vertices < 3 {
508 return Err(GerberError::MissingDataError(
509 "There must be at least 3 vertices in a polygon".into(),
510 ));
511 }
512 if self.vertices > 12 {
513 return Err(GerberError::RangeError(
514 "The maximum number of vertices in a polygon is 12".into(),
515 ));
516 }
517 if self.diameter.is_negative() {
518 return Err(GerberError::RangeError(
519 "The diameter must not be negative".into(),
520 ));
521 }
522 write!(writer, "5,")?;
523 self.exposure.serialize_partial(writer)?;
524 write!(writer, ",{},", self.vertices)?;
525 self.center.0.serialize_partial(writer)?;
526 write!(writer, ",")?;
527 self.center.1.serialize_partial(writer)?;
528 write!(writer, ",")?;
529 self.diameter.serialize_partial(writer)?;
530 write!(writer, ",")?;
531 self.angle.serialize_partial(writer)?;
532 write!(writer, "*")?;
533 Ok(())
534 }
535}
536
537#[derive(Debug, Clone, PartialEq)]
540pub struct MoirePrimitive {
541 pub center: (MacroDecimal, MacroDecimal),
543
544 pub diameter: MacroDecimal,
546
547 pub ring_thickness: MacroDecimal,
549
550 pub gap: MacroDecimal,
552
553 pub max_rings: u32,
555
556 pub cross_hair_thickness: MacroDecimal,
558
559 pub cross_hair_length: MacroDecimal,
561
562 pub angle: MacroDecimal,
571}
572
573impl MoirePrimitive {
574 pub fn new() -> Self {
575 MoirePrimitive {
576 center: (MacroDecimal::Value(0.0), MacroDecimal::Value(0.0)),
577 diameter: MacroDecimal::Value(0.0),
578 ring_thickness: MacroDecimal::Value(0.0),
579 gap: MacroDecimal::Value(0.0),
580 max_rings: 1,
581 cross_hair_thickness: MacroDecimal::Value(0.0),
582 cross_hair_length: MacroDecimal::Value(0.0),
583 angle: MacroDecimal::Value(0.0),
584 }
585 }
586
587 pub fn centered_at(mut self, center: (MacroDecimal, MacroDecimal)) -> Self {
588 self.center = center;
589 self
590 }
591
592 pub fn with_diameter(mut self, diameter: MacroDecimal) -> Self {
593 self.diameter = diameter;
594 self
595 }
596
597 pub fn with_rings_max(mut self, max_rings: u32) -> Self {
598 self.max_rings = max_rings;
599 self
600 }
601
602 pub fn with_ring_thickness(mut self, thickness: MacroDecimal) -> Self {
603 self.ring_thickness = thickness;
604 self
605 }
606
607 pub fn with_gap(mut self, gap: MacroDecimal) -> Self {
608 self.gap = gap;
609 self
610 }
611
612 pub fn with_cross_thickness(mut self, thickness: MacroDecimal) -> Self {
613 self.cross_hair_thickness = thickness;
614 self
615 }
616
617 pub fn with_cross_length(mut self, length: MacroDecimal) -> Self {
618 self.cross_hair_length = length;
619 self
620 }
621
622 pub fn with_angle(mut self, angle: MacroDecimal) -> Self {
623 self.angle = angle;
624 self
625 }
626}
627
628impl<W: Write> PartialGerberCode<W> for MoirePrimitive {
629 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
630 if self.diameter.is_negative() {
632 return Err(GerberError::RangeError(
633 "Outer diameter of a moiré may not be negative".into(),
634 ));
635 }
636 if self.ring_thickness.is_negative() {
637 return Err(GerberError::RangeError(
638 "Ring thickness of a moiré may not be negative".into(),
639 ));
640 }
641 if self.gap.is_negative() {
642 return Err(GerberError::RangeError(
643 "Gap of a moiré may not be negative".into(),
644 ));
645 }
646 if self.cross_hair_thickness.is_negative() {
647 return Err(GerberError::RangeError(
648 "Cross hair thickness of a moiré may not be negative".into(),
649 ));
650 }
651 if self.cross_hair_length.is_negative() {
652 return Err(GerberError::RangeError(
653 "Cross hair length of a moiré may not be negative".into(),
654 ));
655 }
656 write!(writer, "6,")?;
657 self.center.0.serialize_partial(writer)?;
658 write!(writer, ",")?;
659 self.center.1.serialize_partial(writer)?;
660 write!(writer, ",")?;
661 self.diameter.serialize_partial(writer)?;
662 write!(writer, ",")?;
663 self.ring_thickness.serialize_partial(writer)?;
664 write!(writer, ",")?;
665 self.gap.serialize_partial(writer)?;
666 write!(writer, ",{},", self.max_rings)?;
667 self.cross_hair_thickness.serialize_partial(writer)?;
668 write!(writer, ",")?;
669 self.cross_hair_length.serialize_partial(writer)?;
670 write!(writer, ",")?;
671 self.angle.serialize_partial(writer)?;
672 write!(writer, "*")?;
673 Ok(())
674 }
675}
676
677#[derive(Debug, Clone, PartialEq)]
680pub struct ThermalPrimitive {
681 pub center: (MacroDecimal, MacroDecimal),
683
684 pub outer_diameter: MacroDecimal,
686
687 pub inner_diameter: MacroDecimal,
689
690 pub gap: MacroDecimal,
692
693 pub angle: MacroDecimal,
703}
704
705impl ThermalPrimitive {
706 pub fn new(inner: MacroDecimal, outer: MacroDecimal, gap: MacroDecimal) -> Self {
707 ThermalPrimitive {
708 center: (MacroDecimal::Value(0.0), MacroDecimal::Value(0.0)),
709 outer_diameter: outer,
710 inner_diameter: inner,
711 gap,
712 angle: MacroDecimal::Value(0.0),
713 }
714 }
715
716 pub fn centered_at(mut self, center: (MacroDecimal, MacroDecimal)) -> Self {
717 self.center = center;
718 self
719 }
720
721 pub fn with_angle(mut self, angle: MacroDecimal) -> Self {
722 self.angle = angle;
723 self
724 }
725}
726
727impl<W: Write> PartialGerberCode<W> for ThermalPrimitive {
728 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
729 if self.inner_diameter.is_negative() {
731 return Err(GerberError::RangeError(
732 "Inner diameter of a thermal may not be negative".into(),
733 ));
734 }
735 write!(writer, "7,")?;
736 self.center.0.serialize_partial(writer)?;
737 write!(writer, ",")?;
738 self.center.1.serialize_partial(writer)?;
739 write!(writer, ",")?;
740 self.outer_diameter.serialize_partial(writer)?;
741 write!(writer, ",")?;
742 self.inner_diameter.serialize_partial(writer)?;
743 write!(writer, ",")?;
744 self.gap.serialize_partial(writer)?;
745 write!(writer, ",")?;
746 self.angle.serialize_partial(writer)?;
747 write!(writer, "*")?;
748 Ok(())
749 }
750}
751
752#[derive(Debug, Clone, PartialEq, Eq)]
753pub struct VariableDefinition {
754 number: u32,
755 expression: String,
756}
757
758impl VariableDefinition {
759 pub fn new(number: u32, expr: &str) -> Self {
760 VariableDefinition {
761 number,
762 expression: expr.into(),
763 }
764 }
765}
766
767impl<W: Write> PartialGerberCode<W> for VariableDefinition {
768 fn serialize_partial(&self, writer: &mut W) -> GerberResult<()> {
769 write!(writer, "${}={}*", self.number, self.expression)?;
770 Ok(())
771 }
772}
773
774#[cfg(test)]
775mod test {
776 use std::io::BufWriter;
777
778 use crate::traits::PartialGerberCode;
779
780 use super::MacroDecimal::{Value, Variable};
781 use super::*;
782
783 macro_rules! assert_partial_code {
784 ($obj:expr, $expected:expr) => {
785 let mut buf = BufWriter::new(Vec::new());
786 $obj.serialize_partial(&mut buf)
787 .expect("Could not generate Gerber code");
788 let bytes = buf.into_inner().unwrap();
789 let code = String::from_utf8(bytes).unwrap();
790 assert_eq!(&code, $expected);
791 };
792 }
793
794 #[test]
795 fn test_circle_primitive_codegen() {
796 let with_angle = CirclePrimitive {
797 exposure: true,
798 diameter: Value(1.5),
799 center: (Value(0.), Value(0.)),
800 angle: Some(Value(0.)),
801 };
802 assert_partial_code!(with_angle, "1,1,1.5,0,0,0*");
803 let no_angle = CirclePrimitive {
804 exposure: false,
805 diameter: Value(99.9),
806 center: (Value(1.1), Value(2.2)),
807 angle: None,
808 };
809 assert_partial_code!(no_angle, "1,0,99.9,1.1,2.2*");
810 }
811
812 #[test]
813 fn test_vector_line_primitive_codegen() {
814 let line = VectorLinePrimitive {
815 exposure: true,
816 width: Value(0.9),
817 start: (Value(0.), Value(0.45)),
818 end: (Value(12.), Value(0.45)),
819 angle: Value(0.),
820 };
821 assert_partial_code!(line, "20,1,0.9,0,0.45,12,0.45,0*");
822 }
823
824 #[test]
825 fn test_center_line_primitive_codegen() {
826 let line = CenterLinePrimitive {
827 exposure: true,
828 dimensions: (Value(6.8), Value(1.2)),
829 center: (Value(3.4), Value(0.6)),
830 angle: Value(30.0),
831 };
832 assert_partial_code!(line, "21,1,6.8,1.2,3.4,0.6,30*");
833 }
834
835 #[test]
836 fn test_outline_primitive_codegen() {
837 let line = OutlinePrimitive {
838 exposure: true,
839 points: vec![
840 (Value(0.1), Value(0.1)),
841 (Value(0.5), Value(0.1)),
842 (Value(0.5), Value(0.5)),
843 (Value(0.1), Value(0.5)),
844 (Value(0.1), Value(0.1)),
845 ],
846 angle: Value(0.0),
847 };
848 assert_partial_code!(
849 line,
850 "4,1,4,\n0.1,0.1,\n0.5,0.1,\n0.5,0.5,\n0.1,0.5,\n0.1,0.1,\n0*"
851 );
852 }
853
854 #[test]
855 fn test_polygon_primitive_codegen() {
856 let line = PolygonPrimitive {
857 exposure: true,
858 vertices: 8,
859 center: (Value(1.5), Value(2.0)),
860 diameter: Value(8.0),
861 angle: Value(0.0),
862 };
863 assert_partial_code!(line, "5,1,8,1.5,2,8,0*");
864 }
865
866 #[test]
867 fn test_moire_primitive_codegen() {
868 let line = MoirePrimitive {
869 center: (Value(0.0), Value(0.0)),
870 diameter: Value(5.0),
871 ring_thickness: Value(0.5),
872 gap: Value(0.5),
873 max_rings: 2,
874 cross_hair_thickness: Value(0.1),
875 cross_hair_length: Value(6.0),
876 angle: Value(0.0),
877 };
878 assert_partial_code!(line, "6,0,0,5,0.5,0.5,2,0.1,6,0*");
879 }
880
881 #[test]
882 fn test_thermal_primitive_codegen() {
883 let line = ThermalPrimitive {
884 center: (Value(0.0), Value(0.0)),
885 outer_diameter: Value(8.0),
886 inner_diameter: Value(6.5),
887 gap: Value(1.0),
888 angle: Value(45.0),
889 };
890 assert_partial_code!(line, "7,0,0,8,6.5,1,45*");
891 }
892
893 #[test]
894 fn test_aperture_macro_codegen() {
895 let am = ApertureMacro::new("CRAZY")
896 .add_content(MacroContent::Thermal(ThermalPrimitive {
897 center: (Value(0.0), Value(0.0)),
898 outer_diameter: Value(0.08),
899 inner_diameter: Value(0.055),
900 gap: Value(0.0125),
901 angle: Value(45.0),
902 }))
903 .add_content(MacroContent::Moire(MoirePrimitive {
904 center: (Value(0.0), Value(0.0)),
905 diameter: Value(0.125),
906 ring_thickness: Value(0.01),
907 gap: Value(0.01),
908 max_rings: 3,
909 cross_hair_thickness: Value(0.003),
910 cross_hair_length: Value(0.150),
911 angle: Value(0.0),
912 }));
913 assert_partial_code!(
914 am,
915 "AMCRAZY*\n7,0,0,0.08,0.055,0.0125,45*\n6,0,0,0.125,0.01,0.01,3,0.003,0.15,0*"
916 );
917 }
918
919 #[test]
920 fn test_codegen_with_variable() {
921 let line = VectorLinePrimitive {
922 exposure: true,
923 width: Variable(0),
924 start: (Variable(1), 0.45.into()),
925 end: (Value(12.), Variable(2)),
926 angle: Variable(3),
927 };
928 assert_partial_code!(line, "20,1,$0,$1,0.45,12,$2,$3*");
929 }
930
931 #[test]
932 fn test_macro_decimal_into() {
933 let a = Value(1.0);
934 let b: MacroDecimal = 1.0.into();
935 assert_eq!(a, b);
936 let c = Variable(1);
937 let d = Variable(1);
938 assert_eq!(c, d);
939 }
940
941 #[test]
942 fn test_comment_codegen() {
943 let comment = MacroContent::Comment("hello world".to_string());
944 assert_partial_code!(comment, "0 hello world*");
945 }
946
947 #[test]
948 fn test_variable_definition_codegen() {
949 let var = VariableDefinition {
950 number: 17,
951 expression: "$40+2".to_string(),
952 };
953 assert_partial_code!(var, "$17=$40+2*");
954 }
955
956 #[test]
957 fn test_macrocontent_from_into() {
958 let a = MacroContent::Comment("hello".into());
959 let b: MacroContent = "hello".to_string().into();
960 let c: MacroContent = "hello".into();
961 assert_eq!(a, b);
962 assert_eq!(b, c);
963 }
964
965 #[test]
966 fn test_circle_primitive_new() {
967 let c1 = CirclePrimitive::new(Value(3.0)).centered_at((Value(5.0), Value(0.0)));
968 let c2 = CirclePrimitive {
969 exposure: true,
970 diameter: Value(3.0),
971 center: (Value(5.0), Value(0.0)),
972 angle: None,
973 };
974 assert_eq!(c1, c2);
975 }
976
977 #[test]
978 fn test_vectorline_primitive_new() {
979 let vl1 = VectorLinePrimitive::new((Value(0.0), Value(5.3)), (Value(3.9), Value(8.5)))
980 .with_angle(Value(38.0));
981 let vl2 = VectorLinePrimitive {
982 exposure: true,
983 width: Value(0.0),
984 start: (Value(0.0), Value(5.3)),
985 end: (Value(3.9), Value(8.5)),
986 angle: Value(38.0),
987 };
988 assert_eq!(vl1, vl2);
989 }
990
991 #[test]
992 fn test_centerline_primitive_new() {
993 let cl1 = CenterLinePrimitive::new((Value(3.0), Value(4.5))).exposure_on(false);
994 let cl2 = CenterLinePrimitive {
995 exposure: false,
996 dimensions: (Value(3.0), Value(4.5)),
997 center: (Value(0.0), Value(0.0)),
998 angle: Value(0.0),
999 };
1000 assert_eq!(cl1, cl2);
1001 }
1002
1003 #[test]
1004 fn test_outline_primitive_new() {
1005 let op1 = OutlinePrimitive::new()
1006 .add_point((Value(0.0), Value(0.0)))
1007 .add_point((Value(2.0), Value(2.0)))
1008 .add_point((Value(-2.0), Value(-2.0)))
1009 .add_point((Value(0.0), Value(0.0)));
1010
1011 let pts = vec![
1012 (Value(0.0), Value(0.0)),
1013 (Value(2.0), Value(2.0)),
1014 (Value(-2.0), Value(-2.0)),
1015 (Value(0.0), Value(0.0)),
1016 ];
1017
1018 let op2 = OutlinePrimitive {
1019 exposure: true,
1020 points: pts,
1021 angle: Value(0.0),
1022 };
1023 assert_eq!(op1, op2);
1024 }
1025
1026 #[test]
1027 fn test_polygon_primitive_new() {
1028 let pp1 = PolygonPrimitive::new(5)
1029 .with_angle(Value(98.0))
1030 .with_diameter(Value(5.3))
1031 .centered_at((Value(1.0), Value(1.0)));
1032 let pp2 = PolygonPrimitive {
1033 exposure: true,
1034 vertices: 5,
1035 angle: Value(98.0),
1036 diameter: Value(5.3),
1037 center: (Value(1.0), Value(1.0)),
1038 };
1039 assert_eq!(pp1, pp2);
1040 }
1041
1042 #[test]
1043 fn test_moire_primitive_new() {
1044 let mp1 = MoirePrimitive::new()
1045 .with_diameter(Value(3.0))
1046 .with_ring_thickness(Value(0.05))
1047 .with_cross_thickness(Value(0.01))
1048 .with_cross_length(Value(0.5))
1049 .with_rings_max(3);
1050 let mp2 = MoirePrimitive {
1051 center: (MacroDecimal::Value(0.0), MacroDecimal::Value(0.0)),
1052 diameter: MacroDecimal::Value(3.0),
1053 ring_thickness: MacroDecimal::Value(0.05),
1054 gap: MacroDecimal::Value(0.0),
1055 max_rings: 3,
1056 cross_hair_thickness: MacroDecimal::Value(0.01),
1057 cross_hair_length: MacroDecimal::Value(0.5),
1058 angle: MacroDecimal::Value(0.0),
1059 };
1060 assert_eq!(mp1, mp2);
1061 }
1062
1063 #[test]
1064 fn test_thermal_primitive_new() {
1065 let tp1 = ThermalPrimitive::new(Value(1.0), Value(2.0), Value(1.5)).with_angle(Value(87.3));
1066 let tp2 = ThermalPrimitive {
1067 inner_diameter: Value(1.0),
1068 outer_diameter: Value(2.0),
1069 gap: Value(1.5),
1070 angle: Value(87.3),
1071 center: (Value(0.0), Value(0.0)),
1072 };
1073 assert_eq!(tp1, tp2);
1074 }
1075
1076 #[test]
1077 fn test_variabledefinition_new() {
1078 let vd1 = VariableDefinition::new(3, "Test!");
1079 let vd2 = VariableDefinition {
1080 number: 3,
1081 expression: "Test!".into(),
1082 };
1083 assert_eq!(vd1, vd2);
1084 }
1085}