Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.6.4 #184

Merged
merged 17 commits into from
Jul 20, 2023
Merged

1.6.4 #184

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## 1.6.4 (July 20, 2023)

### New features
- Optimized/vectorized point rotation, speeding up the `rotate()` operation by as much as 150x(!) (thanks Bas Nijholt @basnijholt)
- Added proper `pg.fill_rectangle()` [examples and documentation](https://phidl.readthedocs.io/en/latest/geometry_reference.html#Fill-tool)

### Bugfixes
- Fixed `pg.grid()` to allow for empty `shape` parameter (thanks Samuel Gyger @gyger)
- Allow `pg.grid()` spacing to be a single integer (thanks Samuel Gyger @gyger)
- Fix to np.bool


## 1.6.3 (Feb 9, 2023)

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ GDS scripting for Python that's intuitive, fast, and powerful.
- [**Installation / requirements**](#installation--requirements)
- [**Tutorial + examples**](https://phidl.readthedocs.io/en/latest/tutorials.html) (or [try an interactive notebook](https://mybinder.org/v2/gh/amccaugh/phidl/master?filepath=phidl_tutorial_example.ipynb))
- [**Geometry library + function documentation**](https://phidl.readthedocs.io/en/latest/geometry_reference.html)
- [Changelog](https://github.com/amccaugh/phidl/blob/master/CHANGELOG.md) (latest update 1.6.3 on Feb 9, 2023)
- [Changelog](https://github.com/amccaugh/phidl/blob/master/CHANGELOG.md) (latest update 1.6.3 on July 20, 2023)
- New `pg.fill_rectangle()` [examples and documentation](https://phidl.readthedocs.io/en/latest/geometry_reference.html#Fill-tool)

# Citation

Expand Down
242 changes: 217 additions & 25 deletions docs/geometry_reference.ipynb

Large diffs are not rendered by default.

36 changes: 19 additions & 17 deletions phidl/device_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,12 @@
import gdspy.library
import numpy as np
from numpy import cos, mod, pi, sin, sqrt
from numpy.linalg import norm

from phidl.constants import _CSS3_NAMES_TO_HEX

gdspy.library.use_current_library = False

__version__ = "1.6.3"
__version__ = "1.6.4"


# ==============================================================================
Expand Down Expand Up @@ -111,23 +110,26 @@ def _reflect_points(points, p1=(0, 0), p2=(1, 0)):
-------
A new set of points that are reflected across ``p1`` and ``p2``.
"""
# From http://math.stackexchange.com/questions/11515/point-reflection-across-a-line
points = np.array(points)
p1 = np.array(p1)
p2 = np.array(p2)
if np.asarray(points).ndim == 1:
return (
2 * (p1 + (p2 - p1) * np.dot((p2 - p1), (points - p1)) / norm(p2 - p1) ** 2)
- points
)
if np.asarray(points).ndim == 2:
return np.array(
[
2 * (p1 + (p2 - p1) * np.dot((p2 - p1), (p - p1)) / norm(p2 - p1) ** 2)
- p
for p in points
]
)
line_vec = p2 - p1
line_vec_norm = np.linalg.norm(line_vec, axis=-1, keepdims=True) ** 2

# Checking if the input is 1D and adding an extra dimension if it is
input_was_1d = np.asarray(points).ndim == 1
if input_was_1d:
points = np.array([points])

reflected_points_projection = (
np.sum(line_vec * (points - p1), axis=-1, keepdims=True) / line_vec_norm
)
reflected_points = 2 * (p1 + line_vec * reflected_points_projection) - points

# If the input was 1D, remove the extra dimension from the output
if input_was_1d:
reflected_points = np.squeeze(reflected_points, axis=0)

return reflected_points


def _is_iterable(items):
Expand Down
20 changes: 10 additions & 10 deletions phidl/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -3316,8 +3316,8 @@ def grid(
If True, guarantees elements are speparated with a fixed spacing
between; if False, elements are spaced evenly along a grid.
shape : array-like[2]
x, y shape of the grid (see np.reshape). If no shape is given and the
list is 1D, the output is as if np.reshape were run with (1, -1).
x, y shape of the grid (as if np.reshape() were run with shape[::-1]). If no shape is given and the
list is 1D, the output is as if np.reshape were run with (-1, size of list).
align_x : {'x', 'xmin', 'xmax'}
Which edge to perform the x (column) alignment along
align_y : {'y', 'ymin', 'ymax'}
Expand All @@ -3335,9 +3335,8 @@ def grid(
A Device containing all the Devices in `device_list` in a grid.
"""

# Change (y,x) shape to (x,y) shape
shape = shape[::-1]
device_array = np.asarray(device_list)
spacing = np.broadcast_to(spacing, 2)
# Check arguments
if device_array.ndim not in (1, 2):
raise ValueError("[PHIDL] grid() The device_list needs to be 1D or 2D.")
Expand All @@ -3351,12 +3350,15 @@ def grid(
if (shape is None) and (device_array.ndim == 2): # Already in desired shape
shape = device_array.shape
elif (shape is None) and (device_array.ndim == 1):
shape = (device_array.size, -1)
shape = (-1, device_array.size)
elif 0 < shape[0] * shape[1] < device_array.size:
raise ValueError(
"[PHIDL] grid() The shape is too small for all the items in device_list"
)
else:
# Change (x,y) shape to (y,x) shape to follow default row, column format in np.reshape
shape = shape[::-1]

if np.min(shape) == -1:
max_shape = np.max(shape)
min_devices = int(np.ceil(device_array.size / max_shape) * max_shape)
Expand Down Expand Up @@ -3773,7 +3775,7 @@ def _rasterize_polygons(polygons, bounds=[[-100, -100], [100, 100]], dx=1, dy=1)
# Initialize the raster matrix we'll be writing to
xsize = int(np.ceil(bounds[1][0] - bounds[0][0]) / dx)
ysize = int(np.ceil(bounds[1][1] - bounds[0][1]) / dy)
raster = np.zeros((ysize, xsize), dtype=np.bool)
raster = np.zeros((ysize, xsize), dtype=bool)

# TODO: Replace polygon_perimeter with the supercover version
for n in range(len(xpts)):
Expand Down Expand Up @@ -3809,15 +3811,13 @@ def _expand_raster(raster, distance=(4, 2)):
return raster

num_pixels = np.array(np.ceil(distance), dtype=int)
neighborhood = np.zeros(
(num_pixels[1] * 2 + 1, num_pixels[0] * 2 + 1), dtype=np.bool
)
neighborhood = np.zeros((num_pixels[1] * 2 + 1, num_pixels[0] * 2 + 1), dtype=bool)
rr, cc = draw.ellipse(
num_pixels[1], num_pixels[0], distance[1] + 0.5, distance[0] + 0.5
)
neighborhood[rr, cc] = 1

return morphology.binary_dilation(image=raster, selem=neighborhood)
return morphology.binary_dilation(image=raster, footprint=neighborhood)


def _fill_cell_rectangle(
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

setup(
name="phidl",
version="1.6.3",
version="1.6.4",
description="PHIDL",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down
21 changes: 21 additions & 0 deletions tests/test_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,24 @@ def test_packer():
D = D_packed_list[0]
h = D.hash_geometry(precision=1e-4)
assert h == "d90e43693a5840bdc21eae85f56fdaa57fdb88b2"


def test_grid():
device_list = []
for width1 in [1, 6, 9]:
for width2 in [1, 2, 4, 8]:
D = pg.taper(length=10, width1=width1, width2=width2, layer=0)
device_list.append(D)

D = pg.grid(
device_list,
spacing=(5, 1),
separation=True,
shape=(3, 4),
align_x="x",
align_y="y",
edge_x="x",
edge_y="ymax",
)
h = D.hash_geometry(precision=1e-4)
assert h == "9228ee40016e508f5589effd50056df633357de2"