def boolean( A: Component, B: Component, operation: str, precision: float = 1e-4, num_divisions: List[int] = [1, 1], max_points: int = 4000, layer: ListConfig = 0, ) -> Component: """ Performs boolean operations between 2 Device/DeviceReference objects, or lists of Devices/DeviceReferences. ``operation`` should be one of {'not', 'and', 'or', 'xor', 'A-B', 'B-A', 'A+B'}. Note that 'A+B' is equivalent to 'or', 'A-B' is equivalent to 'not', and 'B-A' is equivalent to 'not' with the operands switched """ c = pg.boolean( A=A, B=B, operation=operation, precision=precision, num_divisions=num_divisions, max_points=max_points, layer=layer, ) return import_phidl_component(component=c)
def chip(size=(13000, 18000), keepout=2000, name='chip01', text_size=250, layer_text=10, layer=99): k = keepout DX = size[0] DY = size[1] OUT = pg.rectangle(size=size, layer=layer) IN = pg.rectangle(size=(DX - 2 * k, DY - 2 * k), layer=layer) IN.move((k, k)) CHIP = pg.boolean(A=OUT, B=IN, operation='A-B', layer=layer) #Add name L = pg.text(text=name, size=text_size, layer=1, justify='center') CHIP.add_ref(L).move((DX / 2, k - text_size - 200)) #Add markers M = global_markers() offset = 110 CHIP.add_ref(M).move([k - offset, k - offset]) CHIP.add_ref(M).move([DX - k + offset - 3000, k - offset]) CHIP.add_ref(M).move([k - offset, DY - k + offset]) CHIP.add_ref(M).move([DX - k + offset - 3000, DY - k + offset]) return CHIP
def invert(self, layer, padding=None): """Inverts a pattern from positive to negative or vice versa. Parameters ---------- layer: string Layer of new, inverted device. padding: np.array Array containing padding of bounding box used to substract device [left, right, top, bottom] """ bounding_box = Device('interim_bounding_box') if padding is None: padding = [0, 0, 0, 0] bounding_box.add_polygon( [(self.xmin - padding[0], self.ymin - padding[3]), (self.xmin - padding[0], self.ymax + padding[2]), (self.xmax + padding[1], self.ymax + padding[2]), (self.xmax + padding[1], self.ymin - padding[3])], layer=layer) # perform substraction for positive e-beam resist inverse = pg.boolean(A=bounding_box, B=self, operation='A-B', layer=layer) return inverse
def contact_pads(size=(150, 150), label='', label_size=50, layer=10): # P = Device('Pad') R = pg.rectangle(size, layer) if label != '': L = pg.text(label, label_size, layer=layer) L.move([10, 10]) P = pg.boolean(A=R, B=L, operation='A-B', layer=layer) else: P = R return P
def boolean( A: Union[ComponentOrReference, Tuple[ComponentOrReference, ...]], B: Union[ComponentOrReference, Tuple[ComponentOrReference, ...]], operation: str, precision: float = 1e-4, num_divisions: Union[int, Int2] = (1, 1), max_points: int = 4000, layer: Layer = (1, 0), ) -> Component: """Performs boolean operations between 2 Component/Reference objects, or lists of Devices/DeviceReferences. ``operation`` should be one of {'not', 'and', 'or', 'xor', 'A-B', 'B-A', 'A+B'}. Note that 'A+B' is equivalent to 'or', 'A-B' is equivalent to 'not', and 'B-A' is equivalent to 'not' with the operands switched gdsfactory wrapper for phidl.geometry.boolean You can also use gdsfactory.drc.boolean that uses Klayout backend Args: A: Component(/Reference) or list of Component(/References) B: Component(/Reference) or list of Component(/References) operation: {'not', 'and', 'or', 'xor', 'A-B', 'B-A', 'A+B'} precision: float Desired precision for rounding vertex coordinates. num_divisions: number of divisions with which the geometry is divided into multiple rectangular regions. This allows for each region to be processed sequentially, which is more computationally efficient. max_points: The maximum number of vertices within the resulting polygon. layer: Specific layer to put polygon geometry on. Returns: Component with polygon(s) of the boolean operations between the 2 input Devices performed. Notes ----- 'A+B' is equivalent to 'or'. 'A-B' is equivalent to 'not'. 'B-A' is equivalent to 'not' with the operands switched. """ A = list(A) if isinstance(A, tuple) else A B = list(B) if isinstance(B, tuple) else B c = pg.boolean( A=A, B=B, operation=operation, precision=precision, num_divisions=num_divisions, max_points=max_points, layer=layer, ) return gf.read.from_phidl(component=c)
def boolean( A: Component, B: Component, operation: str, precision: float = 1e-4, num_divisions: Optional[int] = None, max_points: int = 4000, layer: ListConfig = 0, ) -> Component: """Performs boolean operations between 2 Device/DeviceReference objects, or lists of Devices/DeviceReferences. ``operation`` should be one of {'not', 'and', 'or', 'xor', 'A-B', 'B-A', 'A+B'}. Note that 'A+B' is equivalent to 'or', 'A-B' is equivalent to 'not', and 'B-A' is equivalent to 'not' with the operands switched Args: A : Device(/Reference) or list of Device(/Reference) or Polygon Input Devices. B : Device(/Reference) or list of Device(/Reference) or Polygon Input Devices. operation : {'not', 'and', 'or', 'xor', 'A-B', 'B-A', 'A+B'} Boolean operation to perform. precision : float Desired precision for rounding vertex coordinates. num_divisions : array-like[2] of int number of divisions with which the geometry is divided into multiple rectangular regions. This allows for each region to be processed sequentially, which is more computationally efficient. max_points : The maximum number of vertices within the resulting polygon. layer : int, array-like[2], or set Specific layer(s) to put polygon geometry on. Returns: Device A Device containing a polygon(s) with the boolean operations between the 2 input Devices performed. Notes ----- 'A+B' is equivalent to 'or'. 'A-B' is equivalent to 'not'. 'B-A' is equivalent to 'not' with the operands switched. """ num_divisions = num_divisions or [1, 1] c = pg.boolean( A=A, B=B, operation=operation, precision=precision, num_divisions=num_divisions, max_points=max_points, layer=layer, ) return import_phidl_component(component=c)
def add_passivation(cell, margin, scale, layer): rect = pg.rectangle(size=(cell.xsize * scale.x, cell.ysize * scale.y)) margin_rect = pg.rectangle(size=(margin.x * cell.xsize, margin.y * cell.ysize)) rect.move(origin=rect.center, destination=cell.center) margin_rect.move(origin=margin_rect.center, destination=cell.center) pa = pg.boolean(rect, margin_rect, operation='xor') for l in layer: cell.add_polygon(pa.get_polygons(), layer=(l, 0))
#============================================================================== # Making boolean shapes #============================================================================== # If you want to subtract one shape from another, merge two shapes, or # perform an XOR on them, you can do that with the pg.boolean() function. # the ``operation`` argument should be {not, and, or, xor, 'A-B', 'B-A', 'A+B'}. # Note that 'A+B' is equivalent to 'or', 'A-B' is equivalent to 'not', and # 'B-A' is equivalent to 'not' with the operands switched D = Device() E1 = pg.ellipse() E2 = pg.ellipse().movex(15) E3 = pg.ellipse().movex(30) qp([E1, E2, E3]) D2 = pg.boolean(A=[E1, E3], B=E2, operation='A-B') qp(D2) #============================================================================== # Creating outlines of shapes #============================================================================== # Sometimes, when writing in a positive-tone resist, it is useful to produce # an outline of an existing shape. The pg.outline() function allows you to do # exactly that D = pg.ellipse(layer=1) D2 = pg.outline(D, distance=1, layer=2) qp([D, D2]) #============================================================================== # Joining (Unioning) shapes together
import phidl.geometry as pg from phidl import quickplot as qp E = pg.ellipse(radii = (10,5)) D = pg.invert(E, border = 0.5, precision = 1e-6, layer = 0) qp(D) # quickplot the geometry create_image(D, 'invert') # example-boolean import phidl.geometry as pg from phidl import quickplot as qp from phidl import Device E = pg.ellipse(radii = (10,5), layer = 1) R = pg.rectangle(size = [15,5], layer = 2).movey(-1.5) C = pg.boolean(A = E, B = R, operation = 'not', precision = 1e-6, num_divisions = [1,1], layer = 0) # Other operations include 'and', 'or', 'xor', or equivalently 'A-B', 'B-A', 'A+B' # Plot the originals and the result D = Device() D.add_ref(E) D.add_ref(R) D.add_ref(C).movex(30) qp(D) # quickplot the geometry create_image(D, 'boolean') # example-outline import phidl.geometry as pg from phidl import quickplot as qp from phidl import Device
def verniers(scale=[1, 0.5, 0.1], layers=[1, 2], label='TE', text_size=20, reversed=False): """ Create a cell with vernier aligners. Parameters ---------- scale : iterable of float (default [1,0.5,0.25]) each float in list is the offset of a vernier. for each of them a vernier will be created in the X-Y axis layers : 2-len iterable of int (default [1,2]) define the two layers for the verniers. label : str (default "TE") add a label to the set of verniers. text_size : float (default) label size reversed : boolean if true, creates a negative alignment mark for the second layer Returns ------- cell : phidl.Device. """ cell = dl.Device(name="verniers") import numpy if not isinstance(scale, numpy.ndarray): scale = np.array(scale) scale = np.sort(scale) xvern = [] for dim in scale: notch_size = [dim * 5, dim * 25] notch_spacing = dim * 10 num_notches = 5 notch_offset = dim row_spacing = 0 layer1 = layers[0] layer2 = layers[1] cal=pg.litho_calipers(\ notch_size,\ notch_spacing,\ num_notches,\ notch_offset,\ row_spacing,\ layer1,\ layer2) cal.flatten() if reversed: tobedel = cal.get_polygons(by_spec=(layer2, 0)) cal = cal.remove_polygons( lambda pts, layer, datatype: layer == layer2) replica = dl.Device() replica.add(gdspy.PolygonSet(tobedel, layer=layer2)) frame = dl.Device() frame.add(pg.bbox(replica.bbox, layer=layer2)) frame_ext = dl.Device() frame_ext.add( gdspy.PolygonSet(frame.copy('tmp', scale=1.5).get_polygons(), layer=layer2)) frame_ext.flatten() frame_ext.move(origin=frame_ext.center, destination=replica.center) new_cal = pg.boolean(replica, frame_ext, 'xor', layer=layer2) new_cal.rotate(angle=180, center=(cal.xmin + cal.xsize / 2, cal.ymin)) new_cal.move(destination=(0, -notch_size[1])) cal << new_cal cal.flatten() xvern.append(cal) g = dl.Group(xvern) g.distribute(direction='y', spacing=scale[-1] * 20) g.align(alignment='x') xcell = dl.Device(name="x") for x in xvern: xcell << x xcell = pt.join(xcell) vern_x = cell << xcell vern_y = cell << xcell vern_y.rotate(angle=-90) vern_y.move(origin=(vern_y.xmin,vern_y.y),\ destination=(vern_x.x+scale[-1]*10,vern_x.ymin-scale[-1]*10)) cell.absorb(vern_x) cell.absorb(vern_y) label = pg.text(text=label, size=text_size, layer=layers[0]) label.move(destination=(cell.xmax - label.xsize / 2, cell.ymax - 2 * label.ysize)) overlabel = pg.bbox(label.bbox, layer=layers[1]) overlabel_scaled = dl.Device().add( gdspy.PolygonSet(overlabel.copy('tmp', scale=2).get_polygons(), layer=layers[1])) overlabel_scaled.move(origin=overlabel_scaled.center,\ destination=label.center) cutlab = pg.boolean(label, overlabel_scaled, 'xor', layer=layers[1]) cell << label cell << cutlab cell = pt.join(cell) return cell
def test_boolean(): A = pg.cross(length=10, width=3, layer=0) B = pg.ellipse(radii=(10, 5), angle_resolution=2.5, layer=1) D = pg.boolean(A=A, B=B, operation='and', precision=1e-6, layer=2) h = D.hash_geometry(precision=1e-4) assert (h == 'fcf1d0809488be01480027a5914dfb399faf088c')