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 .error import PatternError
from .shapes import Shape from .shapes import Shape
from .label import Label
from .subpattern import SubPattern from .subpattern import SubPattern
from .pattern import Pattern from .pattern import Pattern

View File

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

View File

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