1use nom::branch::alt;
2use nom::sequence::delimited;
3
4use crate::point::{Point, point};
5use crate::primitives::ws;
6use crate::{parse_section, ws_separated};
7use nom::IResult;
8use nom::Parser;
9use nom::bytes::complete::{is_not, tag};
10use nom::character::complete::not_line_ending;
11use nom::multi::many1;
12use nom::number::complete::float;
13
14#[derive(Clone, Debug, PartialEq, Default, PartialOrd)]
21pub struct BoardPanelOutline {
22 pub owner: String, pub thickness: f32,
24 pub outline: Vec<Point>,
25}
26
27pub fn parse_board_panel_outline(input: &str) -> IResult<&str, BoardPanelOutline> {
28 fn interior_contents(input: &str) -> IResult<&str, (&str, f32, Vec<Point>)> {
29 (ws(owner), ws(float), ws(many1(ws(point)))).parse(input)
30 }
31
32 let (remaining, (owner, thickness, outline)) = ws(alt((
33 parse_section!("BOARD_OUTLINE", interior_contents),
34 parse_section!("PANEL_OUTLINE", interior_contents),
35 )))
36 .parse(input)?;
37
38 Ok((
39 remaining,
40 BoardPanelOutline {
41 owner: owner.to_string(),
42 thickness,
43 outline,
44 },
45 ))
46}
47
48#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
55pub struct OtherOutline {
56 pub owner: String, pub id: String,
58 pub extrude_thickness: f32,
59 pub board_side: String, pub outline: Vec<Point>,
61}
62
63pub fn parse_other_outline(input: &str) -> IResult<&str, OtherOutline> {
64 let (remaining, (owner, id, extrude_thickness, board_side, outline)) = parse_section!(
65 "OTHER_OUTLINE",
66 ws_separated!((
67 owner, is_not(" "), float, not_line_ending, many1(ws(point)) ))
73 )
74 .parse(input)?;
75
76 Ok((
77 remaining,
78 OtherOutline {
79 owner: owner.to_string(),
80 id: id.to_string(),
81 extrude_thickness,
82 board_side: board_side.to_string(),
83 outline,
84 },
85 ))
86}
87
88#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
96pub struct RoutingOutline {
97 pub owner: String, pub routing_layers: String, pub outline: Vec<Point>,
100}
101
102pub fn parse_routing_outline(input: &str) -> IResult<&str, RoutingOutline> {
103 let (remaining, (owner, routing_layers, outline)) = parse_section!(
104 "ROUTE_OUTLINE",
105 ws_separated!((owner, not_line_ending, many1(ws(point))))
106 )
107 .parse(input)?;
108
109 Ok((
110 remaining,
111 RoutingOutline {
112 owner: owner.to_string(),
113 routing_layers: routing_layers.to_string(),
114 outline,
115 },
116 ))
117}
118
119#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
127pub struct PlacementOutline {
128 pub owner: String, pub board_side: String, pub outline_height: f32, pub outline: Vec<Point>,
132}
133
134pub fn parse_placement_outline(input: &str) -> IResult<&str, PlacementOutline> {
135 let (remaining, (owner, board_side, outline_height, outline)) = parse_section!(
136 "PLACE_OUTLINE",
137 ws_separated!((
138 owner, is_not(" "), float, many1(ws(point)) ))
143 )
144 .parse(input)?;
145
146 Ok((
147 remaining,
148 PlacementOutline {
149 owner: owner.to_string(),
150 board_side: board_side.to_string(),
151 outline_height,
152 outline,
153 },
154 ))
155}
156
157#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
166pub struct RoutingKeepout {
167 pub owner: String, pub routing_layers: String, pub outline: Vec<Point>,
170}
171
172pub fn parse_routing_keepout(input: &str) -> IResult<&str, RoutingKeepout> {
173 let (remaining, (owner, routing_layers, outline)) = parse_section!(
174 "ROUTE_KEEPOUT",
175 ws_separated!((owner, not_line_ending, many1(ws(point))))
176 )
177 .parse(input)?;
178
179 Ok((
180 remaining,
181 RoutingKeepout {
182 owner: owner.to_string(),
183 routing_layers: routing_layers.to_string(),
184 outline,
185 },
186 ))
187}
188
189#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
198pub struct ViaKeepout {
199 pub owner: String, pub outline: Vec<Point>,
201}
202
203pub fn parse_via_keepout(input: &str) -> IResult<&str, ViaKeepout> {
204 let (remaining, (owner, outline)) =
205 parse_section!("VIA_KEEPOUT", ws_separated!((owner, many1(ws(point))))).parse(input)?;
206
207 Ok((
208 remaining,
209 ViaKeepout {
210 owner: owner.to_string(),
211 outline,
212 },
213 ))
214}
215
216#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
227pub struct PlacementKeepout {
228 pub owner: String, pub board_side: String, pub keepout_height: f32, pub outline: Vec<Point>,
232}
233
234pub fn parse_placement_keepout(input: &str) -> IResult<&str, PlacementKeepout> {
235 let (remaining, (owner, board_side, keepout_height, outline)) = parse_section!(
236 "PLACE_KEEPOUT",
237 ws_separated!((
238 owner,
239 is_not(" "), float, many1(ws(point)) ))
243 )
244 .parse(input)?;
245
246 Ok((
247 remaining,
248 PlacementKeepout {
249 owner: owner.to_string(),
250 board_side: board_side.to_string(),
251 keepout_height,
252 outline,
253 },
254 ))
255}
256
257#[derive(Debug, PartialEq, Clone, Default, PartialOrd)]
266pub struct PlacementGroupArea {
267 pub owner: String, pub board_side: String, pub group_name: String,
270 pub outline: Vec<Point>,
271}
272
273pub fn parse_placement_group_area(input: &str) -> IResult<&str, PlacementGroupArea> {
274 let (remaining, (owner, board_side, group_name, outline)) = parse_section!(
275 "PLACE_REGION",
276 ws_separated!((
277 owner,
278 is_not(" "), not_line_ending, many1(ws(point)) ))
282 )
283 .parse(input)?;
284
285 Ok((
286 remaining,
287 PlacementGroupArea {
288 owner: owner.to_string(),
289 board_side: board_side.to_string(),
290 group_name: group_name.to_string(),
291 outline,
292 },
293 ))
294}
295
296pub fn owner(input: &str) -> IResult<&str, &str> {
298 alt((tag("ECAD"), tag("MCAD"), tag("UNOWNED"))).parse(input)
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304 #[test]
305 fn test_parse_board_outline() {
306 let input = ".BOARD_OUTLINE MCAD
30762.0
3080 5.5 -120.0 0.0
3090 36.1 -120.0 263.266
3101 5127.5 56 360
311.END_BOARD_OUTLINE";
312
313 let (remaining, board_outline) = parse_board_panel_outline(input).unwrap();
314 assert_eq!(remaining, "");
315 assert_eq!(board_outline.owner, "MCAD");
316 assert_eq!(board_outline.thickness, 62.0);
317 assert_eq!(board_outline.outline.len(), 3);
318 assert_eq!(board_outline.outline[0].loop_label, 0);
319 assert_eq!(board_outline.outline[0].x, 5.5);
320 assert_eq!(board_outline.outline[0].y, -120.0);
321 assert_eq!(board_outline.outline[0].angle, 0.0);
322 }
323
324 #[test]
325 fn test_parse_board_outline_2() {
326 let input = ".BOARD_OUTLINE ECAD
327 40.0
3280 0.0 0.0 0.000
3293 1574.0 235.7 90.087
330.END_BOARD_OUTLINE";
331 let (remaining, board_outline) = parse_board_panel_outline(input).unwrap();
332
333 let expected_outline = BoardPanelOutline {
334 owner: "ECAD".to_string(),
335 thickness: 40.0,
336 outline: vec![
337 Point {
338 loop_label: 0,
339 x: 0.0,
340 y: 0.0,
341 angle: 0.0,
342 },
343 Point {
344 loop_label: 3,
345 x: 1574.0,
346 y: 235.7,
347 angle: 90.087,
348 },
349 ],
350 };
351
352 assert_eq!(remaining, "");
353 assert_eq!(board_outline, expected_outline);
354 }
355
356 #[test]
357 fn test_parse_other_outline() {
358 let input = ".OTHER_OUTLINE MCAD
359my_outline 62.0 TOP
3600 5.5 -120.0 0.0
361.END_OTHER_OUTLINE";
362
363 let (remaining, other_outline) = parse_other_outline(input).unwrap();
364 assert_eq!(remaining, "");
365 assert_eq!(other_outline.owner, "MCAD");
366 assert_eq!(other_outline.id, "my_outline");
367 assert_eq!(other_outline.extrude_thickness, 62.0);
368 assert_eq!(other_outline.board_side, "TOP");
369 assert_eq!(other_outline.outline.len(), 1);
370 assert_eq!(other_outline.outline[0].loop_label, 0);
371 assert_eq!(other_outline.outline[0].x, 5.5);
372 assert_eq!(other_outline.outline[0].y, -120.0);
373 assert_eq!(other_outline.outline[0].angle, 0.0);
374 }
375
376 #[test]
377 fn test_parse_routing_outline() {
378 let input = ".ROUTE_OUTLINE ECAD
379ALL
3800 5112.5 150.0 0.0
3810 5112.5 2058.2 0.0
382.END_ROUTE_OUTLINE";
383
384 let (remaining, routing_outline) = parse_routing_outline(input).unwrap();
385 assert_eq!(remaining, "");
386 assert_eq!(routing_outline.owner, "ECAD");
387 assert_eq!(routing_outline.routing_layers, "ALL");
388 assert_eq!(routing_outline.outline.len(), 2);
389 assert_eq!(routing_outline.outline[0].loop_label, 0);
390 assert_eq!(routing_outline.outline[0].x, 5112.5);
391 assert_eq!(routing_outline.outline[0].y, 150.0);
392 assert_eq!(routing_outline.outline[0].angle, 0.0);
393 }
394
395 #[test]
396 fn test_parse_placement_outline() {
397 let input = ".PLACE_OUTLINE MCAD
398TOP 1000.0
3990 -5.0 2034.9 -152.9
400.END_PLACE_OUTLINE";
401
402 let (remaining, placement_outline) = parse_placement_outline(input).unwrap();
403 assert_eq!(remaining, "");
404 assert_eq!(placement_outline.owner, "MCAD");
405 assert_eq!(placement_outline.board_side, "TOP");
406 assert_eq!(placement_outline.outline_height, 1000.0);
407 assert_eq!(placement_outline.outline.len(), 1);
408 assert_eq!(placement_outline.outline[0].loop_label, 0);
409 assert_eq!(placement_outline.outline[0].x, -5.0);
410 assert_eq!(placement_outline.outline[0].y, 2034.9);
411 assert_eq!(placement_outline.outline[0].angle, -152.9);
412 }
413
414 #[test]
415 fn test_parse_routing_keepout() {
416 let input = ".ROUTE_KEEPOUT ECAD
417ALL
4180 2650.0 2350.0 0.0
4190 3100.0 2350.0 360.0
420.END_ROUTE_KEEPOUT";
421
422 let (remaining, routing_keepout) = parse_routing_keepout(input).unwrap();
423 assert_eq!(remaining, "");
424 assert_eq!(routing_keepout.owner, "ECAD");
425 assert_eq!(routing_keepout.routing_layers, "ALL");
426 assert_eq!(routing_keepout.outline.len(), 2);
427 assert_eq!(routing_keepout.outline[0].loop_label, 0);
428 assert_eq!(routing_keepout.outline[0].x, 2650.0);
429 assert_eq!(routing_keepout.outline[0].y, 2350.0);
430 assert_eq!(routing_keepout.outline[0].angle, 0.0);
431 }
432 #[test]
433 fn test_parse_via_keepout() {
434 let input = ".VIA_KEEPOUT ECAD
4350 2650.0 2350.0 0.0
4360 3100.0 2350.0 360.0
437.END_VIA_KEEPOUT";
438
439 let (remaining, via_keepout) = parse_via_keepout(input).unwrap();
440 assert_eq!(remaining, "");
441 assert_eq!(via_keepout.owner, "ECAD");
442 assert_eq!(via_keepout.outline.len(), 2);
443 assert_eq!(via_keepout.outline[0].loop_label, 0);
444 assert_eq!(via_keepout.outline[0].x, 2650.0);
445 assert_eq!(via_keepout.outline[0].y, 2350.0);
446 assert_eq!(via_keepout.outline[0].angle, 0.0);
447 }
448
449 #[test]
450 fn test_parse_placement_keepout() {
451 let input = ".PLACE_KEEPOUT MCAD
452TOP 300.0
4530 3700.0 5000.0 0.0
4540 3700.0 5000.0 0.0
455.END_PLACE_KEEPOUT";
456
457 let (remaining, placement_keepout) = parse_placement_keepout(input).unwrap();
458 assert_eq!(remaining, "");
459 assert_eq!(placement_keepout.owner, "MCAD");
460 assert_eq!(placement_keepout.board_side, "TOP");
461 assert_eq!(placement_keepout.keepout_height, 300.0);
462 assert_eq!(placement_keepout.outline.len(), 2);
463 assert_eq!(placement_keepout.outline[0].loop_label, 0);
464 assert_eq!(placement_keepout.outline[0].x, 3700.0);
465 assert_eq!(placement_keepout.outline[0].y, 5000.0);
466 assert_eq!(placement_keepout.outline[0].angle, 0.0);
467 }
468
469 #[test]
470 fn test_parse_placement_group_area() {
471 let input = ".PLACE_REGION UNOWNED
472TOP the_best_group
4730 5.5 -120.0 0.0
474.END_PLACE_REGION";
475
476 let (remaining, placement_group_area) = parse_placement_group_area(input).unwrap();
477 assert_eq!(remaining, "");
478 assert_eq!(placement_group_area.owner, "UNOWNED");
479 assert_eq!(placement_group_area.board_side, "TOP");
480 assert_eq!(placement_group_area.group_name, "the_best_group");
481 assert_eq!(placement_group_area.outline.len(), 1);
482 assert_eq!(placement_group_area.outline[0].loop_label, 0);
483 assert_eq!(placement_group_area.outline[0].x, 5.5);
484 assert_eq!(placement_group_area.outline[0].y, -120.0);
485 assert_eq!(placement_group_area.outline[0].angle, 0.0);
486 }
487
488 #[test]
489 fn test_point() {
490 let input = "0 5.5 -120.0 0.0";
491 let (remaining, point) = point(input).unwrap();
492
493 assert_eq!(remaining, "");
494 assert_eq!(point.loop_label, 0);
495 assert_eq!(point.x, 5.5);
496 assert_eq!(point.y, -120.0);
497 assert_eq!(point.angle, 0.0);
498 }
499
500 #[test]
501 fn test_owner() {
502 let input = "ECADMCADUNOWNED";
503 let (remaining, owner_str) = owner(input).unwrap();
504 assert_eq!(remaining, "MCADUNOWNED");
505 assert_eq!(owner_str, "ECAD");
506
507 let (remaining, owner_str) = owner(remaining).unwrap();
508 assert_eq!(remaining, "UNOWNED");
509 assert_eq!(owner_str, "MCAD");
510
511 let (remaining, owner_str) = owner(remaining).unwrap();
512 assert_eq!(remaining, "");
513 assert_eq!(owner_str, "UNOWNED");
514 }
515}