Remove support for dose

Since there isn't GDS/OASIS level support for dose, this can be mostly
handled by using arbitrary layers/dtypes directly. Dose scaling isn't
handled as nicely that way, but it corresponds more directly to what
gets written to file.
This commit is contained in:
Jan Petykiewicz 2023-01-18 18:14:33 -08:00 committed by jan
commit c7f3e7ee52
18 changed files with 57 additions and 340 deletions

View file

@ -162,7 +162,6 @@ class Arc(Shape, metaclass=AutoSlots):
rotation: float = 0,
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
annotations: Optional[annotations_t] = None,
raw: bool = False,
@ -179,7 +178,6 @@ class Arc(Shape, metaclass=AutoSlots):
self._repetition = repetition
self._annotations = annotations if annotations is not None else {}
self._layer = layer
self._dose = dose
else:
self.radii = radii
self.angles = angles
@ -189,7 +187,6 @@ class Arc(Shape, metaclass=AutoSlots):
self.repetition = repetition
self.annotations = annotations if annotations is not None else {}
self.layer = layer
self.dose = dose
self.poly_num_points = poly_num_points
self.poly_max_arclen = poly_max_arclen
[self.mirror(a) for a, do in enumerate(mirrored) if do]
@ -256,7 +253,7 @@ class Arc(Shape, metaclass=AutoSlots):
ys = numpy.hstack((ys1, ys2))
xys = numpy.vstack((xs, ys)).T
poly = Polygon(xys, dose=self.dose, layer=self.layer, offset=self.offset, rotation=self.rotation)
poly = Polygon(xys, layer=self.layer, offset=self.offset, rotation=self.rotation)
return [poly]
def get_bounds(self) -> NDArray[numpy.float64]:
@ -368,7 +365,7 @@ class Arc(Shape, metaclass=AutoSlots):
width = self.width
return ((type(self), radii, angles, width / norm_value, self.layer),
(self.offset, scale / norm_value, rotation, False, self.dose),
(self.offset, scale / norm_value, rotation, False),
lambda: Arc(radii=radii * norm_value, angles=angles, width=width * norm_value, layer=self.layer))
def get_cap_edges(self) -> NDArray[numpy.float64]:
@ -425,5 +422,4 @@ class Arc(Shape, metaclass=AutoSlots):
def __repr__(self) -> str:
angles = f'{numpy.rad2deg(self.angles)}'
rotation = f'{numpy.rad2deg(self.rotation):g}' if self.rotation != 0 else ''
dose = f' d{self.dose:g}' if self.dose != 1 else ''
return f'<Arc l{self.layer} o{self.offset} r{self.radii}{angles} w{self.width:g}{rotation}{dose}>'
return f'<Arc l{self.layer} o{self.offset} r{self.radii}{angles} w{self.width:g}{rotation}>'

View file

@ -50,7 +50,6 @@ class Circle(Shape, metaclass=AutoSlots):
poly_max_arclen: Optional[float] = None,
offset: ArrayLike = (0.0, 0.0),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
annotations: Optional[annotations_t] = None,
raw: bool = False,
@ -62,14 +61,12 @@ class Circle(Shape, metaclass=AutoSlots):
self._repetition = repetition
self._annotations = annotations if annotations is not None else {}
self._layer = layer
self._dose = dose
else:
self.radius = radius
self.offset = offset
self.repetition = repetition
self.annotations = annotations if annotations is not None else {}
self.layer = layer
self.dose = dose
self.poly_num_points = poly_num_points
self.poly_max_arclen = poly_max_arclen
@ -105,7 +102,7 @@ class Circle(Shape, metaclass=AutoSlots):
ys = numpy.sin(thetas) * self.radius
xys = numpy.vstack((xs, ys)).T
return [Polygon(xys, offset=self.offset, dose=self.dose, layer=self.layer)]
return [Polygon(xys, offset=self.offset, layer=self.layer)]
def get_bounds(self) -> NDArray[numpy.float64]:
return numpy.vstack((self.offset - self.radius,
@ -126,9 +123,8 @@ class Circle(Shape, metaclass=AutoSlots):
rotation = 0.0
magnitude = self.radius / norm_value
return ((type(self), self.layer),
(self.offset, magnitude, rotation, False, self.dose),
(self.offset, magnitude, rotation, False),
lambda: Circle(radius=norm_value, layer=self.layer))
def __repr__(self) -> str:
dose = f' d{self.dose:g}' if self.dose != 1 else ''
return f'<Circle l{self.layer} o{self.offset} r{self.radius:g}{dose}>'
return f'<Circle l{self.layer} o{self.offset} r{self.radius:g}>'

View file

@ -97,7 +97,6 @@ class Ellipse(Shape, metaclass=AutoSlots):
rotation: float = 0,
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
annotations: Optional[annotations_t] = None,
raw: bool = False,
@ -111,7 +110,6 @@ class Ellipse(Shape, metaclass=AutoSlots):
self._repetition = repetition
self._annotations = annotations if annotations is not None else {}
self._layer = layer
self._dose = dose
else:
self.radii = radii
self.offset = offset
@ -119,7 +117,6 @@ class Ellipse(Shape, metaclass=AutoSlots):
self.repetition = repetition
self.annotations = annotations if annotations is not None else {}
self.layer = layer
self.dose = dose
[self.mirror(a) for a, do in enumerate(mirrored) if do]
self.poly_num_points = poly_num_points
self.poly_max_arclen = poly_max_arclen
@ -167,7 +164,7 @@ class Ellipse(Shape, metaclass=AutoSlots):
ys = r1 * sin_th
xys = numpy.vstack((xs, ys)).T
poly = Polygon(xys, dose=self.dose, layer=self.layer, offset=self.offset, rotation=self.rotation)
poly = Polygon(xys, layer=self.layer, offset=self.offset, rotation=self.rotation)
return [poly]
def get_bounds(self) -> NDArray[numpy.float64]:
@ -199,10 +196,9 @@ class Ellipse(Shape, metaclass=AutoSlots):
scale = self.radius_y
angle = (self.rotation + pi / 2) % pi
return ((type(self), radii, self.layer),
(self.offset, scale / norm_value, angle, False, self.dose),
(self.offset, scale / norm_value, angle, False),
lambda: Ellipse(radii=radii * norm_value, layer=self.layer))
def __repr__(self) -> str:
rotation = f' r{self.rotation*180/pi:g}' if self.rotation != 0 else ''
dose = f' d{self.dose:g}' if self.dose != 1 else ''
return f'<Ellipse l{self.layer} o{self.offset} r{self.radii}{rotation}{dose}>'
return f'<Ellipse l{self.layer} o{self.offset} r{self.radii}{rotation}>'

View file

@ -151,7 +151,6 @@ class Path(Shape, metaclass=AutoSlots):
rotation: float = 0,
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
annotations: Optional[annotations_t] = None,
raw: bool = False,
@ -167,7 +166,6 @@ class Path(Shape, metaclass=AutoSlots):
self._repetition = repetition
self._annotations = annotations if annotations is not None else {}
self._layer = layer
self._dose = dose
self._width = width
self._cap = cap
self._cap_extensions = cap_extensions
@ -177,7 +175,6 @@ class Path(Shape, metaclass=AutoSlots):
self.repetition = repetition
self.annotations = annotations if annotations is not None else {}
self.layer = layer
self.dose = dose
self.width = width
self.cap = cap
self.cap_extensions = cap_extensions
@ -204,7 +201,6 @@ class Path(Shape, metaclass=AutoSlots):
rotation: float = 0,
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
) -> 'Path':
"""
Build a path by specifying the turn angles and travel distances
@ -225,7 +221,6 @@ class Path(Shape, metaclass=AutoSlots):
`mirrored=(True, False)` results in a reflection across the x-axis,
multiplying the path's y-coordinates by -1. Default `(False, False)`
layer: Layer, default `0`
dose: Dose, default `1.0`
Returns:
The resulting Path object
@ -240,7 +235,7 @@ class Path(Shape, metaclass=AutoSlots):
return Path(vertices=verts, width=width, cap=cap, cap_extensions=cap_extensions,
offset=offset, rotation=rotation, mirrored=mirrored,
layer=layer, dose=dose)
layer=layer)
def to_polygons(
self,
@ -255,7 +250,7 @@ class Path(Shape, metaclass=AutoSlots):
if self.width == 0:
verts = numpy.vstack((v, v[::-1]))
return [Polygon(offset=self.offset, vertices=verts, dose=self.dose, layer=self.layer)]
return [Polygon(offset=self.offset, vertices=verts, layer=self.layer)]
perp = dvdir[:, ::-1] * [[1, -1]] * self.width / 2
@ -306,12 +301,12 @@ class Path(Shape, metaclass=AutoSlots):
o1.append(v[-1] - perp[-1])
verts = numpy.vstack((o0, o1[::-1]))
polys = [Polygon(offset=self.offset, vertices=verts, dose=self.dose, layer=self.layer)]
polys = [Polygon(offset=self.offset, vertices=verts, layer=self.layer)]
if self.cap == PathCap.Circle:
#for vert in v: # not sure if every vertex, or just ends?
for vert in [v[0], v[-1]]:
circ = Circle(offset=vert, radius=self.width / 2, dose=self.dose, layer=self.layer)
circ = Circle(offset=vert, radius=self.width / 2, layer=self.layer)
polys += circ.to_polygons(poly_num_points=poly_num_points, poly_max_arclen=poly_max_arclen)
return polys
@ -372,7 +367,7 @@ class Path(Shape, metaclass=AutoSlots):
width0 = self.width / norm_value
return ((type(self), reordered_vertices.data.tobytes(), width0, self.cap, self.layer),
(offset, scale / norm_value, rotation, False, self.dose),
(offset, scale / norm_value, rotation, False),
lambda: Path(reordered_vertices * norm_value, width=self.width * norm_value,
cap=self.cap, layer=self.layer))
@ -419,5 +414,4 @@ class Path(Shape, metaclass=AutoSlots):
def __repr__(self) -> str:
centroid = self.offset + self.vertices.mean(axis=0)
dose = f' d{self.dose:g}' if self.dose != 1 else ''
return f'<Path l{self.layer} centroid {centroid} v{len(self.vertices)} w{self.width} c{self.cap}{dose}>'
return f'<Path l{self.layer} centroid {centroid} v{len(self.vertices)} w{self.width} c{self.cap}>'

View file

@ -79,7 +79,6 @@ class Polygon(Shape, metaclass=AutoSlots):
rotation: float = 0.0,
mirrored: Sequence[bool] = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
annotations: Optional[annotations_t] = None,
raw: bool = False,
@ -92,14 +91,12 @@ class Polygon(Shape, metaclass=AutoSlots):
self._repetition = repetition
self._annotations = annotations if annotations is not None else {}
self._layer = layer
self._dose = dose
else:
self.vertices = vertices
self.offset = offset
self.repetition = repetition
self.annotations = annotations if annotations is not None else {}
self.layer = layer
self.dose = dose
self.rotate(rotation)
[self.mirror(a) for a, do in enumerate(mirrored) if do]
@ -118,7 +115,6 @@ class Polygon(Shape, metaclass=AutoSlots):
rotation: float = 0.0,
offset: ArrayLike = (0.0, 0.0),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
) -> 'Polygon':
"""
@ -129,7 +125,6 @@ class Polygon(Shape, metaclass=AutoSlots):
rotation: Rotation counterclockwise, in radians
offset: Offset, default `(0, 0)`
layer: Layer, default `0`
dose: Dose, default `1.0`
repetition: `Repetition` object, default `None`
Returns:
@ -140,8 +135,7 @@ class Polygon(Shape, metaclass=AutoSlots):
[+1, +1],
[+1, -1]], dtype=float)
vertices = 0.5 * side_length * norm_square
poly = Polygon(vertices, offset=offset, layer=layer, dose=dose,
repetition=repetition)
poly = Polygon(vertices, offset=offset, layer=layer, repetition=repetition)
poly.rotate(rotation)
return poly
@ -153,7 +147,6 @@ class Polygon(Shape, metaclass=AutoSlots):
rotation: float = 0,
offset: ArrayLike = (0.0, 0.0),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
) -> 'Polygon':
"""
@ -165,7 +158,6 @@ class Polygon(Shape, metaclass=AutoSlots):
rotation: Rotation counterclockwise, in radians
offset: Offset, default `(0, 0)`
layer: Layer, default `0`
dose: Dose, default `1.0`
repetition: `Repetition` object, default `None`
Returns:
@ -175,8 +167,7 @@ class Polygon(Shape, metaclass=AutoSlots):
[-lx, +ly],
[+lx, +ly],
[+lx, -ly]], dtype=float)
poly = Polygon(vertices, offset=offset, layer=layer, dose=dose,
repetition=repetition)
poly = Polygon(vertices, offset=offset, layer=layer, repetition=repetition)
poly.rotate(rotation)
return poly
@ -192,7 +183,6 @@ class Polygon(Shape, metaclass=AutoSlots):
ymax: Optional[float] = None,
ly: Optional[float] = None,
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
) -> 'Polygon':
"""
@ -211,7 +201,6 @@ class Polygon(Shape, metaclass=AutoSlots):
ymax: Maximum y coordinate
ly: Length along y direction
layer: Layer, default `0`
dose: Dose, default `1.0`
repetition: `Repetition` object, default `None`
Returns:
@ -277,8 +266,7 @@ class Polygon(Shape, metaclass=AutoSlots):
else:
raise PatternError('Two of ymin, yctr, ymax, ly must be None!')
poly = Polygon.rectangle(lx, ly, offset=(xctr, yctr),
layer=layer, dose=dose, repetition=repetition)
poly = Polygon.rectangle(lx, ly, offset=(xctr, yctr), layer=layer, repetition=repetition)
return poly
@staticmethod
@ -290,7 +278,6 @@ class Polygon(Shape, metaclass=AutoSlots):
center: ArrayLike = (0.0, 0.0),
rotation: float = 0.0,
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
) -> 'Polygon':
"""
@ -310,7 +297,6 @@ class Polygon(Shape, metaclass=AutoSlots):
`0` results in four axis-aligned sides (the long sides of the
irregular octagon).
layer: Layer, default `0`
dose: Dose, default `1.0`
repetition: `Repetition` object, default `None`
Returns:
@ -337,7 +323,7 @@ class Polygon(Shape, metaclass=AutoSlots):
side_length = 2 * inner_radius / s
vertices = 0.5 * side_length * norm_oct
poly = Polygon(vertices, offset=center, layer=layer, dose=dose, repetition=repetition)
poly = Polygon(vertices, offset=center, layer=layer, repetition=repetition)
poly.rotate(rotation)
return poly
@ -390,7 +376,7 @@ class Polygon(Shape, metaclass=AutoSlots):
# TODO: normalize mirroring?
return ((type(self), reordered_vertices.data.tobytes(), self.layer),
(offset, scale / norm_value, rotation, False, self.dose),
(offset, scale / norm_value, rotation, False),
lambda: Polygon(reordered_vertices * norm_value, layer=self.layer))
def clean_vertices(self) -> 'Polygon':
@ -425,5 +411,4 @@ class Polygon(Shape, metaclass=AutoSlots):
def __repr__(self) -> str:
centroid = self.offset + self.vertices.mean(axis=0)
dose = f' d{self.dose:g}' if self.dose != 1 else ''
return f'<Polygon l{self.layer} centroid {centroid} v{len(self.vertices)}{dose}>'
return f'<Polygon l{self.layer} centroid {centroid} v{len(self.vertices)}>'

View file

@ -5,8 +5,8 @@ import numpy
from numpy.typing import NDArray, ArrayLike
from ..traits import (
PositionableImpl, LayerableImpl, DoseableImpl,
Rotatable, Mirrorable, Copyable, Scalable,
PositionableImpl, LayerableImpl,
PivotableImpl, RepeatableImpl, AnnotatableImpl,
)
@ -27,7 +27,7 @@ DEFAULT_POLY_NUM_POINTS = 24
T = TypeVar('T', bound='Shape')
class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable, Copyable, Scalable,
class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Scalable,
PivotableImpl, RepeatableImpl, AnnotatableImpl, metaclass=ABCMeta):
"""
Abstract class specifying functions common to all shapes.
@ -68,7 +68,7 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable
@abstractmethod
def normalized_form(self: T, norm_value: int) -> normalized_shape_tuple:
"""
Writes the shape in a standardized notation, with offset, scale, rotation, and dose
Writes the shape in a standardized notation, with offset, scale, and rotation
information separated out from the remaining values.
Args:
@ -83,7 +83,7 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable
`(intrinsic, extrinsic, constructor)`. These are further broken down as:
`intrinsic`: A tuple of basic types containing all information about the instance that
is not contained in 'extrinsic'. Usually, `intrinsic[0] == type(self)`.
`extrinsic`: `([x_offset, y_offset], scale, rotation, mirror_across_x_axis, dose)`
`extrinsic`: `([x_offset, y_offset], scale, rotation, mirror_across_x_axis)`
`constructor`: A callable (no arguments) which returns an instance of `type(self)` with
internal state equivalent to `intrinsic`.
"""
@ -195,12 +195,10 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable
vertex_lists.append(vlist)
polygon_contours.append(numpy.vstack(vertex_lists))
manhattan_polygons = []
for contour in polygon_contours:
manhattan_polygons.append(Polygon(
vertices=contour,
layer=self.layer,
dose=self.dose))
manhattan_polygons = [
Polygon(vertices=contour, layer=self.layer)
for contour in polygon_contours
]
return manhattan_polygons
@ -298,6 +296,6 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable
manhattan_polygons.append(Polygon(
vertices=vertices,
layer=self.layer,
dose=self.dose))
))
return manhattan_polygons

View file

@ -70,7 +70,6 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
rotation: float = 0.0,
mirrored: ArrayLike = (False, False),
layer: layer_t = 0,
dose: float = 1.0,
repetition: Optional[Repetition] = None,
annotations: Optional[annotations_t] = None,
raw: bool = False,
@ -80,7 +79,6 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
assert(isinstance(mirrored, numpy.ndarray))
self._offset = offset
self._layer = layer
self._dose = dose
self._string = string
self._height = height
self._rotation = rotation
@ -90,7 +88,6 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
else:
self.offset = offset
self.layer = layer
self.dose = dose
self.string = string
self.height = height
self.rotation = rotation
@ -119,7 +116,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
# Move these polygons to the right of the previous letter
for xys in raw_polys:
poly = Polygon(xys, dose=self.dose, layer=self.layer)
poly = Polygon(xys, layer=self.layer)
poly.mirror2d(self.mirrored)
poly.scale_by(self.height)
poly.offset = self.offset + [total_advance, 0]
@ -144,7 +141,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
rotation += self.rotation
rotation %= 2 * pi
return ((type(self), self.string, self.font_path, self.layer),
(self.offset, self.height / norm_value, rotation, mirror_x, self.dose),
(self.offset, self.height / norm_value, rotation, mirror_x),
lambda: Text(string=self.string,
height=self.height * norm_value,
font_path=self.font_path,
@ -254,6 +251,5 @@ def get_char_as_polygons(
def __repr__(self) -> str:
rotation = f'{self.rotation*180/pi:g}' if self.rotation != 0 else ''
dose = f' d{self.dose:g}' if self.dose != 1 else ''
mirrored = ' m{:d}{:d}'.format(*self.mirrored) if self.mirrored.any() else ''
return f'<TextShape "{self.string}" l{self.layer} o{self.offset} h{self.height:g}{rotation}{mirrored}{dose}>'
return f'<TextShape "{self.string}" l{self.layer} o{self.offset} h{self.height:g}{rotation}{mirrored}>'