From ff1fbd1e87e556e565df63507994a660a1018e45 Mon Sep 17 00:00:00 2001 From: Sebastian Goeldi Date: Wed, 31 May 2023 00:49:23 +0200 Subject: [PATCH 1/4] add mirror_x/mirror_y to instance --- changelog.d/92.added.md | 1 + src/kfactory/kcell.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 changelog.d/92.added.md diff --git a/changelog.d/92.added.md b/changelog.d/92.added.md new file mode 100644 index 00000000..92be0581 --- /dev/null +++ b/changelog.d/92.added.md @@ -0,0 +1 @@ +Added mirror_x/mirror_y to Instance \ No newline at end of file diff --git a/src/kfactory/kcell.py b/src/kfactory/kcell.py index b614384f..5f8a6c32 100644 --- a/src/kfactory/kcell.py +++ b/src/kfactory/kcell.py @@ -1996,6 +1996,14 @@ def __repr__(self) -> str: f"{self.parent_cell.name}: ports {port_names}, {self.kcl[self.cell_index]}" ) + def mirror_x(self, x: int = 0) -> None: + """Mirror the instance at an x-axis.""" + self.transform(kdb.Trans(2, True, 2 * x, 0)) + + def mirror_y(self, y: int = 0) -> None: + """Mirror the instance at an y-axis.""" + self.transform(kdb.Trans(0, True, 0, 2 * y)) + class UMInstance: """Make the port able to dynamically give um based info.""" @@ -2082,6 +2090,14 @@ def move( ) ) + def mirror_x(self, x: float = 0) -> None: + """Mirror the instance at an x-axis.""" + self.parent.transform(kdb.DTrans(2, True, 2 * x, 0)) + + def mirror_y(self, y: float = 0) -> None: + """Mirror the instance at an y-axis.""" + self.parent.transform(kdb.DTrans(0, True, 0, 2 * y)) + class Instances: """Holder for instances. From 2e33af0bfc1b9cba15ac5390cb000466b094e3cd Mon Sep 17 00:00:00 2001 From: Sebastian Goeldi Date: Wed, 31 May 2023 01:40:32 +0200 Subject: [PATCH 2/4] add (d)polygon_from_array --- pyproject.toml | 2 +- src/kfactory/__init__.py | 4 ++++ src/kfactory/kcell.py | 26 +++++++++++++++++++------- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d2bc37f9..225b83ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,7 +106,7 @@ exclude = [ "src/kfactory/technology", "src/kfactory/pdk.py", ] -plugins = "pydantic.mypy" +plugins = "pydantic.mypy, numpy.typing.mypy_plugin" [tool.pylsp-mypy] enabled = true diff --git a/src/kfactory/__init__.py b/src/kfactory/__init__.py index 9ef171b1..0b3f135b 100644 --- a/src/kfactory/__init__.py +++ b/src/kfactory/__init__.py @@ -20,6 +20,8 @@ default_save, LayerEnum, show, + polygon_from_array, + dpolygon_from_array, ) from . import cells, placer, routing, utils, port, pdk from .conf import config @@ -50,4 +52,6 @@ "LayerEnum", "logger", "pdk", + "polygon_from_array", + "dpolygon_from_array", ] diff --git a/src/kfactory/kcell.py b/src/kfactory/kcell.py index 5f8a6c32..d20c7ee2 100644 --- a/src/kfactory/kcell.py +++ b/src/kfactory/kcell.py @@ -15,13 +15,12 @@ import socket from collections.abc import Callable, Hashable, Iterable, Iterator -# from enum import IntEnum from enum import Enum, IntEnum from hashlib import sha3_512 from inspect import Parameter, signature from pathlib import Path from tempfile import gettempdir -from typing import ( # ParamSpec, # >= python 3.10 +from typing import ( TYPE_CHECKING, Any, Literal, @@ -30,7 +29,6 @@ overload, ) -# from cachetools import Cache, cached import cachetools.func import numpy as np import ruamel.yaml @@ -40,9 +38,6 @@ from .conf import config from .port import rename_clockwise -# import struct -# from abc import abstractmethod - if TYPE_CHECKING: from .pdk import Pdk @@ -193,7 +188,8 @@ class KCLayout(kdb.Layout): Attributes: editable: Whether the layout should be opened in editable mode (default: True) - rename_function: function that takes an iterable object of ports and renames them + rename_function: function that takes an iterable object of ports and renames + them """ def __init__(self, editable: bool = True, pdk: "Pdk | None" = None) -> None: @@ -2800,6 +2796,22 @@ def show( Path(gds_file).unlink() +def polygon_from_array(array: Iterable[tuple[int, int]]) -> kdb.Polygon: + """Create a DPolygon from a 2D array-like structure. (dbu version). + + Array-like: `[[x1,y1],[x2,y2],...]` + """ + return kdb.Polygon([kdb.Point(int(x), int(y)) for (x, y) in array]) + + +def dpolygon_from_array(array: Iterable[tuple[float, float]]) -> kdb.DPolygon: + """Create a DPolygon from a 2D array-like structure. (um version). + + Array-like: `[[x1,y1],[x2,y2],...]` + """ + return kdb.DPolygon([kdb.DPoint(int(x), int(y)) for (x, y) in array]) + + __all__ = [ "KCell", "Instance", From 6ced67cb9829d75e5401c74c6770b0c82c4535eb Mon Sep 17 00:00:00 2001 From: Sebastian Goeldi Date: Wed, 31 May 2023 01:59:15 +0200 Subject: [PATCH 3/4] add xmin/xmax/ymin/ymax to KCell and Instance --- ...16111.fixed.md => +changelog_fix.fixed.md} | 0 changelog.d/92.added.md | 2 +- src/kfactory/kcell.py | 75 ++++++++++++++++--- 3 files changed, 67 insertions(+), 10 deletions(-) rename changelog.d/{+55816111.fixed.md => +changelog_fix.fixed.md} (100%) diff --git a/changelog.d/+55816111.fixed.md b/changelog.d/+changelog_fix.fixed.md similarity index 100% rename from changelog.d/+55816111.fixed.md rename to changelog.d/+changelog_fix.fixed.md diff --git a/changelog.d/92.added.md b/changelog.d/92.added.md index 92be0581..e641248b 100644 --- a/changelog.d/92.added.md +++ b/changelog.d/92.added.md @@ -1 +1 @@ -Added mirror_x/mirror_y to Instance \ No newline at end of file +Added `mirror_x/mirror_y` to Instance, `xmin/xmax/ymin/ymax` getter & setter to Instance, `xmin/xmax/ymin/ymax` getter to KCell, `polygon_from_array`, `dpolygon_from_arry` diff --git a/src/kfactory/kcell.py b/src/kfactory/kcell.py index d20c7ee2..662e6d43 100644 --- a/src/kfactory/kcell.py +++ b/src/kfactory/kcell.py @@ -14,20 +14,12 @@ import json import socket from collections.abc import Callable, Hashable, Iterable, Iterator - from enum import Enum, IntEnum from hashlib import sha3_512 from inspect import Parameter, signature from pathlib import Path from tempfile import gettempdir -from typing import ( - TYPE_CHECKING, - Any, - Literal, - TypeVar, - cast, - overload, -) +from typing import TYPE_CHECKING, Any, Literal, TypeVar, cast, overload import cachetools.func import numpy as np @@ -1629,6 +1621,11 @@ def transform( no_warn: bool = False, ) -> "Instance | None": """Transforms the instance or cell with the transformation given.""" + config.logger.warning( + "You are transforming the KCell {}. It is highly discouraged to do this." + " You probably want to transform an instance instead.", + self.name, + ) if self._locked: raise LockedError(self) if trans: @@ -1641,6 +1638,26 @@ def transform( else: return self._kdb_cell.transform(inst_or_trans) # type:ignore[arg-type] + @property + def xmin(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._kdb_cell.bbox().left + + @property + def ymin(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._kdb_cell.bbox().bottom + + @property + def xmax(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._kdb_cell.bbox().right + + @property + def ymax(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._kdb_cell.bbox().top + class Instance: """An Instance of a KCell. @@ -2000,6 +2017,46 @@ def mirror_y(self, y: int = 0) -> None: """Mirror the instance at an y-axis.""" self.transform(kdb.Trans(0, True, 0, 2 * y)) + @property + def xmin(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._instance.bbox().left + + @xmin.setter + def xmin(self, __val: int) -> None: + """Moves the instance so that the bbox's left x-coordinate.""" + self.transform(kdb.Trans(__val - self.bbox().left, 0)) + + @property + def ymin(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._instance.bbox().bottom + + @ymin.setter + def ymin(self, __val: int) -> None: + """Moves the instance so that the bbox's left x-coordinate.""" + self.transform(kdb.Trans(0, __val - self._instance.bbox().bottom)) + + @property + def xmax(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._instance.bbox().right + + @xmax.setter + def xmax(self, __val: int) -> None: + """Moves the instance so that the bbox's left x-coordinate.""" + self.transform(kdb.Trans(__val - self.bbox().right, 0)) + + @property + def ymax(self) -> int: + """Returns the x-coordinate of the left edge of the bounding box.""" + return self._instance.bbox().top + + @ymax.setter + def ymax(self, __val: int) -> None: + """Moves the instance so that the bbox's left x-coordinate.""" + self.transform(kdb.Trans(0, __val - self._instance.bbox().top)) + class UMInstance: """Make the port able to dynamically give um based info.""" From 8fef40e3e51246ac109e2a4109b49a45896836ae Mon Sep 17 00:00:00 2001 From: Joaquin Matres <4514346+joamatab@users.noreply.github.com> Date: Tue, 30 May 2023 17:55:22 -0700 Subject: [PATCH 4/4] document new functions --- docs/source/notebooks/00_geometry.py | 35 +++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/source/notebooks/00_geometry.py b/docs/source/notebooks/00_geometry.py index a6c5c4b5..f911ce73 100644 --- a/docs/source/notebooks/00_geometry.py +++ b/docs/source/notebooks/00_geometry.py @@ -29,6 +29,7 @@ # %% import kfactory as kf +import numpy as np # %% # Create a blank cell (essentially an empty GDS cell with some special features) @@ -60,6 +61,14 @@ # # Make a cell similar to the one above that has a second polygon in layer (1, 1) +# %% +c = kf.KCell() +points = np.array([(-8, -6), (6, 8), (7, 17), (9, 5)]) +poly = kf.polygon_from_array(points) +c.shapes(c.kcl.layer(1, 0)).insert(poly1) +c.shapes(c.kcl.layer(1, 1)).insert(poly1) +c + # %% import kfactory as kf @@ -265,9 +274,33 @@ def straight(length=10, width=1, layer=(1, 0)): # %% c2 +# %% +c = kf.KCell() +bend = kf.cells.euler.bend_euler(radius=10, width=1, layer=0) +b1 = c << bend +b2 = c << bend +b2.mirror_x(x=0) +c + +# %% +c = kf.KCell() +bend = kf.cells.euler.bend_euler(radius=10, width=1, layer=0) +b1 = c << bend +b2 = c << bend +b2.mirror_y(y=0) +c + +# %% +c = kf.KCell() +bend = kf.cells.euler.bend_euler(radius=10, width=1, layer=0) +b1 = c << bend +b2 = c << bend +b2.mirror_y(y=0) +b1.ymin = b2.ymax +c + # %% [markdown] # -# self.layout_view.active_cellview().layout().cell(event["owner"].name) # ## Labels # # You can add abstract GDS labels (annotate) to your Cells, in order to record information