add support for labels (e.g. GDS TEXT)

This commit is contained in:
jan 2018-08-30 23:06:31 -07:00
parent 8623dbbeac
commit 108694551b
3 changed files with 57 additions and 13 deletions

View File

@ -26,6 +26,7 @@
from .error import PatternError
from .shapes import Shape
from .label import Label
from .subpattern import SubPattern
from .pattern import Pattern

View File

@ -11,7 +11,7 @@ import re
import numpy
from .utils import mangle_name, make_dose_table
from .. import Pattern, SubPattern, PatternError
from .. import Pattern, SubPattern, PatternError, Label
from ..shapes import Polygon
from ..utils import rotation_matrix_2d, get_bit, set_bit, vector2, is_scalar
@ -74,6 +74,7 @@ def write(patterns: Pattern or List[Pattern],
structure = gdsii.structure.Structure(name=encoded_name)
lib.append(structure)
# Add a Boundary element for each shape
for shape in pat.shapes:
layer, data_type = _mlayer2gds(shape.layer)
@ -83,6 +84,14 @@ def write(patterns: Pattern or List[Pattern],
structure.append(gdsii.elements.Boundary(layer=layer,
data_type=data_type,
xy=xy_closed))
for label in pat.labels:
layer, text_type = _mlayer2gds(label.layer)
xy_closed = numpy.round([label.offset, label.offset]).astype(int)
structure.append(gdsii.elements.Text(layer=layer,
text_type=text_type,
xy=xy_closed,
string=label.string.encode('ASCII')))
# Add an SREF for each subpattern entry
# strans must be set for angle and mag to take effect
for subpat in pat.subpatterns:
@ -200,6 +209,14 @@ def write_dose2dtype(patterns: Pattern or List[Pattern],
structure.append(gdsii.elements.Boundary(layer=layer,
data_type=data_type,
xy=xy_closed))
for label in pat.labels:
layer, text_type = _mlayer2gds(label.layer)
xy_closed = numpy.round([label.offset, label.offset]).astype(int)
structure.append(gdsii.elements.Text(layer=layer,
text_type=text_type,
xy=xy_closed,
string=label.string.encode('ASCII')))
# Add an SREF for each subpattern entry
# strans must be set for angle and mag to take effect
for subpat in pat.subpatterns:
@ -311,6 +328,12 @@ def read(filename: str,
pat.shapes.append(shape)
elif isinstance(element, gdsii.elements.Text):
label = Label(offset=element.xy,
layer=(element.layer, element.text_type),
string=element.string.decode('ASCII'))
pat.labels.append(label)
elif isinstance(element, gdsii.elements.SRef):
pat.subpatterns.append(ref_element_to_subpat(element, element.xy))

View File

@ -13,6 +13,7 @@ import numpy
from .subpattern import SubPattern
from .shapes import Shape, Polygon
from .label import Label
from .utils import rotation_matrix_2d, vector2
from .error import PatternError
@ -32,11 +33,13 @@ class Pattern:
:var name: An identifier for this object. Not necessarily unique.
"""
shapes = None # type: List[Shape]
labels = None # type: List[Labels]
subpatterns = None # type: List[SubPattern]
name = None # type: str
def __init__(self,
shapes: List[Shape]=(),
labels: List[Label]=(),
subpatterns: List[SubPattern]=(),
name: str='',
):
@ -45,6 +48,7 @@ class Pattern:
Non-list inputs for shapes and subpatterns get converted to lists.
:param shapes: Initial shapes in the Pattern
:param labels: Initial labels in the Pattern
:param subpatterns: Initial subpatterns in the Pattern
:param name: An identifier for the Pattern
"""
@ -53,6 +57,11 @@ class Pattern:
else:
self.shapes = list(shapes)
if isinstance(labels, list):
self.labels = labels
else:
self.labels = list(labels)
if isinstance(subpatterns, list):
self.subpatterns = subpatterns
else:
@ -62,27 +71,32 @@ class Pattern:
def append(self, other_pattern: 'Pattern') -> 'Pattern':
"""
Appends all shapes and subpatterns from other_pattern to self's shapes and subpatterns.
Appends all shapes, labels and subpatterns from other_pattern to self's shapes,
labels, and supbatterns.
:param other_pattern: The Pattern to append
:return: self
"""
self.subpatterns += other_pattern.subpatterns
self.shapes += other_pattern.shapes
self.labels += other_pattern.labels
return self
def subset(self,
shapes_func: Callable[[Shape], bool]=None,
labels_func: Callable[[Label], bool]=None,
subpatterns_func: Callable[[SubPattern], bool]=None,
recursive: bool=False,
) -> 'Pattern':
"""
Returns a Pattern containing only the shapes and subpatterns for which shapes_func or
subpatterns_func returns True.
Self is _not_ altered, but shapes and subpatterns are _not_ copied.
Returns a Pattern containing only the entities (e.g. shapes) for which the
given entity_func returns True.
Self is _not_ altered, but shapes, labels, and subpatterns are _not_ copied.
:param shapes_func: Given a shape, returns a boolean denoting whether the shape is a member
of the subset. Default always returns False.
:param labels_func: Given a label, returns a boolean denoting whether the label is a member
of the subset. Default always returns False.
:param subpatterns_func: Given a subpattern, returns a boolean denoting if it is a member
of the subset. Default always returns False.
:param recursive: If True, also calls .subset() recursively on patterns referenced by this
@ -94,6 +108,8 @@ class Pattern:
pat = Pattern(name=src.name)
if shapes_func is not None:
pat.shapes = [s for s in src.shapes if shapes_func(s)]
if labels_func is not None:
pat.labels = [s for s in src.labels if labels_func(s)]
if subpatterns_func is not None:
pat.subpatterns = [s for s in src.subpatterns if subpatterns_func(s)]
return pat
@ -281,7 +297,7 @@ class Pattern:
:return: [[x_min, y_min], [x_max, y_max]] or None
"""
entries = self.shapes + self.subpatterns
entries = self.shapes + self.subpatterns + self.labels
if not entries:
return None
@ -304,17 +320,19 @@ class Pattern:
self.subpatterns = []
for subpat in subpatterns:
subpat.pattern.flatten()
self.shapes += subpat.as_pattern().shapes
p = subpat.as_pattern()
self.shapes += p.shapes
self.labels += p.labels
return self
def translate_elements(self, offset: vector2) -> 'Pattern':
"""
Translates all shapes and subpatterns by the given offset.
Translates all shapes, label, and subpatterns by the given offset.
:param offset: Offset to translate by
:return: self
"""
for entry in self.shapes + self.subpatterns:
for entry in self.shapes + self.subpatterns + self.labels:
entry.translate(offset)
return self
@ -359,12 +377,12 @@ class Pattern:
def rotate_element_centers(self, rotation: float) -> 'Pattern':
"""
Rotate the offsets of all shapes and subpatterns around (0, 0)
Rotate the offsets of all shapes, labels, and subpatterns around (0, 0)
:param rotation: Angle to rotate by (counter-clockwise, radians)
:return: self
"""
for entry in self.shapes + self.subpatterns:
for entry in self.shapes + self.subpatterns + self.labels:
entry.offset = numpy.dot(rotation_matrix_2d(rotation), entry.offset)
return self
@ -381,12 +399,12 @@ class Pattern:
def mirror_element_centers(self, axis: int) -> 'Pattern':
"""
Mirror the offsets of all shapes and subpatterns across an axis
Mirror the offsets of all shapes, labels, and subpatterns across an axis
:param axis: Axis to mirror across
:return: self
"""
for entry in self.shapes + self.subpatterns:
for entry in self.shapes + self.subpatterns + self.labels:
entry.offset[axis - 1] *= -1
return self
@ -435,6 +453,7 @@ class Pattern:
"""
cp = copy.copy(self)
cp.shapes = copy.deepcopy(cp.shapes)
cp.labels = copy.deepcopy(cp.labels)
cp.subpatterns = [copy.copy(subpat) for subpat in cp.subpatterns]
return cp
@ -487,6 +506,7 @@ class Pattern:
:param fill_color: Interiors are drawn with this color (passed to matplotlib PolyCollection)
:param overdraw: Whether to create a new figure or draw on a pre-existing one
"""
# TODO: add text labels to visualize()
from matplotlib import pyplot
import matplotlib.collections