Skip to main content

altium_format/records/pcb/
component.rs

1//! PCB Component (footprint) container.
2
3use super::{PcbPad, PcbRecord};
4use crate::types::{Coord, CoordRect, ParameterCollection};
5
6/// A PCB component (footprint) containing primitives.
7#[derive(Debug, Clone, Default)]
8pub struct PcbComponent {
9    /// Footprint pattern name.
10    pub pattern: String,
11    /// Description.
12    pub description: String,
13    /// Height.
14    pub height: Coord,
15    /// Item GUID.
16    pub item_guid: String,
17    /// Revision GUID.
18    pub revision_guid: String,
19    /// Primitives belonging to this component.
20    pub primitives: Vec<PcbRecord>,
21}
22
23impl PcbComponent {
24    /// Import component data from parameters.
25    pub fn import_from_parameters(&mut self, p: &ParameterCollection) {
26        self.pattern = p
27            .get("PATTERN")
28            .map(|v| v.as_str().to_string())
29            .unwrap_or_default();
30        self.height = Coord::from_raw(p.get("HEIGHT").map(|v| v.as_int_or(0)).unwrap_or(0));
31        self.description = p
32            .get("DESCRIPTION")
33            .map(|v| v.as_str().to_string())
34            .unwrap_or_default();
35        self.item_guid = p
36            .get("ITEMGUID")
37            .map(|v| v.as_str().to_string())
38            .unwrap_or_default();
39        self.revision_guid = p
40            .get("REVISIONGUID")
41            .map(|v| v.as_str().to_string())
42            .unwrap_or_default();
43    }
44
45    /// Export component data to parameters.
46    pub fn export_to_parameters(&self) -> ParameterCollection {
47        let mut params = ParameterCollection::new();
48        params.add("PATTERN", &self.pattern);
49        params.add_int("HEIGHT", self.height.to_raw());
50        if !self.description.is_empty() {
51            params.add("DESCRIPTION", &self.description);
52        }
53        if !self.item_guid.is_empty() {
54            params.add("ITEMGUID", &self.item_guid);
55        }
56        if !self.revision_guid.is_empty() {
57            params.add("REVISIONGUID", &self.revision_guid);
58        }
59        params
60    }
61
62    /// Get the component name (pattern).
63    pub fn name(&self) -> &str {
64        &self.pattern
65    }
66
67    /// Count the number of pads.
68    pub fn pad_count(&self) -> usize {
69        self.primitives
70            .iter()
71            .filter(|p| matches!(p, PcbRecord::Pad(_)))
72            .count()
73    }
74
75    /// Get all pads.
76    pub fn pads(&self) -> impl Iterator<Item = &PcbPad> {
77        self.primitives.iter().filter_map(|p| {
78            if let PcbRecord::Pad(pad) = p {
79                Some(pad.as_ref())
80            } else {
81                None
82            }
83        })
84    }
85
86    /// Total primitive count.
87    pub fn primitive_count(&self) -> usize {
88        self.primitives.len()
89    }
90
91    /// Calculate the bounding rectangle of all primitives.
92    pub fn calculate_bounds(&self) -> CoordRect {
93        let mut bounds = CoordRect::default();
94        let mut first = true;
95
96        for prim in &self.primitives {
97            let prim_bounds = match prim {
98                PcbRecord::Arc(r) => r.calculate_bounds(),
99                PcbRecord::Pad(r) => r.calculate_bounds(),
100                PcbRecord::Via(r) => r.calculate_bounds(),
101                PcbRecord::Track(r) => r.calculate_bounds(),
102                PcbRecord::Text(r) => r.calculate_bounds(),
103                PcbRecord::Fill(r) => r.calculate_bounds(),
104                PcbRecord::Region(r) => r.calculate_bounds(),
105                PcbRecord::ComponentBody(r) => r.calculate_bounds(),
106                PcbRecord::Polygon(_) => continue, // Polygons don't belong to components
107                PcbRecord::Unknown { .. } => continue,
108            };
109
110            if first {
111                bounds = prim_bounds;
112                first = false;
113            } else {
114                bounds = bounds.union(prim_bounds);
115            }
116        }
117
118        bounds
119    }
120}