add support for labels (e.g. GDS TEXT)
This commit is contained in:
parent
8623dbbeac
commit
108694551b
@ -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
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user