add support for annotations
and other fixes
This commit is contained in:
parent
c611699fc8
commit
73c9050138
28 changed files with 400 additions and 133 deletions
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue