Formally allow instances to point to None (i.e. an 'empty' pattern)

This commit is contained in:
Jan Petykiewicz 2020-05-11 18:58:57 -07:00
parent f8c49cdb5e
commit 00394a62f0
6 changed files with 54 additions and 17 deletions

View File

@ -100,7 +100,9 @@ def write(patterns: Pattern or List[Pattern],
# Get a dict of id(pattern) -> pattern # Get a dict of id(pattern) -> pattern
patterns_by_id = {id(pattern): pattern for pattern in patterns} patterns_by_id = {id(pattern): pattern for pattern in patterns}
for pattern in patterns: for pattern in patterns:
patterns_by_id.update(pattern.referenced_patterns_by_id()) for i, p in pattern.referenced_patterns_by_id().items():
if p is not None:
patterns_by_id[i] = p
disambiguate_func(patterns_by_id.values()) disambiguate_func(patterns_by_id.values())
@ -170,7 +172,9 @@ def dose2dtype(patterns: List[Pattern],
# Get a dict of id(pattern) -> pattern # Get a dict of id(pattern) -> pattern
patterns_by_id = {id(pattern): pattern for pattern in patterns} patterns_by_id = {id(pattern): pattern for pattern in patterns}
for pattern in patterns: for pattern in patterns:
patterns_by_id.update(pattern.referenced_patterns_by_id()) for i, p in pattern.referenced_patterns_by_id().items():
if p is not None:
patterns_by_id[i] = p
# Get a table of (id(pat), written_dose) for each pattern and subpattern # Get a table of (id(pat), written_dose) for each pattern and subpattern
sd_table = make_dose_table(patterns) sd_table = make_dose_table(patterns)
@ -466,6 +470,8 @@ def _subpatterns_to_refs(subpatterns: List[SubPattern or GridRepetition]
) -> List[gdsii.elements.ARef or gdsii.elements.SRef]: ) -> List[gdsii.elements.ARef or gdsii.elements.SRef]:
refs = [] refs = []
for subpat in subpatterns: for subpat in subpatterns:
if subpat.pattern is None:
continue
encoded_name = subpat.pattern.name encoded_name = subpat.pattern.name
# Note: GDS mirrors first and rotates second # Note: GDS mirrors first and rotates second

View File

@ -61,6 +61,8 @@ def writefile(pattern: Pattern,
# Now create a group for each row in sd_table (ie, each pattern + dose combination) # Now create a group for each row in sd_table (ie, each pattern + dose combination)
# and add in any Boundary and Use elements # and add in any Boundary and Use elements
for pat in patterns_by_id.values(): for pat in patterns_by_id.values():
if pat is None:
continue
svg_group = svg.g(id=mangle_name(pat), fill='blue', stroke='red') svg_group = svg.g(id=mangle_name(pat), fill='blue', stroke='red')
for shape in pat.shapes: for shape in pat.shapes:
@ -75,6 +77,8 @@ def writefile(pattern: Pattern,
svg_group.add(path) svg_group.add(path)
for subpat in pat.subpatterns: for subpat in pat.subpatterns:
if subpat.pattern is None:
continue
transform = 'scale({:g}) rotate({:g}) translate({:g},{:g})'.format( transform = 'scale({:g}) rotate({:g}) translate({:g},{:g})'.format(
subpat.scale, subpat.rotation, subpat.offset[0], subpat.offset[1]) subpat.scale, subpat.rotation, subpat.offset[0], subpat.offset[1])
use = svg.use(href='#' + mangle_name(subpat.pattern), transform=transform) use = svg.use(href='#' + mangle_name(subpat.pattern), transform=transform)

View File

@ -38,6 +38,8 @@ def make_dose_table(patterns: List[Pattern], dose_multiplier: float=1.0) -> Set[
dose_table = {(id(pattern), dose_multiplier) for pattern in patterns} dose_table = {(id(pattern), dose_multiplier) for pattern in patterns}
for pattern in patterns: for pattern in patterns:
for subpat in pattern.subpatterns: for subpat in pattern.subpatterns:
if subpat.pattern is None:
continue
subpat_dose_entry = (id(subpat.pattern), subpat.dose * dose_multiplier) subpat_dose_entry = (id(subpat.pattern), subpat.dose * dose_multiplier)
if subpat_dose_entry not in dose_table: if subpat_dose_entry not in dose_table:
subpat_dose_table = make_dose_table([subpat.pattern], subpat.dose * dose_multiplier) subpat_dose_table = make_dose_table([subpat.pattern], subpat.dose * dose_multiplier)

View File

@ -151,7 +151,9 @@ class Pattern:
A Pattern containing all the shapes and subpatterns for which the parameter A Pattern containing all the shapes and subpatterns for which the parameter
functions return True functions return True
""" """
def do_subset(src): def do_subset(src: Optional['Pattern']) -> Optional['Pattern']:
if src is None:
return None
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)]
@ -165,6 +167,8 @@ class Pattern:
pat = self.apply(do_subset) pat = self.apply(do_subset)
else: else:
pat = do_subset(self) pat = do_subset(self)
assert(pat is not None)
return pat return pat
def apply(self, def apply(self,
@ -197,8 +201,12 @@ class Pattern:
if pat_id not in memo: if pat_id not in memo:
memo[pat_id] = None memo[pat_id] = None
pat = func(self) pat = func(self)
for subpat in pat.subpatterns: if pat is not None:
subpat.pattern = subpat.pattern.apply(func, memo) for subpat in pat.subpatterns:
if subpat.pattern is None:
subpat.pattern = func(None)
else:
subpat.pattern = subpat.pattern.apply(func, memo)
memo[pat_id] = pat memo[pat_id] = pat
elif memo[pat_id] is None: elif memo[pat_id] is None:
raise PatternError('.apply() called on pattern with circular reference') raise PatternError('.apply() called on pattern with circular reference')
@ -277,11 +285,12 @@ class Pattern:
else: else:
sp_transform = False sp_transform = False
subpattern.pattern = subpattern.pattern.dfs(visit_before=visit_before, if subpattern.pattern is not None:
visit_after=visit_after, subpattern.pattern = subpattern.pattern.dfs(visit_before=visit_before,
transform=sp_transform, visit_after=visit_after,
memo=memo, transform=sp_transform,
hierarchy=hierarchy + (self,)) memo=memo,
hierarchy=hierarchy + (self,))
if visit_after is not None: if visit_after is not None:
pat = visit_after(pat, hierarchy=hierarchy, memo=memo, transform=transform) pat = visit_after(pat, hierarchy=hierarchy, memo=memo, transform=transform)
@ -311,7 +320,8 @@ class Pattern:
(shape.to_polygons(poly_num_points, poly_max_arclen) (shape.to_polygons(poly_num_points, poly_max_arclen)
for shape in old_shapes))) for shape in old_shapes)))
for subpat in self.subpatterns: for subpat in self.subpatterns:
subpat.pattern.polygonize(poly_num_points, poly_max_arclen) if subpat.pattern is not None:
subpat.pattern.polygonize(poly_num_points, poly_max_arclen)
return self return self
def manhattanize(self, def manhattanize(self,
@ -368,6 +378,8 @@ class Pattern:
if recursive: if recursive:
for subpat in self.subpatterns: for subpat in self.subpatterns:
if subpat.pattern is None:
continue
subpat.pattern.subpatternize(recursive=True, subpat.pattern.subpatternize(recursive=True,
norm_value=norm_value, norm_value=norm_value,
exclude_types=exclude_types) exclude_types=exclude_types)
@ -431,7 +443,8 @@ class Pattern:
for subpat in self.subpatterns: for subpat in self.subpatterns:
if id(subpat.pattern) not in ids: if id(subpat.pattern) not in ids:
ids[id(subpat.pattern)] = subpat.pattern ids[id(subpat.pattern)] = subpat.pattern
ids.update(subpat.pattern.referenced_patterns_by_id()) if subpat.pattern is not None:
ids.update(subpat.pattern.referenced_patterns_by_id())
return ids return ids
def referenced_patterns_by_name(self) -> List[Tuple[str, 'Pattern']]: def referenced_patterns_by_name(self) -> List[Tuple[str, 'Pattern']]:
@ -446,7 +459,7 @@ class Pattern:
List of `(pat.name, pat)` tuples for all referenced Pattern objects List of `(pat.name, pat)` tuples for all referenced Pattern objects
""" """
pats_by_id = self.referenced_patterns_by_id() pats_by_id = self.referenced_patterns_by_id()
pat_list = [(p.name, p) for p in pats_by_id.values()] pat_list = [(p.name if p is not None else None, p) for p in pats_by_id.values()]
return pat_list return pat_list
def get_bounds(self) -> Union[numpy.ndarray, None]: def get_bounds(self) -> Union[numpy.ndarray, None]:
@ -496,6 +509,8 @@ class Pattern:
self.subpatterns = [] self.subpatterns = []
shape_counts = {} shape_counts = {}
for subpat in subpatterns: for subpat in subpatterns:
if subpat.pattern is None:
continue
subpat.pattern.flatten() subpat.pattern.flatten()
p = subpat.as_pattern() p = subpat.as_pattern()
@ -839,7 +854,7 @@ class Pattern:
if pat in memo: if pat in memo:
return memo return memo
children = set(sp.pattern for sp in pat.subpatterns) children = set(sp.pattern for sp in pat.subpatterns if sp.pattern is not None)
new_children = children - memo new_children = children - memo
memo |= children memo |= children

View File

@ -298,6 +298,7 @@ class GridRepetition:
A copy of self.pattern which has been scaled, rotated, repeated, etc. A copy of self.pattern which has been scaled, rotated, repeated, etc.
etc. according to this `GridRepetition`'s properties. etc. according to this `GridRepetition`'s properties.
""" """
assert(self.pattern is not None)
patterns = [] patterns = []
for a in range(self.a_count): for a in range(self.a_count):
@ -411,7 +412,7 @@ class GridRepetition:
self.rotation *= -1 self.rotation *= -1
return self return self
def get_bounds(self) -> numpy.ndarray or None: def get_bounds(self) -> Optional[numpy.ndarray]:
""" """
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
extent of the `GridRepetition` in each dimension. extent of the `GridRepetition` in each dimension.
@ -420,6 +421,8 @@ class GridRepetition:
Returns: Returns:
`[[x_min, y_min], [x_max, y_max]]` or `None` `[[x_min, y_min], [x_max, y_max]]` or `None`
""" """
if self.pattern is None:
return None
return self.as_pattern().get_bounds() return self.as_pattern().get_bounds()
def scale_by(self, c: float) -> 'GridRepetition': def scale_by(self, c: float) -> 'GridRepetition':
@ -496,6 +499,7 @@ class GridRepetition:
Returns: Returns:
self self
""" """
assert(self.pattern is not None)
self.lock() self.lock()
self.pattern.deeplock() self.pattern.deeplock()
return self return self
@ -510,6 +514,7 @@ class GridRepetition:
Returns: Returns:
self self
""" """
assert(self.pattern is not None)
self.unlock() self.unlock()
self.pattern.deepunlock() self.pattern.deepunlock()
return self return self

View File

@ -55,7 +55,7 @@ class SubPattern:
#TODO more documentation? #TODO more documentation?
def __init__(self, def __init__(self,
pattern: 'Pattern' or None, pattern: Optional['Pattern'],
offset: vector2 = (0.0, 0.0), offset: vector2 = (0.0, 0.0),
rotation: float = 0.0, rotation: float = 0.0,
mirrored: List[bool] = None, mirrored: List[bool] = None,
@ -176,6 +176,7 @@ class SubPattern:
A copy of self.pattern which has been scaled, rotated, etc. according to this A copy of self.pattern which has been scaled, rotated, etc. according to this
`SubPattern`'s properties. `SubPattern`'s properties.
""" """
assert(self.pattern is not None)
pattern = self.pattern.deepcopy().deepunlock() pattern = self.pattern.deepcopy().deepunlock()
pattern.scale_by(self.scale) pattern.scale_by(self.scale)
[pattern.mirror(ax) for ax, do in enumerate(self.mirrored) if do] [pattern.mirror(ax) for ax, do in enumerate(self.mirrored) if do]
@ -242,7 +243,7 @@ class SubPattern:
self.rotation *= -1 self.rotation *= -1
return self return self
def get_bounds(self) -> numpy.ndarray or None: def get_bounds(self) -> Optional[numpy.ndarray]:
""" """
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
extent of the `SubPattern` in each dimension. extent of the `SubPattern` in each dimension.
@ -251,6 +252,8 @@ class SubPattern:
Returns: Returns:
`[[x_min, y_min], [x_max, y_max]]` or `None` `[[x_min, y_min], [x_max, y_max]]` or `None`
""" """
if self.pattern is None:
return None
return self.as_pattern().get_bounds() return self.as_pattern().get_bounds()
def scale_by(self, c: float) -> 'SubPattern': def scale_by(self, c: float) -> 'SubPattern':
@ -311,6 +314,7 @@ class SubPattern:
Returns: Returns:
self self
""" """
assert(self.pattern is not None)
self.lock() self.lock()
self.pattern.deeplock() self.pattern.deeplock()
return self return self
@ -325,6 +329,7 @@ class SubPattern:
Returns: Returns:
self self
""" """
assert(self.pattern is not None)
self.unlock() self.unlock()
self.pattern.deepunlock() self.pattern.deepunlock()
return self return self