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 __init__(self): # Important: initialize the super class super(CPW, self).__init__() # declare the parameters self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0)) self.param("s", self.TypeShape, "", default = pya.DPath(0, 0)) self.param("w", self.TypeDouble, "Center Conductor width", default = 10) self.param("g", self.TypeInt, "Gap Width", default = 6) self.param("name", self.TypeString, "Name", default= "cpw") # this hidden parameter is used to determine whether the radius has changed # or the "s" handle has been moved self.param("length", self.TypeDouble, "Length", default=0.0, readonly=True) self.param("frequency", self.TypeDouble, "Frequency", default = 0.0, readonly= True) self.param("impedance", self.TypeDouble, "Impedance", default = 50., readonly = True)
def port_to_pin_helper(ports_list, cell, layerPinRec): # Create the pins, as short paths: from siepic_tools.config import PIN_LENGTH dbu = cell.layout().dbu for port in ports_list: if port.name.startswith("el"): pin_length = port.width else: pin_length = PIN_LENGTH * dbu port_position_i = port.position.to_itype(dbu) cell.shapes(layerPinRec).insert( pya.DPath([port.position - 0.5 * pin_length * port.direction, port.position + 0.5 * pin_length * port.direction], port.width).to_itype(dbu)) cell.shapes(layerPinRec).insert(pya.Text(port.name, pya.Trans( pya.Trans.R0, port_position_i.x, port_position_i.y))).text_size = 2 / dbu
def translate_from_center(self, offset): from math import pi, cos, sin, acos, sqrt from .utils import angle_vector pts = [pt for pt in self.get_dpoints()] tpts = [pt for pt in self.get_dpoints()] for i in range(0, len(pts)): if i == 0: u = pts[i] - pts[i + 1] v = -u elif i == (len(pts) - 1): u = pts[i - 1] - pts[i] v = -u else: u = pts[i - 1] - pts[i] v = pts[i + 1] - pts[i] if offset < 0: o1 = pya.DPoint(abs(offset) * cos(angle_vector(u) * pi / 180 - pi / 2), abs(offset) * sin(angle_vector(u) * pi / 180 - pi / 2)) o2 = pya.DPoint(abs(offset) * cos(angle_vector(v) * pi / 180 + pi / 2), abs(offset) * sin(angle_vector(v) * pi / 180 + pi / 2)) else: o1 = pya.DPoint(abs(offset) * cos(angle_vector(u) * pi / 180 + pi / 2), abs(offset) * sin(angle_vector(u) * pi / 180 + pi / 2)) o2 = pya.DPoint(abs(offset) * cos(angle_vector(v) * pi / 180 - pi / 2), abs(offset) * sin(angle_vector(v) * pi / 180 - pi / 2)) p1 = u + o1 p2 = o1 p3 = v + o2 p4 = o2 d = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x) if round(d, 10) == 0: tpts[i] += p2 else: tpts[i] += pya.DPoint(((p1.x * p2.y - p1.y * p2.x) * (p3.x - p4.x) - (p1.x - p2.x) * (p3.x * p4.y - p3.y * p4.x)) / d, ((p1.x * p2.y - p1.y * p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x * p4.y - p3.y * p4.x)) / d) if self.__class__ == pya.Path: return pya.Path([pya.Point(pt.x, pt.y) for pt in tpts], self.width) elif self.__class__ == pya.DPath: return pya.DPath(tpts, self.width)
def layout_waveguide_rel(cell, layer, start_point, points, w, radius): # create a path, then convert to a polygon waveguide with bends # cell: cell into which to place the waveguide # layer: layer to draw on # start_point: starting vertex for the waveguide # points: array of vertices, relative to start_point # w: waveguide width # example usage: # cell = pya.Application.instance().main_window().current_view().active_cellview().cell # LayerSi = LayerInfo(1, 0) # points = [ [15, 2.75], [30, 2.75] ] # units of microns. # layout_waveguide_rel(cell, LayerSi, [0,0], points, 0.5, 10) #print("* layout_waveguide_rel(%s, %s, %s, %s)" % (cell.name, layer, w, radius) ) ly = cell.layout() dbu = cell.layout().dbu start_point = [start_point[0] / dbu, start_point[1] / dbu] a1 = [] for p in points: a1.append(pya.DPoint(float(p[0]), float(p[1]))) wg_path = pya.DPath(a1, w) npoints = points_per_circle(radius / dbu) param = { "npoints": npoints, "radius": float(radius), "path": wg_path, "layer": layer } pcell = ly.create_cell("ROUND_PATH", "Basic", param) # Configure the cell location trans = Trans(Point(start_point[0], start_point[1])) # Place the PCell cell.insert(pya.CellInstArray(pcell.cell_index(), trans))
pya.Point(50, 0), pya.Point(50, 50), pya.Point(40, 60), pya.Point(0, 50) ] polygon1 = pya.Polygon(pts) top.shapes(l1).insert(polygon1) #DPath dpts = [ pya.DPoint(0.4, 0), pya.DPoint(50, 0), pya.DPoint(50, 50), pya.DPoint(40.5, 60.6), pya.DPoint(0, 50) ] dpath1 = pya.DPath(dpts, 4, 5, 0, True) top.shapes(l1).insert(pya.Path.from_dpath(dpath1)) #DCplxTrans #倍数,逆时针度数,是否绕x翻转,平移x,平移y tr = pya.DCplxTrans(10, 45, False, 1000, 1000) #xxx.transform(tr)#本身改变 #xxx.transformed(tr)本身不变返回新的 #对一个点pt做变换的方法 #pya.DEdge(pya.DPoint(),pt).transformed(DCplxTrans).p2 #DText text1 = pya.DText("TEST_Text", pya.DTrans(-10, -10), 100, 1) top.shapes(l1).insert(pya.Text.from_dtext(text1)) #a text can be printed @ruby #it dose not work in python #lib.layout.pcell_declaration can't be found
def test_3_DTrans(self): c = pya.DCplxTrans(5.0, -7.0) self.assertEqual(str(c), "r0 *1 5,-7") c = pya.DCplxTrans(pya.DCplxTrans.M135) self.assertEqual(str(c), "m135 *1 0,0") self.assertEqual(c.is_unity(), False) self.assertEqual(c.is_ortho(), True) self.assertEqual(c.is_mag(), False) self.assertEqual(c.is_mirror(), True) self.assertEqual(c.rot(), pya.DCplxTrans.M135.rot()) self.assertEqual(str(c.s_trans()), "m135 0,0") self.assertAlmostEqual(c.angle, 270) self.assertEqual(str(c.trans(pya.DEdge(0, 1, 2, 3))), "(-3,-2;-1,0)") self.assertEqual(str((c * pya.DEdge(0, 1, 2, 3))), "(-3,-2;-1,0)") self.assertEqual(str(c.trans(pya.DBox(0, 1, 2, 3))), "(-3,-2;-1,0)") self.assertEqual(str((c * pya.DBox(0, 1, 2, 3))), "(-3,-2;-1,0)") self.assertEqual(str(c.trans(pya.DText("text", pya.DVector(0, 1)))), "('text',m135 -1,0)") self.assertEqual(str((c * pya.DText("text", pya.DVector(0, 1)))), "('text',m135 -1,0)") self.assertEqual( str( c.trans( pya.DPolygon([ pya.DPoint(0, 1), pya.DPoint(2, -3), pya.DPoint(4, 5) ]))), "(-5,-4;-1,0;3,-2)") self.assertEqual( str((c * pya.DPolygon( [pya.DPoint(0, 1), pya.DPoint(2, -3), pya.DPoint(4, 5)]))), "(-5,-4;-1,0;3,-2)") self.assertEqual( str(c.trans(pya.DPath( [pya.DPoint(0, 1), pya.DPoint(2, 3)], 10))), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false") self.assertEqual( str((c * pya.DPath( [pya.DPoint(0, 1), pya.DPoint(2, 3)], 10))), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false") c = pya.DCplxTrans.from_itrans(pya.CplxTrans.M135) self.assertEqual(str(c), "m135 *1 0,0") c = pya.DCplxTrans(1.5) self.assertEqual(str(c), "r0 *1.5 0,0") self.assertEqual(c.is_unity(), False) self.assertEqual(c.is_ortho(), True) self.assertEqual(c.is_mag(), True) self.assertEqual(c.is_mirror(), False) self.assertEqual(c.rot(), pya.DCplxTrans.R0.rot()) self.assertEqual(str(c.s_trans()), "r0 0,0") self.assertAlmostEqual(c.angle, 0) c = pya.DCplxTrans(0.75, 45, True, 2.5, -12.5) self.assertEqual(str(c), "m22.5 *0.75 2.5,-12.5") c = pya.DCplxTrans(0.75, 45, True, pya.DPoint(2.5, -12.5)) self.assertEqual(str(c), "m22.5 *0.75 2.5,-12.5") self.assertEqual(c.is_unity(), False) self.assertEqual(c.is_ortho(), False) self.assertEqual(c.is_mag(), True) self.assertEqual(c.rot(), pya.DCplxTrans.M0.rot()) self.assertEqual(str(c.s_trans()), "m0 2.5,-12.5") self.assertAlmostEqual(c.angle, 45) self.assertEqual(str(c.ctrans(5)), "3.75") self.assertEqual(str(c.trans(pya.DPoint(12, 16))), "17.3492424049,-14.6213203436") self.assertEqual(str(pya.DCplxTrans()), "r0 *1 0,0") self.assertEqual(pya.DCplxTrans().is_unity(), True) self.assertEqual((c * c.inverted()).is_unity(), True) c.mirror = False self.assertEqual(str(c), "r45 *0.75 2.5,-12.5") c.mag = 1.5 self.assertEqual(str(c), "r45 *1.5 2.5,-12.5") c.disp = pya.DPoint(-1.0, 5.5) self.assertEqual(str(c), "r45 *1.5 -1,5.5") self.assertEqual(c.mag, 1.5) c.angle = 60 self.assertEqual(str(c), "r60 *1.5 -1,5.5") self.assertEqual(("%g" % c.angle), "60") # Constructor variations self.assertEqual(str(pya.ICplxTrans()), "r0 *1 0,0") self.assertEqual(str(pya.ICplxTrans(1.5)), "r0 *1.5 0,0") self.assertEqual(str(pya.ICplxTrans(pya.Trans(1, False, 10, 20), 1.5)), "r90 *1.5 10,20") self.assertEqual(str(pya.ICplxTrans(pya.Trans(1, False, 10, 20))), "r90 *1 10,20") self.assertEqual( str(pya.ICplxTrans(1.5, 80, True, pya.Vector(100, 200))), "m40 *1.5 100,200") self.assertEqual(str(pya.ICplxTrans(1.5, 80, True, 100, 200)), "m40 *1.5 100,200") self.assertEqual(str(pya.ICplxTrans(pya.Vector(100, 200))), "r0 *1 100,200") self.assertEqual(str(pya.ICplxTrans(100, 200)), "r0 *1 100,200") self.assertEqual(str(pya.ICplxTrans(pya.ICplxTrans(100, 200))), "r0 *1 100,200") self.assertEqual(str(pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5)), "r0 *1.5 150,300") self.assertEqual( str( pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5, pya.Vector(10, 20))), "r0 *1.5 160,320") self.assertEqual( str(pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5, 10, 20)), "r0 *1.5 160,320") self.assertEqual(str(pya.DCplxTrans()), "r0 *1 0,0") self.assertEqual(str(pya.DCplxTrans(1.5)), "r0 *1.5 0,0") self.assertEqual( str(pya.DCplxTrans(pya.DTrans(1, False, 0.01, 0.02), 1.5)), "r90 *1.5 0.01,0.02") self.assertEqual(str(pya.DCplxTrans(pya.DTrans(1, False, 0.01, 0.02))), "r90 *1 0.01,0.02") self.assertEqual( str(pya.DCplxTrans(1.5, 80, True, pya.DVector(0.1, 0.2))), "m40 *1.5 0.1,0.2") self.assertEqual(str(pya.DCplxTrans(1.5, 80, True, 0.1, 0.2)), "m40 *1.5 0.1,0.2") self.assertEqual(str(pya.DCplxTrans(pya.DVector(0.1, 0.2))), "r0 *1 0.1,0.2") self.assertEqual(str(pya.DCplxTrans(0.1, 0.2)), "r0 *1 0.1,0.2") self.assertEqual(str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2))), "r0 *1 0.1,0.2") self.assertEqual(str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5)), "r0 *1.5 0.15,0.3") self.assertEqual( str( pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5, pya.DVector(0.01, 0.02))), "r0 *1.5 0.16,0.32") self.assertEqual( str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5, 0.01, 0.02)), "r0 *1.5 0.16,0.32")
def layout_path_with_ends(cell, layer, point_iterator, w): dpath = pya.DPath(list(point_iterator), w, w / 2, w / 2) cell.shapes(layer).insert(dpath)
def layout_path(cell, layer, point_iterator, w): path = pya.DPath(list(point_iterator), w, 0, 0).to_itype(cell.layout().dbu) cell.shapes(layer).insert(pya.Path.from_dpath(path))
def layout_path_with_ends(cell, layer, point_iterator, w): ''' Simple wrapper for pya.DPath.''' dpath = pya.DPath(list(point_iterator), w, w / 2, w / 2) cell.shapes(layer).insert(dpath)
def produce_impl(self): size = [] if len(self.size) < 2: if self.debug: print("Size < 2 dimension") if len(self.size) == 0: if self.debug: print( "paramter size has been adjusted to default - invalid data have provided" ) size = [100.0, 100.0] else: if self.debug: print("Size has been adjusted to {}:{}".format( self.size[0] / dbu, self.size[0] / dbu)) size.append(float(self.size[0])) size.append(float(self.size[0])) else: size.append(float(self.size[0])) size.append(float(self.size[1])) ovSize = [] if len(self.ovsize) < 2: if self.debug: print("overal size < 2 dimension") if len(self.ovsize) == 0: if self.debug: print( "paramter size has been adjusted to default - invalid data have provided" ) ovSize = [100.0, 100.0] else: ovSize.append(float(self.ovsize[0])) ovSize.append(float(self.ovsize[0])) else: ovSize.append(float(self.ovsize[0])) ovSize.append(float(self.ovsize[1])) #armLenght = self.armLenght armWidth = self.armWidth armBSS = self.armBMS activeArea = [size[0] - self.actOffset, size[1] - self.actOffset] #woW = self.woW/dbu #woOP = self.woOP/dbu # Membrane Geometry: memParts = [] # Firstly generate the centerpart memCenter = pya.Polygon( pya.DPolygon( pya.DBox(-size[0] / 2, -size[1] / 2, size[0] / 2, size[1] / 2))) memParts.append(memParts) # Time for beams # Calculate the BeamArmcenter to the membrane edge, alias beam to memrane spacing memBMS = [(ovSize[0]-size[0]/2) -armBSS -armWidth/2,\ (ovSize[1]-size[1]/2) -armBSS -armWidth/2] memBeam1 = pya.Polygon(pya.DPath([\ pya.DPoint((size[0]-armWidth)/2, size[1]/2),\ pya.DPoint((size[0]-armWidth)/2, size[1]/2+memBMS[1]),\ pya.DPoint(-ovSize[0]/2, size[1]/2+memBMS[1])], armWidth).polygon()) memParts.append(memBeam1) memBeam2 = pya.Polygon(pya.DPath([\ pya.DPoint(-size[0]/2, (size[1]/2)-armWidth/2),\ pya.DPoint(-size[0]-memBMS[0], size[1]/2-armWidth/2),\ pya.DPoint(-size[0]-memBMS[0], -ovSize[1]/2)], armWidth).polygon()) memParts.append(memBeam2) memBeam3 = pya.Polygon(pya.DPath([\ pya.DPoint((-size[0]+armWidth)/2, -size[1]/2),\ pya.DPoint((-size[0]+armWidth)/2, -size[1]/2-memBMS[1]),\ pya.DPoint(ovSize[0]/2, -size[1]/2-memBMS[1])], armWidth).polygon()) memParts.append(memBeam3) memBeam4 = pya.Polygon(pya.DPath([\ pya.DPoint(size[0]/2, (-size[1]-armWidth)/2),\ pya.DPoint(size[0]+memBMS[0], -size[1]/2+armWidth/2),\ pya.DPoint(size[0]+memBMS[0], ovSize[1]/2)], armWidth).polygon()) memParts.append(memBeam4) #here it would be probably useful to put them all into one polygon object (hopefully it\ # would work ;)) if self.debug: print(memParts) for member in memParts: self.cell.shapes(self.l_layer).insert(member) tr = pya.DCplxTrans(1000.0) #workaround for difference in DBU region = pya.Region(memParts) region.merge() region.transform(tr) self.cell.shapes(self.l_layer).insert(region) #Active Area actBox = pya.DBox(-activeArea[0]/2, -activeArea[1]/2,\ activeArea[0]/2, activeArea[1]/2) if self.showAct: self.cell.shapes(self.la_layer).insert(actBox) # Etch area - in this variant the overal size of membrane etchRegion = pya.Region(actBox) etchRegion.transform(tr) tempRegion = region ^ etchRegion etchRegion = tempRegion & etchRegion self.cell.shapes(self.ool_layer).insert(etchRegion)
def produce_impl(self): #Calculate layout database unit #dbu = self.layout.dbu dbu = 1 size = [] if len(self.size) < 2: if self.debug: print("Size < 2 dimension") if len(self.size) == 0: if self.debug: print( "paramter size has been adjusted to default - invalid data have provided" ) size = [100.0, 100.0] else: if self.debug: print("Size has been adjusted to {}:{}".format( self.size[0] / dbu, self.size[0] / dbu)) size.append(float(self.size[0]) / dbu) size.append(float(self.size[0]) / dbu) else: size.append(float(self.size[0]) / dbu) size.append(float(self.size[1]) / dbu) ovSize = [] if len(self.ovsize) < 2: if self.debug: print("overal size < 2 dimension") if len(self.ovsize) == 0: if self.debug: print( "paramter size has been adjusted to default - invalid data have provided" ) ovSize = [100.0, 100.0] else: ovSize.append(float(self.ovsize[0]) / dbu) ovSize.append(float(self.ovsize[0]) / dbu) else: ovSize.append(float(self.ovsize[0]) / dbu) ovSize.append(float(self.ovsize[1]) / dbu) armLenght = self.armLenght / dbu armWidth = self.armWidth / dbu activeArea = [size[0] - self.actOffset, size[1] - self.actOffset] woW = self.woW / dbu woOP = self.woOP / dbu # Membrane Geometry: ## arm location on a rectangle = edgeArmOffset edgeArmOffset = armWidth / 2 * math.sqrt(2) if self.debug: print("Size 0:{:.3f}, {}, {}".format(size[0], armLenght, armWidth)) ## arm starts at following points pointArmA = pya.DPoint(size[0] / 2 - edgeArmOffset, size[1] / 2) pointArmD = pya.DPoint(size[0] / 2, size[1] / 2 - edgeArmOffset) ## arm ends in the point P - might be usefull as a connector point pointP = pya.DPoint(size[0] / 2 + armLenght / math.sqrt(2), size[1] / 2 + armLenght / math.sqrt(2)) ## arm edge points offsets from the center point P armEndPointoffset = armWidth / 2 / math.sqrt(2) ## Arm edge points in relation to the P point pointArmB = pya.DPoint(pointP.x - armEndPointoffset, pointP.y + armEndPointoffset) pointArmC = pya.DPoint(pointP.x + armEndPointoffset, pointP.y - armEndPointoffset) ## Lets Try to assemble the membrane as 1/4 polyPoints = [] polyPoints.append(pya.DPoint(0.0, 0.0)) polyPoints.append(pya.DPoint(0.0, size[1] / 2)) polyPoints.append(pointArmA) polyPoints.append(pointArmB) polyPoints.append(pointArmC) polyPoints.append(pointArmD) polyPoints.append(pya.DPoint(size[0] / 2, 0.0)) #Lets put it there shapeSet = [] Poly = pya.DPolygon(polyPoints) shapeSet.append(Poly) t = pya.DCplxTrans(1.0, 180, False, 0.0, 0.0) Poly1 = pya.DPolygon(polyPoints) Poly1.transform(t) shapeSet.append(Poly1) t = pya.DCplxTrans(1.0, 0, True, 0.0, 0.0) Poly2 = pya.DPolygon(polyPoints) Poly2.transform(t) shapeSet.append(Poly2) t = pya.DCplxTrans(1.0, 180, True, 0.0, 0.0) Poly3 = pya.DPolygon(polyPoints) Poly3.transform(t) shapeSet.append(Poly3) tr = pya.DCplxTrans(1000.0) region = pya.Region(shapeSet) region.merge() region.transform(tr) self.cell.shapes(self.l_layer).insert(region) #Active Area if self.showAct: actBox = pya.DBox(-activeArea[0] / 2, -activeArea[1] / 2, activeArea[0] / 2, activeArea[1] / 2) self.cell.shapes(self.la_layer).insert(actBox) # Etch area - a rectangele limited by the membrane shape and P point etchBox = pya.DBox(-pointP.x, -pointP.y, pointP.x, pointP.y) etchRegion = pya.Region(etchBox) etchRegion.transform(tr) tempRegion = region ^ etchRegion etchRegion = tempRegion & etchRegion self.cell.shapes(self.ool_layer).insert(etchRegion) # Heater wire if self.genHeater: if self.heatType == 0: #Hilbert is defined only for square areas. We would fit whatever is smaller if activeArea[0] != activeArea[1]: if (activeArea[0] > activeArea[1]): wireArea = activeArea[1] / 2 else: wireArea = activeArea[0] / 2 else: wireArea = activeArea[0] / 2 #issue num2: # the diagonal contact placemnet is required # so we have to calculate space for the return path # segment separation 1sqg => seg = wireArea / 2^n + 1 Hcnt = 2**self.heatOrder + 1 Hseg = wireArea / (Hcnt) print("Hseq: {:.3f}".format(Hseg)) wireAreaRed = wireArea - Hseg a = wireAreaRed + wireAreaRed * 1j b = wireAreaRed - wireAreaRed * 1j z = 0 for i in range(1, self.heatOrder + 1): w = 1j * z.conjugate() z = numpy.array([w - a, z - b, z + a, b - w]) / 2 z = z.flatten() X = [x.real for x in z] Y = [x.imag for x in z] heatPoints = [] for i in range(0, len(X)): heatPoints.append(pya.DPoint(X[i], Y[i])) #lets add the return path # start with calculation of intersection to the beam # linEqa = -1*(pointP.y / pointP.x) - valid only for Square # #print("Linear equation is y = {:.3f}.x".format(linEqa)) heatInitial = heatPoints[0] pointS1 = pya.DPoint(-size[0] / 2, size[1] / 2) #pointS2 = pya.DPoint(activeArea[0]/2, -activeArea[1]/2) if self.debug: print("P:{:.3f},{:.3f} ; S:{:.3f},{:.3f}".format( -pointP.x, pointP.y, pointS1.x, pointS1.y)) linEqa = (pointP.y - pointS1.y) / (-pointP.x - pointS1.x) linEqb = pointP.y - linEqa * -pointP.x if self.debug: print("Line equation is: y={:.3f}x+{:.3f}".format( linEqa, linEqb)) heatPoints.insert( 0, pya.DPoint(heatPoints[0].x - 2 * Hseg, heatPoints[0].y)) heatPoints.insert( 0, pya.DPoint(heatPoints[0].x, linEqa * (heatPoints[0].x + Hseg) + linEqb)) heatPoints.append(pya.DPoint(heatPoints[len(heatPoints)-1].x, \ linEqa*(heatPoints[len(heatPoints)-1].x+Hseg)-linEqb)) heatPoints.append(pya.DPoint(pointP.x - Hseg, -pointP.y)) #arm contacts heatPoints.insert(0, pya.DPoint(-pointP.x - Hseg, pointP.y)) #probably somewhere here is a good time to calculate perforations # teoretically first opening should be -Heg/2 to the left of the very first # point and should repeat in X and Y axis with interval of Hseg # # center is HeatPoints[2] -Hseg/2 ? if self.perfAct: perfW = self.perfSize / 2 / dbu #perfCenter = pya.DPoint(heatPoints[2].x - Hseg, heatPoints[2].y - Hseg) #perfBox = pya.DBox(perfCenter.x-perfW, perfCenter.y-perfW, perfCenter.x+perfW, perfCenter.y-perfW) elCell = self.layout.create_cell("Perforator") perfBox = pya.DPolygon( pya.DBox(-perfW, -perfW, perfW, perfW)) if self.roundPath: perfBox = perfBox.round_corners(Hseg / 2, Hseg / 2, 32) elCell.shapes(self.perfl_layer).insert(perfBox) #lets make an array of them x_vect = pya.DVector(2 * Hseg, 0.0) y_vect = pya.DVector(0.0, 2 * Hseg) t = pya.DCplxTrans(heatInitial.x, heatInitial.y + Hseg) perfArr = pya.DCellInstArray(elCell.cell_index(), t, x_vect, y_vect, Hcnt - 1, Hcnt - 2) self.cell.insert(perfArr) #move to the right coordinates pathT = pya.DCplxTrans(Hseg, 0) heatPath = pya.DPath(heatPoints, self.heatW) heatPathT = heatPath.transformed(pathT) if self.roundPath: heatPathT = heatPath.round_corners(Hseg / 2, 32, 0.001) heatCenter = heatPathT.bbox().center() print(heatCenter) print("Rounded Path center: {}:{}".format( heatCenter.x, heatCenter.y)) pathTr = pya.DCplxTrans(-heatCenter.x, -heatCenter.y) heatPathT = heatPathT.transformed(pathTr) self.cell.shapes(self.heatl_layer).insert(heatPathT) else: print("Wire definition has not been found!") #TODO ... other types of heaters if self.genWO: #we would make a wire connection from the P point to the edge of the membrane # overpass on both sides as an option # it has to be realized as a set of the 4 path print("Overal size: {}:{}".format(ovSize[0], ovSize[1])) woPathA = pya.DPath( [pointP, pya.DPoint(ovSize[0] / 2, ovSize[1] / 2)], woW, woOP, woOP) woPathB = pya.DPath([pya.DPoint(-pointP.x, pointP.y), pya.DPoint(-ovSize[0]/2, ovSize[1]/2)],\ woW, woOP, woOP) woPathC = pya.DPath([pya.DPoint(-pointP.x, -pointP.y), pya.DPoint(-ovSize[0]/2, -ovSize[1]/2)],\ woW, woOP, woOP) woPathD = pya.DPath([pya.DPoint(pointP.x, -pointP.y), pya.DPoint(ovSize[0]/2, -ovSize[1]/2)],\ woW, woOP, woOP) self.cell.shapes(self.cntl_layer).insert(woPathA) self.cell.shapes(self.cntl_layer).insert(woPathB) self.cell.shapes(self.cntl_layer).insert(woPathC) self.cell.shapes(self.cntl_layer).insert(woPathD) if self.genCnt: # Ok that would be fun ... # so at first we should be able to find how many of the IGC we would be able to fit # in between of the perforations (maybe we should count also for the minimal separation) # principally: # single IGS pair consists of 2 wires and 2 gaps = IGSpairW? # testing condition is therefore IGSCnt = floor((Hseg - perfW) / IGSpairW) cntW = self.cntW / dbu cntSp = self.cntSp / dbu cntB = self.cntB / dbu cntBunchW = 2 * (cntW + cntSp) cntCnt = math.floor((2 * Hseg - 2 * perfW) / cntBunchW) if self.debug: print("IDC W={}".format(cntBunchW)) print("IDCs per bunch: {}".format(cntCnt)) if cntCnt == 0: print( "Error: Interdigital contacts with given specs could not be realized because of geometric containts!" ) else: #lets make a subcell with interdigital pair # so first calculate the active area - contact bars to get the lenght # contacts singles cntCell = self.layout.create_cell("IDC_subcell") cntArrCell = self.layout.create_cell("IDC_cell") #cntLenght = activeArea - 2*cntB - cntSp cntPath_p1 = pya.DPoint((cntSp + cntW) / 2, activeArea[1] / 2 - cntB) cntPath_p2 = pya.DPoint((cntSp + cntW) / 2, -activeArea[1] / 2 + cntSp + cntB) #TODO tohle je asi blbe ... cntPath_pA = [cntPath_p1, cntPath_p2] cntPath_pB = [cntPath_p1 * -1, cntPath_p2 * -1] cntPath_A = pya.DPath(cntPath_pA, cntW, 0.0, 0.0) cntPath_B = pya.DPath(cntPath_pB, cntW, 0.0, 0.0) cntCell.shapes(self.idcl_layer).insert(cntPath_A) cntCell.shapes(self.idcl_layer).insert(cntPath_B) #now lets make bunches of cntCnt and center them # TODO: tady jsem skoncil ... potreba projit odstavec pod #BEGIN x_vect = pya.DVector(cntBunchW, 0.0) y_vect = pya.DVector(0.0, 0.0) if self.debug: print("IDC bunch Vectors: {}, {}, {}, {}".format(\ x_vect.x, x_vect.y, y_vect.x, y_vect.y)) t = pya.DCplxTrans(0, 0) cntArr = pya.DCellInstArray(cntCell.cell_index(), t, x_vect, y_vect, cntCnt, 1) #center the origins on top of each other # here we have a bunch of IDCs cntArr_center = cntArr.bbox(self.layout).center() if self.debug: print("Bunch center: {},{}".format(cntArr_center.x, cntArr_center.y)) t = pya.DCplxTrans(1.0, 0, False, -cntArr_center.x, -cntArr_center.y) cntArr.transform(t) cntArrCell.insert(cntArr) # move the array to the position of Hilb. initial and paste it into the overal array a_vect = pya.DVector(2 * Hseg, 0.0) b_vect = pya.DVector(0.0, 0.0) cntLoct = pya.DCplxTrans(1.0, 0, False, heatInitial.x - Hseg, 0.0) cntArrAll = pya.DCellInstArray(cntArrCell.cell_index(), cntLoct, a_vect, b_vect, Hcnt, 1) self.cell.insert(cntArrAll) #Top and bottom contact # by principle the bar-contact should be horizontally oriented across the active zone # then they should continue to the respective P-points (upright, lowerleft) # Contact bar would be a box from the edge to the edge of active area with a width of # cntB # pointCNT1A = pya.DPoint(activeArea[0]/2, activeArea[1]/2) # if self.debug: # print("P:{:.3f},{:.3f} ; CNT:{:.3f},{:.3f}".format(-pointP.x, pointP.y, pointCNT1A.x, pointCNT1A.y)) # linCntEqa = (pointP.y-pointCNT1A.y)/(-pointP.x-pointCNT1A.x) # linCntEqb = pointP.y - linCntEqa*-pointP.x # if self.debug: # print("CNT line equation is: y={:.3f}x+{:.3f}".format(linEqa,linEqb)) # pointCNT1B = # Contact Bars cntBarW = self.cntB / dbu cntWoW = self.cntWO / dbu shapeSetCNT = [] #cntBarA shapeSetCNT.append(pya.DBox(-activeArea[0]/2, activeArea[1]/2-cntBarW,\ activeArea[0]/2, activeArea[1]/2)) #cntBarB shapeSetCNT.append(pya.DBox(-activeArea[0]/2, -activeArea[1]/2,\ activeArea[0]/2, -activeArea[1]/2+cntBarW)) pointS2 = pya.DPoint(activeArea[0] / 2, activeArea[1] / 2) #cntWOPathA shapeSetCNT.append( pya.DPath([pointS2, pointP], cntWoW, cntWoW / 2, cntWoW).polygon()) #cntWOPathB shapeSetCNT.append( pya.DPath([-pointS2, -pointP], cntWoW, cntWoW / 2, cntWoW).polygon()) for shape in shapeSetCNT: self.cell.shapes(self.idcl_layer).insert(shape) #Vias #TODO: repair position of the vias cntViaW = cntWoW * 0.9 / 2 # 10% smaller then the wire tr = pya.DCplxTrans(1.0, 45.0, False, pya.DVector(pointP)) cntViaA = pya.DPolygon(pya.DBox(-cntViaW, -cntViaW,\ cntViaW, cntViaW)).transform(tr) tr = pya.DCplxTrans(1.0, 45.0, False, pya.DVector(-pointP)) cntViaB = pya.DPolygon(pya.DBox(-cntViaW, -cntViaW,\ cntViaW, cntViaW)).transformed(tr) self.cell.shapes(self.lvia_layer).insert(cntViaA) self.cell.shapes(self.lvia_layer).insert(cntViaB)
def DrawParametricCurve(cell, layer, brush, xfunc, yfunc, pointnumber, startlength, deltalength, number, lengthlist): ''' 沿参数曲线画空心线, 并每一段间隔变宽一小段 返回曲线参数为0和参数为1的两端的笔刷 [brush0,brush1] lengthlist=[l1,l2,d1,w1,w2] 描述变宽部分, 其内外长度和间隔, 外内宽度 xfunc,yfunc 是曲线参数函数, 参数均匀从取0~1中取pointnumber个, pointnumber尽量取大但是也不要大到让程序变慢 待改进 todo : 智能选点匹配到 IO.pointdistance ''' def getp(ll, p1, p2): bl = p1.distance(p2) dx = p2.x - p1.x dy = p2.y - p1.y k = 1.0 * ll / bl return pya.DPoint(p1.x + k * dx, p1.y + k * dy) # cpts = [ pya.DPoint(xfunc(ii / (pointnumber - 1)), yfunc(ii / (pointnumber - 1))) for ii in range(pointnumber) ] # todo : cpts智能选点匹配到 IO.pointdistance # outpolygons = [] inpolygons = [] # path = pya.DPath(cpts, brush.widout, 0, 0) polygon = path.polygon() outpolygons.append(polygon) path = pya.DPath(cpts, brush.widin, 3, 3) polygon = path.polygon() inpolygons.append(polygon) # l1 = lengthlist[0] l2 = lengthlist[1] d1 = lengthlist[2] w1 = lengthlist[3] w2 = lengthlist[4] # finishnumber = 0 distance = 0 startchecklength = startlength bigstartindex = 0 bigstartp = None smallstartindex = 0 smallstartp = None status = 0 # 1进入大矩阵 2进入小矩阵 3出小矩阵 0出大矩阵 for i, pt in enumerate(cpts[1:-1], 1): delda = pt.distance(cpts[i - 1]) distance = distance + delda # 进入大矩阵 if status == 0 and distance >= startchecklength: status = 1 bigstartindex = i bigstartp = getp(distance - startchecklength, pt, cpts[i - 1]) # 进入小矩阵 if status == 1 and distance >= startchecklength + d1: status = 2 smallstartindex = i smallstartp = getp(distance - startchecklength - d1, pt, cpts[i - 1]) # 出小矩阵 if status == 2 and distance >= startchecklength + d1 + l2: status = 3 smallendindex = i ep = getp(distance - startchecklength - d1 - l2, pt, cpts[i - 1]) temp = [smallstartp] temp.extend(cpts[smallstartindex:smallendindex - 1]) temp.append(ep) path = pya.DPath(temp, w2, 0, 0) polygon = path.polygon() inpolygons.append(polygon) # 出大矩阵 if status == 3 and distance >= startchecklength + l1: status = 0 bigendindex = i ep = getp(distance - startchecklength - l1, pt, cpts[i - 1]) temp = [bigstartp] temp.extend(cpts[bigstartindex:bigendindex - 1]) temp.append(ep) path = pya.DPath(temp, w1, 0, 0) polygon = path.polygon() outpolygons.append(polygon) startchecklength += deltalength finishnumber += 1 if finishnumber == number: break if status == 3: inpolygons.pop() # 状态是最后一个大矩阵未完成, 此时弹出不应有的对应的小矩阵 region = pya.Region([pya.Polygon.from_dpoly(x) for x in outpolygons]) - \ pya.Region([pya.Polygon.from_dpoly(x) for x in inpolygons]) region.transform(pya.ICplxTrans.from_dtrans(brush.DCplxTrans)) BasicPainter.Draw(cell, layer, region) p0 = cpts[0] p1 = cpts[1] brush0 = CavityBrush(pointc=p0, angle=atan2(p0.y - p1.y, p0.x - p1.x), widout=brush.widout, widin=brush.widin, bgn_ext=0) p0 = cpts[-1] p1 = cpts[-2] brush1 = CavityBrush(pointc=p0, angle=atan2(p0.y - p1.y, p0.x - p1.x), widout=brush.widout, widin=brush.widin, bgn_ext=0) return [brush0, brush1]
def to_dtype(self, dbu): Dpath = pya.DPath(self.get_dpoints(), self.width) * dbu Dpath.width = self.width * dbu return Dpath
def layout_ebeam_waveguide_from_points(cell, points_list, radius=None, width=None): """ Takes a list of points and calls SiEPIC Waveguide Pcell The points_list should be a rough shape of the waveguide. The points_list must be a manhattan path! This function will create a smooth waveguide by rounding the trace at every corner with a particular radius. For example, if points_list has three points forming a square of size R, then the output will be a 90-degree arc of radius R. """ from math import floor TECHNOLOGY = EBEAM_TECH if radius is None: radius = WAVEGUIDE_RADIUS if width is None: width = WAVEGUIDE_WIDTH for point in points_list: point.x = floor(point.x * 1000) / 1000 point.y = floor(point.y * 1000) / 1000 for i in range(0, len(points_list) - 1): """ Calculate the x distance and the y distance to the next point. Get the max of the above two numbers Set the other number to zero """ posx = abs(points_list[i + 1].x - points_list[i].x) posy = abs(points_list[i + 1].y - points_list[i].y) if (abs(points_list[i + 1].x - points_list[i].x) > 0 and abs(points_list[i + 1].y - points_list[i].y) > 0): if posx < posy: points_list[i + 1].x = points_list[i].x else: points_list[i + 1].y = points_list[i].y wg_dpath = pya.DPath(points_list, 0.5) layout = cell.layout() with suppress_stdout(): wg_cell = layout.create_cell( "Waveguide", TECHNOLOGY["technology_name"], { "path": wg_dpath, "radius": radius, "width": width, "layers": ["Si"], "widths": [width], "offsets": [0], }, ) layerSi = layout.layer(TECHNOLOGY["Si"]) # let's just get the shapes cell.shapes(layerSi).insert(wg_cell.shapes(layerSi)) layout.delete_cell(wg_cell.cell_index()) return cell