def init_regions( self ): self.P0 = DPoint( 0,0 ) self.P1 = self.P0 + DPoint( self.Z1.gap,0 ) self.P2 = self.P1 + DPoint( self.Z1.width,0 ) self.P3 = self.P2 + DPoint( 0,self.Z1.gap ) self.P4 = self.P2 + DPoint( self.Z1.gap, self.Z2.gap ) self.P5 = self.P4 + DPoint( 0,self.Z2.width ) self.P6 = DPoint( 0, self.Z1.gap + self.Z1.width ) self.P7 = DPoint( 0, self.Z1.gap ) self.P8 = self.P7 + DPoint( self.Z1.gap,0 ) self.P9 = self.P2 + DPoint( self.Z1.gap,0 ) self.P10 = self.P5 + DPoint( 0, self.Z2.gap ) self.P11 = self.P10 - DPoint( 2*self.Z1.gap + self.Z1.width, 0 ) self.P12 = self.P6 + DPoint( 0, self.Z1.gap ) self.connections = [(self.P6 + self.P7)*0.5,(self.P1 + self.P2)*0.5,(self.P5 + self.P4)*0.5] self.angle_connections = [0,3/2*pi,0] self.metal_polygon = DPolygon( [self.P1,self.P2,self.P3,self.P4,self.P5,self.P6,self.P7,self.P8] ) self.empty1_polygon = DPolygon( [self.P0,self.P1,self.P8,self.P7] ) self.empty2_polygon = DPolygon( [self.P2,self.P9,self.P4,self.P3] ) self.empty3_polygon = DPolygon( [self.P5,self.P10,self.P12,self.P6] ) self.gnd_polygon = DPolygon( [self.P10,self.P11,self.P12] ) self.metal_region = pya.Region( list(map(pya.Polygon().from_dpoly,[self.metal_polygon,self.gnd_polygon])) ) self.empty_region = pya.Region( list(map(pya.Polygon().from_dpoly ,[self.empty1_polygon,self.empty2_polygon,self.empty3_polygon])) )
def sub(layout, cell, slayers, dlayers, ex_amount, layers, out_cell=None): """Analogous to :func:`~kppc.photonics.dataprep.add` Instead of perfoming a combination with the destination layers, this function will substract the input region. """ if out_cell: o_cell = out_cell else: o_cell = cell am = ex_amount / layout.dbu srclayers = [slayers, ] if isinstance(slayers, str) else slayers dstlayers = [dlayers, ] if isinstance(dlayers, str) else dlayers in_layers = [layout.layer(layers[m][0], layers[m][1]) for m in srclayers] region = pya.Region() for layer in in_layers: if layer != -1: shapeit = layout.begin_shapes(cell, layer) region.insert(shapeit) region.merge() if ex_amount != 0: region.size(am) region.merge() for layer in dstlayers: sub_region = pya.Region() layer_n, layer_d = layers[layer] l = layout.layer(layer_n, layer_d) shapeit = o_cell.begin_shapes_rec(l) sub_region.insert(shapeit) sub_region.merge() o_cell.shapes(l).clear() o_cell.shapes(l).insert(sub_region - region)
def DrawBoxesInRegion(cell, layer, region, dlength, dgap, dx=0, dy=0, filterfunc=None): d = dlength + dgap area = region.bbox() dx = dx % d dy = dy % d left = floor((area.left - dx) / d) bottom = floor((area.bottom - dy) / d) right = ceil((area.right - dx) / d) top = ceil((area.top - dy) / d) x0 = left * d + dx y0 = bottom * d + dy boxesregion = pya.Region() for ii in range(right - left): for jj in range(top - bottom): x1 = x0 + ii * d y1 = y0 + jj * d box = pya.Box(x1, y1, x1 + dlength, y1 + dlength) boxesregion.insert(box) andRegion = boxesregion & region if filterfunc: andRegion_ = pya.Region() for pp in andRegion.each(): if filterfunc(pp): andRegion_.insert(pp) andRegion = andRegion_ BasicPainter.Draw(cell, layer, andRegion) return andRegion
def do_fill(): cfg = read_cfg() setup_cfg(cfg) # Map outline from GDS to layer index outline = cfg['outline'] outline_index = main_layout.find_layer(outline['layer'], outline['datatype']) for layer, layer_cfg in cfg['layers'].items(): print('INFO: Performing fill for {} [gds={}:{} klayout={}]'.format( layer, layer_cfg['layer'], layer_cfg['datatype'], layer_cfg['klayout'])) outline_area = pya.Region(fill_top_cell.bbox_per_layer(outline_index)) outline_area.size(-layer_cfg['space_to_outline']) shapes_area = pya.Region(fill_top_cell.shapes(layer_cfg['klayout'])) do_non_opc_fill(fill_top_cell, layer, layer_cfg, outline_area, shapes_area) do_opc_fill(fill_top_cell, layer, layer_cfg, outline_area, shapes_area) # Discards the flatten shapes from the design but keeps the fill instances fill_top_cell.clear_shapes() print('INFO: Writing gds: ' + out_gds) main_layout.write(out_gds) print('INFO: Fill finished') print('\t time taken: %.2f seconds ' % (time.time() - start_time))
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu ly = self.layout LayerSi = self.layer LayerSiN = ly.layer(self.layer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) LayerTextN = ly.layer(self.textl) LayerEtch = ly.layer(self.etch) TextLayerN = ly.layer(self.textl) # Fetch all the parameters: a = self.a/dbu r = self.r/dbu n_vertices = self.n_vertices n = int(math.ceil(self.n/2)) #print(n) n_sweep = self.n_sweep n_x = n n_y = n # Define Si slab and hole region for future subtraction Si_slab = pya.Region() hole = pya.Region() ruler = pya.Region() #hole_r = [r+50,r} '''
def main(): # Loop each cut map, & export each GDS as unique name for key in cut_d: filename = "2_" + key # Add prefix to the filename print("\nfilename : ", filename) # Get 1_UNIT (no cuts as a reference) & create SINGLE instance for i in gdsFiles: layout.read(i) for cell in layout.top_cells(): # we don't want to insert the topcell itself if (cell.name != "1_UNIT_CUTS"): print("Adding : " + cell.name) cell_index = cell.cell_index() new_instance = pya.CellInstArray( cell_index, pya.Trans(pya.Point(0, 0))) UNIT.insert(new_instance) # Define imported metal as a region (for boolean) region_metal = pya.Region(layout.top_cell().begin_shapes_rec(l_metal)) # Define cut area & make as region for ind, each_cut in enumerate(cut_d[key]): cut_box_coord = get_cut_coord(each_cut, d) UNIT.shapes(l_cut_box).insert(cut_box_coord) region_cut_box = pya.Region(cut_box_coord) # Do boolean (XOR) # For more than 1 cuts, need to loop and take XOR of previous XOR results if ind == 0: region_xor = region_metal ^ region_cut_box else: region_xor = region_xor ^ region_cut_box # Remove existings metal layer + cut boxes # (!!! SKIP THIS TO CHECK CUT BOXES IN GDS !!!) layout.clear_layer(l_metal) layout.clear_layer(l_cut_box) # INSERT BOOLEAN RESULT AS ORIGINAL METAL LAYER UNIT.shapes(l_metal).insert(region_xor) # Check if filename gds exists -> If so skip "write" if os.path.isfile(root + "/" + filename + ".gds"): print("**** GDS name by : " + filename + ".gds already exists!") print("**** SKIPPING GDS WRITE!!!") else: # Export GDS layout.write(filename + ".gds") # Check if this cell can be used as another at different coordinate dup_l = get_dup_names(filename + ".gds") if dup_l[0] != '': for each in dup_l: print("Create copy as : ", each, " <-----------------------------------------") layout.write(each)
def pieceHolderCassette(self, dbu=1): ''' pieceHolderCassette() Generates the shape of the Jeol JBX-5500FS piece holder cassette Parameters --------- dbu : double The database unit Returns ------ region : [pya.Region] A region containing the piece holder cassette shape Description ------ The center of this piece holder shape (0,0) is at stage position (62.5mm, 37.5mm) Jeol Stage Y axis is reverse of KLayout Y axis ''' # Create the quarter circle r = 36100 / dbu rx = 62500 / dbu ry = -37500 / dbu polygon = pya.Polygon([ pya.Point(-r, -r), pya.Point(-r, r), pya.Point(r, r), pya.Point(r, -r) ]) polygon = polygon.round_corners(0, r, 128) rectangle = pya.Polygon([ pya.Point(-r, 0), pya.Point(-r, r), pya.Point(r, r), pya.Point(r, 0) ]) tt = pya.ICplxTrans(1, 90, False, 0, 0) qCircle = pya.Region(polygon) - pya.Region(rectangle) - pya.Region( rectangle.transform(tt)) # Create the trapezoid trapezoid = pya.Polygon([ pya.Point(49500 / dbu, -70500 / dbu), pya.Point(40500 / dbu, -20500 / dbu), pya.Point(60500 / dbu, -20500 / dbu), pya.Point(51500 / dbu, -70500 / dbu) ]) tt = pya.ICplxTrans(-rx, -ry) cassette = qCircle + pya.Region(trapezoid.transform(tt)) return cassette
def ring(self, outerDiameter, innerDiameter, vertices=128, fracture=True): ''' circle(outerDiameter, innerDiameter, vertices) Generates a circle shape Parameters --------- outerDiameter : integer The outer diameter of the ring innerDiameter : integer The inner diameter of the ring vertices : integer (128) Number of vertices in the circle (coerce to multiples of 4) fracture : boolean (True) Create the inner polygon with vertices that is optimal for fracturing horizontally Returns ------ region : [pya.Region] A region containing the circle shape ''' ro = int(outerDiameter / 2) ri = int(innerDiameter / 2) vertices = int(vertices / 4) * 4 # Create a circle polygon = pya.Polygon([ pya.Point(-ro, -ro), pya.Point(-ro, ro), pya.Point(ro, ro), pya.Point(ro, -ro) ]) polygonOuter = polygon.round_corners(0, ro, vertices) polygon = pya.Polygon([ pya.Point(-ri, -ri), pya.Point(-ri, ri), pya.Point(ri, ri), pya.Point(ri, -ri) ]) if fracture: points = polygonOuter.each_point_hull() polygonInnerPoints = [] r2 = np.power(ri, 2) for point in points: if (ri > np.absolute(point.y)): x = np.sqrt(r2 - np.power(point.y, 2)) polygonInnerPoints.append( pya.Point(np.sign(point.x) * x, point.y)) polygonInner = pya.Polygon(polygonInnerPoints) else: polygonInner = polygon.round_corners(0, ri, vertices) return pya.Region(polygonOuter) - pya.Region(polygonInner)
def siWafer(self, diameter, primaryFlat, secondaryFlat, angle, vertices = 128): ''' siWafer(diameter, secondaryFlatAngle) Generates a Silicon Wafer shape Parameters --------- diameter : integer The diameter of a standard silicon wafer primaryFlat : integer The length of the primary flat secondaryFlat : integer The length of the secondary flat angle : double The location of the secondary flat relative (counterclockwise) to primary flat vertices : integer (coerce to even number) The number of vertices used to generate the circle Returns ------ region : [pya.Region] A region containing the Si Wafer shape Description --------- SEMI Wafer Flat M1-0302 Specification Wafer Size = [2", 3", 100mm, 125mm, 150mm, 200mm, 300mm] Diameter [mm] = [50.8, 76.2, 100, 125, 150, 200, 300] Thickness [um] = [279, 381, 525 or 625, 625, 675 or 625, 725, 775] Primary Flat Length = [15.88, 22.22, 32.5, 42.5, 57.5, Notch, Notch] Secondary Flat Length = [8, 11.18, 18, 27.5, 37.5, NA, NA] ''' dList = [50800, 76200, 10000, 12500, 15000] pFlatLengthList = [15.88, 22.22, 32.5, 42.5, 57.5] sFlatLengthList = [8, 11.18, 18, 27.5, 37.5] r = int(diameter/2) #Height of arc position (https://mathworld.wolfram.com/CircularSegment.html) pH = r- int(np.sqrt(4*np.power(r,2)-np.power(primaryFlat,2))/2) sH = r - int(np.sqrt(4*np.power(r,2)-np.power(secondaryFlat,2))/2) # Create a circle polygon = pya.Polygon([pya.Point(-r,-r), pya.Point(-r,r), pya.Point(r,r), pya.Point(r,-r)]) polygon = polygon.round_corners(0,r,vertices) #Create a rectangle to produce the primary flat pRectangle = pya.Polygon([pya.Point(-r,r-pH), pya.Point(-r,r+pH), pya.Point(r,r+pH), pya.Point(r,r-pH)]) #Create a rectangle to produce the secondary flat sRectangle = pya.Polygon([pya.Point(-r,r-sH), pya.Point(-r,r+sH), pya.Point(r,r+sH), pya.Point(r,r-sH)]) tt = pya.ICplxTrans(1, angle, False, 0, 0) return pya.Region(polygon)-pya.Region(pRectangle)-pya.Region(sRectangle.transform(tt))
def Output_Region(self): polygonsout=[] for x in self.regionlistout: if isinstance(x,pya.DPolygon): polygonsout.append(pya.Polygon.from_dpoly(x)) self.regionlistout=[] polygonsin=[] for x in self.regionlistin: if isinstance(x,pya.DPolygon): polygonsin.append(pya.Polygon.from_dpoly(x)) self.regionlistin=[] return pya.Region(polygonsout)-pya.Region(polygonsin)
def main(): # Loop each cut locations, & assign GDS name from key for key in cut_loc: filename = key print("\nfilename : ", filename) # Read 1_UNIT (no cuts as a reference) & create SINGLE instance for each_gds in gds_files: KLAYOUT.read(each_gds) # Read Top Cell for each GDS file for top_cell_read in KLAYOUT.top_cells(): if (top_cell_read.name != "1_UNIT_CUTS" ): # Don't insert TOP_CELL("1_UNIT_CUTS") on itself # print ( "Adding " + top_cell_read.name ) cell_index = top_cell_read.cell_index() new_instance = pya.CellInstArray( cell_index, pya.Trans(pya.Point(0, 0))) # pya.Trans(pya.Point(0,0)) --> defines the LOCATION at which instance should be placed TOP_CELL.insert(new_instance) # Define imported metal as a region (for boolean) region_metal = pya.Region(KLAYOUT.top_cell().begin_shapes_rec(l_metal)) # Define cut area & make as region for ind, each_cut in enumerate(cut_loc[key]): cut_box_coord = get_cut_coord(each_cut, dim) TOP_CELL.shapes(l_cut_box).insert(cut_box_coord) region_cut_box = pya.Region(cut_box_coord) # Do boolean (XOR) # For more than 1 cuts, need to loop and take XOR of previous XOR results if ind == 0: region_xor = region_metal ^ region_cut_box else: region_xor = region_xor ^ region_cut_box # Remove existings metal layer + cut boxes # (!!! SKIP THIS TO CHECK CUT BOXES IN GDS !!!) KLAYOUT.clear_layer(l_metal) KLAYOUT.clear_layer(l_cut_box) # INSERT BOOLEAN RESULT AS ORIGINAL METAL LAYER TOP_CELL.shapes(l_metal).insert(region_xor) # Check if filename gds exists -> If so skip "write" if os.path.isfile(root + "/" + filename + ".gds"): print("**** GDS name by : " + filename + ".gds already exists!") print("**** SKIPPING GDS WRITE!!!") else: # Export GDS KLAYOUT.write(filename + ".gds")
def getShapesFromCellAndLayer(cellList, box, layerList=None, layermod='not in'): layers = Collision.getLayers(layerList=layerList, layermod=layermod) outregion = pya.Region(box) inregion = pya.Region() for cell in cellList: for layer in layers: s = cell.begin_shapes_rec_touching(layer, box) inregion.insert(s) inregion.merge() return [outregion, inregion]
def do_opc_fill(fill_top_cell, layer, layer_cfg, outline_area, shapes_area): has_opc = 'opc' in layer_cfg if not has_opc: return opc_cfg = layer_cfg['opc'] sp_non = opc_cfg['space_to_non_fill'] sp_fill = opc_cfg['space_to_fill'] halo = opc_cfg['halo'] non_opc_cfg = layer_cfg['non-opc'] sp_fill_non_opc = non_opc_cfg['space_to_fill'] - half(sp_fill) non_opc_fill = pya.Region( fill_top_cell.begin_shapes_rec(non_opc_cfg['klayout'])) if 'klayout2' in non_opc_cfg: non_opc_fill |= pya.Region( fill_top_cell.begin_shapes_rec(non_opc_cfg['klayout2'])) fill_base_area = outline_area & ( shapes_area.sized(halo) - shapes_area.sized(sp_non - half(sp_fill)) - non_opc_fill.sized(sp_fill_non_opc)) is_h = layer_cfg['dir'] == 'H' for w, h in zip(opc_cfg['width'], opc_cfg['height']): w_space = h_space = half(sp_fill) le = opc_cfg.get('space_line_end', 0) if is_h: w, h = (max(w, h), min(w, h)) w_space += half(le) else: w, h = (min(w, h), max(w, h)) h_space += half(le) opc_fill = pya.Region( fill_top_cell.begin_shapes_rec(opc_cfg['klayout'])) if 'klayout2' in opc_cfg: opc_fill |= pya.Region( fill_top_cell.begin_shapes_rec(opc_cfg['klayout2'])) fill_area = fill_base_area - opc_fill.sized(w_space, h_space, 2) small_cell, fill_cell_bbox = create_fill_cell(layer + '_opc', opc_cfg, w, h) while not fill_area.is_empty(): fill_top_cell.fill_region(fill_area, small_cell.cell_index(), fill_cell_bbox, None, fill_area, pya.Point(0, 0), None) if 'datatype2' in opc_cfg: e2_cell, _ = create_fill_cell(layer + '_opc_e2', opc_cfg, w, h, True) double_pattern(fill_top_cell, small_cell, e2_cell)
def circle(self, diameter, vertices=128): ''' circle(diameter, vertices) Generates a circle shape Parameters --------- diameter : integer The diameter of a circle vertices : integer (128) Number of vertices in the circle (coerce to multiple of 4) Returns ------ region : [pya.Region] A region containing the circle shape Description ------ The number of vertices is coerced to even numbers to ensure good fracturing ''' r = int(diameter / 2) vertices = int(vertices / 4) * 4 # Create a circle polygon = pya.Polygon([ pya.Point(-r, -r), pya.Point(-r, r), pya.Point(r, r), pya.Point(r, -r) ]) polygon = polygon.round_corners(0, r, vertices) return pya.Region(polygon)
def getRegionFromLayers(layerList=None, layermod='in'): layers = Collision.getLayers(layerList=layerList, layermod=layermod) region = pya.Region() for layer in layers: region.insert(IO.top.begin_shapes_rec(layer)) region.merge() return region
def test_1_Region(self): r = pya.Region() self.assertEqual(str(r), "") r.insert(pya.Box(0, 100, 200, 300)) self.assertEqual(str(r), "(0,100;0,300;200,300;200,100)") r2 = pya.Region(pya.Box(50, 150, 250, 350)) self.assertEqual(str(r2), "(50,150;50,350;250,350;250,150)") r += r2 self.assertEqual(str(r), "(0,100;0,300;200,300;200,100);(50,150;50,350;250,350;250,150)") r.merge() self.assertEqual(str(r), "(0,100;0,300;50,300;50,350;250,350;250,150;200,150;200,100)")
def do_non_opc_fill(fill_top_cell, layer, layer_cfg, outline_area, shapes_area): non_opc_cfg = layer_cfg['non-opc'] sp_non = non_opc_cfg['space_to_non_fill'] sp_fill = non_opc_cfg['space_to_fill'] # Constant across iterations fill_base_area = outline_area - shapes_area.sized(sp_non - half(sp_fill)) for w, h in zip(non_opc_cfg['width'], non_opc_cfg['height']): # Orient the fill in the preferred direction if layer_cfg['dir'] == 'H': w, h = (max(w, h), min(w, h)) else: w, h = (min(w, h), max(w, h)) # The set of non-OPC fill shapes may expand on each iteration so we # recompute it non_opc_fill = pya.Region( fill_top_cell.begin_shapes_rec(non_opc_cfg['klayout'])) fill_area = fill_base_area - non_opc_fill.sized(half(sp_fill)) small_cell, fill_cell_bbox = create_fill_cell(layer + '_non_opc', non_opc_cfg, w, h) while not fill_area.is_empty(): fill_top_cell.fill_region(fill_area, small_cell.cell_index(), fill_cell_bbox, None, fill_area, pya.Point(0, 0), None) if 'datatype2' in non_opc_cfg: e2_cell, _ = create_fill_cell(layer + '_non_opc_e2', non_opc_cfg, w, h, True) double_pattern(fill_top_cell, small_cell, e2_cell)
def Output_Region(self): polygons=[] for x in self.outputlist: if isinstance(x,pya.DPolygon): polygons.append(pya.Polygon.from_dpoly(x)) self.outputlist=[] return pya.Region(polygons)
def get_polygons(self, include_pins=True): from .utils import get_layout_variables TECHNOLOGY, lv, ly, cell = get_layout_variables() r = pya.Region() s = self.cell.begin_shapes_rec(ly.layer(TECHNOLOGY['Waveguide'])) while not (s.at_end()): if s.shape().is_polygon() or s.shape().is_box() or s.shape( ).is_path(): r.insert(s.shape().polygon.transformed(s.itrans())) s.next() if include_pins: s = self.cell.begin_shapes_rec(ly.layer(TECHNOLOGY['PinRec'])) import math from .utils import angle_vector while not (s.at_end()): if s.shape().is_path(): p = s.shape().path.transformed(s.itrans()) # extend the pin path by 1 micron for FDTD simulations pts = [pt for pt in p.each_point()] # direction / angle of the optical pin rotation = angle_vector(pts[0] - pts[1]) * math.pi / 180 pts[1] = ( pts[1] - pya.Point(int(math.cos(rotation) * 1000), int(math.sin(rotation) * 1000))).to_p() r.insert(pya.Path(pts, p.width).polygon()) s.next() r.merge() polygons = [p for p in r.each_merged()] return polygons
def conflict(self, other): if isinstance(other, Collision): return self.region.interacting(other.region) if isinstance(other, pya.DPoint): region = pya.Region( pya.DPolygon( BasicPainter.arc(other, self.pointRadius, 8, 0, 360))) return self.region.interacting(region) raise TypeError('Invalid input')
def convfunc(cell,layer,layerList,distanceList,numberList,box): layers = Collision.getLayers(layerList=layerList, layermod='in') outregion = pya.Region(box) inregions = [] region = pya.Region() for layeri,radius,number in zip(layers,distanceList,numberList): inregion = pya.Region() s = IO.top.begin_shapes_rec_touching(layeri, box) inregion.insert(s) inregion.merge() xys = [(radius*cos(2*pi*ii/number), radius*sin(2*pi*ii/number)) for ii in range(number)] for x, y in xys: region += inregion.transformed(pya.Trans(int(x), int(y))) region.merge() region += inregion region.merge() region &= outregion fregion = pya.Region() for polygon in region.each(): pts=list(polygon.each_point_hull()) npts=[] for ii,pt in enumerate(pts): if ii%2==0 or ii==len(pts)-1: npts.append(pt) continue if pt.distance(pts[ii+1])>10000 or pt.distance(pts[ii-1])>10000: npts.append(pt) continue fregion.insert(pya.Polygon(npts)) # xx=[] # yy=[] # for pt in polygon.to_simple_polygon().each_point(): # xx.append(str(pt.x)) # yy.append(str(pt.y)) # pushln('xx_=['+','.join(xx)+'];') # pushln('yy_=['+','.join(yy)+'];') # pushln(vname+'{end+1}={xx_,yy_};') BasicPainter.Draw(cell, layer, fregion)
def vernier(self, width, length, pitch): ''' vernier(width, length, pitch) Generates a vernier scale Parameters --------- width : integer The width of each tick marker length : integer The length of the central tick mark The major tick marks are 3/4 this length The minor tick marks are half this length pitch : integer The distance between each tick mark Returns ------ region : [pya.Region] A region containing the vernier scale Description --------- A pair of vernier scale can be used to measure misalignment by eye. In photolithography the wafer will contain one vernier pattern (width = 4, length = 40, pitch = 8, units = micron) and the mask will contain a second vernier pattern (pitch = 8.2) providing an alignment measurement resolution of 0.2 micron. ''' scaleM = 0.75 scaleS = 0.5 # Create the large tick mark polygons = [] #tick = pya.Polygon([pya.Point(0,0), pya.Point(length,0), pya.Point(length,width), pya.Point(0,width)]) tick = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length,width), pya.Point(length,0)]) tc = pya.ICplxTrans(int(-length/2),int(-width/2)) polygons.append(tc.trans(tick)) # Create the medium tick mark #tickm = pya.Polygon([pya.Point(0,0), pya.Point(length*scaleM,0), pya.Point(length*scaleM,width), pya.Point(0,width)]) tickm = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length*scaleM,width), pya.Point(length*scaleM,0)]) pos = [-2, -1, 1, 2] for i in pos: tt = pya.ICplxTrans(0,int(i*pitch*5)) polygons.append(tc.trans(tt.trans(tickm))) # Create the small tick mark #ticks = pya.Polygon([pya.Point(0,0), pya.Point(length*scaleS,0), pya.Point(length*scaleS,width), pya.Point(0,width)]) ticks = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length*scaleS,width), pya.Point(length*scaleS,0)]) pos = [-9, -8, -7, -6, -4, -3, -2, -1, 1, 2, 3, 4, 6, 7, 8, 9] for i in pos: tt = pya.ICplxTrans(0,int(i*pitch)) polygons.append(tc.trans(tt.trans(ticks))) return pya.Region(polygons)
def DrawAirbridgeWithCollisionCheck(self, cell, centerlinelist, newcellname, boxY, boxWidth, boxHeight, push=10000, extend=5000): # get all shapes region = Collision.getRegionFromLayers(layerList=[], layermod='not in') # insert regionInsert = pya.Region() regionInsert.insert( pya.Box(-boxWidth/2, boxY-boxHeight/2, boxWidth/2, boxY+boxHeight/2)) regionInsert.insert( pya.Box(-boxWidth/2, -boxY-boxHeight/2, boxWidth/2, -boxY+boxHeight/2)) return self.DrawAirbridge(cell, centerlinelist, newcellname, collision={'region': region, 'regionInsert': regionInsert, 'push': push,'extend': extend})
def Border(leng=3050000,siz=3050000,wed=50000): polygons=[] pts=[pya.Point(-siz,-siz),pya.Point(-siz+leng,-siz),pya.Point(-siz+leng,-siz+wed)] pts.extend([pya.Point(-siz+wed,-siz+wed),pya.Point(-siz+wed,-siz+leng),pya.Point(-siz,-siz+leng)]) polygon1=pya.Polygon(pts) polygons.append(polygon1) polygons.append(polygon1.transformed(pya.Trans(pya.Trans.R90))) polygons.append(polygon1.transformed(pya.Trans(pya.Trans.R180))) polygons.append(polygon1.transformed(pya.Trans(pya.Trans.R270))) return pya.Region(polygons)
def DPathPolygon(pts, width, start, end, giveupsomepoints=False): region1 = pya.Region([ pya.Polygon.from_dpoly( pya.DPath(pts, width, start, end).polygon()) ]) region2 = pya.Region([ pya.Polygon.from_dpoly( pya.DPath(pts, width + 2000, start, end).polygon()) ]) polygon = list((region1 & region2).each_merged())[0] if giveupsomepoints: pts = [] sourcepts = list(polygon.each_point_hull()) for i, pt in enumerate(sourcepts): if i == 0 or pt.distance(pts[-1]) >= min( 1000, max(100, rounded)): pts.append(pt) return pya.DPolygon(pts) return polygon
def add(layout, cell, slayers, dlayers, ex_amount, layers, out_cell=None): """Combines all slayers' shapes into a region and merges this region with each of dlayers' regions. :param layout: the layout on which the cells are located :param cell: the cell from which to copy the layers (source shapes) :param slayers: the layers to copy :param dlayers: the layers where to copy to :param ex_amount: the amount added around the source shapes :param layers: the layermapping :param out_cell: the cell where to put the shapes. If not specified, the input cell will be used. """ # adjust amount from microns to database units am = ex_amount / layout.dbu if out_cell: o_cell = out_cell else: o_cell = cell srclayers = [slayers, ] if isinstance(slayers, str) else slayers dstlayers = [dlayers, ] if isinstance(dlayers, str) else dlayers in_layers = [layout.layer(layers[m][0], layers[m][1]) for m in srclayers] region = pya.Region() for layer in in_layers: if layer != -1: shapeit = cell.begin_shapes_rec(layer) region.insert(shapeit) region.merge() if ex_amount > 0: # increase the size of the region region.size(am) region.merge() for layer in dstlayers: layer_n, layer_d = layers[layer] l = layout.layer(layer_n, layer_d) shapeit = o_cell.begin_shapes_rec(l) add_region = pya.Region() add_region.insert(shapeit) add_region.merge() o_cell.shapes(l).clear() o_cell.shapes(l).insert(add_region + region)
def _candidatePoints(potentialPts, region, size): candidatePts = [] for pt in potentialPts: x1 = pt.x y1 = pt.y check_box = pya.Box(x1 - size / 2, y1 - size / 2, x1 + size / 2, y1 + size / 2) empty = (pya.Region(check_box) & region).is_empty() if empty: candidatePts.append(pt) return candidatePts
def Output_Region(self): region = super().Output_Region() polygonsex = [] for x in self.regionlistex: if isinstance(x, pya.DPolygon): polygonsex.append(pya.Polygon.from_dpoly(x)) self.regionlistex = [] region2 = pya.Region(polygonsex) region2.merge() self.region = region2 + region return self.region
def test_deep1(self): ut_testsrc = os.getenv("TESTSRC") # construction/destruction magic ... self.assertEqual(pya.DeepShapeStore.instance_count(), 0) dss = pya.DeepShapeStore() dss._create() self.assertEqual(pya.DeepShapeStore.instance_count(), 1) dss = None self.assertEqual(pya.DeepShapeStore.instance_count(), 0) dss = pya.DeepShapeStore() ly = pya.Layout() ly.read( os.path.join(ut_testsrc, "testdata", "algo", "deep_region_l1.gds")) l1 = ly.layer(1, 0) r = pya.Region(ly.top_cell().begin_shapes_rec(l1), dss) rf = pya.Region(ly.top_cell().begin_shapes_rec(l1)) self.assertEqual(r.area(), 53120000) self.assertEqual(rf.area(), 53120000) ly_new = pya.Layout() tc = ly_new.add_cell("TOP") l1 = ly_new.layer(1, 0) l2 = ly_new.layer(2, 0) ly_new.insert(tc, l1, r) ly_new.insert(tc, l2, rf) s1 = {} s2 = {} for cell in ly_new.each_cell(): s1[cell.name] = cell.shapes(l1).size() s2[cell.name] = cell.shapes(l2).size() self.assertEqual(s1, {"INV2": 1, "TOP": 0, "TRANS": 0}) self.assertEqual(s2, {"INV2": 0, "TOP": 10, "TRANS": 0}) # force destroy, so the unit tests pass on the next iteration dss = None self.assertEqual(pya.DeepShapeStore.instance_count(), 0)
def cut(layerlist=None, layermod='not in', box=None, mergeanddraw=True): if layerlist == None: layerlist = [(0, 0)] if type(box) == type(None): box = Interactive._box_selected() if not box: return # celllist=[] # cells=[] # def buildcells(cell): # if cell.name.split('$')[0] in celllist:return # cells.append(cell) # for ii in cell.each_child_cell(): # buildcells(layout.cell(ii)) # buildcells(top) # cellnames=[c.name for c in cells] cells = [IO.top] _layerlist = [] for ii in layerlist: if type(ii) == str: _layerlist.append(IO.layout.find_layer(ii)) else: _layerlist.append(IO.layout.find_layer(ii[0], ii[1])) layers = [ index for index in IO.layout.layer_indices() if index in _layerlist ] if layermod == 'in' else [ index for index in IO.layout.layer_indices() if index not in _layerlist ] outregion = pya.Region(box) inregion = pya.Region() for cell in cells: for layer in layers: s = cell.begin_shapes_rec_touching(layer, box) inregion.insert(s) if not mergeanddraw: return outregion, inregion return Interactive._merge_and_draw(outregion, inregion)[0]