repetition related fixup
This commit is contained in:
parent
bab40474a0
commit
794ebb6b37
@ -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)
|
||||||
|
@ -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],
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user