Formally allow instances to point to None (i.e. an 'empty' pattern)
This commit is contained in:
parent
f8c49cdb5e
commit
00394a62f0
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user