altium_format/records/pcb/
component_body.rs1use std::io::Read;
4
5use altium_format_derive::AltiumRecord;
6
7use super::{PcbOutline, primitive::PcbPrimitiveCommon};
8use crate::error::Result;
9use crate::traits::FromBinary;
10use crate::types::{Coord, CoordPoint, CoordRect, ParameterCollection};
11
12#[derive(Debug, Clone, Default)]
14pub struct PcbComponentBody {
15 pub common: PcbPrimitiveCommon,
17 pub outline: Vec<CoordPoint>,
19 pub v7_layer: String,
21 pub name: String,
23 pub kind: i32,
25 pub sub_poly_index: i32,
27 pub union_index: i32,
29 pub arc_resolution: Coord,
31 pub is_shape_based: bool,
33 pub standoff_height: Coord,
35 pub overall_height: Coord,
37 pub body_projection: i32,
39 pub body_color_3d: i32,
41 pub body_opacity_3d: f64,
43 pub unique_id: String,
45 pub texture: String,
47 pub texture_center: CoordPoint,
49 pub texture_size: CoordPoint,
51 pub texture_rotation: f64,
53 pub model_id: String,
55 pub model_checksum: i32,
57 pub model_embed: bool,
59 pub model_2d_location: CoordPoint,
61 pub model_2d_rotation: f64,
63 pub model_3d_rot_x: f64,
65 pub model_3d_rot_y: f64,
67 pub model_3d_rot_z: f64,
69 pub model_3d_dz: Coord,
71 pub model_snap_count: i32,
73 pub model_type: i32,
75 pub step_model: Option<String>,
77}
78
79#[derive(Debug, Clone, Default, AltiumRecord)]
80#[altium(format = "binary")]
81struct PcbComponentBodyBinary {
82 #[altium(flatten)]
83 common: PcbPrimitiveCommon,
84 _unknown1: u32,
85 _unknown2: u8,
86 parameters: ParameterCollection,
87 outline: PcbOutline,
88}
89
90impl FromBinary for PcbComponentBody {
91 fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
92 let raw = <PcbComponentBodyBinary as FromBinary>::read_from(reader)?;
93 let mut body = PcbComponentBody {
94 common: raw.common,
95 outline: raw.outline.into(),
96 ..Default::default()
97 };
98 body.import_from_parameters(&raw.parameters);
99 Ok(body)
100 }
101}
102
103impl PcbComponentBody {
104 fn import_from_parameters(&mut self, p: &ParameterCollection) {
106 self.v7_layer = p
107 .get("V7_LAYER")
108 .map(|v| v.as_str().to_string())
109 .unwrap_or_default();
110 self.name = p
111 .get("NAME")
112 .map(|v| v.as_str().to_string())
113 .unwrap_or_default();
114 self.kind = p.get("KIND").map(|v| v.as_int_or(0)).unwrap_or(0);
115 self.sub_poly_index = p.get("SUBPOLYINDEX").map(|v| v.as_int_or(-1)).unwrap_or(-1);
116 self.union_index = p.get("UNIONINDEX").map(|v| v.as_int_or(0)).unwrap_or(0);
117 self.arc_resolution =
118 Coord::from_raw(p.get("ARCRESOLUTION").map(|v| v.as_int_or(0)).unwrap_or(0));
119 self.is_shape_based = p
120 .get("ISSHAPEBASED")
121 .map(|v| v.as_bool_or(false))
122 .unwrap_or(false);
123 self.standoff_height =
124 Coord::from_raw(p.get("STANDOFFHEIGHT").map(|v| v.as_int_or(0)).unwrap_or(0));
125 self.overall_height =
126 Coord::from_raw(p.get("OVERALLHEIGHT").map(|v| v.as_int_or(0)).unwrap_or(0));
127 self.body_projection = p.get("BODYPROJECTION").map(|v| v.as_int_or(0)).unwrap_or(0);
128 self.body_color_3d = p
129 .get("BODYCOLOR3D")
130 .map(|v| v.as_int_or(14737632))
131 .unwrap_or(14737632);
132 self.body_opacity_3d = p
133 .get("BODYOPACITY3D")
134 .map(|v| v.as_double_or(1.0))
135 .unwrap_or(1.0);
136
137 if let Some(id_str) = p.get("IDENTIFIER") {
138 let chars: Vec<char> = id_str
139 .as_str()
140 .split(',')
141 .filter_map(|s| s.parse::<u32>().ok())
142 .filter_map(char::from_u32)
143 .collect();
144 self.unique_id = chars.into_iter().collect();
145 }
146
147 self.texture = p
148 .get("TEXTURE")
149 .map(|v| v.as_str().to_string())
150 .unwrap_or_default();
151 self.texture_center = CoordPoint::from_raw(
152 p.get("TEXTURECENTERX").map(|v| v.as_int_or(0)).unwrap_or(0),
153 p.get("TEXTURECENTERY").map(|v| v.as_int_or(0)).unwrap_or(0),
154 );
155 self.texture_size = CoordPoint::from_raw(
156 p.get("TEXTURESIZEX").map(|v| v.as_int_or(0)).unwrap_or(0),
157 p.get("TEXTURESIZEY").map(|v| v.as_int_or(0)).unwrap_or(0),
158 );
159 self.texture_rotation = p
160 .get("TEXTUREROTATION")
161 .map(|v| v.as_double_or(0.0))
162 .unwrap_or(0.0);
163 self.model_id = p
164 .get("MODELID")
165 .map(|v| v.as_str().to_string())
166 .unwrap_or_default();
167 self.model_checksum = p.get("MODEL.CHECKSUM").map(|v| v.as_int_or(0)).unwrap_or(0);
168 self.model_embed = p
169 .get("MODEL.EMBED")
170 .map(|v| v.as_bool_or(true))
171 .unwrap_or(true);
172 self.model_2d_location = CoordPoint::from_raw(
173 p.get("MODEL.2D.X").map(|v| v.as_int_or(0)).unwrap_or(0),
174 p.get("MODEL.2D.Y").map(|v| v.as_int_or(0)).unwrap_or(0),
175 );
176 self.model_2d_rotation = p
177 .get("MODEL.2D.ROTATION")
178 .map(|v| v.as_double_or(0.0))
179 .unwrap_or(0.0);
180 self.model_3d_rot_x = p
181 .get("MODEL.3D.ROTX")
182 .map(|v| v.as_double_or(0.0))
183 .unwrap_or(0.0);
184 self.model_3d_rot_y = p
185 .get("MODEL.3D.ROTY")
186 .map(|v| v.as_double_or(0.0))
187 .unwrap_or(0.0);
188 self.model_3d_rot_z = p
189 .get("MODEL.3D.ROTZ")
190 .map(|v| v.as_double_or(0.0))
191 .unwrap_or(0.0);
192 self.model_3d_dz =
193 Coord::from_raw(p.get("MODEL.3D.DZ").map(|v| v.as_int_or(0)).unwrap_or(0));
194 self.model_snap_count = p
195 .get("MODEL.SNAPCOUNT")
196 .map(|v| v.as_int_or(0))
197 .unwrap_or(0);
198 self.model_type = p
199 .get("MODEL.MODELTYPE")
200 .map(|v| v.as_int_or(1))
201 .unwrap_or(1);
202 }
203
204 pub fn calculate_bounds(&self) -> CoordRect {
206 if self.outline.is_empty() {
207 return CoordRect::default();
208 }
209
210 let min_x = self.outline.iter().map(|p| p.x.to_raw()).min().unwrap_or(0);
211 let max_x = self.outline.iter().map(|p| p.x.to_raw()).max().unwrap_or(0);
212 let min_y = self.outline.iter().map(|p| p.y.to_raw()).min().unwrap_or(0);
213 let max_y = self.outline.iter().map(|p| p.y.to_raw()).max().unwrap_or(0);
214
215 CoordRect::from_raw(min_x, min_y, max_x - min_x, max_y - min_y)
216 }
217
218 fn export_to_parameters(&self) -> ParameterCollection {
220 let mut p = ParameterCollection::new();
221
222 if !self.v7_layer.is_empty() {
223 p.add("V7_LAYER", &self.v7_layer);
224 }
225 if !self.name.is_empty() {
226 p.add("NAME", &self.name);
227 }
228 p.add_int("KIND", self.kind);
229 p.add_int("SUBPOLYINDEX", self.sub_poly_index);
230 p.add_int("UNIONINDEX", self.union_index);
231 p.add_int("ARCRESOLUTION", self.arc_resolution.to_raw());
232 p.add("ISSHAPEBASED", if self.is_shape_based { "T" } else { "F" });
233 p.add_int("STANDOFFHEIGHT", self.standoff_height.to_raw());
234 p.add_int("OVERALLHEIGHT", self.overall_height.to_raw());
235 p.add_int("BODYPROJECTION", self.body_projection);
236 p.add_int("BODYCOLOR3D", self.body_color_3d);
237 p.add("BODYOPACITY3D", &format!("{}", self.body_opacity_3d));
238
239 if !self.unique_id.is_empty() {
240 let codes: Vec<String> = self
241 .unique_id
242 .chars()
243 .map(|c| (c as u32).to_string())
244 .collect();
245 p.add("IDENTIFIER", &codes.join(","));
246 }
247
248 if !self.texture.is_empty() {
249 p.add("TEXTURE", &self.texture);
250 }
251 p.add_int("TEXTURECENTERX", self.texture_center.x.to_raw());
252 p.add_int("TEXTURECENTERY", self.texture_center.y.to_raw());
253 p.add_int("TEXTURESIZEX", self.texture_size.x.to_raw());
254 p.add_int("TEXTURESIZEY", self.texture_size.y.to_raw());
255 p.add("TEXTUREROTATION", &format!("{}", self.texture_rotation));
256
257 if !self.model_id.is_empty() {
258 p.add("MODELID", &self.model_id);
259 }
260 p.add_int("MODEL.CHECKSUM", self.model_checksum);
261 p.add("MODEL.EMBED", if self.model_embed { "T" } else { "F" });
262 p.add_int("MODEL.2D.X", self.model_2d_location.x.to_raw());
263 p.add_int("MODEL.2D.Y", self.model_2d_location.y.to_raw());
264 p.add("MODEL.2D.ROTATION", &format!("{}", self.model_2d_rotation));
265 p.add("MODEL.3D.ROTX", &format!("{}", self.model_3d_rot_x));
266 p.add("MODEL.3D.ROTY", &format!("{}", self.model_3d_rot_y));
267 p.add("MODEL.3D.ROTZ", &format!("{}", self.model_3d_rot_z));
268 p.add_int("MODEL.3D.DZ", self.model_3d_dz.to_raw());
269 p.add_int("MODEL.SNAPCOUNT", self.model_snap_count);
270 p.add_int("MODEL.MODELTYPE", self.model_type);
271
272 p
273 }
274}
275
276use crate::traits::ToBinary;
277use std::io::Write;
278
279impl ToBinary for PcbComponentBody {
280 fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
281 use byteorder::{LittleEndian, WriteBytesExt};
282
283 self.common.write_to(writer)?;
285
286 writer.write_u32::<LittleEndian>(0)?; writer.write_u8(0)?; let params = self.export_to_parameters();
292 params.write_to(writer)?;
293
294 let outline: PcbOutline = self.outline.clone().into();
296 outline.write_to(writer)?;
297
298 Ok(())
299 }
300
301 fn binary_size(&self) -> usize {
302 let outline: PcbOutline = self.outline.clone().into();
303 let params = self.export_to_parameters();
304 13 + 5 + params.binary_size() + outline.binary_size()
305 }
306}