repetition related fixup

This commit is contained in:
Jan Petykiewicz 2020-07-22 21:48:34 -07:00
parent bab40474a0
commit 794ebb6b37
11 changed files with 112 additions and 68 deletions

View File

@ -299,14 +299,14 @@ def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.M
rotated_a = rotation_matrix_2d(-subpat.rotation) @ a rotated_a = rotation_matrix_2d(-subpat.rotation) @ a
rotated_b = rotation_matrix_2d(-subpat.rotation) @ b rotated_b = rotation_matrix_2d(-subpat.rotation) @ b
if rotated_a[1] == 0 and rotated_b[0] == 0: if rotated_a[1] == 0 and rotated_b[0] == 0:
attribs['column_count'] = subpat.a_count attribs['column_count'] = rep.a_count
attribs['row_count'] = subpat.b_count attribs['row_count'] = rep.b_count
attribs['column_spacing'] = rotated_a[0] attribs['column_spacing'] = rotated_a[0]
attribs['row_spacing'] = rotated_b[1] attribs['row_spacing'] = rotated_b[1]
block.add_blockref(encoded_name, subpat.offset, dxfattribs=attribs) block.add_blockref(encoded_name, subpat.offset, dxfattribs=attribs)
elif rotated_a[0] == 0 and rotated_b[1] == 0: elif rotated_a[0] == 0 and rotated_b[1] == 0:
attribs['column_count'] = subpat.b_count attribs['column_count'] = rep.b_count
attribs['row_count'] = subpat.a_count attribs['row_count'] = rep.a_count
attribs['column_spacing'] = rotated_b[0] attribs['column_spacing'] = rotated_b[0]
attribs['row_spacing'] = rotated_a[1] attribs['row_spacing'] = rotated_a[1]
block.add_blockref(encoded_name, subpat.offset, dxfattribs=attribs) block.add_blockref(encoded_name, subpat.offset, dxfattribs=attribs)

View File

@ -365,8 +365,8 @@ def _ref_to_subpat(element: Union[gdsii.elements.SRef,
if isinstance(element, gdsii.elements.ARef): if isinstance(element, gdsii.elements.ARef):
a_count = element.cols a_count = element.cols
b_count = element.rows b_count = element.rows
a_vector = (element.xy[1] - offset) / counts[0] a_vector = (element.xy[1] - offset) / a_count
b_vector = (element.xy[2] - offset) / counts[1] b_vector = (element.xy[2] - offset) / b_count
repetition = Grid(a_vector=a_vector, b_vector=b_vector, repetition = Grid(a_vector=a_vector, b_vector=b_vector,
a_count=a_count, b_count=b_count) a_count=a_count, b_count=b_count)
@ -389,9 +389,10 @@ def _subpatterns_to_refs(subpatterns: List[SubPattern]
# Note: GDS mirrors first and rotates second # Note: GDS mirrors first and rotates second
mirror_across_x, extra_angle = normalize_mirror(subpat.mirrored) mirror_across_x, extra_angle = normalize_mirror(subpat.mirrored)
ref: Union[gdsii.elements.SRef, gdsii.elements.ARef]
rep = subpat.repetition rep = subpat.repetition
new_refs: List[Union[gdsii.elements.SRef, gdsii.elements.ARef]]
ref: Union[gdsii.elements.SRef, gdsii.elements.ARef]
if isinstance(rep, Grid): if isinstance(rep, Grid):
xy = numpy.array(subpat.offset) + [ xy = numpy.array(subpat.offset) + [
[0, 0], [0, 0],

View File

@ -469,7 +469,7 @@ def _placement_to_subpat(placement: fatrec.Placement) -> SubPattern:
'identifier': (name,), 'identifier': (name,),
} }
mrep: Repetition mrep: Optional[Repetition]
rep = placement.repetition rep = placement.repetition
if isinstance(rep, fatamorgana.GridRepetition): if isinstance(rep, fatamorgana.GridRepetition):
mrep = Grid(a_vector=rep.a_vector, mrep = Grid(a_vector=rep.a_vector,
@ -477,8 +477,10 @@ def _placement_to_subpat(placement: fatrec.Placement) -> SubPattern:
a_count=rep.a_count, a_count=rep.a_count,
b_count=rep.b_count) b_count=rep.b_count)
elif isinstance(rep, fatamorgana.ArbitraryRepetition): elif isinstance(rep, fatamorgana.ArbitraryRepetition):
mrep = Arbitrary(numpy.cumsum(numpy.column_stack((rep.x_displacements, displacements = numpy.cumsum(numpy.column_stack((rep.x_displacements,
rep.y_displacements)))) rep.y_displacements)))
displacements = numpy.vstack(([0, 0], displacements))
mrep = Arbitrary(displacements)
elif rep is None: elif rep is None:
mrep = None mrep = None
@ -510,8 +512,10 @@ def _subpatterns_to_refs(subpatterns: List[SubPattern]
b_count=numpy.round(rep.b_count).astype(int)) b_count=numpy.round(rep.b_count).astype(int))
elif isinstance(rep, Arbitrary): elif isinstance(rep, Arbitrary):
diffs = numpy.diff(rep.displacements, axis=0) diffs = numpy.diff(rep.displacements, axis=0)
args['repetition'] = fatamorgana.ArbitraryRepetition( diff_ints = numpy.round(diffs).astype(int)
numpy.round(diffs).astype(int)) args['repetition'] = fatamorgana.ArbitraryRepetition(diff_ints[:, 0], diff_ints[:, 1])
args['x'] += rep.displacements[0, 0]
args['y'] += rep.displacements[0, 1]
else: else:
assert(rep is None) assert(rep is None)

View File

@ -217,7 +217,7 @@ class Grid(LockableImpl, Repetition, metaclass=AutoSlots):
corners = ((0, 0), a_extent, b_extent, a_extent + b_extent) corners = ((0, 0), a_extent, b_extent, a_extent + b_extent)
xy_min = numpy.min(corners, axis=0) xy_min = numpy.min(corners, axis=0)
xy_max = numpy.min(corners, axis=0) xy_max = numpy.max(corners, axis=0)
return numpy.array((xy_min, xy_max)) return numpy.array((xy_min, xy_max))
def scale_by(self, c: float) -> 'Grid': def scale_by(self, c: float) -> 'Grid':
@ -298,9 +298,6 @@ class Arbitrary(LockableImpl, Repetition, metaclass=AutoSlots):
of the instances. of the instances.
""" """
locked: bool
""" If `True`, disallows changes to the object. """
@property @property
def displacements(self) -> numpy.ndarray: def displacements(self) -> numpy.ndarray:
return self._displacements return self._displacements
@ -311,6 +308,18 @@ class Arbitrary(LockableImpl, Repetition, metaclass=AutoSlots):
val = numpy.sort(val.view([('', val.dtype)] * val.shape[1]), 0).view(val.dtype) # sort rows val = numpy.sort(val.view([('', val.dtype)] * val.shape[1]), 0).view(val.dtype) # sort rows
self._displacements = val self._displacements = val
def __init__(self,
displacements: numpy.ndarray,
locked: bool = False,):
"""
Args:
displacements: List of vectors (Nx2 ndarray) specifying displacements.
locked: Whether the object is locked after initialization.
"""
object.__setattr__(self, 'locked', False)
self.displacements = displacements
self.locked = locked
def lock(self) -> 'Arbitrary': def lock(self) -> 'Arbitrary':
""" """
Lock the object, disallowing changes. Lock the object, disallowing changes.
@ -343,3 +352,56 @@ class Arbitrary(LockableImpl, Repetition, metaclass=AutoSlots):
if self.locked != other.locked: if self.locked != other.locked:
return False return False
return numpy.array_equal(self.displacements, other.displacements) return numpy.array_equal(self.displacements, other.displacements)
def rotate(self, rotation: float) -> 'Arbitrary':
"""
Rotate dispacements (around (0, 0))
Args:
rotation: Angle to rotate by (counterclockwise, radians)
Returns:
self
"""
self.displacements = numpy.dot(rotation_matrix_2d(rotation), self.displacements.T).T
return self
def mirror(self, axis: int) -> 'Arbitrary':
"""
Mirror the displacements across an axis.
Args:
axis: Axis to mirror across.
(0: mirror across x-axis, 1: mirror across y-axis)
Returns:
self
"""
self.displacements[1-axis] *= -1
return self
def get_bounds(self) -> Optional[numpy.ndarray]:
"""
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
extent of the `displacements` in each dimension.
Returns:
`[[x_min, y_min], [x_max, y_max]]` or `None`
"""
xy_min = numpy.min(self.displacements, axis=0)
xy_max = numpy.max(self.displacements, axis=0)
return numpy.array((xy_min, xy_max))
def scale_by(self, c: float) -> 'Arbitrary':
"""
Scale the displacements by a factor
Args:
c: scaling factor
Returns:
self
"""
self.displacements *= c
return self

View File

@ -35,27 +35,12 @@ class SubPattern(PositionableImpl, DoseableImpl, RotatableImpl, ScalableImpl, Mi
_pattern: Optional['Pattern'] _pattern: Optional['Pattern']
""" The `Pattern` being instanced """ """ The `Pattern` being instanced """
# _offset: numpy.ndarray
# """ (x, y) offset for the instance """
# _rotation: float
# """ rotation for the instance, radians counterclockwise """
# _dose: float
# """ dose factor for the instance """
# _scale: float
# """ scale factor for the instance """
_mirrored: numpy.ndarray # ndarray[bool] _mirrored: numpy.ndarray # ndarray[bool]
""" Whether to mirror the instance across the x and/or y axes. """ """ Whether to mirror the instance across the x and/or y axes. """
identifier: Tuple[Any, ...] identifier: Tuple[Any, ...]
""" Arbitrary identifier, used internally by some `masque` functions. """ """ Arbitrary identifier, used internally by some `masque` functions. """
# locked: bool
# """ If `True`, disallows changes to the SubPattern"""
def __init__(self, def __init__(self,
pattern: Optional['Pattern'], pattern: Optional['Pattern'],
offset: vector2 = (0.0, 0.0), offset: vector2 = (0.0, 0.0),
@ -79,7 +64,6 @@ class SubPattern(PositionableImpl, DoseableImpl, RotatableImpl, ScalableImpl, Mi
identifier: Arbitrary tuple, used internally by some `masque` functions. identifier: Arbitrary tuple, used internally by some `masque` functions.
""" """
LockableImpl.unlock(self) LockableImpl.unlock(self)
# object.__setattr__(self, 'locked', False)
self.identifier = identifier self.identifier = identifier
self.pattern = pattern self.pattern = pattern
self.offset = offset self.offset = offset
@ -146,9 +130,9 @@ class SubPattern(PositionableImpl, DoseableImpl, RotatableImpl, ScalableImpl, Mi
pattern.translate_elements(self.offset) pattern.translate_elements(self.offset)
pattern.scale_element_doses(self.dose) pattern.scale_element_doses(self.dose)
if pattern.repetition is not None: if self.repetition is not None:
combined = type(pat)(name='__repetition__') combined = type(pattern)(name='__repetition__')
for dd in pattern.repetition.displacements: for dd in self.repetition.displacements:
temp_pat = pattern.deepcopy() temp_pat = pattern.deepcopy()
temp_pat.translate_elements(dd) temp_pat.translate_elements(dd)
combined.append(temp_pat) combined.append(temp_pat)

View File

@ -28,10 +28,10 @@ class Doseable(metaclass=ABCMeta):
""" """
pass pass
@dose.setter # @dose.setter
@abstractmethod # @abstractmethod
def dose(self, val: float): # def dose(self, val: float):
pass # pass
''' '''
---- Methods ---- Methods

View File

@ -25,12 +25,12 @@ class Layerable(metaclass=ABCMeta):
""" """
Layer number or name (int, tuple of ints, or string) Layer number or name (int, tuple of ints, or string)
""" """
return self._layer pass
@layer.setter # @layer.setter
@abstractmethod # @abstractmethod
def layer(self, val: layer_t): # def layer(self, val: layer_t):
self._layer = val # pass
''' '''
---- Methods ---- Methods

View File

@ -19,18 +19,6 @@ class Lockable(metaclass=ABCMeta):
''' '''
---- Methods ---- Methods
''' '''
def set_dose(self: T, dose: float) -> T:
"""
Set the dose
Args:
dose: new value for dose
Returns:
self
"""
pass
def lock(self: T) -> T: def lock(self: T) -> T:
""" """
Lock the object, disallowing further changes Lock the object, disallowing further changes

View File

@ -6,7 +6,7 @@ import numpy
from ..error import PatternError, PatternLockedError from ..error import PatternError, PatternLockedError
T = TypeVar('T', bound='Mirrorable') T = TypeVar('T', bound='Mirrorable')
T = TypeVar('T', bound='MirrorableImpl') #I = TypeVar('I', bound='MirrorableImpl')
class Mirrorable(metaclass=ABCMeta): class Mirrorable(metaclass=ABCMeta):

View File

@ -30,10 +30,10 @@ class Positionable(metaclass=ABCMeta):
""" """
pass pass
@offset.setter # @offset.setter
@abstractmethod # @abstractmethod
def offset(self, val: vector2): # def offset(self, val: vector2):
pass # pass
''' '''
--- Abstract methods --- Abstract methods

View File

@ -1,4 +1,4 @@
from typing import List, Tuple, Callable, TypeVar, Optional from typing import List, Tuple, Callable, TypeVar, Optional, TYPE_CHECKING
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
import copy import copy
import numpy import numpy
@ -6,6 +6,10 @@ import numpy
from ..error import PatternError, PatternLockedError from ..error import PatternError, PatternLockedError
if TYPE_CHECKING:
from ..repetition import Repetition
T = TypeVar('T', bound='Repeatable') T = TypeVar('T', bound='Repeatable')
I = TypeVar('I', bound='RepeatableImpl') I = TypeVar('I', bound='RepeatableImpl')
@ -27,14 +31,15 @@ class Repeatable(metaclass=ABCMeta):
""" """
pass pass
@repetition.setter # @repetition.setter
@abstractmethod # @abstractmethod
def repetition(self, repetition: Optional['Repetition']): # def repetition(self, repetition: Optional['Repetition']):
pass # pass
''' '''
---- Methods ---- Methods
''' '''
@abstractmethod
def set_repetition(self: T, repetition: Optional['Repetition']) -> T: def set_repetition(self: T, repetition: Optional['Repetition']) -> T:
""" """
Set the repetition Set the repetition
@ -74,6 +79,6 @@ class RepeatableImpl(Repeatable, metaclass=ABCMeta):
''' '''
---- Non-abstract methods ---- Non-abstract methods
''' '''
def set_repetition(self: I, repetition: 'Repetition') -> I: def set_repetition(self: I, repetition: Optional['Repetition']) -> I:
self.repetition = repetition self.repetition = repetition
return self return self