Skip to main content

gdsr/elements/
element.rs

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/// A GDSII element: one of [`Path`], [`Polygon`], [`GdsBox`], [`Node`], [`Text`], or [`Reference`].
8#[derive(Clone, Debug, PartialEq)]
9pub enum Element {
10    /// A path element.
11    Path(Path),
12    /// A polygon element.
13    Polygon(Polygon),
14    /// A box element.
15    Box(GdsBox),
16    /// A node element.
17    Node(Node),
18    /// A text annotation element.
19    Text(Text),
20    /// A reference to another cell or element.
21    Reference(Reference),
22}
23
24impl Element {
25    /// Returns the inner [`Path`] if this is a `Path` variant, or `None`.
26    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    /// Returns the inner [`Polygon`] if this is a `Polygon` variant, or `None`.
35    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    /// Returns the inner [`Text`] if this is a `Text` variant, or `None`.
44    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    /// Returns the inner [`GdsBox`] if this is a `Box` variant, or `None`.
53    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    /// Returns the inner [`Node`] if this is a `Node` variant, or `None`.
62    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    /// Returns the inner [`Reference`] if this is a `Reference` variant, or `None`.
71    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    /// Returns the inner [`Path`] mutably if this is a `Path` variant, or `None`.
80    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    /// Returns the inner [`Polygon`] mutably if this is a `Polygon` variant, or `None`.
89    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    /// Returns the inner [`Text`] mutably if this is a `Text` variant, or `None`.
98    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    /// Returns the inner [`GdsBox`] mutably if this is a `Box` variant, or `None`.
107    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    /// Returns the inner [`Node`] mutably if this is a `Node` variant, or `None`.
116    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    /// Returns the inner [`Reference`] mutably if this is a `Reference` variant, or `None`.
125    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    /// Remaps the layer and data type using the given mapping.
134    /// For references, remaps the inline element if present.
135    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    /// Converts the inner element to integer units.
147    #[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    /// Converts the inner element to float units.
160    #[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}