Skip to content

Commit

Permalink
Merge pull request #184 from amccaugh/dev
Browse files Browse the repository at this point in the history
1.6.4
  • Loading branch information
amccaugh authored Jul 20, 2023
2 parents d17f156 + 59c5746 commit 47881c0
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 54 deletions.
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"

0 comments on commit 47881c0

Please sign in to comment.