maker_panel/
features.rs

1//! Components which compose a panel.
2
3use dyn_clone::DynClone;
4use geo::{Coordinate, MultiPolygon};
5use std::fmt;
6
7mod array;
8mod circle;
9mod mechanical_solder_point;
10mod negative;
11mod pos;
12mod r_mount;
13mod rect;
14pub mod repeating;
15mod rotate;
16mod screw_hole;
17mod smiley;
18mod triangle;
19mod unit;
20pub use array::Column;
21pub use circle::Circle;
22pub use mechanical_solder_point::MechanicalSolderPoint;
23pub use negative::Negative;
24pub use pos::{AtPos, Positioning};
25pub use r_mount::RMount;
26pub use rect::Rect;
27pub use rotate::Rotate;
28pub use screw_hole::ScrewHole;
29pub use smiley::Smiley;
30pub use triangle::Triangle;
31pub use unit::Unit;
32
33/// Specifies geometry interior to the bounds of the panel.
34pub trait InnerFeature: fmt::Display + DynClone + fmt::Debug {
35    fn name(&self) -> &'static str;
36    fn translate(&mut self, v: Coordinate<f64>);
37    fn atoms(&self) -> Vec<InnerAtom>;
38}
39
40dyn_clone::clone_trait_object!(InnerFeature);
41
42impl<'a> InnerFeature for Box<dyn InnerFeature + 'a> {
43    fn name(&self) -> &'static str {
44        self.as_ref().name()
45    }
46
47    fn translate(&mut self, v: Coordinate<f64>) {
48        self.as_mut().translate(v)
49    }
50
51    fn atoms(&self) -> Vec<InnerAtom> {
52        self.as_ref().atoms()
53    }
54}
55
56/// A top-level unit that makes up the geometry of the panel.
57pub trait Feature: fmt::Display + DynClone + fmt::Debug {
58    /// Human-readable name describing the construction.
59    fn name(&self) -> &'static str;
60    /// Adjust all coordinates by the specified amount. Should
61    /// affect all geometries returned from [`Feature::edge_union`],
62    /// [`Feature::edge_subtract`], and [`Feature::interior`].
63    fn translate(&mut self, v: Coordinate<f64>);
64
65    /// Returns the outer geometry describing the boundaries of the
66    /// panel, which should be unioned with the outer geometry of all
67    /// other features.
68    fn edge_union(&self) -> Option<MultiPolygon<f64>>;
69
70    /// Returns the inner geometry describing features on the panel,
71    /// within the bounds of the computed edge geometry.
72    fn interior(&self) -> Vec<InnerAtom>;
73
74    /// Returns the outer geometry describing the boundaries of the
75    /// panel, which should be subtracted from the outer geometry of all
76    /// other features.
77    fn edge_subtract(&self) -> Option<MultiPolygon<f64>> {
78        None
79    }
80}
81
82dyn_clone::clone_trait_object!(Feature);
83
84impl<'a> Feature for Box<dyn Feature + 'a> {
85    fn name(&self) -> &'static str {
86        self.as_ref().name()
87    }
88
89    fn translate(&mut self, v: Coordinate<f64>) {
90        self.as_mut().translate(v)
91    }
92
93    fn edge_union(&self) -> Option<MultiPolygon<f64>> {
94        self.as_ref().edge_union()
95    }
96
97    fn interior(&self) -> Vec<InnerAtom> {
98        self.as_ref().interior()
99    }
100
101    fn edge_subtract(&self) -> Option<MultiPolygon<f64>> {
102        self.as_ref().edge_subtract()
103    }
104}
105
106/// The smallest geometries from which inner features are composed.
107#[derive(Debug, Clone)]
108pub enum InnerAtom {
109    Drill {
110        center: Coordinate<f64>,
111        radius: f64,
112        plated: bool,
113    },
114    Circle {
115        center: Coordinate<f64>,
116        radius: f64,
117        layer: super::Layer,
118    },
119    Rect {
120        rect: geo::Rect<f64>,
121        layer: super::Layer,
122    },
123    VScoreH(f64),
124    VScoreV(f64),
125}
126
127impl InnerAtom {
128    pub fn stroke(&self) -> Option<usvg::Stroke> {
129        match self {
130            InnerAtom::VScoreH(_) | InnerAtom::VScoreV(_) => Some(usvg::Stroke {
131                paint: usvg::Paint::Color(usvg::Color::new(0x6, 0x6, 0x6)),
132                width: usvg::StrokeWidth::new(0.1),
133                opacity: usvg::Opacity::new(0.5),
134                dasharray: Some(vec![0.8, 0.8]),
135                ..usvg::Stroke::default()
136            }),
137            _ => None,
138        }
139    }
140
141    pub fn fill(&self) -> Option<usvg::Fill> {
142        match self {
143            InnerAtom::Drill { .. } => Some(usvg::Fill {
144                paint: usvg::Paint::Color(usvg::Color::new(0x25, 0x25, 0x25)),
145                ..usvg::Fill::default()
146            }),
147            InnerAtom::Circle { layer, .. } => Some(usvg::Fill {
148                paint: usvg::Paint::Color(layer.color()),
149                ..usvg::Fill::default()
150            }),
151            InnerAtom::Rect { layer, .. } => Some(usvg::Fill {
152                paint: usvg::Paint::Color(layer.color()),
153                ..usvg::Fill::default()
154            }),
155            InnerAtom::VScoreH(_) | InnerAtom::VScoreV(_) => None,
156        }
157    }
158
159    pub fn bounds(&self) -> Option<geo::Rect<f64>> {
160        match self {
161            InnerAtom::Drill { center, radius, .. } => Some(geo::Rect::new(
162                Coordinate {
163                    x: center.x - radius,
164                    y: center.y - radius,
165                },
166                Coordinate {
167                    x: center.x + radius,
168                    y: center.y + radius,
169                },
170            )),
171            InnerAtom::Circle { center, radius, .. } => Some(geo::Rect::new(
172                Coordinate {
173                    x: center.x - radius,
174                    y: center.y - radius,
175                },
176                Coordinate {
177                    x: center.x + radius,
178                    y: center.y + radius,
179                },
180            )),
181            InnerAtom::Rect { rect, .. } => Some(rect.clone()),
182            InnerAtom::VScoreH(_) | InnerAtom::VScoreV(_) => None,
183        }
184    }
185
186    pub fn translate(&mut self, x: f64, y: f64) {
187        match self {
188            InnerAtom::Drill { ref mut center, .. } => {
189                *center = *center + Coordinate { x, y };
190            }
191            InnerAtom::Circle { center, .. } => {
192                *center = *center + Coordinate { x, y };
193            }
194            InnerAtom::Rect { rect, .. } => {
195                use geo::algorithm::translate::Translate;
196                rect.translate_inplace(x, y);
197            }
198            InnerAtom::VScoreH(ref mut y2) => {
199                *y2 = *y2 + y;
200            }
201            InnerAtom::VScoreV(ref mut x2) => {
202                *x2 = *x2 + x;
203            }
204        }
205    }
206}