1use std::sync::Arc;
2
3use crate::elements::{GdsBox, Node, Path, Polygon, Reference, Text};
4use crate::traits::ToGds;
5use crate::{Dimensions, Instance, Movable, Point, Transformable, Transformation};
6
7#[derive(Clone, Debug, PartialEq)]
9pub enum Element {
10 Path(Path),
12 Polygon(Polygon),
14 Box(GdsBox),
16 Node(Node),
18 Text(Text),
20 Reference(Reference),
22}
23
24impl Element {
25 pub fn as_path(&self) -> Option<&Path> {
27 if let Self::Path(v) = self {
28 Some(v)
29 } else {
30 None
31 }
32 }
33
34 pub fn as_polygon(&self) -> Option<&Polygon> {
36 if let Self::Polygon(v) = self {
37 Some(v)
38 } else {
39 None
40 }
41 }
42
43 pub fn as_text(&self) -> Option<&Text> {
45 if let Self::Text(v) = self {
46 Some(v)
47 } else {
48 None
49 }
50 }
51
52 pub fn as_box(&self) -> Option<&GdsBox> {
54 if let Self::Box(v) = self {
55 Some(v)
56 } else {
57 None
58 }
59 }
60
61 pub fn as_node(&self) -> Option<&Node> {
63 if let Self::Node(v) = self {
64 Some(v)
65 } else {
66 None
67 }
68 }
69
70 pub fn as_reference(&self) -> Option<&Reference> {
72 if let Self::Reference(v) = self {
73 Some(v)
74 } else {
75 None
76 }
77 }
78
79 pub fn as_path_mut(&mut self) -> Option<&mut Path> {
81 if let Self::Path(v) = self {
82 Some(v)
83 } else {
84 None
85 }
86 }
87
88 pub fn as_polygon_mut(&mut self) -> Option<&mut Polygon> {
90 if let Self::Polygon(v) = self {
91 Some(v)
92 } else {
93 None
94 }
95 }
96
97 pub fn as_text_mut(&mut self) -> Option<&mut Text> {
99 if let Self::Text(v) = self {
100 Some(v)
101 } else {
102 None
103 }
104 }
105
106 pub fn as_box_mut(&mut self) -> Option<&mut GdsBox> {
108 if let Self::Box(v) = self {
109 Some(v)
110 } else {
111 None
112 }
113 }
114
115 pub fn as_node_mut(&mut self) -> Option<&mut Node> {
117 if let Self::Node(v) = self {
118 Some(v)
119 } else {
120 None
121 }
122 }
123
124 pub fn as_reference_mut(&mut self) -> Option<&mut Reference> {
126 if let Self::Reference(v) = self {
127 Some(v)
128 } else {
129 None
130 }
131 }
132
133 pub fn remap_layers(&mut self, mapping: &crate::LayerMapping) {
136 match self {
137 Self::Path(path) => path.remap_layers(mapping),
138 Self::Polygon(polygon) => polygon.remap_layers(mapping),
139 Self::Box(gds_box) => gds_box.remap_layers(mapping),
140 Self::Node(node) => node.remap_layers(mapping),
141 Self::Text(text) => text.remap_layers(mapping),
142 Self::Reference(reference) => reference.remap_layers(mapping),
143 }
144 }
145
146 #[must_use]
148 pub fn to_integer_unit(self) -> Self {
149 match self {
150 Self::Path(path) => Self::Path(path.to_integer_unit()),
151 Self::Polygon(polygon) => Self::Polygon(polygon.to_integer_unit()),
152 Self::Box(gds_box) => Self::Box(gds_box.to_integer_unit()),
153 Self::Node(node) => Self::Node(node.to_integer_unit()),
154 Self::Text(text) => Self::Text(text.to_integer_unit()),
155 Self::Reference(reference) => Self::Reference(reference.to_integer_unit()),
156 }
157 }
158
159 #[must_use]
161 pub fn to_float_unit(self) -> Self {
162 match self {
163 Self::Path(path) => Self::Path(path.to_float_unit()),
164 Self::Polygon(polygon) => Self::Polygon(polygon.to_float_unit()),
165 Self::Box(gds_box) => Self::Box(gds_box.to_float_unit()),
166 Self::Node(node) => Self::Node(node.to_float_unit()),
167 Self::Text(text) => Self::Text(text.to_float_unit()),
168 Self::Reference(reference) => Self::Reference(reference.to_float_unit()),
169 }
170 }
171}
172
173impl std::fmt::Display for Element {
174 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
175 match self {
176 Self::Path(path) => write!(f, "{path}"),
177 Self::Polygon(polygon) => write!(f, "{polygon}"),
178 Self::Box(gds_box) => write!(f, "{gds_box}"),
179 Self::Node(node) => write!(f, "{node}"),
180 Self::Text(text) => write!(f, "{text}"),
181 Self::Reference(reference) => write!(f, "{reference}"),
182 }
183 }
184}
185
186macro_rules! impl_from_element_reference {
187 (@impl $type:ident, $variant:ident) => {
188 impl From<$type> for Element {
189 fn from(value: $type) -> Self {
190 Self::$variant(value)
191 }
192 }
193
194 impl From<$type> for Instance {
195 fn from(value: $type) -> Self {
196 Element::from(value).into()
197 }
198 }
199 };
200 () => {};
201 ($type:ident => $variant:ident $(, $($rest:tt)*)?) => {
202 impl_from_element_reference!(@impl $type, $variant);
203 $(impl_from_element_reference!($($rest)*);)?
204 };
205 ($type:ident $(, $($rest:tt)*)?) => {
206 impl_from_element_reference!(@impl $type, $type);
207 $(impl_from_element_reference!($($rest)*);)?
208 };
209}
210
211impl_from_element_reference!(Path, Polygon, GdsBox => Box, Node, Text, Reference);
212
213impl From<Element> for Instance {
214 fn from(v: Element) -> Self {
215 Self::Element(Arc::new(Box::new(v)))
216 }
217}
218
219impl ToGds for Element {
220 fn to_gds_impl(&self, scale: f64) -> Result<Vec<u8>, crate::error::GdsError> {
221 match self {
222 Self::Path(path) => path.to_gds_impl(scale),
223 Self::Polygon(polygon) => polygon.to_gds_impl(scale),
224 Self::Box(gds_box) => gds_box.to_gds_impl(scale),
225 Self::Node(node) => node.to_gds_impl(scale),
226 Self::Reference(reference) => reference.to_gds_impl(scale),
227 Self::Text(text) => text.to_gds_impl(scale),
228 }
229 }
230}
231
232impl Transformable for Element {
233 fn transform_impl(self, transformation: &Transformation) -> Self {
234 match self {
235 Self::Path(path) => Self::Path(path.transform_impl(transformation)),
236 Self::Polygon(polygon) => Self::Polygon(polygon.transform_impl(transformation)),
237 Self::Box(gds_box) => Self::Box(gds_box.transform_impl(transformation)),
238 Self::Node(node) => Self::Node(node.transform_impl(transformation)),
239 Self::Reference(reference) => Self::Reference(reference.transform_impl(transformation)),
240 Self::Text(text) => Self::Text(text.transform_impl(transformation)),
241 }
242 }
243}
244
245impl Movable for Element {
246 fn move_to(self, target: Point) -> Self {
247 match self {
248 Self::Path(path) => Self::Path(path.move_to(target)),
249 Self::Polygon(polygon) => Self::Polygon(polygon.move_to(target)),
250 Self::Box(gds_box) => Self::Box(gds_box.move_to(target)),
251 Self::Node(node) => Self::Node(node.move_to(target)),
252 Self::Reference(reference) => Self::Reference(reference.move_to(target)),
253 Self::Text(text) => Self::Text(text.move_to(target)),
254 }
255 }
256}
257
258impl Dimensions for Element {
259 fn bounding_box(&self) -> (Point, Point) {
260 match self {
261 Self::Path(path) => path.bounding_box(),
262 Self::Polygon(polygon) => polygon.bounding_box(),
263 Self::Box(gds_box) => gds_box.bounding_box(),
264 Self::Node(node) => node.bounding_box(),
265 Self::Text(text) => text.bounding_box(),
266 Self::Reference(_) => (Point::default(), Point::default()),
267 }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274 use crate::{DataType, GdsBox, Grid, Layer, Node, Point};
275
276 const UNITS: f64 = 1e-9;
277
278 fn p(x: i32, y: i32) -> Point {
279 Point::integer(x, y, UNITS)
280 }
281
282 fn pf(x: f64, y: f64) -> Point {
283 Point::float(x, y, 1e-6)
284 }
285
286 fn origin() -> Point {
287 p(0, 0)
288 }
289
290 fn simple_polygon() -> Polygon {
291 Polygon::new(
292 vec![p(0, 0), p(10, 0), p(10, 10)],
293 Layer::new(1),
294 DataType::new(0),
295 )
296 }
297
298 fn simple_path() -> Path {
299 Path::new(
300 vec![p(0, 0), p(10, 10)],
301 Layer::new(1),
302 DataType::new(0),
303 None,
304 None,
305 None,
306 None,
307 )
308 }
309
310 fn simple_grid() -> Grid {
311 Grid::default()
312 .with_columns(2)
313 .with_rows(2)
314 .with_spacing_x(Some(p(10, 0)))
315 .with_spacing_y(Some(p(0, 10)))
316 }
317
318 fn simple_reference() -> Reference {
319 Reference::new(simple_polygon())
320 }
321
322 fn simple_text() -> Text {
323 Text::default()
324 }
325
326 fn simple_gds_box() -> GdsBox {
327 GdsBox::new(p(0, 0), p(10, 10), Layer::new(1), DataType::new(0))
328 }
329
330 fn simple_node() -> Node {
331 Node::new(vec![p(0, 0), p(5, 5)], Layer::new(1), DataType::new(0))
332 }
333
334 fn all_elements() -> [Element; 6] {
335 [
336 simple_path().into(),
337 simple_polygon().into(),
338 simple_gds_box().into(),
339 simple_node().into(),
340 simple_text().into(),
341 simple_reference().with_grid(simple_grid()).into(),
342 ]
343 }
344
345 #[test]
346 fn test_element_from_path() {
347 let path = simple_path();
348 let element: Element = path.clone().into();
349
350 assert!(element.as_polygon().is_none());
351 assert!(element.as_reference().is_none());
352 assert!(element.as_text().is_none());
353 assert_eq!(element.as_path().unwrap(), &path);
354
355 insta::assert_snapshot!(element.to_string(), @"Path with 2 points on layer 1 with data type 0, Square and width 0 (1.000e-9)");
356 }
357
358 #[test]
359 fn test_element_from_polygon() {
360 let polygon = simple_polygon();
361 let element: Element = polygon.clone().into();
362
363 assert!(element.as_path().is_none());
364 assert_eq!(element.as_polygon().unwrap(), &polygon);
365
366 insta::assert_snapshot!(element.to_string(), @"Polygon with 4 point(s), starting at (0 (1.000e-9), 0 (1.000e-9)) on layer 1, data type 0");
367 }
368
369 #[test]
370 fn test_element_from_text() {
371 let text = simple_text();
372 let element: Element = text.clone().into();
373
374 assert_eq!(element.as_text().unwrap(), &text);
375
376 insta::assert_snapshot!(element.to_string(), @"Text '' vertical: Middle, horizontal: Centre at Point(0 (1.000e-9), 0 (1.000e-9))");
377 }
378
379 #[test]
380 fn test_element_from_box() {
381 let gds_box = simple_gds_box();
382 let element: Element = gds_box.clone().into();
383
384 assert!(element.as_path().is_none());
385 assert!(element.as_polygon().is_none());
386 assert!(element.as_text().is_none());
387 assert!(element.as_reference().is_none());
388 assert_eq!(element.as_box().unwrap(), &gds_box);
389
390 insta::assert_snapshot!(element.to_string(), @"Box from (0.000000 (1.000e-9), 0.000000 (1.000e-9)) to (10.000000 (1.000e-9), 10.000000 (1.000e-9)) on layer 1, box type 0");
391 }
392
393 #[test]
394 fn test_element_from_node() {
395 let node = simple_node();
396 let element: Element = node.clone().into();
397
398 assert!(element.as_path().is_none());
399 assert!(element.as_polygon().is_none());
400 assert!(element.as_box().is_none());
401 assert!(element.as_text().is_none());
402 assert!(element.as_reference().is_none());
403 assert_eq!(element.as_node().unwrap(), &node);
404
405 insta::assert_snapshot!(element.to_string(), @"Node with 2 point(s) on layer 1, node type 0");
406 }
407
408 #[test]
409 fn test_element_from_reference() {
410 let reference = simple_reference().with_grid(simple_grid());
411 let element: Element = reference.clone().into();
412
413 assert_eq!(element.as_reference().unwrap(), &reference);
414
415 insta::assert_snapshot!(element.to_string(), @"Reference to Element instance: Polygon with 4 point(s), starting at (0 (1.000e-9), 0 (1.000e-9)) on layer 1, data type 0 with grid Grid at Point(0 (1.000e-9), 0 (1.000e-9)) with 2 columns and 2 rows, spacing (Point(10 (1.000e-9), 0 (1.000e-9)), Point(0 (1.000e-9), 10 (1.000e-9))), magnification 1.0, angle 0.0, x_reflection false");
416 }
417
418 #[test]
419 fn test_element_to_integer_unit_preserves_variant() {
420 for element in all_elements() {
421 let converted = element.clone().to_integer_unit();
422 assert_eq!(
423 std::mem::discriminant(&element),
424 std::mem::discriminant(&converted)
425 );
426 }
427 }
428
429 #[test]
430 fn test_element_to_float_unit_preserves_variant() {
431 for element in all_elements() {
432 let converted = element.clone().to_float_unit();
433 assert_eq!(
434 std::mem::discriminant(&element),
435 std::mem::discriminant(&converted)
436 );
437 }
438 }
439
440 #[test]
441 fn test_element_to_integer_unit_converts_points() {
442 let polygon = Polygon::new(
443 [pf(1.5, 2.5), pf(10.0, 0.0), pf(10.0, 10.0)],
444 Layer::new(1),
445 DataType::new(0),
446 );
447 let element: Element = polygon.into();
448 let converted = element.to_integer_unit();
449
450 for point in converted.as_polygon().unwrap().points() {
451 assert_eq!(*point, point.to_integer_unit());
452 }
453 }
454
455 #[test]
456 fn test_element_to_float_unit_converts_points() {
457 let element: Element = simple_polygon().into();
458 let converted = element.to_float_unit();
459
460 for point in converted.as_polygon().unwrap().points() {
461 assert_eq!(*point, point.to_float_unit());
462 }
463 }
464
465 #[test]
466 fn test_element_transform_preserves_variant() {
467 let centre = origin();
468 for element in all_elements() {
469 let rotated = element.clone().rotate(std::f64::consts::PI / 2.0, centre);
470 assert_eq!(
471 std::mem::discriminant(&element),
472 std::mem::discriminant(&rotated)
473 );
474 }
475 }
476
477 #[test]
478 fn test_element_move_to() {
479 let element: Element = simple_polygon().into();
480 let moved = element.move_to(p(20, 20));
481 assert!(moved.as_polygon().is_some());
482 }
483
484 #[test]
485 fn test_element_move_by() {
486 let element: Element = simple_path().into();
487 let moved = element.move_by(p(5, 5));
488 assert!(moved.as_path().is_some());
489 }
490
491 #[test]
492 fn test_element_bounding_box_polygon() {
493 let element: Element = simple_polygon().into();
494 let (min, max) = element.bounding_box();
495 assert_eq!(min, p(0, 0));
496 assert_eq!(max, p(10, 10));
497 }
498
499 #[test]
500 fn test_element_bounding_box_path() {
501 let path = Path::new(
502 vec![p(-5, 3), p(10, 7)],
503 Layer::new(1),
504 DataType::new(0),
505 None,
506 None,
507 None,
508 None,
509 );
510 let element: Element = path.into();
511 let (min, max) = element.bounding_box();
512 assert_eq!(min, p(-5, 3));
513 assert_eq!(max, p(10, 7));
514 }
515
516 #[test]
517 fn test_element_bounding_box_text() {
518 let text = Text::default().set_origin(p(5, 10));
519 let element: Element = text.into();
520 let (min, max) = element.bounding_box();
521 assert_eq!(min, p(5, 10));
522 assert_eq!(max, p(5, 10));
523 }
524
525 #[test]
526 fn test_element_bounding_box_reference() {
527 let element: Element = simple_reference().into();
528 let (min, max) = element.bounding_box();
529 assert_eq!(min, Point::default());
530 assert_eq!(max, Point::default());
531 }
532}