def add_keepout( component: Component, target_layers: Layers, keepout_layers: Layers, margin: float = 2.0, ) -> Component: """Adds keepout after Looking up all polygons in a cell. You can also use add_padding Args: component target_layers: list of layers to read keepout_layers: list of layers to add keepout margin: offset from tareget to keepout_layers """ c = Component(f"{component.name}_ko") c << component for layer in target_layers: polygons = component.get_polygons(by_spec=layer) if polygons: for ko_layer in keepout_layers: ko_layer = _parse_layer(ko_layer) polygon_keepout = [ polygon_grow(polygon, margin) for polygon in polygons ] c.add_polygon(polygon_keepout, ko_layer) return c
def to_3d( component: Component, layer_set: LayerSet, layer_stack: LayerStack = LAYER_STACK, exclude_layers: Optional[Tuple[Layer, ...]] = None, ) -> Scene: """Return the Component 3D trimesh Scene. Args: component: layer_set: layer colors from Klayout Layer Properties file layer_stack: contains thickness and zmin for each layer exclude_layers: layers to exclude """ scene = Scene() layer_to_thickness = layer_stack.get_layer_to_thickness() layer_to_zmin = layer_stack.get_layer_to_zmin() exclude_layers = exclude_layers or [] for layer, polygons in component.get_polygons(by_spec=True).items(): if (layer not in exclude_layers and layer in layer_to_thickness and layer in layer_to_zmin): height = layer_to_thickness[layer] zmin = layer_to_zmin[layer] color_hex = layer_set.get_from_tuple(layer).color color_rgb = matplotlib.colors.to_rgb(color_hex) for polygon in polygons: p = shapely.geometry.Polygon(polygon) mesh = extrude_polygon(p, height=height) mesh.apply_translation((0, 0, zmin)) mesh.visual.face_colors = (*color_rgb, 0.5) scene.add_geometry(mesh) return scene
def union( component: Component, by_layer: bool = False, precision: float = 1e-4, join_first: bool = True, max_points: int = 4000, layer: Layer = (1, 0), ) -> Component: """Creates an inverted version of the input shapes with an additional border around the edges. adapted from phidl.geometry.invert Args: D: Component(/Reference), list of Component(/Reference), or Polygon A Component containing the polygons to perform union on. by_Layer: performs the union operation layer-wise so each layer can be individually combined. precision: Desired precision for rounding vertex coordinates. join_first: before offsetting to avoid unnecessary joins in adjacent polygon max_points: The maximum number of vertices within the resulting polygon. layer : Specific layer to put polygon geometry on. Returns Component containing the union of the polygons """ U = Component() if by_layer: all_polygons = component.get_polygons(by_spec=True) for layer, polygons in all_polygons.items(): unioned_polygons = pg._union_polygons(polygons, precision=precision, max_points=max_points) U.add_polygon(unioned_polygons, layer=layer) else: all_polygons = component.get_polygons(by_spec=False) unioned_polygons = pg._union_polygons(all_polygons, precision=precision, max_points=max_points) U.add_polygon(unioned_polygons, layer=layer) return U
def xor_polygons(A: Component, B: Component, hash_geometry: bool = True): """Given two devices A and B, performs a layer-by-layer XOR diff between A and B, and returns polygons representing the differences between A and B. Adapted from lytest/kdb_xor.py """ # first do a geometry hash to vastly speed up if they are equal if hash_geometry and (A.hash_geometry() == B.hash_geometry()): return Component() D = Component() A_polys = A.get_polygons(by_spec=True) B_polys = B.get_polygons(by_spec=True) A_layers = A_polys.keys() B_layers = B_polys.keys() all_layers = set() all_layers.update(A_layers) all_layers.update(B_layers) for layer in all_layers: if (layer in A_layers) and (layer in B_layers): p = gdspy.fast_boolean( A_polys[layer], B_polys[layer], operation="xor", precision=0.001, max_points=4000, layer=layer[0], datatype=layer[1], ) elif layer in A_layers: p = A_polys[layer] elif layer in B_layers: p = B_polys[layer] if p is not None: D.add_polygon(p, layer=layer) return D
def to_stl( component: Component, filepath: str, layer_set: LayerSet, layer_stack: LayerStack = LAYER_STACK, exclude_layers: Optional[Tuple[Layer, ...]] = None, ) -> None: """Exports a Component into STL. Args: component: filepath: layer_set: layer colors from Klayout Layer Properties file layer_stack: contains thickness and zmin for each layer exclude_layers: layers to exclude """ from trimesh.creation import extrude_polygon layer_to_thickness = layer_stack.get_layer_to_thickness() layer_to_zmin = layer_stack.get_layer_to_zmin() filepath = pathlib.Path(filepath) exclude_layers = exclude_layers or [] for layer, polygons in component.get_polygons(by_spec=True).items(): if ( layer not in exclude_layers and layer in layer_to_thickness and layer in layer_to_zmin ): height = layer_to_thickness[layer] zmin = layer_to_zmin[layer] color_hex = layer_set.get_from_tuple(layer).color color_rgb = matplotlib.colors.to_rgb(color_hex) filepath_layer = ( filepath.parent / f"{filepath.stem}_{layer[0]}_{layer[1]}{filepath.suffix}" ) print(filepath_layer) for polygon in polygons: p = shapely.geometry.Polygon(polygon) mesh = extrude_polygon(p, height=height) mesh.apply_translation((0, 0, zmin)) mesh.visual.face_colors = (*color_rgb, 0.5) mesh.export(filepath_layer)
def compute_area(c: Component, target_layer: Tuple[int, int]) -> float64: """Returns Computed area of the component for a given layer.""" # _print("Computing area ", c.name) c.flatten() # return c.area(by_spec=True)[layer] polys_by_spec = c.get_polygons(by_spec=True) _area = 0 for (layer, polys) in polys_by_spec.items(): # _print(layer) if layer == target_layer: joined_polys = gp.boolean(polys, None, operation="or") _print(joined_polys) try: _area += sum([abs(area(p)) for p in joined_polys.polygons]) except BaseException: print( f"Warning, {c.name} joinedpoly {joined_polys} could not be added" ) return _area
def to_np( component: Component, nm_per_pixel: int = 20, layers: Layers = ((1, 0), ), values: Optional[Floats] = None, pad_width: int = 1, ) -> np.ndarray: """Returns a pixelated numpy array from Component polygons. Args: component: Component nm_per_pixel: you can go from 20 (coarse) to 4 (fine) layers: to convert. Order matters (latter overwrite former) values: associated to each layer (defaults to 1) pad_width: padding pixels around the image """ pixels_per_um = (1 / nm_per_pixel) * 1e3 xmin, ymin = component.bbox[0] xmax, ymax = component.bbox[1] shape = ( int(np.ceil(xmax - xmin) * pixels_per_um), int(np.ceil(ymax - ymin) * pixels_per_um), ) img = np.zeros(shape, dtype=float) layer_to_polygons = component.get_polygons(by_spec=True, depth=None) values = values or [1] * len(layers) for layer, value in zip(layers, values): if layer in layer_to_polygons: polygons = layer_to_polygons[layer] for polygon in polygons: r = polygon[:, 0] - xmin c = polygon[:, 1] - ymin rr, cc = skdraw.polygon(r * pixels_per_um, c * pixels_per_um, shape=shape) img[rr, cc] = value img_with_padding = np.pad(img, pad_width=pad_width) return img_with_padding