diff --git a/masque/file/gdsii.py b/masque/file/gdsii.py index c91c061..a8a583d 100644 --- a/masque/file/gdsii.py +++ b/masque/file/gdsii.py @@ -103,6 +103,8 @@ def build(patterns: Union[Pattern, List[Pattern]], if not modify_originals: patterns = [p.deepunlock() for p in copy.deepcopy(patterns)] + patterns = [p.wrap_repeated_shapes() for p in patterns] + # Create library lib = gdsii.library.Library(version=600, name=library_name.encode('ASCII'), diff --git a/masque/file/oasis.py b/masque/file/oasis.py index 981d1e0..834949d 100644 --- a/masque/file/oasis.py +++ b/masque/file/oasis.py @@ -260,18 +260,19 @@ def read(stream: io.BufferedIOBase, for element in cell.geometry: if isinstance(element, fatrec.XElement): logger.warning('Skipping XElement record') + # note XELEMENT has no repetition continue - if element.repetition is not None: - # note XELEMENT has no repetition - raise PatternError('masque OASIS reader does not implement repetitions for shapes yet') + + repetition = repetition_fata2masq(element.repetition) # Switch based on element type: if isinstance(element, fatrec.Polygon): vertices = numpy.cumsum(numpy.vstack(((0, 0), element.get_point_list())), axis=0) poly = Polygon(vertices=vertices, layer=element.get_layer_tuple(), - offset=element.get_xy()) + offset=element.get_xy(), + repetition=repetition) if clean_vertices: try: @@ -297,6 +298,7 @@ def read(stream: io.BufferedIOBase, path = Path(vertices=vertices, layer=element.get_layer_tuple(), offset=element.get_xy(), + repeition=repetition, width=element.get_half_width() * 2, cap=cap, **path_args) @@ -314,6 +316,7 @@ def read(stream: io.BufferedIOBase, height = element.get_height() rect = Polygon(layer=element.get_layer_tuple(), offset=element.get_xy(), + repetition=repetition, vertices=numpy.array(((0, 0), (1, 0), (1, 1), (0, 1))) * (width, height), ) pat.shapes.append(rect) @@ -345,6 +348,7 @@ def read(stream: io.BufferedIOBase, trapz = Polygon(layer=element.get_layer_tuple(), offset=element.get_xy(), + repetition=repetition, vertices=vertices, ) pat.shapes.append(trapz) @@ -396,20 +400,23 @@ def read(stream: io.BufferedIOBase, vertices[0, 1] += width ctrapz = Polygon(layer=element.get_layer_tuple(), - offset=element.get_xy(), - vertices=vertices, - ) + offset=element.get_xy(), + repetition=repetition, + vertices=vertices, + ) pat.shapes.append(ctrapz) elif isinstance(element, fatrec.Circle): circle = Circle(layer=element.get_layer_tuple(), offset=element.get_xy(), + repetition=repetition, radius=float(element.get_radius())) pat.shapes.append(circle) elif isinstance(element, fatrec.Text): label = Label(layer=element.get_layer_tuple(), offset=element.get_xy(), + repetition=repetition, string=str(element.get_string())) pat.labels.append(label) @@ -467,24 +474,10 @@ def _placement_to_subpat(placement: fatrec.Placement) -> SubPattern: 'rotation': float(placement.angle * pi/180), 'scale': mag, 'identifier': (name,), + 'repetition': repetition_fata2masq(placement.repetition), } - mrep: Optional[Repetition] - rep = placement.repetition - if isinstance(rep, fatamorgana.GridRepetition): - mrep = Grid(a_vector=rep.a_vector, - b_vector=rep.b_vector, - a_count=rep.a_count, - b_count=rep.b_count) - elif isinstance(rep, fatamorgana.ArbitraryRepetition): - displacements = numpy.cumsum(numpy.column_stack((rep.x_displacements, - rep.y_displacements))) - displacements = numpy.vstack(([0, 0], displacements)) - mrep = Arbitrary(displacements) - elif rep is None: - mrep = None - - subpat = SubPattern(offset=xy, repetition=mrep, **args) + subpat = SubPattern(offset=xy, **args) return subpat @@ -497,27 +490,14 @@ def _subpatterns_to_refs(subpatterns: List[SubPattern] # Note: OASIS mirrors first and rotates second mirror_across_x, extra_angle = normalize_mirror(subpat.mirrored) - xy = numpy.round(subpat.offset).astype(int) - args: Dict[str, Any] = { - 'x': xy[0], - 'y': xy[1], - } + frep, rep_offset = repetition_masq2fata(subpat.repetition) - rep = subpat.repetition - if isinstance(rep, Grid): - args['repetition'] = fatamorgana.GridRepetition( - a_vector=numpy.round(rep.a_vector).astype(int), - b_vector=numpy.round(rep.b_vector).astype(int), - a_count=numpy.round(rep.a_count).astype(int), - b_count=numpy.round(rep.b_count).astype(int)) - elif isinstance(rep, Arbitrary): - diffs = numpy.diff(rep.displacements, axis=0) - diff_ints = numpy.round(diffs).astype(int) - args['repetition'] = fatamorgana.ArbitraryRepetition(diff_ints[:, 0], diff_ints[:, 1]) - args['x'] += rep.displacements[0, 0] - args['y'] += rep.displacements[0, 1] - else: - assert(rep is None) + offset = numpy.round(subpat.offset + rep_offset).astype(int) + args: Dict[str, Any] = { + 'x': offset[0], + 'y': offset[1], + 'repetition': frep, + } angle = numpy.rad2deg(subpat.rotation + extra_angle) % 360 ref = fatrec.Placement( @@ -538,17 +518,19 @@ def _shapes_to_elements(shapes: List[Shape], elements: List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]] = [] for shape in shapes: layer, datatype = layer2oas(shape.layer) + repetition, rep_offset = repetition_masq2fata(shape.repetition) if isinstance(shape, Circle): - offset = numpy.round(shape.offset).astype(int) + offset = numpy.round(shape.offset + rep_offset).astype(int) radius = numpy.round(shape.radius).astype(int) circle = fatrec.Circle(layer=layer, datatype=datatype, radius=radius, x=offset[0], - y=offset[1]) + y=offset[1], + repetition=repetition) elements.append(circle) elif isinstance(shape, Path): - xy = numpy.round(shape.offset + shape.vertices[0]).astype(int) + xy = numpy.round(shape.offset + shape.vertices[0] + rep_offset).astype(int) deltas = numpy.round(numpy.diff(shape.vertices, axis=0)).astype(int) half_width = numpy.round(shape.width / 2).astype(int) path_type = next(k for k, v in path_cap_map.items() if v == shape.cap) #reverse lookup @@ -562,17 +544,19 @@ def _shapes_to_elements(shapes: List[Shape], y=xy[1], extension_start=extension_start, #TODO implement multiple cap types? extension_end=extension_end, + repetition=repetition, ) elements.append(path) else: for polygon in shape.to_polygons(): - xy = numpy.round(polygon.offset + polygon.vertices[0]).astype(int) + xy = numpy.round(polygon.offset + polygon.vertices[0] + rep_offset).astype(int) points = numpy.round(numpy.diff(polygon.vertices, axis=0)).astype(int) elements.append(fatrec.Polygon(layer=layer, datatype=datatype, x=xy[0], y=xy[1], - point_list=points)) + point_list=points, + repetition=repetition)) return elements @@ -582,12 +566,14 @@ def _labels_to_texts(labels: List[Label], texts = [] for label in labels: layer, datatype = layer2oas(label.layer) - xy = numpy.round(label.offset).astype(int) + repetition, rep_offset = repetition_masq2fata(shape.repetition) + xy = numpy.round(label.offset + rep_offset).astype(int) texts.append(fatrec.Text(layer=layer, datatype=datatype, x=xy[0], y=xy[1], - string=label.string)) + string=label.string, + repetition=repetition)) return texts @@ -619,3 +605,40 @@ def disambiguate_pattern_names(patterns, pat.name = suffixed_name used_names.append(suffixed_name) + + +def repetition_fata2masq(rep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None] + ) -> Optional[Repetition]: + if isinstance(rep, fatamorgana.GridRepetition): + mrep = Grid(a_vector=rep.a_vector, + b_vector=rep.b_vector, + a_count=rep.a_count, + b_count=rep.b_count) + elif isinstance(rep, fatamorgana.ArbitraryRepetition): + displacements = numpy.cumsum(numpy.column_stack((rep.x_displacements, + rep.y_displacements))) + displacements = numpy.vstack(([0, 0], displacements)) + mrep = Arbitrary(displacements) + elif rep is None: + mrep = None + return mrep + + +def repetition_masq2fata(rep: Optional[Repetition]): + if isinstance(rep, Grid): + frep = fatamorgana.GridRepetition( + a_vector=numpy.round(rep.a_vector).astype(int), + b_vector=numpy.round(rep.b_vector).astype(int), + a_count=numpy.round(rep.a_count).astype(int), + b_count=numpy.round(rep.b_count).astype(int)) + offset = (0, 0) + elif isinstance(rep, Arbitrary): + diffs = numpy.diff(rep.displacements, axis=0) + diff_ints = numpy.round(diffs).astype(int) + frep = fatamorgana.ArbitraryRepetition(diff_ints[:, 0], diff_ints[:, 1]) + offset = rep.displacements[0, :] + else: + assert(rep is None) + frep = None + offset = (0, 0) + return frep, offset