from __future__ import annotations
import pathlib
import tempfile
import uuid
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.typings import ComponentOrPath
valid_operations = ("xor", "not", "and", "or")
[docs]
@gf.cell
def boolean_klayout(
gdspath1: ComponentOrPath,
gdspath2: ComponentOrPath,
layer1: tuple[int, int] = (1, 0),
layer2: tuple[int, int] = (1, 0),
layer3: tuple[int, int] = (2, 0),
operation: str = "xor",
) -> Component:
"""Returns a boolean operation between two components Uses KLayout python API.
Args:
gdspath1: path to GDS or Component.
gdspath2: path to GDS or Component.
layer1: tuple for gdspath1.
layer2: tuple for gdspath2.
layer3: for the result of the operation.
"""
import klayout.db as pya
if operation not in valid_operations:
raise ValueError(f"{operation} not in {valid_operations}")
if isinstance(gdspath1, Component):
gdspath1.flatten()
gdspath1 = gdspath1.write_gds()
if isinstance(gdspath2, Component):
gdspath2.flatten()
gdspath2 = gdspath2.write_gds()
layout1 = pya.Layout()
layout1.read(str(gdspath1))
cell1 = layout1.top_cell()
layout2 = pya.Layout()
layout2.read(str(gdspath2))
cell2 = layout2.top_cell()
cellname = f"boolean_{str(uuid.uuid4())[:8]}"
layout3 = pya.Layout()
layout3_top = layout3.create_cell(cellname)
a = pya.Region(cell1.begin_shapes_rec(layout1.layer(layer1[0], layer1[1])))
b = pya.Region(cell2.begin_shapes_rec(layout2.layer(layer2[0], layer2[1])))
if operation == "xor":
result = a ^ b
elif operation == "not":
result = a - b
elif operation == "and":
result = a & b
elif operation == "or":
result = a | b
layout3_top.shapes(layout3.layer(layer3[0], layer3[1])).insert(result)
dirpath_build = pathlib.Path(tempfile.TemporaryDirectory().name)
dirpath_build.mkdir(exist_ok=True, parents=True)
gdspath = str(dirpath_build / f"{cellname}.gds")
layout3.write(gdspath)
return gf.import_gds(gdspath)
def _demo() -> None:
import klayout.db as pya
import gdsfactory as gf
gdspath1 = gf.Component("ellipse1")
gdspath1.add_ref(gf.components.ellipse(radii=[10, 5], layer=(1, 0)))
gdspath2 = gf.Component("ellipse2")
gdspath2.add_ref(gf.components.ellipse(radii=[11, 4], layer=(1, 0))).movex(4)
layer1 = layer2 = (1, 0)
layer3 = (1, 0)
if isinstance(gdspath1, Component):
gdspath1.flatten()
gdspath1 = gdspath1.write_gds()
if isinstance(gdspath2, Component):
gdspath2.flatten()
gdspath2 = gdspath2.write_gds()
gf.show(gdspath2)
layout1 = pya.Layout()
layout1.read(str(gdspath1))
cell1 = layout1.top_cell()
layout2 = pya.Layout()
layout2.read(str(gdspath2))
cell2 = layout2.top_cell()
layout3 = pya.Layout()
layout3_top = layout3.create_cell("top")
a = pya.Region(cell1.begin_shapes_rec(layout1.layer(layer1[0], layer1[1])))
b = pya.Region(cell2.begin_shapes_rec(layout2.layer(layer2[0], layer2[1])))
rxor = a ^ b
layout3_top.shapes(layout3.layer(layer3[0], layer3[1])).insert(rxor)
layout3_top.write("boolean.gds")
gf.show("boolean.gds")
def _show_shapes() -> None:
c1 = gf.components.ellipse(radii=[8, 8], layer=(1, 0))
c2 = gf.components.ellipse(radii=[11, 4], layer=(1, 0))
c3 = gf.Component()
c3 << c1
c3 << c2
c3.show()
if __name__ == "__main__":
# _show_shapes()
c1 = gf.components.ellipse(radii=[8, 8], layer=(1, 0))
c2 = gf.components.ellipse(radii=[11, 4], layer=(1, 0))
c = boolean_klayout(c1, c2, operation="not")
c.show(show_ports=True)