| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2023-09-17 21:33:22 -07:00
										 |  |  |   Object representing a one multi-layer lithographic layout. | 
					
						
							|  |  |  |   A single level of hierarchical references is included. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 18:43:44 -07:00
										 |  |  | from typing import Callable, Sequence, cast, Mapping, Self, Any, Iterable, TypeVar, MutableMapping | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | import copy | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2020-09-18 19:46:57 -07:00
										 |  |  | from itertools import chain | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | from collections import defaultdict | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 15:47:38 -08:00
										 |  |  | import numpy | 
					
						
							| 
									
										
										
										
											2023-04-13 17:54:52 -07:00
										 |  |  | from numpy import inf, pi, nan | 
					
						
							| 
									
										
										
										
											2022-02-23 15:47:38 -08:00
										 |  |  | from numpy.typing import NDArray, ArrayLike | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | # .visualize imports matplotlib and matplotlib.collections | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 23:38:53 -08:00
										 |  |  | from .ref import Ref | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | from .shapes import Shape, Polygon, Path, DEFAULT_POLY_NUM_VERTICES | 
					
						
							| 
									
										
										
										
											2018-08-30 23:06:31 -07:00
										 |  |  | from .label import Label | 
					
						
							| 
									
										
										
										
											2023-04-14 22:19:56 -07:00
										 |  |  | from .utils import rotation_matrix_2d, annotations_t, layer_t | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  | from .error import PatternError | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionable, Repeatable, Bounded | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  | from .ports import Port, PortList | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | logger = logging.getLogger(__name__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-25 23:19:25 -08:00
										 |  |  | class Pattern(PortList, AnnotatableImpl, Mirrorable): | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |     2D layout consisting of some set of shapes, labels, and references to other Pattern objects | 
					
						
							| 
									
										
										
										
											2023-09-17 21:33:22 -07:00
										 |  |  |      (via Ref). Shapes are assumed to inherit from `masque.shapes.Shape` or provide equivalent functions. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-01-23 22:27:26 -08:00
										 |  |  |     __slots__ = ( | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         'shapes', 'labels', 'refs', '_ports', | 
					
						
							| 
									
										
										
										
											2023-01-23 22:27:26 -08:00
										 |  |  |         # inherited | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         '_offset', '_annotations', | 
					
						
							| 
									
										
										
										
											2023-01-23 22:27:26 -08:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     shapes: defaultdict[layer_t, list[Shape]] | 
					
						
							|  |  |  |     """ Stores of all shapes in this Pattern, indexed by layer.
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |     Elements in this list are assumed to inherit from Shape or provide equivalent functions. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     labels: defaultdict[layer_t, list[Label]] | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |     """ List of all labels in this Pattern. """ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     refs: defaultdict[str | None, list[Ref]] | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |     """ List of all references to other patterns (`Ref`s) in this `Pattern`.
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |     Multiple objects in this list may reference the same Pattern object | 
					
						
							| 
									
										
										
										
											2020-07-22 02:45:16 -07:00
										 |  |  |       (i.e. multiple instances of the same object). | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |     _ports: dict[str, Port] | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |     """ Uniquely-named ports which can be used to snap to other Pattern instances""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |     @property | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |     def ports(self) -> dict[str, Port]: | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         return self._ports | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @ports.setter | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |     def ports(self, value: dict[str, Port]) -> None: | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         self._ports = value | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |     def __init__( | 
					
						
							|  |  |  |             self, | 
					
						
							|  |  |  |             *, | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             shapes: Mapping[layer_t, Sequence[Shape]] | None = None, | 
					
						
							|  |  |  |             labels: Mapping[layer_t, Sequence[Label]] | None = None, | 
					
						
							|  |  |  |             refs: Mapping[str | None, Sequence[Ref]] | None = None, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |             annotations: annotations_t | None = None, | 
					
						
							|  |  |  |             ports: Mapping[str, 'Port'] | None = None | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |             ) -> None: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Basic init; arguments get assigned to member variables. | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |          Non-list inputs for shapes and refs get converted to lists. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             shapes: Initial shapes in the Pattern | 
					
						
							|  |  |  |             labels: Initial labels in the Pattern | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |             refs: Initial refs in the Pattern | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             annotations: Initial annotations for the pattern | 
					
						
							|  |  |  |             ports: Any ports in the pattern | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         self.shapes = defaultdict(list) | 
					
						
							|  |  |  |         self.labels = defaultdict(list) | 
					
						
							|  |  |  |         self.refs = defaultdict(list) | 
					
						
							|  |  |  |         if shapes: | 
					
						
							|  |  |  |             for layer, sseq in shapes.items(): | 
					
						
							|  |  |  |                 self.shapes[layer].extend(sseq) | 
					
						
							|  |  |  |         if labels: | 
					
						
							|  |  |  |             for layer, lseq in labels.items(): | 
					
						
							|  |  |  |                 self.labels[layer].extend(lseq) | 
					
						
							|  |  |  |         if refs: | 
					
						
							|  |  |  |             for target, rseq in refs.items(): | 
					
						
							|  |  |  |                 self.refs[target].extend(rseq) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |         if ports is not None: | 
					
						
							| 
									
										
										
										
											2023-01-24 12:45:44 -08:00
										 |  |  |             self.ports = dict(copy.deepcopy(ports)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.ports = {} | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-10 20:06:58 -07:00
										 |  |  |         self.annotations = annotations if annotations is not None else {} | 
					
						
							| 
									
										
										
										
											2019-12-12 00:38:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |     def __repr__(self) -> str: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         nshapes = sum(len(seq) for seq in self.shapes.values()) | 
					
						
							|  |  |  |         nrefs = sum(len(seq) for seq in self.refs.values()) | 
					
						
							|  |  |  |         nlabels = sum(len(seq) for seq in self.labels.values()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         s = f'<Pattern: s{nshapes} r{nrefs} l{nlabels} [' | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |         for name, port in self.ports.items(): | 
					
						
							|  |  |  |             s += f'\n\t{name}: {port}' | 
					
						
							|  |  |  |         s += ']>' | 
					
						
							|  |  |  |         return s | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-13 20:33:14 -08:00
										 |  |  |     def __copy__(self) -> 'Pattern': | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         logger.warning('Making a shallow copy of a Pattern... old shapes are re-referenced!') | 
					
						
							|  |  |  |         new = Pattern( | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |             annotations=copy.deepcopy(self.annotations), | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             ports=copy.deepcopy(self.ports), | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for target, rseq in self.refs.items(): | 
					
						
							|  |  |  |             new.refs[target].extend(rseq) | 
					
						
							|  |  |  |         for layer, sseq in self.shapes.items(): | 
					
						
							|  |  |  |             new.shapes[layer].extend(sseq) | 
					
						
							|  |  |  |         for layer, lseq in self.labels.items(): | 
					
						
							|  |  |  |             new.labels[layer].extend(lseq) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-15 00:19:37 -07:00
										 |  |  |         return new | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | #    def __deepcopy__(self, memo: dict | None = None) -> 'Pattern': | 
					
						
							|  |  |  | #        memo = {} if memo is None else memo | 
					
						
							|  |  |  | #        new = Pattern( | 
					
						
							|  |  |  | #            shapes=copy.deepcopy(self.shapes, memo), | 
					
						
							|  |  |  | #            labels=copy.deepcopy(self.labels, memo), | 
					
						
							|  |  |  | #            refs=copy.deepcopy(self.refs, memo), | 
					
						
							|  |  |  | #            annotations=copy.deepcopy(self.annotations, memo), | 
					
						
							|  |  |  | #            ports=copy.deepcopy(self.ports), | 
					
						
							|  |  |  | #            ) | 
					
						
							|  |  |  | #        return new | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def append(self, other_pattern: 'Pattern') -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |         Appends all shapes, labels and refs from other_pattern to self's shapes, | 
					
						
							| 
									
										
										
										
											2018-08-30 23:06:31 -07:00
										 |  |  |           labels, and supbatterns. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |            other_pattern: The Pattern to append | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for target, rseq in other_pattern.refs.items(): | 
					
						
							|  |  |  |             self.refs[target].extend(rseq) | 
					
						
							|  |  |  |         for layer, sseq in other_pattern.shapes.items(): | 
					
						
							|  |  |  |             self.shapes[layer].extend(sseq) | 
					
						
							|  |  |  |         for layer, lseq in other_pattern.labels.items(): | 
					
						
							|  |  |  |             self.labels[layer].extend(lseq) | 
					
						
							| 
									
										
										
										
											2023-01-21 23:38:53 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         annotation_conflicts = set(self.annotations.keys()) & set(other_pattern.annotations.keys()) | 
					
						
							|  |  |  |         if annotation_conflicts: | 
					
						
							|  |  |  |             raise PatternError(f'Annotation keys overlap: {annotation_conflicts}') | 
					
						
							|  |  |  |         self.annotations.update(other_pattern.annotations) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         port_conflicts = set(self.ports.keys()) & set(other_pattern.ports.keys()) | 
					
						
							|  |  |  |         if port_conflicts: | 
					
						
							|  |  |  |             raise PatternError(f'Port names overlap: {port_conflicts}') | 
					
						
							|  |  |  |         self.ports.update(other_pattern.ports) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |     def subset( | 
					
						
							|  |  |  |             self, | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             shapes: Callable[[layer_t, Shape], bool] | None = None, | 
					
						
							|  |  |  |             labels: Callable[[layer_t, Label], bool] | None = None, | 
					
						
							|  |  |  |             refs: Callable[[str | None, Ref], bool] | None = None, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |             annotations: Callable[[str, list[int | float | str]], bool] | None = None, | 
					
						
							|  |  |  |             ports: Callable[[str, Port], bool] | None = None, | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             default_keep: bool = False | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |             ) -> 'Pattern': | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2018-08-30 23:06:31 -07:00
										 |  |  |         Returns a Pattern containing only the entities (e.g. shapes) for which the | 
					
						
							|  |  |  |           given entity_func returns True. | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |         Self is _not_ altered, but shapes, labels, and refs are _not_ copied, just referenced. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             shapes: Given a layer and shape, returns a boolean denoting whether the shape is a | 
					
						
							|  |  |  |                 member of the subset. | 
					
						
							|  |  |  |             labels: Given a layer and label, returns a boolean denoting whether the label is a | 
					
						
							|  |  |  |                 member of the subset. | 
					
						
							|  |  |  |             refs: Given a target and ref, returns a boolean denoting if it is a member of the subset. | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             annotations: Given an annotation, returns a boolean denoting if it is a member of the subset. | 
					
						
							|  |  |  |             ports: Given a port, returns a boolean denoting if it is a member of the subset. | 
					
						
							|  |  |  |             default_keep: If `True`, keeps all elements of a given type if no function is supplied. | 
					
						
							|  |  |  |                 Default `False` (discards all elements). | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |             A Pattern containing all the shapes and refs for which the parameter | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |                 functions return True | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         pat = Pattern() | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         if shapes is not None: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             for layer in self.shapes: | 
					
						
							|  |  |  |                 pat.shapes[layer] = [ss for ss in self.shapes[layer] if shapes(layer, ss)] | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |         elif default_keep: | 
					
						
							|  |  |  |             pat.shapes = copy.copy(self.shapes) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         if labels is not None: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             for layer in self.labels: | 
					
						
							|  |  |  |                 pat.labels[layer] = [ll for ll in self.labels[layer] if labels(layer, ll)] | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |         elif default_keep: | 
					
						
							|  |  |  |             pat.labels = copy.copy(self.labels) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |         if refs is not None: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             for target in self.refs: | 
					
						
							|  |  |  |                 pat.refs[target] = [rr for rr in self.refs[target] if refs(target, rr)] | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |         elif default_keep: | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |             pat.refs = copy.copy(self.refs) | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if annotations is not None: | 
					
						
							| 
									
										
										
										
											2023-01-21 23:38:53 -08:00
										 |  |  |             pat.annotations = {k: v for k, v in self.annotations.items() if annotations(k, v)} | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |         elif default_keep: | 
					
						
							|  |  |  |             pat.annotations = copy.copy(self.annotations) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ports is not None: | 
					
						
							| 
									
										
										
										
											2023-01-21 23:38:53 -08:00
										 |  |  |             pat.ports = {k: v for k, v in self.ports.items() if ports(k, v)} | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |         elif default_keep: | 
					
						
							|  |  |  |             pat.ports = copy.copy(self.ports) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-18 15:04:33 -07:00
										 |  |  |         return pat | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |     def polygonize( | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |             self, | 
					
						
							| 
									
										
										
										
											2023-03-19 10:17:09 -07:00
										 |  |  |             num_vertices: int | None = DEFAULT_POLY_NUM_VERTICES, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |             max_arclen: float | None = None, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |             ) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         Calls `.to_polygons(...)` on all the shapes in this Pattern, replacing them with the returned polygons. | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Arguments are passed directly to `shape.to_polygons(...)`. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-03-19 10:17:09 -07:00
										 |  |  |             num_vertices: Number of points to use for each polygon. Can be overridden by | 
					
						
							| 
									
										
										
										
											2023-02-23 11:25:40 -08:00
										 |  |  |                 `max_arclen` if that results in more points. Optional, defaults to shapes' | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |                 internal defaults. | 
					
						
							| 
									
										
										
										
											2023-02-23 11:25:40 -08:00
										 |  |  |             max_arclen: Maximum arclength which can be approximated by a single line | 
					
						
							| 
									
										
										
										
											2018-04-15 16:42:00 -07:00
										 |  |  |              segment. Optional, defaults to shapes' internal defaults. | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for layer in self.shapes: | 
					
						
							|  |  |  |             self.shapes[layer] = list(chain.from_iterable( | 
					
						
							|  |  |  |                 ss.to_polygons(num_vertices, max_arclen) | 
					
						
							|  |  |  |                 for ss in self.shapes[layer] | 
					
						
							|  |  |  |                 )) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |     def manhattanize( | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |             self, | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |             grid_x: ArrayLike, | 
					
						
							|  |  |  |             grid_y: ArrayLike, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |             ) -> Self: | 
					
						
							| 
									
										
										
										
											2017-09-06 01:16:24 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         Calls `.polygonize()` on the pattern, then calls `.manhattanize()` on all the | 
					
						
							| 
									
										
										
										
											2017-09-06 01:16:24 -07:00
										 |  |  |          resulting shapes, replacing them with the returned Manhattan polygons. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             grid_x: List of allowed x-coordinates for the Manhattanized polygon edges. | 
					
						
							|  |  |  |             grid_y: List of allowed y-coordinates for the Manhattanized polygon edges. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2017-09-06 01:16:24 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         self.polygonize() | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for layer in self.shapes: | 
					
						
							|  |  |  |             self.shapes[layer] = list(chain.from_iterable(( | 
					
						
							|  |  |  |                 ss.manhattanize(grid_x, grid_y) | 
					
						
							|  |  |  |                 for ss in self.shapes[layer] | 
					
						
							|  |  |  |                 ))) | 
					
						
							| 
									
										
										
										
											2017-09-06 01:16:24 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |     def as_polygons(self, library: Mapping[str, 'Pattern']) -> list[NDArray[numpy.float64]]: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Represents the pattern as a list of polygons. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Deep-copies the pattern, then calls `.polygonize()` and `.flatten()` on the copy in order to | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |          generate the list of polygons. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Returns: | 
					
						
							|  |  |  |             A list of `(Ni, 2)` `numpy.ndarray`s specifying vertices of the polygons. Each ndarray | 
					
						
							|  |  |  |              is of the form `[[x0, y0], [x1, y1],...]`. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         pat = self.deepcopy().polygonize().flatten(library=library) | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         polys = [ | 
					
						
							|  |  |  |             cast(Polygon, shape).vertices + cast(Polygon, shape).offset | 
					
						
							|  |  |  |             for shape in chain_elements(pat.shapes) | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |         return polys | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |     def referenced_patterns(self) -> set[str | None]: | 
					
						
							| 
									
										
										
										
											2020-03-14 15:52:10 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         Get all pattern namers referenced by this pattern. Non-recursive. | 
					
						
							| 
									
										
										
										
											2020-05-17 14:03:17 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-14 15:52:10 -07:00
										 |  |  |         Returns: | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |             A set of all pattern names referenced by this pattern. | 
					
						
							| 
									
										
										
										
											2020-03-14 15:52:10 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         return set(self.refs.keys()) | 
					
						
							| 
									
										
										
										
											2020-03-14 15:52:10 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |     def get_bounds( | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |             self, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |             library: Mapping[str, 'Pattern'] | None = None, | 
					
						
							| 
									
										
										
										
											2023-01-26 23:47:16 -08:00
										 |  |  |             recurse: bool = True, | 
					
						
							| 
									
										
										
										
											2023-04-13 17:54:52 -07:00
										 |  |  |             cache: MutableMapping[str, NDArray[numpy.float64] | None] | None = None, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |             ) -> NDArray[numpy.float64] | None: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |          extent of the Pattern's contents in each dimension. | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Returns `None` if the Pattern is empty. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-26 23:47:16 -08:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |             library: If `recurse=True`, any referenced patterns are loaded from this library. | 
					
						
							|  |  |  |             recurse: If `False`, do not evaluate the bounds of any refs (i.e. assume they are empty). | 
					
						
							|  |  |  |                 If `True`, evaluate the bounds of all refs and their conained geometry recursively. | 
					
						
							|  |  |  |                 Default `True`. | 
					
						
							|  |  |  |             cache: Mapping of `{name: bounds}` for patterns for which the bounds have already been calculated. | 
					
						
							|  |  |  |                 Modified during the run (any referenced pattern's bounds are added). | 
					
						
							| 
									
										
										
										
											2023-01-26 23:47:16 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Returns: | 
					
						
							|  |  |  |             `[[x_min, y_min], [x_max, y_max]]` or `None` | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-09-18 19:46:57 -07:00
										 |  |  |         if self.is_empty(): | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:54:52 -07:00
										 |  |  |         n_elems = sum(1 for _ in chain_elements(self.shapes, self.labels)) | 
					
						
							|  |  |  |         ebounds = numpy.full((n_elems, 2, 2), nan) | 
					
						
							|  |  |  |         for ee, entry in enumerate(chain_elements(self.shapes, self.labels)): | 
					
						
							|  |  |  |             maybe_ebounds = cast(Bounded, entry).get_bounds() | 
					
						
							|  |  |  |             if maybe_ebounds is not None: | 
					
						
							|  |  |  |                 ebounds[ee] = maybe_ebounds | 
					
						
							|  |  |  |         mask = ~numpy.isnan(ebounds[:, 0, 0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if mask.any(): | 
					
						
							|  |  |  |             cbounds = numpy.vstack(( | 
					
						
							| 
									
										
										
										
											2023-04-14 22:27:44 -07:00
										 |  |  |                 numpy.min(ebounds[mask, 0, :], axis=0), | 
					
						
							|  |  |  |                 numpy.max(ebounds[mask, 1, :], axis=0), | 
					
						
							| 
									
										
										
										
											2023-04-13 17:54:52 -07:00
										 |  |  |                 )) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             cbounds = numpy.array(( | 
					
						
							|  |  |  |                 (+inf, +inf), | 
					
						
							|  |  |  |                 (-inf, -inf), | 
					
						
							|  |  |  |                 )) | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         if recurse and self.has_refs(): | 
					
						
							|  |  |  |             if library is None: | 
					
						
							|  |  |  |                 raise PatternError('Must provide a library to get_bounds() to resolve refs') | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 18:21:37 -07:00
										 |  |  |             if cache is None: | 
					
						
							|  |  |  |                 cache = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             for target, refs in self.refs.items(): | 
					
						
							|  |  |  |                 if target is None: | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if not refs: | 
					
						
							| 
									
										
										
										
											2023-01-26 23:47:16 -08:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2023-04-12 18:21:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if target in cache: | 
					
						
							|  |  |  |                     unrot_bounds = cache[target] | 
					
						
							| 
									
										
										
										
											2023-04-12 21:41:18 -07:00
										 |  |  |                 elif any(numpy.isclose(ref.rotation % (pi / 2), 0) for ref in refs): | 
					
						
							| 
									
										
										
										
											2023-04-12 18:21:37 -07:00
										 |  |  |                     unrot_bounds = library[target].get_bounds(library=library, recurse=recurse, cache=cache) | 
					
						
							|  |  |  |                     cache[target] = unrot_bounds | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |                 for ref in refs: | 
					
						
							| 
									
										
										
										
											2023-04-12 18:43:44 -07:00
										 |  |  |                     if numpy.isclose(ref.rotation % (pi / 2), 0): | 
					
						
							| 
									
										
										
										
											2023-04-12 18:21:37 -07:00
										 |  |  |                         if unrot_bounds is None: | 
					
						
							|  |  |  |                             bounds = None | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             ubounds = unrot_bounds.copy() | 
					
						
							| 
									
										
										
										
											2023-04-14 22:19:56 -07:00
										 |  |  |                             if ref.mirrored: | 
					
						
							| 
									
										
										
										
											2023-04-12 18:21:37 -07:00
										 |  |  |                                 ubounds[:, 1] *= -1 | 
					
						
							| 
									
										
										
										
											2023-04-12 22:40:08 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-14 22:19:56 -07:00
										 |  |  |                             corners = (rotation_matrix_2d(ref.rotation) @ ubounds.T).T | 
					
						
							| 
									
										
										
										
											2023-04-12 22:40:08 -07:00
										 |  |  |                             bounds = numpy.vstack((numpy.min(corners, axis=0), | 
					
						
							|  |  |  |                                                    numpy.max(corners, axis=0))) * ref.scale + [ref.offset] | 
					
						
							| 
									
										
										
										
											2023-04-12 19:31:17 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 18:21:37 -07:00
										 |  |  |                     else: | 
					
						
							|  |  |  |                         # Non-manhattan rotation, have to figure out bounds by rotating the pattern | 
					
						
							|  |  |  |                         bounds = ref.get_bounds(library[target], library=library) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |                     if bounds is None: | 
					
						
							|  |  |  |                         continue | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 19:31:17 -07:00
										 |  |  |                     cbounds[0] = numpy.minimum(cbounds[0], bounds[0]) | 
					
						
							|  |  |  |                     cbounds[1] = numpy.maximum(cbounds[1], bounds[1]) | 
					
						
							| 
									
										
										
										
											2023-04-12 18:21:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 19:31:17 -07:00
										 |  |  |         if (cbounds[1] < cbounds[0]).any(): | 
					
						
							| 
									
										
										
										
											2019-05-25 12:40:59 -07:00
										 |  |  |             return None | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-04-12 19:31:17 -07:00
										 |  |  |             return cbounds | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |     def get_bounds_nonempty( | 
					
						
							|  |  |  |             self, | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |             library: Mapping[str, 'Pattern'] | None = None, | 
					
						
							| 
									
										
										
										
											2023-01-26 23:47:16 -08:00
										 |  |  |             recurse: bool = True, | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |             ) -> NDArray[numpy.float64]: | 
					
						
							| 
									
										
										
										
											2022-02-27 21:21:34 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Convenience wrapper for `get_bounds()` which asserts that the Pattern as non-None bounds. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-26 23:47:16 -08:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |             library: If `recurse=True`, any referenced patterns are loaded from this library. | 
					
						
							|  |  |  |             recurse: If `False`, do not evaluate the bounds of any refs (i.e. assume they are empty). | 
					
						
							|  |  |  |                 If `True`, evaluate the bounds of all refs and their conained geometry recursively. | 
					
						
							|  |  |  |                 Default `True`. | 
					
						
							|  |  |  |             cache: Mapping of `{name: bounds}` for patterns for which the bounds have already been calculated. | 
					
						
							|  |  |  |                 Modified during the run (any referenced pattern's bounds are added). | 
					
						
							| 
									
										
										
										
											2023-01-26 23:47:16 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-27 21:21:34 -08:00
										 |  |  |         Returns: | 
					
						
							|  |  |  |             `[[x_min, y_min], [x_max, y_max]]` | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         bounds = self.get_bounds(library) | 
					
						
							| 
									
										
										
										
											2023-01-23 22:27:26 -08:00
										 |  |  |         assert bounds is not None | 
					
						
							| 
									
										
										
										
											2022-02-27 21:21:34 -08:00
										 |  |  |         return bounds | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def translate_elements(self, offset: ArrayLike) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         Translates all shapes, label, refs, and ports by the given offset. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             offset: (x, y) to translate by | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for entry in chain(chain_elements(self.shapes, self.labels, self.refs), self.ports.values()): | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             cast(Positionable, entry).translate(offset) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def scale_elements(self, c: float) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """"
 | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |         Scales all shapes and refs by the given value. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             c: factor to scale by | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for entry in chain_elements(self.shapes, self.refs): | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             cast(Scalable, entry).scale_by(c) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def scale_by(self, c: float) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Scale this Pattern by the given value | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |          (all shapes and refs and their offsets are scaled, | 
					
						
							|  |  |  |           as are all label and port offsets) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             c: factor to scale by | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for entry in chain_elements(self.shapes, self.refs): | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             cast(Positionable, entry).offset *= c | 
					
						
							|  |  |  |             cast(Scalable, entry).scale_by(c) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             rep = cast(Repeatable, entry).repetition | 
					
						
							|  |  |  |             if rep: | 
					
						
							|  |  |  |                 rep.scale_by(c) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for label in chain_elements(self.labels): | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             cast(Positionable, label).offset *= c | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             rep = cast(Repeatable, label).repetition | 
					
						
							|  |  |  |             if rep: | 
					
						
							|  |  |  |                 rep.scale_by(c) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for port in self.ports.values(): | 
					
						
							|  |  |  |             port.offset *= c | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def rotate_around(self, pivot: ArrayLike, rotation: float) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Rotate the Pattern around the a location. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             pivot: (x, y) location to rotate around | 
					
						
							|  |  |  |             rotation: Angle to rotate by (counter-clockwise, radians) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         pivot = numpy.array(pivot) | 
					
						
							|  |  |  |         self.translate_elements(-pivot) | 
					
						
							|  |  |  |         self.rotate_elements(rotation) | 
					
						
							|  |  |  |         self.rotate_element_centers(rotation) | 
					
						
							|  |  |  |         self.translate_elements(+pivot) | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def rotate_element_centers(self, rotation: float) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         Rotate the offsets of all shapes, labels, refs, and ports around (0, 0) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             rotation: Angle to rotate by (counter-clockwise, radians) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for entry in chain(chain_elements(self.shapes, self.refs, self.labels), self.ports.values()): | 
					
						
							| 
									
										
										
										
											2023-01-19 22:20:16 -08:00
										 |  |  |             old_offset = cast(Positionable, entry).offset | 
					
						
							|  |  |  |             cast(Positionable, entry).offset = numpy.dot(rotation_matrix_2d(rotation), old_offset) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def rotate_elements(self, rotation: float) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         Rotate each shape, ref, and port around its origin (offset) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             rotation: Angle to rotate by (counter-clockwise, radians) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for entry in chain(chain_elements(self.shapes, self.refs), self.ports.values()): | 
					
						
							| 
									
										
										
										
											2020-11-09 22:04:04 -08:00
										 |  |  |             cast(Rotatable, entry).rotate(rotation) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-14 22:19:56 -07:00
										 |  |  |     def mirror_element_centers(self, across_axis: int = 0) -> Self: | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |         Mirror the offsets of all shapes, labels, and refs across an axis | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |             across_axis: Axis to mirror across | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |                 (0: mirror across x axis, 1: mirror across y axis) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for entry in chain(chain_elements(self.shapes, self.refs, self.labels), self.ports.values()): | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |             cast(Positionable, entry).offset[across_axis - 1] *= -1 | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-14 22:19:56 -07:00
										 |  |  |     def mirror_elements(self, across_axis: int = 0) -> Self: | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         Mirror each shape, ref, and pattern across an axis, relative | 
					
						
							|  |  |  |           to its offset | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |             across_axis: Axis to mirror across | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |                 (0: mirror across x axis, 1: mirror across y axis) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for entry in chain(chain_elements(self.shapes, self.refs), self.ports.values()): | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |             cast(Mirrorable, entry).mirror(across_axis) | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-14 22:19:56 -07:00
										 |  |  |     def mirror(self, across_axis: int = 0) -> Self: | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Mirror the Pattern across an axis | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |             across_axis: Axis to mirror across | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |                 (0: mirror across x axis, 1: mirror across y axis) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-24 23:25:10 -08:00
										 |  |  |         self.mirror_elements(across_axis) | 
					
						
							|  |  |  |         self.mirror_element_centers(across_axis) | 
					
						
							| 
									
										
										
										
											2018-04-14 15:29:19 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def copy(self) -> Self: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 21:43:58 -07:00
										 |  |  |         Convenience method for `copy.deepcopy(pattern)` (same as `Pattern.deepcopy()`). | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         See also: `Pattern.deepcopy()` | 
					
						
							| 
									
										
										
										
											2018-04-14 15:02:35 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Returns: | 
					
						
							| 
									
										
										
										
											2023-04-12 21:43:58 -07:00
										 |  |  |             A deep copy of the current Pattern. | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 21:43:58 -07:00
										 |  |  |         return copy.deepcopy(self) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |     def deepcopy(self) -> Self: | 
					
						
							| 
									
										
										
										
											2018-04-14 15:02:35 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Convenience method for `copy.deepcopy(pattern)` | 
					
						
							| 
									
										
										
										
											2018-04-14 15:02:35 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Returns: | 
					
						
							|  |  |  |             A deep copy of the current Pattern. | 
					
						
							| 
									
										
										
										
											2018-04-14 15:02:35 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         return copy.deepcopy(self) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-15 00:11:44 -07:00
										 |  |  |     def is_empty(self) -> bool: | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Returns: | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |             True if the pattern is contains no shapes, labels, or refs. | 
					
						
							| 
									
										
										
										
											2019-05-15 00:11:44 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         return not (self.has_refs() or self.has_shapes() or self.has_labels()) | 
					
						
							| 
									
										
										
										
											2019-05-15 00:11:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     def has_refs(self) -> bool: | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             True if the pattern contains any refs. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         return any(True for _ in chain.from_iterable(self.refs.values())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def has_shapes(self) -> bool: | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             True if the pattern contains any shapes. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         return any(True for _ in chain.from_iterable(self.shapes.values())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def has_labels(self) -> bool: | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             True if the pattern contains any labels. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         return any(True for _ in chain.from_iterable(self.labels.values())) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 21:29:12 -07:00
										 |  |  |     def has_ports(self) -> bool: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             True if the pattern contains any ports. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return bool(self.ports) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     def ref(self, target: str | None, *args: Any, **kwargs: Any) -> Self: | 
					
						
							| 
									
										
										
										
											2020-11-01 19:34:47 -08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |         Convenience function which constructs a `Ref` object and adds it | 
					
						
							| 
									
										
										
										
											2020-11-01 19:34:47 -08:00
										 |  |  |          to this pattern. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             target: Target for the ref | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |             *args: Passed to `Ref()` | 
					
						
							|  |  |  |             **kwargs: Passed to `Ref()` | 
					
						
							| 
									
										
										
										
											2020-11-01 19:34:47 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         self.refs[target].append(Ref(*args, **kwargs)) | 
					
						
							| 
									
										
										
										
											2020-11-01 19:34:47 -08:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     def polygon(self, layer: layer_t, *args: Any, **kwargs: Any) -> Self: | 
					
						
							| 
									
										
										
										
											2023-02-23 13:42:26 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Convenience function which constructs a `Polygon` object and adds it | 
					
						
							|  |  |  |          to this pattern. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             layer: Layer for the polygon | 
					
						
							| 
									
										
										
										
											2023-02-23 13:42:26 -08:00
										 |  |  |             *args: Passed to `Polygon()` | 
					
						
							|  |  |  |             **kwargs: Passed to `Polygon()` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         self.shapes[layer].append(Polygon(*args, **kwargs)) | 
					
						
							| 
									
										
										
										
											2023-02-23 13:42:26 -08:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     def rect(self, layer: layer_t, *args: Any, **kwargs: Any) -> Self: | 
					
						
							| 
									
										
										
										
											2023-01-25 23:19:25 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Convenience function which calls `Polygon.rect` to construct a | 
					
						
							|  |  |  |          rectangle and adds it to this pattern. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             layer: Layer for the rectangle | 
					
						
							| 
									
										
										
										
											2023-01-25 23:19:25 -08:00
										 |  |  |             *args: Passed to `Polygon.rect()` | 
					
						
							|  |  |  |             **kwargs: Passed to `Polygon.rect()` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         self.shapes[layer].append(Polygon.rect(*args, **kwargs)) | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def path(self, layer: layer_t, *args: Any, **kwargs: Any) -> Self: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Convenience function which constructs a `Path` object and adds it | 
					
						
							|  |  |  |          to this pattern. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |             layer: Layer for the path | 
					
						
							|  |  |  |             *args: Passed to `Path()` | 
					
						
							|  |  |  |             **kwargs: Passed to `Path()` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.shapes[layer].append(Path(*args, **kwargs)) | 
					
						
							| 
									
										
										
										
											2023-01-25 23:19:25 -08:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     def label(self, layer: layer_t, *args: Any, **kwargs: Any) -> Self: | 
					
						
							| 
									
										
										
										
											2023-02-23 13:42:26 -08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Convenience function which constructs a `Label` object | 
					
						
							|  |  |  |          and adds it to this pattern. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             layer: Layer for the label | 
					
						
							| 
									
										
										
										
											2023-02-23 13:42:26 -08:00
										 |  |  |             *args: Passed to `Label()` | 
					
						
							|  |  |  |             **kwargs: Passed to `Label()` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         self.labels[layer].append(Label(*args, **kwargs)) | 
					
						
							| 
									
										
										
										
											2023-02-23 13:42:26 -08:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-02 21:15:16 -07:00
										 |  |  |     def prune_layers(self) -> Self: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Removes empty layers (empty lists) in `self.shapes` and `self.labels`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         for layer in list(self.shapes): | 
					
						
							|  |  |  |             if not self.shapes[layer]: | 
					
						
							|  |  |  |                 del self.shapes[layer] | 
					
						
							|  |  |  |         for layer in list(self.labels): | 
					
						
							|  |  |  |             if not self.labels[layer]: | 
					
						
							|  |  |  |                 del self.labels[layer] | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def prune_refs(self) -> Self: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Remove empty ref lists in `self.refs`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         for target in list(self.refs): | 
					
						
							|  |  |  |             if not self.refs[target]: | 
					
						
							|  |  |  |                 del self.refs[target] | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |     def flatten( | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |             self, | 
					
						
							|  |  |  |             library: Mapping[str, 'Pattern'], | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |             flatten_ports: bool = False, | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |             ) -> 'Pattern': | 
					
						
							| 
									
										
										
										
											2019-12-12 00:38:11 -08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |         Removes all refs (recursively) and adds equivalent shapes. | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |         Alters the current pattern in-place. | 
					
						
							|  |  |  |         For a version which creates copies, see `Library.flatten`. | 
					
						
							| 
									
										
										
										
											2019-12-12 00:38:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             library: Source for referenced patterns. | 
					
						
							| 
									
										
										
										
											2023-07-17 21:28:42 -07:00
										 |  |  |             flatten_ports: If `True`, keep ports from any referenced | 
					
						
							|  |  |  |                 patterns; otherwise discard them. | 
					
						
							| 
									
										
										
										
											2019-12-12 00:38:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Returns: | 
					
						
							|  |  |  |             self | 
					
						
							| 
									
										
										
										
											2019-12-12 00:38:11 -08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-02-23 16:23:06 -08:00
										 |  |  |         flattened: dict[str | None, 'Pattern | None'] = {} | 
					
						
							| 
									
										
										
										
											2019-12-12 00:38:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:15:32 -08:00
										 |  |  |         def flatten_single(name: str | None) -> None: | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |             if name is None: | 
					
						
							|  |  |  |                 pat = self | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 pat = library[name].deepcopy() | 
					
						
							|  |  |  |                 flattened[name] = None | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |             for target, refs in pat.refs.items(): | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |                 if target is None: | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |                 if not refs: | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |                 if target not in flattened: | 
					
						
							|  |  |  |                     flatten_single(target) | 
					
						
							| 
									
										
										
										
											2023-02-09 16:38:33 -08:00
										 |  |  |                 target_pat = flattened[target] | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 16:38:33 -08:00
										 |  |  |                 if target_pat is None: | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |                     raise PatternError(f'Circular reference in {name} to {target}') | 
					
						
							| 
									
										
										
										
											2023-02-09 16:43:06 -08:00
										 |  |  |                 if target_pat.is_empty():        # avoid some extra allocations | 
					
						
							| 
									
										
										
										
											2023-02-09 16:38:33 -08:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |                 for ref in refs: | 
					
						
							|  |  |  |                     p = ref.as_pattern(pattern=target_pat) | 
					
						
							|  |  |  |                     if not flatten_ports: | 
					
						
							|  |  |  |                         p.ports.clear() | 
					
						
							|  |  |  |                     pat.append(p) | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |             pat.refs.clear() | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |             flattened[name] = pat | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         flatten_single(None) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |     def visualize( | 
					
						
							| 
									
										
										
										
											2023-02-23 13:37:34 -08:00
										 |  |  |             self, | 
					
						
							|  |  |  |             library: Mapping[str, 'Pattern'] | None = None, | 
					
						
							| 
									
										
										
										
											2022-02-23 15:47:38 -08:00
										 |  |  |             offset: ArrayLike = (0., 0.), | 
					
						
							| 
									
										
										
										
											2022-02-23 11:27:11 -08:00
										 |  |  |             line_color: str = 'k', | 
					
						
							|  |  |  |             fill_color: str = 'none', | 
					
						
							|  |  |  |             overdraw: bool = False, | 
					
						
							|  |  |  |             ) -> None: | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Draw a picture of the Pattern and wait for the user to inspect it | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Imports `matplotlib`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Note that this can be slow; it is often faster to export to GDSII and use | 
					
						
							|  |  |  |          klayout or a different GDS viewer! | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 21:02:53 -08:00
										 |  |  |         Args: | 
					
						
							|  |  |  |             offset: Coordinates to offset by before drawing | 
					
						
							|  |  |  |             line_color: Outlines are drawn with this color (passed to `matplotlib.collections.PolyCollection`) | 
					
						
							|  |  |  |             fill_color: Interiors are drawn with this color (passed to `matplotlib.collections.PolyCollection`) | 
					
						
							|  |  |  |             overdraw: Whether to create a new figure or draw on a pre-existing one | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2018-08-30 23:06:31 -07:00
										 |  |  |         # TODO: add text labels to visualize() | 
					
						
							| 
									
										
										
										
											2020-09-18 19:47:31 -07:00
										 |  |  |         from matplotlib import pyplot       # type: ignore | 
					
						
							|  |  |  |         import matplotlib.collections       # type: ignore | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         if self.has_refs() and library is None: | 
					
						
							| 
									
										
										
										
											2023-01-21 21:22:11 -08:00
										 |  |  |             raise PatternError('Must provide a library when visualizing a pattern with refs') | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         offset = numpy.array(offset, dtype=float) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not overdraw: | 
					
						
							|  |  |  |             figure = pyplot.figure() | 
					
						
							|  |  |  |             pyplot.axis('equal') | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             figure = pyplot.gcf() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         axes = figure.gca() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         polygons = [] | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for shape in chain.from_iterable(self.shapes.values()): | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |             polygons += [offset + s.offset + s.vertices for s in shape.to_polygons()] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 11:27:29 -07:00
										 |  |  |         mpl_poly_collection = matplotlib.collections.PolyCollection( | 
					
						
							|  |  |  |             polygons, | 
					
						
							|  |  |  |             facecolors=fill_color, | 
					
						
							|  |  |  |             edgecolors=line_color, | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |         axes.add_collection(mpl_poly_collection) | 
					
						
							|  |  |  |         pyplot.axis('equal') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         for target, refs in self.refs.items(): | 
					
						
							|  |  |  |             if target is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             if not refs: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             assert library is not None | 
					
						
							|  |  |  |             target_pat = library[target] | 
					
						
							|  |  |  |             for ref in refs: | 
					
						
							|  |  |  |                 ref.as_pattern(target_pat).visualize( | 
					
						
							|  |  |  |                     library=library, | 
					
						
							|  |  |  |                     offset=offset, | 
					
						
							|  |  |  |                     overdraw=True, | 
					
						
							|  |  |  |                     line_color=line_color, | 
					
						
							|  |  |  |                     fill_color=fill_color, | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if not overdraw: | 
					
						
							| 
									
										
										
										
											2021-01-08 21:20:03 -08:00
										 |  |  |             pyplot.xlabel('x') | 
					
						
							|  |  |  |             pyplot.ylabel('y') | 
					
						
							| 
									
										
										
										
											2016-03-15 19:12:39 -07:00
										 |  |  |             pyplot.show() | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TT = TypeVar('TT') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def chain_elements(*args: Mapping[Any, Iterable[TT]]) -> Iterable[TT]: | 
					
						
							| 
									
										
										
										
											2023-09-17 21:33:22 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Iterate over each element in one or more {layer: elements} mappings. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Useful when you want to do some operation on all shapes and/or labels, | 
					
						
							|  |  |  |     disregarding which layer they are on. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |         *args: One or more {layer: [element0, ...]} mappings. | 
					
						
							|  |  |  |             Can also be applied to e.g. {target: [ref0, ...]} mappings. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |         An iterable containing all elements, regardless of layer. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     return chain(*(chain.from_iterable(aa.values()) for aa in args)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def map_layers( | 
					
						
							|  |  |  |         elements: Mapping[layer_t, Sequence[TT]], | 
					
						
							| 
									
										
										
										
											2023-04-12 14:47:10 -07:00
										 |  |  |         map_layer: Callable[[layer_t], layer_t], | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         ) -> defaultdict[layer_t, list[TT]]: | 
					
						
							| 
									
										
										
										
											2023-09-17 21:33:22 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Move all the elements from one layer onto a different layer. | 
					
						
							|  |  |  |     Can also handle multiple such mappings simultaneously. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |         elements: Mapping of {old_layer: geometry_or_labels}. | 
					
						
							|  |  |  |         map_layer: Callable which may be called with each layer present in `elements`, | 
					
						
							|  |  |  |             and should return the new layer to which it will be mapped. | 
					
						
							|  |  |  |             A simple example which maps `old_layer` to `new_layer` and leaves all others | 
					
						
							|  |  |  |             as-is would look like `lambda layer: {old_layer: new_layer}.get(layer, layer)` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |         Mapping of {new_layer: geometry_or_labels} | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     new_elements: defaultdict[layer_t, list[TT]] = defaultdict(list) | 
					
						
							|  |  |  |     for old_layer, seq in elements.items(): | 
					
						
							| 
									
										
										
										
											2023-04-12 14:47:10 -07:00
										 |  |  |         new_layer = map_layer(old_layer) | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         new_elements[new_layer].extend(seq) | 
					
						
							|  |  |  |     return new_elements | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def map_targets( | 
					
						
							|  |  |  |         refs: Mapping[str | None, Sequence[Ref]], | 
					
						
							| 
									
										
										
										
											2023-04-12 14:47:10 -07:00
										 |  |  |         map_target: Callable[[str | None], str | None], | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         ) -> defaultdict[str | None, list[Ref]]: | 
					
						
							| 
									
										
										
										
											2023-09-17 21:33:22 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Change the target of all references to a given cell. | 
					
						
							|  |  |  |     Can also handle multiple such mappings simultaneously. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |         refs: Mapping of {old_target: ref_objects}. | 
					
						
							|  |  |  |         map_target: Callable which may be called with each target present in `refs`, | 
					
						
							|  |  |  |             and should return the new target to which it will be mapped. | 
					
						
							|  |  |  |             A simple example which maps `old_target` to `new_target` and leaves all others | 
					
						
							|  |  |  |             as-is would look like `lambda target: {old_target: new_target}.get(target, target)` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |         Mapping of {new_target: ref_objects} | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |     new_refs: defaultdict[str | None, list[Ref]] = defaultdict(list) | 
					
						
							|  |  |  |     for old_target, seq in refs.items(): | 
					
						
							| 
									
										
										
										
											2023-04-12 14:47:10 -07:00
										 |  |  |         new_target = map_target(old_target) | 
					
						
							| 
									
										
										
										
											2023-04-12 13:56:50 -07:00
										 |  |  |         new_refs[new_target].extend(seq) | 
					
						
							|  |  |  |     return new_refs |