add support for annotations

and other fixes
This commit is contained in:
Jan Petykiewicz 2020-09-10 20:06:58 -07:00
commit 73c9050138
28 changed files with 400 additions and 133 deletions

View file

@ -11,17 +11,18 @@ Note that GDSII references follow the same convention as `masque`,
Scaling, rotation, and mirroring apply to individual instances, not grid
vectors or offsets.
"""
from typing import List, Any, Dict, Tuple, Callable, Union, Sequence, Iterable, Optional, Sequence
from typing import List, Any, Dict, Tuple, Callable, Union, Sequence, Iterable, Optional
from typing import Sequence, Mapping
import re
import io
import copy
import numpy
import base64
import struct
import logging
import pathlib
import gzip
import numpy # type: ignore
# python-gdsii
import gdsii.library
import gdsii.structure
@ -32,7 +33,7 @@ from .. import Pattern, SubPattern, PatternError, Label, Shape
from ..shapes import Polygon, Path
from ..repetition import Grid
from ..utils import rotation_matrix_2d, get_bit, set_bit, vector2, is_scalar, layer_t
from ..utils import remove_colinear_vertices, normalize_mirror
from ..utils import remove_colinear_vertices, normalize_mirror, annotations_t
#TODO absolute positioning
@ -99,6 +100,7 @@ def build(patterns: Union[Pattern, List[Pattern]],
if disambiguate_func is None:
disambiguate_func = disambiguate_pattern_names
assert(disambiguate_func is not None) # placate mypy
if not modify_originals:
patterns = [p.deepunlock() for p in copy.deepcopy(patterns)]
@ -124,6 +126,8 @@ def build(patterns: Union[Pattern, List[Pattern]],
structure = gdsii.structure.Structure(name=pat.name.encode('ASCII'))
lib.append(structure)
structure.properties = _annotations_to_properties(pat.annotations, 512)
structure += _shapes_to_elements(pat.shapes)
structure += _labels_to_texts(pat.labels)
structure += _subpatterns_to_refs(pat.subpatterns)
@ -238,6 +242,9 @@ def read(stream: io.BufferedIOBase,
patterns = []
for structure in lib:
pat = Pattern(name=structure.name.decode('ASCII'))
if pat.annotations:
logger.warning('Dropping Pattern-level annotations; they are not supported by python-gdsii')
# pat.annotations = {str(k): v for k, v in structure.properties}
for element in structure:
# Switch based on element type:
if isinstance(element, gdsii.elements.Boundary):
@ -343,6 +350,7 @@ def _ref_to_subpat(element: Union[gdsii.elements.SRef,
rotation=rotation,
scale=scale,
mirrored=(mirror_across_x, False),
annotations=_properties_to_annotations(element.properties),
repetition=repetition)
subpat.identifier = (element.struct_name,)
return subpat
@ -359,6 +367,7 @@ def _gpath_to_mpath(element: gdsii.elements.Path, raw_mode: bool) -> Path:
'width': element.width if element.width is not None else 0.0,
'cap': cap,
'offset': numpy.zeros(2),
'annotations':_properties_to_annotations(element.properties),
'raw': raw_mode,
}
@ -376,6 +385,7 @@ def _boundary_to_polygon(element: gdsii.elements.Boundary, raw_mode: bool) -> Po
args = {'vertices': element.xy[:-1].astype(float),
'layer': (element.layer, element.data_type),
'offset': numpy.zeros(2),
'annotations':_properties_to_annotations(element.properties),
'raw': raw_mode,
}
return Polygon(**args)
@ -420,11 +430,38 @@ def _subpatterns_to_refs(subpatterns: List[SubPattern]
# strans must be non-None for angle and mag to take effect
ref.strans = set_bit(0, 15 - 0, mirror_across_x)
ref.mag = subpat.scale
ref.properties = _annotations_to_properties(subpat.annotations, 512)
refs += new_refs
return refs
def _properties_to_annotations(properties: List[Tuple[int, bytes]]) -> annotations_t:
return {str(k): [v.decode()] for k, v in properties}
def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -> List[Tuple[int, bytes]]:
cum_len = 0
props = []
for key, vals in annotations.items():
try:
i = int(key)
except:
raise PatternError(f'Annotation key {key} is not convertable to an integer')
if not (0 < i < 126):
raise PatternError(f'Annotation key {key} converts to {i} (must be in the range [1,125])')
val_strings = ' '.join(str(val) for val in vals)
b = val_strings.encode()
if len(b) > 126:
raise PatternError(f'Annotation value {b!r} is longer than 126 characters!')
cum_len += numpy.ceil(len(b) / 2) * 2 + 2
if cum_len > max_len:
raise PatternError(f'Sum of annotation data will be longer than {max_len} bytes! Generated bytes were {b!r}')
props.append((i, b))
return props
def _shapes_to_elements(shapes: List[Shape],
polygonize_paths: bool = False
) -> List[Union[gdsii.elements.Boundary, gdsii.elements.Path]]:
@ -432,6 +469,7 @@ def _shapes_to_elements(shapes: List[Shape],
# Add a Boundary element for each shape, and Path elements if necessary
for shape in shapes:
layer, data_type = _mlayer2gds(shape.layer)
properties = _annotations_to_properties(shape.annotations, 128)
if isinstance(shape, Path) and not polygonize_paths:
xy = numpy.round(shape.vertices + shape.offset).astype(int)
width = numpy.round(shape.width).astype(int)
@ -441,26 +479,32 @@ def _shapes_to_elements(shapes: List[Shape],
xy=xy)
path.path_type = path_type
path.width = width
path.properties = properties
elements.append(path)
else:
for polygon in shape.to_polygons():
xy_open = numpy.round(polygon.vertices + polygon.offset).astype(int)
xy_closed = numpy.vstack((xy_open, xy_open[0, :]))
elements.append(gdsii.elements.Boundary(layer=layer,
data_type=data_type,
xy=xy_closed))
boundary = gdsii.elements.Boundary(layer=layer,
data_type=data_type,
xy=xy_closed)
boundary.properties = properties
elements.append(boundary)
return elements
def _labels_to_texts(labels: List[Label]) -> List[gdsii.elements.Text]:
texts = []
for label in labels:
properties = _annotations_to_properties(label.annotations, 128)
layer, text_type = _mlayer2gds(label.layer)
xy = numpy.round([label.offset]).astype(int)
texts.append(gdsii.elements.Text(layer=layer,
text_type=text_type,
xy=xy,
string=label.string.encode('ASCII')))
text = gdsii.elements.Text(layer=layer,
text_type=text_type,
xy=xy,
string=label.string.encode('ASCII'))
text.properties = properties
texts.append(text)
return texts