diff --git a/examples/tutorial/pcgen.py b/examples/tutorial/pcgen.py index 023079c..c265f64 100644 --- a/examples/tutorial/pcgen.py +++ b/examples/tutorial/pcgen.py @@ -233,8 +233,8 @@ def ln_shift_defect( # Shift holes # Expand shifts as necessary - tmp_a = numpy.asarray(shifts_a) - tmp_r = numpy.asarray(shifts_r) + tmp_a = numpy.array(shifts_a) + tmp_r = numpy.array(shifts_r) n_shifted = max(tmp_a.size, tmp_r.size) shifts_a = numpy.ones(n_shifted) diff --git a/masque/abstract.py b/masque/abstract.py index 248c8a5..e3ab46e 100644 --- a/masque/abstract.py +++ b/masque/abstract.py @@ -97,7 +97,7 @@ class Abstract(PortList): Returns: self """ - pivot = numpy.asarray(pivot, dtype=float) + pivot = numpy.array(pivot) self.translate_ports(-pivot) self.rotate_ports(rotation) self.rotate_port_offsets(rotation) diff --git a/masque/file/dxf.py b/masque/file/dxf.py index dc3d6f3..aa73279 100644 --- a/masque/file/dxf.py +++ b/masque/file/dxf.py @@ -212,9 +212,9 @@ def _read_block(block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace) -> for element in block: if isinstance(element, LWPolyline | Polyline): if isinstance(element, LWPolyline): - points = numpy.asarray(element.get_points()) + points = numpy.array(element.get_points()) elif isinstance(element, Polyline): - points = numpy.asarray(element.points())[:, :2] + points = numpy.array(element.points())[:, :2] attr = element.dxfattribs() layer = attr.get('layer', DEFAULT_LAYER) @@ -241,7 +241,7 @@ def _read_block(block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace) -> elif isinstance(element, Text): args = dict( - offset=numpy.asarray(element.get_placement()[1])[:2], + offset=numpy.array(element.get_placement()[1])[:2], layer=element.dxfattribs().get('layer', DEFAULT_LAYER), ) string = element.dxfattribs().get('text', '') @@ -262,7 +262,7 @@ def _read_block(block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace) -> mirrored, extra_angle = normalize_mirror((yscale < 0, xscale < 0)) rotation = numpy.deg2rad(attr.get('rotation', 0)) + extra_angle - offset = numpy.asarray(attr.get('insert', (0, 0, 0)))[:2] + offset = numpy.array(attr.get('insert', (0, 0, 0)))[:2] args = dict( target=attr.get('name', None), diff --git a/masque/file/gdsii.py b/masque/file/gdsii.py index 71ea94f..11e1574 100644 --- a/masque/file/gdsii.py +++ b/masque/file/gdsii.py @@ -357,7 +357,7 @@ def _mrefs_to_grefs(refs: dict[str | None, list[Ref]]) -> list[klamath.library.R if isinstance(rep, Grid): b_vector = rep.b_vector if rep.b_vector is not None else numpy.zeros(2) b_count = rep.b_count if rep.b_count is not None else 1 - xy = numpy.asarray(ref.offset) + numpy.array([ + xy = numpy.array(ref.offset) + numpy.array([ [0.0, 0.0], rep.a_vector * rep.a_count, b_vector * b_count, diff --git a/masque/file/svg.py b/masque/file/svg.py index 148f6d4..8c4b43d 100644 --- a/masque/file/svg.py +++ b/masque/file/svg.py @@ -154,7 +154,7 @@ def poly2path(vertices: ArrayLike) -> str: Returns: SVG path-string. """ - verts = numpy.asarray(vertices) + verts = numpy.array(vertices, copy=False) commands = 'M{:g},{:g} '.format(verts[0][0], verts[0][1]) # noqa: UP032 for vertex in verts[1:]: commands += 'L{:g},{:g}'.format(vertex[0], vertex[1]) # noqa: UP032 diff --git a/masque/label.py b/masque/label.py index 711ef35..7eb9068 100644 --- a/masque/label.py +++ b/masque/label.py @@ -49,7 +49,7 @@ class Label(PositionableImpl, RepeatableImpl, AnnotatableImpl, Bounded, Pivotabl annotations: annotations_t | None = None, ) -> None: self.string = string - self.offset = numpy.array(offset, dtype=float) + self.offset = numpy.array(offset, dtype=float, copy=True) self.repetition = repetition self.annotations = annotations if annotations is not None else {} @@ -94,7 +94,7 @@ class Label(PositionableImpl, RepeatableImpl, AnnotatableImpl, Bounded, Pivotabl Returns: self """ - pivot = numpy.asarray(pivot, dtype=float) + pivot = numpy.array(pivot, dtype=float) self.translate(-pivot) self.offset = numpy.dot(rotation_matrix_2d(rotation), self.offset) self.translate(+pivot) diff --git a/masque/library.py b/masque/library.py index 11a3c1e..9771eaf 100644 --- a/masque/library.py +++ b/masque/library.py @@ -460,7 +460,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta): if transform is None or transform is True: transform = numpy.zeros(4) elif transform is not False: - transform = numpy.asarray(transform, dtype=float) + transform = numpy.array(transform, dtype=float, copy=False) original_pattern = pattern diff --git a/masque/pattern.py b/masque/pattern.py index 1816762..47f64c5 100644 --- a/masque/pattern.py +++ b/masque/pattern.py @@ -690,7 +690,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): Returns: self """ - pivot = numpy.asarray(pivot, dtype=float) + pivot = numpy.array(pivot) self.translate_elements(-pivot) self.rotate_elements(rotation) self.rotate_element_centers(rotation) @@ -1023,7 +1023,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): if self.has_refs() and library is None: raise PatternError('Must provide a library when visualizing a pattern with refs') - offset = numpy.asarray(offset, dtype=float) + offset = numpy.array(offset, dtype=float) if not overdraw: figure = pyplot.figure() diff --git a/masque/repetition.py b/masque/repetition.py index a365909..68b6b19 100644 --- a/masque/repetition.py +++ b/masque/repetition.py @@ -156,11 +156,12 @@ class Grid(Repetition): @a_vector.setter def a_vector(self, val: ArrayLike) -> None: - val = numpy.array(val, dtype=float) + if not isinstance(val, numpy.ndarray): + val = numpy.array(val, dtype=float) if val.size != 2: raise PatternError('a_vector must be convertible to size-2 ndarray') - self._a_vector = val.flatten() + self._a_vector = val.flatten().astype(float) # b_vector property @property @@ -169,7 +170,8 @@ class Grid(Repetition): @b_vector.setter def b_vector(self, val: ArrayLike) -> None: - val = numpy.array(val, dtype=float) + if not isinstance(val, numpy.ndarray): + val = numpy.array(val, dtype=float, copy=True) if val.size != 2: raise PatternError('b_vector must be convertible to size-2 ndarray') @@ -332,9 +334,9 @@ class Arbitrary(Repetition): @displacements.setter def displacements(self, val: ArrayLike) -> None: - vala = numpy.array(val, dtype=float) - order = numpy.lexsort(vala.T[::-1]) # sortrows - self._displacements = vala[order] + vala: NDArray[numpy.float64] = numpy.array(val, dtype=float) + vala = numpy.sort(vala.view([('', vala.dtype)] * vala.shape[1]), 0).view(vala.dtype) # sort rows + self._displacements = vala def __init__( self, diff --git a/masque/shapes/arc.py b/masque/shapes/arc.py index eb565bf..8d14f0f 100644 --- a/masque/shapes/arc.py +++ b/masque/shapes/arc.py @@ -472,7 +472,7 @@ class Arc(Shape): a1 += sign * 2 * pi a.append((a0, a1)) - return numpy.array(a, dtype=float) + return numpy.array(a) def __repr__(self) -> str: angles = f' a°{numpy.rad2deg(self.angles)}' diff --git a/masque/shapes/path.py b/masque/shapes/path.py index aaac0d2..dfd89f1 100644 --- a/masque/shapes/path.py +++ b/masque/shapes/path.py @@ -33,7 +33,8 @@ class Path(Shape): A path, consisting of a bunch of vertices (Nx2 ndarray), a width, an end-cap shape, and an offset. - Note that the setter for `Path.vertices` will create a copy of the passed vertex coordinates. + Note that the setter for `Path.vertices` may (but may not) create a copy of the + passed vertex coordinates. See `numpy.array(..., copy=False)` for details. A normalized_form(...) is available, but can be quite slow with lots of vertices. """ @@ -117,7 +118,8 @@ class Path(Shape): """ Vertices of the path (Nx2 ndarray: `[[x0, y0], [x1, y1], ...]` - When setting, note that a copy of the provided vertices will be made. + When setting, note that a copy of the provided vertices may or may not be made, + following the rules from `numpy.array(.., copy=False)`. """ return self._vertices diff --git a/masque/shapes/polygon.py b/masque/shapes/polygon.py index 1e0352f..f6add4d 100644 --- a/masque/shapes/polygon.py +++ b/masque/shapes/polygon.py @@ -20,8 +20,8 @@ class Polygon(Shape): A polygon, consisting of a bunch of vertices (Nx2 ndarray) which specify an implicitly-closed boundary, and an offset. - Note that the setter for `Polygon.vertices` may creates a copy of the - passed vertex coordinates. + Note that the setter for `Polygon.vertices` may (but may not) create a copy of the + passed vertex coordinates. See `numpy.array(..., copy=False)` for details. A `normalized_form(...)` is available, but can be quite slow with lots of vertices. """ @@ -40,7 +40,8 @@ class Polygon(Shape): """ Vertices of the polygon (Nx2 ndarray: `[[x0, y0], [x1, y1], ...]`) - When setting, note that a copy of the provided vertices will be made, + When setting, note that a copy of the provided vertices may or may not be made, + following the rules from `numpy.array(.., copy=False)`. """ return self._vertices diff --git a/masque/shapes/text.py b/masque/shapes/text.py index e936796..fa0038b 100644 --- a/masque/shapes/text.py +++ b/masque/shapes/text.py @@ -230,8 +230,7 @@ def get_char_as_polygons( outline = slot.outline start = 0 - all_verts_list = [] - all_codes = [] + all_verts_list, all_codes = [], [] for end in outline.contours: points = outline.points[start:end + 1] points.append(points[0]) diff --git a/masque/traits/mirrorable.py b/masque/traits/mirrorable.py index 6d4ec3c..bb7f011 100644 --- a/masque/traits/mirrorable.py +++ b/masque/traits/mirrorable.py @@ -60,7 +60,7 @@ class Mirrorable(metaclass=ABCMeta): # def mirrored(self, val: Sequence[bool]) -> None: # if is_scalar(val): # raise MasqueError('Mirrored must be a 2-element list of booleans') -# self._mirrored = numpy.array(val, dtype=bool) +# self._mirrored = numpy.array(val, dtype=bool, copy=True) # # # # # Methods diff --git a/masque/traits/positionable.py b/masque/traits/positionable.py index 66e6e7d..2b9c02e 100644 --- a/masque/traits/positionable.py +++ b/masque/traits/positionable.py @@ -81,11 +81,12 @@ class PositionableImpl(Positionable, metaclass=ABCMeta): @offset.setter def offset(self, val: ArrayLike) -> None: - val = numpy.array(val, dtype=float) + if not isinstance(val, numpy.ndarray) or val.dtype != numpy.float64: + val = numpy.array(val, dtype=float) if val.size != 2: raise MasqueError('Offset must be convertible to size-2 ndarray') - self._offset = val.flatten() + self._offset = val.flatten() # type: ignore # # Methods diff --git a/masque/traits/rotatable.py b/masque/traits/rotatable.py index f873ce4..45caa31 100644 --- a/masque/traits/rotatable.py +++ b/masque/traits/rotatable.py @@ -112,7 +112,7 @@ class PivotableImpl(Pivotable, metaclass=ABCMeta): """ `[x_offset, y_offset]` """ def rotate_around(self, pivot: ArrayLike, rotation: float) -> Self: - pivot = numpy.asarray(pivot, dtype=float) + pivot = numpy.array(pivot, dtype=float) cast(Positionable, self).translate(-pivot) cast(Rotatable, self).rotate(rotation) self.offset = numpy.dot(rotation_matrix_2d(rotation), self.offset) # type: ignore # mypy#3004 diff --git a/masque/utils/pack2d.py b/masque/utils/pack2d.py index ce6b006..5de9728 100644 --- a/masque/utils/pack2d.py +++ b/masque/utils/pack2d.py @@ -38,8 +38,8 @@ def maxrects_bssf( Raises: MasqueError if `allow_rejects` is `True` but some `rects` could not be placed. """ - regions = numpy.asarray(containers, dtype=float) - rect_sizes = numpy.asarray(rects, dtype=float) + regions = numpy.array(containers, copy=False, dtype=float) + rect_sizes = numpy.array(rects, copy=False, dtype=float) rect_locs = numpy.zeros_like(rect_sizes) rejected_inds = set() @@ -139,8 +139,8 @@ def guillotine_bssf_sas( Raises: MasqueError if `allow_rejects` is `True` but some `rects` could not be placed. """ - regions = numpy.asarray(containers, dtype=float) - rect_sizes = numpy.asarray(rects, dtype=float) + regions = numpy.array(containers, copy=False, dtype=float) + rect_sizes = numpy.array(rects, copy=False, dtype=float) rect_locs = numpy.zeros_like(rect_sizes) rejected_inds = set() @@ -227,7 +227,7 @@ def pack_patterns( MasqueError if `allow_rejects` is `True` but some `rects` could not be placed. """ - half_spacing = numpy.asarray(spacing, dtype=float) / 2 + half_spacing = numpy.array(spacing, copy=False, dtype=float) / 2 bounds = [library[pp].get_bounds() for pp in patterns] sizes = [bb[1] - bb[0] + spacing if bb is not None else spacing for bb in bounds] diff --git a/masque/utils/vertices.py b/masque/utils/vertices.py index 23fb601..0c5f03b 100644 --- a/masque/utils/vertices.py +++ b/masque/utils/vertices.py @@ -15,9 +15,9 @@ def remove_duplicate_vertices(vertices: ArrayLike, closed_path: bool = True) -> (i.e. the last vertex will be removed if it is the same as the first) Returns: - `vertices` with no consecutive duplicates. This may be a view into the original array. + `vertices` with no consecutive duplicates. """ - vertices = numpy.asarray(vertices) + vertices = numpy.array(vertices) duplicates = (vertices == numpy.roll(vertices, 1, axis=0)).all(axis=1) if not closed_path: duplicates[0] = False @@ -35,7 +35,7 @@ def remove_colinear_vertices(vertices: ArrayLike, closed_path: bool = True) -> N closed path. If `False`, the path is assumed to be open. Default `True`. Returns: - `vertices` with colinear (superflous) vertices removed. May be a view into the original array. + `vertices` with colinear (superflous) vertices removed. """ vertices = remove_duplicate_vertices(vertices) @@ -73,8 +73,8 @@ def poly_contains_points( Returns: ndarray of booleans, [point0_is_in_shape, point1_is_in_shape, ...] """ - points = numpy.asarray(points, dtype=float) - vertices = numpy.asarray(vertices, dtype=float) + points = numpy.array(points, copy=False) + vertices = numpy.array(vertices, copy=False) if points.size == 0: return numpy.zeros(0, dtype=numpy.int8)