diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 12dfd18d..67fe2f1a 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -9,7 +9,7 @@ jobs: pytest: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10"] fail-fast: false runs-on: ubuntu-latest steps: @@ -20,14 +20,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -U \ - matplotlib \ - scipy \ - rectpack \ - klayout \ - freetype-py \ - pytest - pip install -e . + pip install -e '.[test]' - name: Run test uses: GabrielBB/xvfb-action@v1 with: diff --git a/README.md b/README.md index f57f4e65..5624c818 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,12 @@ If you found PHIDL useful, please consider citing it in (just one!) of your publ # Installation / requirements - Install or upgrade with `pip install -U phidl` +- Install with `pip install -U phidl[all]` to include optional dependencies (e.g. freetype-py, klayout, rectpack) - Python version >=3.6 +## Testing +- Install with test dependencies with `pip install -U phidl[test]` (includes `all` extras as well) +- Run tests with `pytest` (or `python -m pytest`) # About PHIDL diff --git a/phidl/device_layout.py b/phidl/device_layout.py index c1cc6816..f9ef05e1 100644 --- a/phidl/device_layout.py +++ b/phidl/device_layout.py @@ -353,9 +353,13 @@ def _line_distances(points, start, end): if np.all(start == end): return np.linalg.norm(points - start, axis=1) - vec = end - start - cross = np.cross(vec, start - points) - return np.divide(abs(cross), np.linalg.norm(vec)) + line_vec = end - start + relative_points = start - points + cross = ( + line_vec[..., 0] * relative_points[..., 1] + - line_vec[..., 1] * relative_points[..., 0] + ) + return np.divide(abs(cross), np.linalg.norm(line_vec)) def _simplify(points, tolerance=0): @@ -2728,7 +2732,7 @@ def append(self, path): and np.issubdtype(np.array(path).dtype, np.number) and (np.shape(path)[1] == 2) ): - points = np.asfarray(path) + points = np.asarray(path, dtype=np.float64) nx1, ny1 = points[1] - points[0] start_angle = np.arctan2(ny1, nx1) / np.pi * 180 nx2, ny2 = points[-1] - points[-2] diff --git a/phidl/geometry.py b/phidl/geometry.py index 12f077f1..5081a315 100644 --- a/phidl/geometry.py +++ b/phidl/geometry.py @@ -943,7 +943,7 @@ def _merge_floating_point_errors(polygons, tol=1e-10): Set of corrected polygons. """ if len(polygons) == 0: - return np.asfarray([]) + return np.asarray([], dtype=np.float64) stacked_polygons = np.vstack(polygons) x = stacked_polygons[:, 0] y = stacked_polygons[:, 1] @@ -1488,7 +1488,7 @@ def _kl_region_to_device(kl_region, layer, name, precision): for polygon in kl_region.each(): polygon = polygon.to_simple_polygon() points = _kl_polygon_to_array(polygon) - points = np.asfarray(points) * precision + points = np.asarray(points, dtype=np.float64) * precision D.add_polygon(points, layer=layer) return D @@ -2225,7 +2225,7 @@ def import_gds(filename, cellname=None, flatten=False): rotation = 0 l = D.add_label( text=label.text, - position=np.asfarray(label.position), + position=np.asarray(label.position, dtype=np.float64), magnification=label.magnification, rotation=rotation * 180 / np.pi, layer=(label.layer, label.texttype), diff --git a/phidl/path.py b/phidl/path.py index 1f1aad7e..21c86988 100644 --- a/phidl/path.py +++ b/phidl/path.py @@ -1,3 +1,5 @@ +import math + import numpy as np from phidl.device_layout import CrossSection, Path, _rotate_points @@ -74,8 +76,8 @@ def _fresnel(R0, s, num_pts, n_iter=8): y = np.zeros(num_pts) for n in range(0, n_iter): - x += (-1) ** n * t ** (4 * n + 1) / (np.math.factorial(2 * n) * (4 * n + 1)) - y += (-1) ** n * t ** (4 * n + 3) / (np.math.factorial(2 * n + 1) * (4 * n + 3)) + x += (-1) ** n * t ** (4 * n + 1) / (math.factorial(2 * n) * (4 * n + 1)) + y += (-1) ** n * t ** (4 * n + 3) / (math.factorial(2 * n + 1) * (4 * n + 3)) return np.array([np.sqrt(2) * R0 * x, np.sqrt(2) * R0 * y]) @@ -142,7 +144,7 @@ def euler(radius=3, angle=90, p=1.0, use_eff=False, num_pts=720): dx = xp - Rp * np.sin(p * alpha / 2) dy = yp - Rp * (1 - np.cos(p * alpha / 2)) else: - xbend1 = ybend1 = np.asfarray([]) + xbend1 = ybend1 = np.array([], dtype=np.float64) dx = 0 dy = 0 @@ -293,7 +295,7 @@ def spiral(num_turns=5, gap=1, inner_gap=2, num_pts=10000): def _compute_segments(points): - points = np.asfarray(points) + points = np.asarray(points, dtype=np.float64) normals = np.diff(points, axis=0) normals = (normals.T / np.linalg.norm(normals, axis=1)).T dx = np.diff(points[:, 0]) diff --git a/requirements.txt b/requirements.txt index 0769c5c5..a3c81ce2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ -six +gdspy +klayout numpy matplotlib -gdspy +rectpack +six diff --git a/ruff.toml b/ruff.toml index 921a9bb3..70f87082 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,7 +1,7 @@ line-length = 88 [lint] -select = ["C", "E", "F", "B", "B9", "UP", "I"] +select = ["C", "E", "F", "B", "B9", "UP", "I", "NPY201"] ignore = [ "E203", "E266", diff --git a/setup.py b/setup.py index e8de67a5..022d842a 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,10 @@ "matplotlib", ] +extras_require = {} +extras_require["all"] = ["freetype-py", "klayout", "rectpack", "scipy"] +extras_require["test"] = extras_require["all"] + ["pytest"] + # read the contents of your README file this_directory = path.abspath(path.dirname(__file__)) @@ -24,6 +28,7 @@ long_description=long_description, long_description_content_type="text/markdown", install_requires=install_requires, + extras_require=extras_require, author="Adam McCaughan", author_email="amccaugh@gmail.com", packages=["phidl"], diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 84430b70..63d5613d 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -207,8 +207,10 @@ def test_text(): def test_truetype(): - _ = pytest.importorskip( - "phidl.font", reason="Testing of ttf/otf fonts requires the freetype package." + pytest.importorskip( + "phidl.font", + reason="Testing of ttf/otf fonts requires the freetype package.", + exc_type=ImportError, ) from os import path @@ -229,8 +231,10 @@ def test_truetype(): @pytest.mark.skipif(sys.version_info < (3, 0), reason="unicode test requires python3") def test_unicode(): - _ = pytest.importorskip( - "phidl.font", reason="Testing of ttf/otf fonts requires the freetype package." + pytest.importorskip( + "phidl.font", + reason="Testing of ttf/otf fonts requires the freetype package.", + exc_type=ImportError, ) from os import path