Add repetitions and split up code into traits
This commit is contained in:
parent
d4fbdd8d27
commit
bab40474a0
27 changed files with 1183 additions and 929 deletions
|
|
@ -16,8 +16,9 @@ from numpy import pi
|
|||
import ezdxf
|
||||
|
||||
from .utils import mangle_name, make_dose_table
|
||||
from .. import Pattern, SubPattern, GridRepetition, PatternError, Label, Shape, subpattern_t
|
||||
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
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ def write(pattern: Pattern,
|
|||
If you want pattern polygonized with non-default arguments, just call `pattern.polygonize()`
|
||||
prior to calling this function.
|
||||
|
||||
Only `GridRepetition` objects with manhattan basis vectors are preserved as arrays. Since DXF
|
||||
Only `Grid` repetition objects with manhattan basis vectors are preserved as arrays. Since DXF
|
||||
rotations apply to basis vectors while `masque`'s rotations do not, the basis vectors of an
|
||||
array with rotated instances must be manhattan _after_ having a compensating rotation applied.
|
||||
|
||||
|
|
@ -276,7 +277,7 @@ def _read_block(block, clean_vertices):
|
|||
|
||||
|
||||
def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||
subpatterns: List[subpattern_t]):
|
||||
subpatterns: List[SubPattern]):
|
||||
for subpat in subpatterns:
|
||||
if subpat.pattern is None:
|
||||
continue
|
||||
|
|
@ -289,9 +290,12 @@ def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.M
|
|||
'rotation': rotation,
|
||||
}
|
||||
|
||||
if isinstance(subpat, GridRepetition):
|
||||
a = subpat.a_vector
|
||||
b = subpat.b_vector if subpat.b_vector is not None else numpy.zeros(2)
|
||||
rep = subpat.repetition
|
||||
if rep is None:
|
||||
block.add_blockref(encoded_name, subpat.offset, dxfattribs=attribs)
|
||||
elif isinstance(rep, Grid):
|
||||
a = rep.a_vector
|
||||
b = rep.b_vector if rep.b_vector is not None else numpy.zeros(2)
|
||||
rotated_a = rotation_matrix_2d(-subpat.rotation) @ a
|
||||
rotated_b = rotation_matrix_2d(-subpat.rotation) @ b
|
||||
if rotated_a[1] == 0 and rotated_b[0] == 0:
|
||||
|
|
@ -310,11 +314,11 @@ def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.M
|
|||
#NOTE: We could still do non-manhattan (but still orthogonal) grids by getting
|
||||
# creative with counter-rotated nested patterns, but probably not worth it.
|
||||
# Instead, just break appart the grid into individual elements:
|
||||
for aa in numpy.arange(subpat.a_count):
|
||||
for bb in numpy.arange(subpat.b_count):
|
||||
block.add_blockref(encoded_name, subpat.offset + aa * a + bb * b, dxfattribs=attribs)
|
||||
for dd in rep.displacements:
|
||||
block.add_blockref(encoded_name, subpat.offset + dd, dxfattribs=attribs)
|
||||
else:
|
||||
block.add_blockref(encoded_name, subpat.offset, dxfattribs=attribs)
|
||||
for dd in rep.displacements:
|
||||
block.add_blockref(encoded_name, subpat.offset + dd, dxfattribs=attribs)
|
||||
|
||||
|
||||
def _shapes_to_elements(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||
|
|
|
|||
|
|
@ -28,8 +28,9 @@ import gdsii.structure
|
|||
import gdsii.elements
|
||||
|
||||
from .utils import mangle_name, make_dose_table, dose2dtype, dtype2dose
|
||||
from .. import Pattern, SubPattern, GridRepetition, PatternError, Label, Shape, subpattern_t
|
||||
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
|
||||
|
||||
|
|
@ -291,11 +292,9 @@ def read(stream: io.BufferedIOBase,
|
|||
string=element.string.decode('ASCII'))
|
||||
pat.labels.append(label)
|
||||
|
||||
elif isinstance(element, gdsii.elements.SRef):
|
||||
pat.subpatterns.append(_sref_to_subpat(element))
|
||||
|
||||
elif isinstance(element, gdsii.elements.ARef):
|
||||
pat.subpatterns.append(_aref_to_gridrep(element))
|
||||
elif (isinstance(element, gdsii.elements.SRef) or
|
||||
isinstance(element, gdsii.elements.ARef)):
|
||||
pat.subpatterns.append(_ref_to_subpat(element))
|
||||
|
||||
if use_dtype_as_dose:
|
||||
logger.warning('use_dtype_as_dose will be removed in the future!')
|
||||
|
|
@ -330,40 +329,11 @@ def _mlayer2gds(mlayer: layer_t) -> Tuple[int, int]:
|
|||
return layer, data_type
|
||||
|
||||
|
||||
def _sref_to_subpat(element: gdsii.elements.SRef) -> SubPattern:
|
||||
def _ref_to_subpat(element: Union[gdsii.elements.SRef,
|
||||
gdsii.elements.ARef]
|
||||
) -> SubPattern:
|
||||
"""
|
||||
Helper function to create a SubPattern from an SREF. Sets subpat.pattern to None
|
||||
and sets the instance .identifier to (struct_name,).
|
||||
|
||||
BUG:
|
||||
"Absolute" means not affected by parent elements.
|
||||
That's not currently supported by masque at all, so need to either tag it and
|
||||
undo the parent transformations, or implement it in masque.
|
||||
"""
|
||||
subpat = SubPattern(pattern=None, offset=element.xy)
|
||||
subpat.identifier = (element.struct_name,)
|
||||
if element.strans is not None:
|
||||
if element.mag is not None:
|
||||
subpat.scale = element.mag
|
||||
# Bit 13 means absolute scale
|
||||
if get_bit(element.strans, 15 - 13):
|
||||
#subpat.offset *= subpat.scale
|
||||
raise PatternError('Absolute scale is not implemented yet!')
|
||||
if element.angle is not None:
|
||||
subpat.rotation = element.angle * numpy.pi / 180
|
||||
# Bit 14 means absolute rotation
|
||||
if get_bit(element.strans, 15 - 14):
|
||||
#subpat.offset = numpy.dot(rotation_matrix_2d(subpat.rotation), subpat.offset)
|
||||
raise PatternError('Absolute rotation is not implemented yet!')
|
||||
# Bit 0 means mirror x-axis
|
||||
if get_bit(element.strans, 15 - 0):
|
||||
subpat.mirrored[0] = 1
|
||||
return subpat
|
||||
|
||||
|
||||
def _aref_to_gridrep(element: gdsii.elements.ARef) -> GridRepetition:
|
||||
"""
|
||||
Helper function to create a GridRepetition from an AREF. Sets gridrep.pattern to None
|
||||
Helper function to create a SubPattern from an SREF or AREF. Sets subpat.pattern to None
|
||||
and sets the instance .identifier to (struct_name,).
|
||||
|
||||
BUG:
|
||||
|
|
@ -375,6 +345,7 @@ def _aref_to_gridrep(element: gdsii.elements.ARef) -> GridRepetition:
|
|||
offset = numpy.array(element.xy[0])
|
||||
scale = 1
|
||||
mirror_across_x = False
|
||||
repetition = None
|
||||
|
||||
if element.strans is not None:
|
||||
if element.mag is not None:
|
||||
|
|
@ -383,7 +354,7 @@ def _aref_to_gridrep(element: gdsii.elements.ARef) -> GridRepetition:
|
|||
if get_bit(element.strans, 15 - 13):
|
||||
raise PatternError('Absolute scale is not implemented yet!')
|
||||
if element.angle is not None:
|
||||
rotation = element.angle * numpy.pi / 180
|
||||
rotation = numpy.deg2rad(element.angle)
|
||||
# Bit 14 means absolute rotation
|
||||
if get_bit(element.strans, 15 - 14):
|
||||
raise PatternError('Absolute rotation is not implemented yet!')
|
||||
|
|
@ -391,25 +362,24 @@ def _aref_to_gridrep(element: gdsii.elements.ARef) -> GridRepetition:
|
|||
if get_bit(element.strans, 15 - 0):
|
||||
mirror_across_x = True
|
||||
|
||||
counts = [element.cols, element.rows]
|
||||
a_vector = (element.xy[1] - offset) / counts[0]
|
||||
b_vector = (element.xy[2] - offset) / counts[1]
|
||||
if isinstance(element, gdsii.elements.ARef):
|
||||
a_count = element.cols
|
||||
b_count = element.rows
|
||||
a_vector = (element.xy[1] - offset) / counts[0]
|
||||
b_vector = (element.xy[2] - offset) / counts[1]
|
||||
repetition = Grid(a_vector=a_vector, b_vector=b_vector,
|
||||
a_count=a_count, b_count=b_count)
|
||||
|
||||
gridrep = GridRepetition(pattern=None,
|
||||
a_vector=a_vector,
|
||||
b_vector=b_vector,
|
||||
a_count=counts[0],
|
||||
b_count=counts[1],
|
||||
offset=offset,
|
||||
rotation=rotation,
|
||||
scale=scale,
|
||||
mirrored=(mirror_across_x, False))
|
||||
gridrep.identifier = (element.struct_name,)
|
||||
|
||||
return gridrep
|
||||
subpat = SubPattern(pattern=None,
|
||||
offset=offset,
|
||||
rotation=rotation,
|
||||
scale=scale,
|
||||
mirrored=(mirror_across_x, False))
|
||||
subpat.identifier = (element.struct_name,)
|
||||
return subpat
|
||||
|
||||
|
||||
def _subpatterns_to_refs(subpatterns: List[subpattern_t]
|
||||
def _subpatterns_to_refs(subpatterns: List[SubPattern]
|
||||
) -> List[Union[gdsii.elements.ARef, gdsii.elements.SRef]]:
|
||||
refs = []
|
||||
for subpat in subpatterns:
|
||||
|
|
@ -420,26 +390,35 @@ def _subpatterns_to_refs(subpatterns: List[subpattern_t]
|
|||
# Note: GDS mirrors first and rotates second
|
||||
mirror_across_x, extra_angle = normalize_mirror(subpat.mirrored)
|
||||
ref: Union[gdsii.elements.SRef, gdsii.elements.ARef]
|
||||
if isinstance(subpat, GridRepetition):
|
||||
|
||||
rep = subpat.repetition
|
||||
if isinstance(rep, Grid):
|
||||
xy = numpy.array(subpat.offset) + [
|
||||
[0, 0],
|
||||
subpat.a_vector * subpat.a_count,
|
||||
subpat.b_vector * subpat.b_count,
|
||||
rep.a_vector * rep.a_count,
|
||||
rep.b_vector * rep.b_count,
|
||||
]
|
||||
ref = gdsii.elements.ARef(struct_name=encoded_name,
|
||||
xy=numpy.round(xy).astype(int),
|
||||
cols=numpy.round(subpat.a_count).astype(int),
|
||||
rows=numpy.round(subpat.b_count).astype(int))
|
||||
else:
|
||||
cols=numpy.round(rep.a_count).astype(int),
|
||||
rows=numpy.round(rep.b_count).astype(int))
|
||||
new_refs = [ref]
|
||||
elif rep is None:
|
||||
ref = gdsii.elements.SRef(struct_name=encoded_name,
|
||||
xy=numpy.round([subpat.offset]).astype(int))
|
||||
new_refs = [ref]
|
||||
else:
|
||||
new_refs = [gdsii.elements.SRef(struct_name=encoded_name,
|
||||
xy=numpy.round([subpat.offset + dd]).astype(int))
|
||||
for dd in rep.displacements]
|
||||
|
||||
ref.angle = ((subpat.rotation + extra_angle) * 180 / numpy.pi) % 360
|
||||
# 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
|
||||
for ref in new_refs:
|
||||
ref.angle = numpy.rad2deg(subpat.rotation + extra_angle) % 360
|
||||
# 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
|
||||
|
||||
refs.append(ref)
|
||||
refs += new_refs
|
||||
return refs
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,9 @@ import fatamorgana.records as fatrec
|
|||
from fatamorgana.basic import PathExtensionScheme
|
||||
|
||||
from .utils import mangle_name, make_dose_table
|
||||
from .. import Pattern, SubPattern, GridRepetition, PatternError, Label, Shape, subpattern_t
|
||||
from .. import Pattern, SubPattern, PatternError, Label, Shape
|
||||
from ..shapes import Polygon, Path, Circle
|
||||
from ..repetition import Grid, Arbitrary, Repetition
|
||||
from ..utils import rotation_matrix_2d, get_bit, set_bit, vector2, is_scalar, layer_t
|
||||
from ..utils import remove_colinear_vertices, normalize_mirror
|
||||
|
||||
|
|
@ -221,7 +222,7 @@ def read(stream: io.BufferedIOBase,
|
|||
"""
|
||||
Read a OASIS file and translate it into a dict of Pattern objects. OASIS cells are
|
||||
translated into Pattern objects; Polygons are translated into polygons, and Placements
|
||||
are translated into SubPattern or GridRepetition objects.
|
||||
are translated into SubPattern objects.
|
||||
|
||||
Additional library info is returned in a dict, containing:
|
||||
'units_per_micrometer': number of database units per micrometer (all values are in database units)
|
||||
|
|
@ -417,7 +418,7 @@ def read(stream: io.BufferedIOBase,
|
|||
continue
|
||||
|
||||
for placement in cell.placements:
|
||||
pat.subpatterns += _placement_to_subpats(placement)
|
||||
pat.subpatterns.append(_placement_to_subpat(placement))
|
||||
|
||||
patterns.append(pat)
|
||||
|
||||
|
|
@ -451,7 +452,7 @@ def _mlayer2oas(mlayer: layer_t) -> Tuple[int, int]:
|
|||
return layer, data_type
|
||||
|
||||
|
||||
def _placement_to_subpats(placement: fatrec.Placement) -> List[subpattern_t]:
|
||||
def _placement_to_subpat(placement: fatrec.Placement) -> SubPattern:
|
||||
"""
|
||||
Helper function to create a SubPattern from a placment. Sets subpat.pattern to None
|
||||
and sets the instance .identifier to (struct_name,).
|
||||
|
|
@ -468,27 +469,24 @@ def _placement_to_subpats(placement: fatrec.Placement) -> List[subpattern_t]:
|
|||
'identifier': (name,),
|
||||
}
|
||||
|
||||
subpats: List[subpattern_t]
|
||||
mrep: Repetition
|
||||
rep = placement.repetition
|
||||
if isinstance(rep, fatamorgana.GridRepetition):
|
||||
subpat = GridRepetition(a_vector=rep.a_vector,
|
||||
b_vector=rep.b_vector,
|
||||
a_count=rep.a_count,
|
||||
b_count=rep.b_count,
|
||||
offset=xy,
|
||||
**args)
|
||||
subpats = [subpat]
|
||||
mrep = Grid(a_vector=rep.a_vector,
|
||||
b_vector=rep.b_vector,
|
||||
a_count=rep.a_count,
|
||||
b_count=rep.b_count)
|
||||
elif isinstance(rep, fatamorgana.ArbitraryRepetition):
|
||||
subpats = []
|
||||
for rep_offset in numpy.cumsum(numpy.column_stack((rep.x_displacements,
|
||||
rep.y_displacements))):
|
||||
subpats.append(SubPattern(offset=xy + rep_offset, **args))
|
||||
mrep = Arbitrary(numpy.cumsum(numpy.column_stack((rep.x_displacements,
|
||||
rep.y_displacements))))
|
||||
elif rep is None:
|
||||
subpats = [SubPattern(offset=xy, **args)]
|
||||
return subpats
|
||||
mrep = None
|
||||
|
||||
subpat = SubPattern(offset=xy, repetition=mrep, **args)
|
||||
return subpat
|
||||
|
||||
|
||||
def _subpatterns_to_refs(subpatterns: List[subpattern_t]
|
||||
def _subpatterns_to_refs(subpatterns: List[SubPattern]
|
||||
) -> List[fatrec.Placement]:
|
||||
refs = []
|
||||
for subpat in subpatterns:
|
||||
|
|
@ -503,14 +501,21 @@ def _subpatterns_to_refs(subpatterns: List[subpattern_t]
|
|||
'y': xy[1],
|
||||
}
|
||||
|
||||
if isinstance(subpat, GridRepetition):
|
||||
rep = subpat.repetition
|
||||
if isinstance(rep, Grid):
|
||||
args['repetition'] = fatamorgana.GridRepetition(
|
||||
a_vector=numpy.round(subpat.a_vector).astype(int),
|
||||
b_vector=numpy.round(subpat.b_vector).astype(int),
|
||||
a_count=numpy.round(subpat.a_count).astype(int),
|
||||
b_count=numpy.round(subpat.b_count).astype(int))
|
||||
a_vector=numpy.round(rep.a_vector).astype(int),
|
||||
b_vector=numpy.round(rep.b_vector).astype(int),
|
||||
a_count=numpy.round(rep.a_count).astype(int),
|
||||
b_count=numpy.round(rep.b_count).astype(int))
|
||||
elif isinstance(rep, Arbitrary):
|
||||
diffs = numpy.diff(rep.displacements, axis=0)
|
||||
args['repetition'] = fatamorgana.ArbitraryRepetition(
|
||||
numpy.round(diffs).astype(int))
|
||||
else:
|
||||
assert(rep is None)
|
||||
|
||||
angle = ((subpat.rotation + extra_angle) * 180 / numpy.pi) % 360
|
||||
angle = numpy.rad2deg(subpat.rotation + extra_angle) % 360
|
||||
ref = fatrec.Placement(
|
||||
name=subpat.pattern.name,
|
||||
flip=mirror_across_x,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue