def test_offset(): gdspy.current_library = gdspy.GdsLibrary() r = gdspy.Rectangle((0, 0), (1, 2)) result = gdspy.Rectangle((-1, -1), (2, 3)) assert equals(gdspy.offset(r, 1), result) c = gdspy.Cell("OFFSET").add(r) ca = gdspy.CellArray(c, 2, 1, (1, 0)) result = gdspy.Rectangle((0.2, 0.2), (1.8, 1.8)) assert equals(gdspy.offset([ca], -0.2, join_first=True), result) v = [gdspy.Rectangle((-1, -1), (1, 1)), [(0, 0), (1, 0), (1, 1), (0, 1)]] x = 1 + 0.1 * numpy.tan(numpy.pi / 8) result = gdspy.Polygon( [ (-1.1, -x), (-1.1, x), (-x, 1.1), (x, 1.1), (1.1, x), (1.1, -x), (x, -1.1), (-x, -1.1), ], layer=8, ) assert equals(gdspy.offset(v, 0.1, join="bevel", layer=12), result)
def convert_to_positive_resist(cell, parts, buffer_radius=3): w_cut = 15 l_cut = 15 outer = gdspy.offset(parts, buffer_radius, join_first=True) diff = gdspy.boolean(outer, parts, 'not') x_max = 1 x_min = 0 if isinstance(parts, list): for onePart in parts: [[x_min_temp, __], [x_max_temp, __]] = onePart.get_bounding_box() if x_max_temp > x_max: x_max = x_max_temp if x_min_temp < x_min: x_min = x_min_temp else: [[x_min, __], [x_max, __]] = parts.get_bounding_box() points = [(x_min, w_cut), (x_min, -w_cut), (x_min - l_cut, -w_cut), (x_min - l_cut, w_cut)] poly1 = gdspy.Polygon(points) points = [(x_max, w_cut), (x_max, -w_cut), (x_max + l_cut, -w_cut), (x_max + l_cut, w_cut)] poly2 = gdspy.Polygon(points) cut = gdspy.boolean(poly1, poly2, 'or') diff = gdspy.boolean(diff, cut, 'not', layer=1, datatype=1) cell.add(diff)
def offset( elements, distance=0.1, join_first=True, precision=1e-4, num_divisions=[1, 1], join="miter", tolerance=2, max_points=4000, layer=0, ): """ returns an element containing all polygons with an offset from phidl geometry """ if type(elements) is not list: elements = [elements] polygons_to_offset = [] for e in elements: if isinstance(e, (Device, DeviceReference)): polygons_to_offset += e.get_polygons(by_spec=False) elif isinstance(e, (Polygon, gdspy.Polygon)): polygons_to_offset.append(e) if len(polygons_to_offset) == 0: return pp.Component("offset") polygons_to_offset = _merge_floating_point_errors( polygons_to_offset, tol=precision / 1000 ) gds_layer, gds_datatype = _parse_layer(layer) if all(np.array(num_divisions) == np.array([1, 1])): p = gdspy.offset( polygons_to_offset, distance=distance, join=join, tolerance=tolerance, precision=precision, join_first=join_first, max_points=max_points, layer=gds_layer, datatype=gds_datatype, ) else: p = _offset_polygons_parallel( polygons_to_offset, distance=distance, num_divisions=num_divisions, join_first=join_first, precision=precision, join=join, tolerance=tolerance, ) D = pp.Component("offset") polygons = D.add_polygon(p, layer=layer) [ polygon.fracture(max_points=max_points, precision=precision) for polygon in polygons ] return D
def draw_vias(cls, polygon, layer): """Fills ``polygon`` with vias according to appropriate rules. Returns list of gdspy geometries on ``layer`` """ polygon = gdspy.offset(polygon, -cls.min_properties[layer][2]) return LibTools.via_fill_simple( polygon.polygons[0], cls.min_properties[layer][0], cls.min_properties[layer][1], layer)
def bench_gdspy(output=None): poly = gdspy.Round((0, 0), 1.5, number_of_points=6, layer=1) orig = gdspy.Cell("OFF", exclude_from_current=True) orig.add(poly) ref = gdspy.CellArray(orig, 4, 1, (2, 0), origin=(-3, 5)) off = gdspy.offset([poly, ref], 0.2, "round", layer=0) boo = gdspy.boolean(off, [poly, ref], "not", layer=2) if output: cell = gdspy.Cell("MAIN", exclude_from_current=True) cell.add([ref, poly, off, boo]) cell.write_svg(output, 50)
def assertsame(c1, c2, tolerance=1e-6): d1 = c1.get_polygons(by_spec=True) d2 = c2.get_polygons(by_spec=True) for key in d1: assert key in d2 result = gdspy.boolean(d1[key], d2[key], "xor", precision=1e-7, layer=key[0], datatype=100) if result is not None: r1 = gdspy.boolean( d1[key], gdspy.offset(d2[key], tolerance, precision=1e-7), "not", precision=1e-7, layer=key[0], datatype=99, ) r2 = gdspy.boolean( d2[key], gdspy.offset(d1[key], tolerance, precision=1e-7), "not", precision=1e-7, layer=key[0], datatype=99, ) # if not (r1 is None and r2 is None): # c1.add(result) # c2.add(result) # if r1 is not None: # c1.add(r1) # if r2 is not None: # c2.add(r2) # gdspy.LayoutViewer(cells=[c1, c2]) assert r1 is None assert r2 is None else: assert result is None
def buffer(parts, buffer_radius=3): w_cut = 15 l_cut= 15 outer = gdspy.offset(parts, buffer_radius, join_first=True) x_max = 1 x_min = 0 if isinstance(parts,list): for onePart in parts: [[x_min_temp, __], [x_max_temp, __]] = onePart.get_bounding_box() if x_max_temp > x_max: x_max = x_max_temp if x_min_temp < x_min: x_min = x_min_temp else: [[x_min, __], [x_max, __]] = parts.get_bounding_box() points = [(x_min, w_cut), (x_min, -w_cut), (x_min-l_cut, -w_cut), (x_min-l_cut, w_cut)] poly1 = gdspy.Polygon(points) points = [(x_max, w_cut), (x_max, -w_cut), (x_max+l_cut, -w_cut), (x_max+l_cut, w_cut)] poly2= gdspy.Polygon(points) cut = gdspy.boolean(poly1, poly2, 'or') outer2 = gdspy.boolean(outer, cut, 'not', **ld_cld) return outer2
# Keep only the left side of slices1, the center part of slices2 # and the right side of slices3 slices.add(slices1[0]) slices.add(slices2[1]) slices.add(slices3[1]) draw(slices, "slice_operation") # Offset Operation rect1 = gdspy.Rectangle((-4, -4), (1, 1)) rect2 = gdspy.Rectangle((-1, -1), (4, 4)) # Offset both polygons # Because we join them first, a single polygon is created. outer = gdspy.offset([rect1, rect2], 0.5, join_first=True, layer=1) draw(gdspy.Cell("offset_operation").add([outer, rect1, rect2])) # Fillet Operation multi_path = gdspy.Path(2, (-3, -2)) multi_path.segment(4, "+x") multi_path.turn(2, "l").turn(2, "r") multi_path.segment(4) # Create a copy with joined polygons and no fracturing joined = gdspy.boolean(multi_path, None, "or", max_points=0) joined.translate(0, -5) # Fillet applied to each polygon in the path multi_path.fillet(0.5)
path_dc2.turn(radius_bend, 'r') path_dc2.segment(l_DC) path_dc2.turn(radius_bend, 'r') path_dc2.segment(l_ver) path_dc2.turn(radius_bend, 'l') path_dc2.segment(l_heater) path_dc2.turn(radius_bend, 'l') path_dc2.segment(l_ver) path_dc2.turn(radius_bend, 'r') path_dc2.segment(l_DC) path_dc2.turn(radius_bend, 'r') path_dc2.segment(l_ver) path_dc2.turn(radius_bend, 'l') path_dc2.segment(l_PortOut) path_dc_buffer = gdspy.offset([path_dc, path_dc2], 3, join_first=True) path_positive = gdspy.boolean(path_dc_buffer, [path_dc, path_dc2], 'xor') x0 = 0 point = [(x0, 20), (x0 - 20, 20), (x0 - 20, -1000), (x0, -1000)] poly1 = gdspy.Polygon(point) x0 = l_heater * 2 + 12 * radius_bend + l_PortIn + l_PortOut + 3 * l_DC + 20 point = [(x0, 20), (x0 - 20, 20), (x0 - 20, -1000), (x0, -1000)] poly2 = gdspy.Polygon(point) path_positive = gdspy.boolean(path_positive, [poly1, poly2], 'not', layer=1, datatype=1) DC = lib.new_cell("DC")
def __init__(self, cell, grid_size, buffer=None, layers=None, precision=0.001): start_time = time.time() self.grid_size = grid_size if layers is None: layers = list(cell.get_layers()) if not isinstance(layers, list): layers = [layers] if buffer is None: buffer = grid_size / 2 bounding_box = cell.get_bounding_box() x_length = int( (bounding_box[1][0] - bounding_box[0][0] + 2 * grid_size) / grid_size) + 1 y_length = int( (bounding_box[1][1] - bounding_box[0][1] + 2 * grid_size) / grid_size) + 1 out_string = '-- Constructing map with ' + str(int( x_length * y_length)) + ' (' + str(int(x_length)) + ' x ' + str( int(y_length)) + ') entries' print(out_string, end=(7 - len(out_string) // 8) * '\t', flush=True) x_linspace = np.linspace(bounding_box[0][0] - grid_size, bounding_box[1][0] + grid_size, x_length) y_linspace = np.flip( np.linspace(bounding_box[0][1] - grid_size, bounding_box[1][1] + grid_size, y_length)) x_array, y_array = np.meshgrid(x_linspace, y_linspace) xy_array = np.array(list(zip(x_array.ravel(), y_array.ravel()))).reshape( *x_array.shape, 2) self.mask = np.zeros((y_length, x_length)) self.copy_cell = cell.copy(cell.name + "_copy", deep_copy=True) self.copy_cell.remove_polygons( lambda pts, layer, datatype: layer not in layers) self.copy_polygonset = gp.offset(self.copy_cell.get_polygons(), distance=buffer) map_gdspy = np.array( gp.inside(np.array(xy_array).reshape(-1, 2), self.copy_polygonset, precision=precision)).reshape((y_length, x_length)) for i in range(y_length): for j in range(x_length): if not map_gdspy[i][j]: if self.mask[i][j] != 1: self.mask[i][j] = 0 else: try: self.mask[i][j] = self.mask[i - 1][j] = self.mask[ i + 1][j] = self.mask[i][j - 1] = self.mask[i][j + 1] = 1 except: pass elapsed_time = round(time.time() - start_time, 3) print('| Finished after ' + str(elapsed_time) + ' s --') self.x_length = x_length self.y_length = y_length self.x_linspace = x_linspace self.y_linspace = y_linspace self.x_array = x_array self.y_array = y_array self.xy_array = xy_array
layer=1)) # Polygon offset (inset and outset) can be used, for instance, to # define safety margins around shapes. spec = {'layer': 7} path4 = gdspy.Path(0.5, (21, -5)).segment(3, '+x', **spec)\ .turn(4, 'r', **spec).turn(4, 'rr', **spec)\ .segment(3, **spec) oper_cell.add(path4) # Merge all parts into a single polygon. merged = gdspy.fast_boolean(path4, None, 'or', max_points=0) # Offset the path shape by 0.5 and add it to the cell. oper_cell.add(gdspy.offset(merged, 1, layer=8)) # ------------------------------------------------------------------ # # SLICING POLYGONS # ------------------------------------------------------------------ # # If there is the need to cut a polygon or set of polygons, it's better # to use the slice function than set up a boolean operation, since it # runs much faster. Slices are multiple cuts perpendicular to an axis. slice_cell = gdspy.Cell('SLICE') original = gdspy.Round((0, 0), 10, inner_radius=5) # Slice the original ring along x = -7 and x = 7. result = gdspy.slice(original, [-7, 7], 0, layer=1) # The result is a tuple of polygon sets, one for each slice. To keep
def makeBorder(conf: DefaultConfig, parts): return cut(gdspy.offset(parts, conf.borderWidth, layer=conf.borderLayer), parts)
def offset( elements: Component, distance: float = 0.1, join_first: bool = True, precision: float = 1e-4, num_divisions: Tuple[int, int] = (1, 1), join: str = "miter", tolerance: int = 2, max_points: int = 4000, layer: Layer = (1, 0), ) -> Component: """Returns an element containing all polygons with an offset Shrinks or expands a polygon or set of polygons. adapted from phidl.geometry Args: elements: Component(/Reference), list of Component(/Reference), or Polygon Polygons to offset or Component containing polygons to offset. distance: Distance to offset polygons. Positive values expand, negative shrink. precision: Desired precision for rounding vertex coordinates. num_divisions: The 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. join: {'miter', 'bevel', 'round'} Type of join used to create polygon offset tolerance: For miter joints, this number must be at least 2 represents the maximal distance in multiples of offset between new vertices and their original position before beveling to avoid spikes at acute joints. For round joints, it indicates the curvature resolution in number of points per full circle. max_points: The maximum number of vertices within the resulting polygon. layer: Specific layer to put polygon geometry on. Returns Component containing a polygon(s) with the specified offset applied. """ if not isinstance(elements, list): elements = [elements] polygons_to_offset = [] for e in elements: if isinstance(e, (Device, DeviceReference)): polygons_to_offset += e.get_polygons(by_spec=False) elif isinstance(e, (Polygon, gdspy.Polygon)): polygons_to_offset.append(e) if len(polygons_to_offset) == 0: return gf.Component("offset") polygons_to_offset = _merge_floating_point_errors( polygons_to_offset, tol=precision / 1000 ) gds_layer, gds_datatype = _parse_layer(layer) if all(np.array(num_divisions) == np.array([1, 1])): p = gdspy.offset( polygons_to_offset, distance=distance, join=join, tolerance=tolerance, precision=precision, join_first=join_first, max_points=max_points, layer=gds_layer, datatype=gds_datatype, ) else: p = _offset_polygons_parallel( polygons_to_offset, distance=distance, num_divisions=num_divisions, join_first=join_first, precision=precision, join=join, tolerance=tolerance, ) component = gf.Component("offset") polygons = component.add_polygon(p, layer=layer) [ polygon.fracture(max_points=max_points, precision=precision) for polygon in polygons ] return component
'not', layer=1)) ## Polygon offset (inset and outset) can be used, for instance, to ## define safety margins around shapes. spec = {'layer': 7} path4 = gdspy.Path(0.5, (21, -5)).segment(3, '+x', **spec)\ .turn(4, 'r', **spec).turn(4, 'rr', **spec)\ .segment(3, **spec) oper_cell.add(path4) ## Merge all parts into a single polygon. merged = gdspy.fast_boolean(path4, None, 'or', max_points=0) ## Offset the path shape by 0.5 and add it to the cell. oper_cell.add(gdspy.offset(merged, 1, layer=8)) ## ------------------------------------------------------------------ ## ## SLICING POLYGONS ## ------------------------------------------------------------------ ## ## If there is the need to cut a polygon or set of polygons, it's better ## to use the slice function than set up a boolean operation, since it ## runs much faster. Slices are multiple cuts perpendicular to an axis. slice_cell = gdspy.Cell('SLICE') original = gdspy.Round((0, 0), 10, inner_radius=5) ## Slice the original ring along x = -7 and x = 7. result = gdspy.slice(original, [-7, 7], 0, layer=1)