1use crate::types::{Coord, Layer, ParameterCollection};
7
8use super::polygon::{HatchStyle, PolygonType, PolygonVertex, PolygonVertexKind};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12#[repr(u8)]
13pub enum DisplayUnit {
14 #[default]
16 Imperial = 0,
17 Metric = 1,
19}
20
21impl DisplayUnit {
22 pub fn from_int(value: i32) -> Self {
24 match value {
25 1 => DisplayUnit::Metric,
26 _ => DisplayUnit::Imperial,
27 }
28 }
29
30 pub fn to_int(self) -> i32 {
32 self as i32
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38#[repr(u8)]
39pub enum DesignatorDisplayMode {
40 #[default]
42 Physical = 0,
43 Logical = 1,
45}
46
47impl DesignatorDisplayMode {
48 pub fn from_int(value: i32) -> Self {
50 match value {
51 1 => DesignatorDisplayMode::Logical,
52 _ => DesignatorDisplayMode::Physical,
53 }
54 }
55
56 pub fn to_int(self) -> i32 {
58 self as i32
59 }
60}
61
62#[derive(Debug, Clone, Default)]
67pub struct PcbBoard {
68 pub layer: Layer,
70 pub locked: bool,
72 pub polygon_outline: bool,
74 pub filename: String,
76 pub kind: String,
78 pub version: String,
80 pub date: String,
82 pub time: String,
84 pub origin_x: Coord,
86 pub origin_y: Coord,
88 pub big_visible_grid_size: f64,
90 pub visible_grid_size: f64,
92 pub electrical_grid_range: Coord,
94 pub electrical_grid_enabled: bool,
96 pub snap_grid_size: f64,
98 pub snap_grid_size_x: f64,
100 pub snap_grid_size_y: f64,
102 pub track_grid_size: f64,
104 pub via_grid_size: f64,
106 pub component_grid_size: f64,
108 pub component_grid_size_x: f64,
110 pub component_grid_size_y: f64,
112 pub dot_grid: bool,
114 pub display_unit: DisplayUnit,
116 pub designator_display_mode: DesignatorDisplayMode,
118 pub primitive_lock: bool,
120 pub polygon_type: PolygonType,
122 pub pour_over: bool,
124 pub remove_dead: bool,
126 pub grid_size: Coord,
128 pub track_width: Coord,
130 pub hatch_style: HatchStyle,
132 pub use_octagons: bool,
134 pub min_prim_length: Coord,
136 pub outline: Vec<PolygonVertex>,
138 pub params: ParameterCollection,
140}
141
142impl PcbBoard {
143 pub fn from_params(params: &ParameterCollection) -> Self {
145 let mut board = Self {
146 layer: params
147 .get("LAYER")
148 .map(|v| v.as_layer())
149 .unwrap_or_default(),
150 locked: params
151 .get("LOCKED")
152 .map(|v| v.as_bool_or(false))
153 .unwrap_or(false),
154 polygon_outline: params
155 .get("POLYGONOUTLINE")
156 .map(|v| v.as_bool_or(false))
157 .unwrap_or(false),
158 filename: params
159 .get("FILENAME")
160 .map(|v| v.as_str().to_string())
161 .unwrap_or_default(),
162 kind: params
163 .get("KIND")
164 .map(|v| v.as_str().to_string())
165 .unwrap_or_default(),
166 version: params
167 .get("VERSION")
168 .map(|v| v.as_str().to_string())
169 .unwrap_or_default(),
170 date: params
171 .get("DATE")
172 .map(|v| v.as_str().to_string())
173 .unwrap_or_default(),
174 time: params
175 .get("TIME")
176 .map(|v| v.as_str().to_string())
177 .unwrap_or_default(),
178 origin_x: params
179 .get("ORIGINX")
180 .and_then(|v| v.as_coord().ok())
181 .unwrap_or_default(),
182 origin_y: params
183 .get("ORIGINY")
184 .and_then(|v| v.as_coord().ok())
185 .unwrap_or_default(),
186 big_visible_grid_size: params
187 .get("BIGVISIBLEGRIDSIZE")
188 .map(|v| v.as_double_or(0.0))
189 .unwrap_or(0.0),
190 visible_grid_size: params
191 .get("VISIBLEGRIDSIZE")
192 .map(|v| v.as_double_or(0.0))
193 .unwrap_or(0.0),
194 electrical_grid_range: params
195 .get("ELECTRICALGRIDRANGE")
196 .and_then(|v| v.as_coord().ok())
197 .unwrap_or_default(),
198 electrical_grid_enabled: params
199 .get("ELECTRICALGRIDENABLED")
200 .map(|v| v.as_bool_or(true))
201 .unwrap_or(true),
202 snap_grid_size: params
203 .get("SNAPGRIDSIZE")
204 .map(|v| v.as_double_or(0.0))
205 .unwrap_or(0.0),
206 snap_grid_size_x: params
207 .get("SNAPGRIDSIZEX")
208 .map(|v| v.as_double_or(0.0))
209 .unwrap_or(0.0),
210 snap_grid_size_y: params
211 .get("SNAPGRIDSIZEY")
212 .map(|v| v.as_double_or(0.0))
213 .unwrap_or(0.0),
214 track_grid_size: params
215 .get("TRACKGRIDSIZE")
216 .map(|v| v.as_double_or(0.0))
217 .unwrap_or(0.0),
218 via_grid_size: params
219 .get("VIAGRIDSIZE")
220 .map(|v| v.as_double_or(0.0))
221 .unwrap_or(0.0),
222 component_grid_size: params
223 .get("COMPONENTGRIDSIZE")
224 .map(|v| v.as_double_or(0.0))
225 .unwrap_or(0.0),
226 component_grid_size_x: params
227 .get("COMPONENTGRIDSIZEX")
228 .map(|v| v.as_double_or(0.0))
229 .unwrap_or(0.0),
230 component_grid_size_y: params
231 .get("COMPONENTGRIDSIZEY")
232 .map(|v| v.as_double_or(0.0))
233 .unwrap_or(0.0),
234 dot_grid: params
235 .get("DOTGRID")
236 .map(|v| v.as_bool_or(true))
237 .unwrap_or(true),
238 display_unit: params
239 .get("DISPLAYUNIT")
240 .map(|v| DisplayUnit::from_int(v.as_int_or(0)))
241 .unwrap_or_default(),
242 designator_display_mode: params
243 .get("DESIGNATORDISPLAYMODE")
244 .map(|v| DesignatorDisplayMode::from_int(v.as_int_or(0)))
245 .unwrap_or_default(),
246 primitive_lock: params
247 .get("PRIMITIVELOCK")
248 .map(|v| v.as_bool_or(false))
249 .unwrap_or(false),
250 polygon_type: params
251 .get("POLYGONTYPE")
252 .map(|v| PolygonType::parse(v.as_str()))
253 .unwrap_or_default(),
254 pour_over: params
255 .get("POUROVER")
256 .map(|v| v.as_bool_or(false))
257 .unwrap_or(false),
258 remove_dead: params
259 .get("REMOVEDEAD")
260 .map(|v| v.as_bool_or(false))
261 .unwrap_or(false),
262 grid_size: params
263 .get("GRIDSIZE")
264 .and_then(|v| v.as_coord().ok())
265 .unwrap_or_default(),
266 track_width: params
267 .get("TRACKWIDTH")
268 .and_then(|v| v.as_coord().ok())
269 .unwrap_or_default(),
270 hatch_style: params
271 .get("HATCHSTYLE")
272 .map(|v| HatchStyle::parse(v.as_str()))
273 .unwrap_or_default(),
274 use_octagons: params
275 .get("USEOCTAGONS")
276 .map(|v| v.as_bool_or(false))
277 .unwrap_or(false),
278 min_prim_length: params
279 .get("MINPRIMLENGTH")
280 .and_then(|v| v.as_coord().ok())
281 .unwrap_or_default(),
282 outline: Vec::new(),
283 params: params.clone(),
284 };
285
286 let mut idx = 0;
288 loop {
289 let vx_key = format!("VX{}", idx);
290 let vy_key = format!("VY{}", idx);
291
292 if !params.contains(&vx_key) {
293 break;
294 }
295
296 let vertex = PolygonVertex {
297 kind: params
298 .get(&format!("KIND{}", idx))
299 .map(|v| PolygonVertexKind::from_int(v.as_int_or(0)))
300 .unwrap_or_default(),
301 x: params
302 .get(&vx_key)
303 .and_then(|v| v.as_coord().ok())
304 .unwrap_or_default(),
305 y: params
306 .get(&vy_key)
307 .and_then(|v| v.as_coord().ok())
308 .unwrap_or_default(),
309 center_x: params
310 .get(&format!("CX{}", idx))
311 .and_then(|v| v.as_coord().ok())
312 .unwrap_or_default(),
313 center_y: params
314 .get(&format!("CY{}", idx))
315 .and_then(|v| v.as_coord().ok())
316 .unwrap_or_default(),
317 start_angle: params
318 .get(&format!("SA{}", idx))
319 .map(|v| v.as_double_or(0.0))
320 .unwrap_or(0.0),
321 end_angle: params
322 .get(&format!("EA{}", idx))
323 .map(|v| v.as_double_or(0.0))
324 .unwrap_or(0.0),
325 radius: params
326 .get(&format!("R{}", idx))
327 .and_then(|v| v.as_coord().ok())
328 .unwrap_or_default(),
329 };
330
331 board.outline.push(vertex);
332 idx += 1;
333 }
334
335 board
336 }
337
338 pub fn to_params(&self) -> ParameterCollection {
340 let mut params = self.params.clone();
341
342 params.add("LAYER", &self.layer.to_string());
343 params.add("LOCKED", if self.locked { "TRUE" } else { "FALSE" });
344 params.add(
345 "POLYGONOUTLINE",
346 if self.polygon_outline {
347 "TRUE"
348 } else {
349 "FALSE"
350 },
351 );
352 params.add("FILENAME", &self.filename);
353 params.add("KIND", &self.kind);
354 params.add("VERSION", &self.version);
355 params.add("DATE", &self.date);
356 params.add("TIME", &self.time);
357 params.add_coord("ORIGINX", self.origin_x);
358 params.add_coord("ORIGINY", self.origin_y);
359 params.add_double("BIGVISIBLEGRIDSIZE", self.big_visible_grid_size, 3);
360 params.add_double("VISIBLEGRIDSIZE", self.visible_grid_size, 3);
361 params.add_coord("ELECTRICALGRIDRANGE", self.electrical_grid_range);
362 params.add(
363 "ELECTRICALGRIDENABLED",
364 if self.electrical_grid_enabled {
365 "TRUE"
366 } else {
367 "FALSE"
368 },
369 );
370 params.add_double("SNAPGRIDSIZE", self.snap_grid_size, 6);
371 params.add_double("SNAPGRIDSIZEX", self.snap_grid_size_x, 6);
372 params.add_double("SNAPGRIDSIZEY", self.snap_grid_size_y, 6);
373 params.add_double("TRACKGRIDSIZE", self.track_grid_size, 6);
374 params.add_double("VIAGRIDSIZE", self.via_grid_size, 6);
375 params.add_double("COMPONENTGRIDSIZE", self.component_grid_size, 6);
376 params.add_double("COMPONENTGRIDSIZEX", self.component_grid_size_x, 6);
377 params.add_double("COMPONENTGRIDSIZEY", self.component_grid_size_y, 6);
378 params.add("DOTGRID", if self.dot_grid { "TRUE" } else { "FALSE" });
379 params.add_int("DISPLAYUNIT", self.display_unit.to_int());
380 params.add_int(
381 "DESIGNATORDISPLAYMODE",
382 self.designator_display_mode.to_int(),
383 );
384 params.add(
385 "PRIMITIVELOCK",
386 if self.primitive_lock { "TRUE" } else { "FALSE" },
387 );
388 params.add("POLYGONTYPE", self.polygon_type.as_str());
389 params.add("POUROVER", if self.pour_over { "TRUE" } else { "FALSE" });
390 params.add(
391 "REMOVEDEAD",
392 if self.remove_dead { "TRUE" } else { "FALSE" },
393 );
394 params.add_coord("GRIDSIZE", self.grid_size);
395 params.add_coord("TRACKWIDTH", self.track_width);
396 params.add("HATCHSTYLE", self.hatch_style.as_str());
397 params.add(
398 "USEOCTAGONS",
399 if self.use_octagons { "TRUE" } else { "FALSE" },
400 );
401 params.add_coord("MINPRIMLENGTH", self.min_prim_length);
402
403 for (idx, vertex) in self.outline.iter().enumerate() {
405 params.add_int(&format!("KIND{}", idx), vertex.kind.to_int());
406 params.add_coord(&format!("VX{}", idx), vertex.x);
407 params.add_coord(&format!("VY{}", idx), vertex.y);
408 params.add_coord(&format!("CX{}", idx), vertex.center_x);
409 params.add_coord(&format!("CY{}", idx), vertex.center_y);
410 params.add_double(&format!("SA{}", idx), vertex.start_angle, 14);
411 params.add_double(&format!("EA{}", idx), vertex.end_angle, 14);
412 params.add_coord(&format!("R{}", idx), vertex.radius);
413 }
414
415 params
416 }
417
418 pub fn outline_vertex_count(&self) -> usize {
420 self.outline.len()
421 }
422
423 pub fn is_metric(&self) -> bool {
425 matches!(self.display_unit, DisplayUnit::Metric)
426 }
427}