def constructors1(self, pointc=pya.DPoint(0, 0), angle=0, widout=20000, widin=10000, bgn_ext=0): tr = pya.DCplxTrans(1, angle, False, pointc) self.edgeout = pya.DEdge(0, widout / 2, 0, -widout / 2).transformed(tr) self.edgein = pya.DEdge(bgn_ext, widin / 2, bgn_ext, -widin / 2).transformed(tr)
def Narrow(self,widout,widin,length=6000): assert(self.end_ext==0) centerx,centery,angle=self.Getinfo()[0:3] tr=pya.DCplxTrans(1,angle,False,centerx,centery) edgeout=pya.DEdge(length,-widout/2,length,widout/2).transformed(tr) edgein=pya.DEdge(length,-widin/2,length,widin/2).transformed(tr) self.regionlistout.append(pya.DPolygon([self.painterout.pointl,self.painterout.pointr,edgeout.p1,edgeout.p2])) self.regionlistin.append(pya.DPolygon([self.painterin.pointl,self.painterin.pointr,edgein.p1,edgein.p2])) self.painterout.Setpoint(edgeout.p1,edgeout.p2) self.painterin.Setpoint(edgein.p1,edgein.p2) return length
def __init__(self,pointc=pya.DPoint(0,8000),angle=0,widout=20000,widin=10000,bgn_ext=0,end_ext=0): self.regionlistout=[] self.regionlistin=[] self.path=lambda painter:None self.bgn_ext=bgn_ext self.end_ext=end_ext tr=pya.DCplxTrans(1,angle,False,pointc) edgeout=pya.DEdge(0,-widout/2,0,widout/2).transformed(tr) edgein=pya.DEdge(bgn_ext,-widin/2,bgn_ext,widin/2).transformed(tr) self.painterout=LinePainter(edgeout.p1,edgeout.p2) self.painterin=LinePainter(edgein.p1,edgein.p2) self.centerlineinfos=[]
def Narrow(self, widout, widin, length=6000): assert (self.end_ext == 0) tr = self.brush.DCplxTrans edgeout = pya.DEdge(length, widout / 2, length, -widout / 2).transformed(tr) edgein = pya.DEdge(length, widin / 2, length, -widin / 2).transformed(tr) self.regionlistout.append( pya.DPolygon([ self.painterout.pointl, self.painterout.pointr, edgeout.p2, edgeout.p1 ])) self.regionlistin.append( pya.DPolygon([ self.painterin.pointl, self.painterin.pointr, edgein.p2, edgein.p1 ])) self.painterout.Setpoint(edgeout.p1, edgeout.p2) self.painterin.Setpoint(edgein.p1, edgein.p2) return length
def _link_move_edges(angles, pts, edges, das, reverse=False): ''' 挪边 ''' deltaangle, maxlength, boundAngle, gridAngle, extendlength, turningr = Interactive._link_define_utils( ) n = len(edges) if not reverse: argsArr = [0, 1, (lambda ii: ii < n - 2), 0, 1, 0, 1, 1, 1] else: argsArr = [n, -1, (lambda ii: ii > 2), -2, -1, -1, -2, -2, -2] initii, iid, condition, dad, dld, angle0d, angled, ptd, eid = argsArr ii = initii last = 0 moved = False while condition(ii): da = das[ii + dad] sda = (da > 0) - (da < 0) da *= sda dl = turningr * tan(da / 180 * pi / 2) ll = pts[ii].distance(pts[ii + dld]) - last - dl last = dl if (ll < 0): # move pt angle0 = angles[ii + angle0d] angle = angles[ii + angled] pt = pts[ii + ptd] ei = ii + eid edges[ei] = pya.DEdge( pt.x + maxlength * cos(angle / 180 * pi) + (extendlength - ll) * cos(angle0 / 180 * pi), pt.y + maxlength * sin(angle / 180 * pi) + (extendlength - ll) * sin(angle0 / 180 * pi), pt.x - maxlength * cos(angle / 180 * pi) + (extendlength - ll) * cos(angle0 / 180 * pi), pt.y - maxlength * sin(angle / 180 * pi) + (extendlength - ll) * sin(angle0 / 180 * pi)) if not all([ edges[ei].crossed_by(edges[ei - 1]), edges[ei].crossed_by(edges[ei + 1]) ]): IO.warning.warning( "paintlib.Interactive.link", "Error : Invalid path leads to no crossing point when adjusting conflict", pya.MessageBox.Ok) return pts[ei] = edges[ei].crossing_point(edges[ei - 1]) pts[ei + 1] = edges[ei].crossing_point(edges[ei + 1]) moved = True ii += iid return moved, angles, pts, edges, das
def test_touches(self): p1 = pya.Polygon(pya.Box(10, 20, 30, 40)) self.assertEqual(p1.touches(pya.Polygon(pya.Box(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.Polygon(pya.Box(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.Polygon(pya.Box(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.Box(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.Box(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.Box(29, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.Edge(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.Edge(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.Edge(29, 20, 40, 50)), True) p1 = pya.SimplePolygon(pya.Box(10, 20, 30, 40)) self.assertEqual(p1.touches(pya.Polygon(pya.Box(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.Polygon(pya.Box(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.Polygon(pya.Box(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.SimplePolygon(pya.Box(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.Box(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.Box(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.Box(29, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.Edge(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.Edge(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.Edge(29, 20, 40, 50)), True) p1 = pya.DPolygon(pya.DBox(10, 20, 30, 40)) self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DBox(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.DBox(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.DBox(29, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.DEdge(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.DEdge(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.DEdge(29, 20, 40, 50)), True) p1 = pya.DSimplePolygon(pya.DBox(10, 20, 30, 40)) self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.DPolygon(pya.DBox(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(30, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(31, 20, 40, 50))), False) self.assertEqual(p1.touches(pya.DSimplePolygon(pya.DBox(29, 20, 40, 50))), True) self.assertEqual(p1.touches(pya.DBox(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.DBox(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.DBox(29, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.DEdge(30, 20, 40, 50)), True) self.assertEqual(p1.touches(pya.DEdge(31, 20, 40, 50)), False) self.assertEqual(p1.touches(pya.DEdge(29, 20, 40, 50)), True)
def TurningInterpolation(self, radius, angle=90): #有待改进 #radius非负向右,负是向左 pass if angle < 0: angle = -angle radius = -radius angle = 90 delta = self.pointr.distance(self.pointl) dx = (self.pointr.x - self.pointl.x) / delta dy = (self.pointr.y - self.pointl.y) / delta dtheta = atan2(dy, dx) * 180 / pi centerx = self.pointr.x + (radius - delta / 2) * dx centery = self.pointr.y + (radius - delta / 2) * dy n = int( ceil(1.3 * (abs(radius) + delta / 2) * angle * pi / 180 / IO.pointdistance) + 2) # rsgn = (radius > 0) - (radius < 0) pointr2 = pya.DPoint(centerx - rsgn * (radius - delta / 2) * dy, centery + rsgn * (radius - delta / 2) * dx) pointl2 = pya.DPoint(centerx - rsgn * (radius + delta / 2) * dy, centery + rsgn * (radius + delta / 2) * dx) pts1 = BasicPainter.arc_NewtonInterpolation(n, abs(radius) + delta / 2) pts2 = BasicPainter.arc_NewtonInterpolation(n, abs(radius) - delta / 2) pts1.extend(reversed(pts2)) arc1 = pya.DPolygon(pts1) trans = pya.DCplxTrans(1, 180 + dtheta + 45 * rsgn, False, centerx, centery) arc1.transform(trans) self.outputlist.append(arc1) self.pointr = pointr2 self.pointl = pointl2 pts3 = BasicPainter.arc_NewtonInterpolation(n, abs(radius)) cpts = [ pya.DEdge(pya.DPoint(), pt).transformed(trans).p2 for pt in pts3 ] if abs(cpts[-1].distance(self.pointr) - delta / 2) < IO.pointdistance: if self.centerlinepts == []: self.centerlinepts = cpts else: self.centerlinepts.extend(cpts[1:]) else: if self.centerlinepts == []: self.centerlinepts = cpts[::-1] else: self.centerlinepts.extend(cpts[-2::-1]) return pi * 0.5 * abs(radius)
def DrawText(self, cell, layer1, textstr, DCplxTrans1): #左下角坐标,每个字宽0.6*倍数高0.7*倍数线宽0.1*倍数 #tr=pya.DCplxTrans(10,0,False,0,0) #倍数,逆时针度数,是否绕x翻转,平移x,平移y tr = pya.CplxTrans.from_dtrans(DCplxTrans1) textstr = "%s" % (textstr) param = {"text": textstr, "layer": layer1, "mag": 1} pv = [] for p in self.TEXT_decl.get_parameters(): if p.name in param: pv.append(param[p.name]) else: pv.append(p.default) text_cell = IO.layout.create_cell("TEXT(\"%s\")" % (textstr)) self.TEXT_decl.produce(IO.layout, [layer1], pv, text_cell) cell.insert(pya.CellInstArray(text_cell.cell_index(), tr)) edge1 = pya.DEdge(len(textstr) * 0.6, 0, len(textstr) * 0.6, 0.7).transformed(DCplxTrans1) return [edge1.p1, edge1.p2]
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_waveguide(cell, layer, points_list, width): """ Lays out a waveguide (or trace) with a certain width with along given points. This is very useful for laying out Bezier curves with or without adiabatic tapers. Args: cell: cell to place into layer: layer to place into. It is done with cell.shapes(layer).insert(pya.Polygon) points_list: list of pya.DPoint (at least 2 points) width (microns): constant or list. If list, then it has to have the same length as points """ if len(points_list) < 2: raise NotImplemented("ERROR: points_list too short") return try: if len(width) == len(points_list): width_iterator = iter(width) else: width_iterator = repeat(width[0]) except TypeError: width_iterator = repeat(width) finally: points_iterator = iter(points_list) dbu = cell.layout().dbu points_low = list() points_high = list() def norm(self): return sqrt(self.x**2 + self.y**2) def cos_angle(point1, point2): return point1 * point2 / norm(point1) / norm(point2) point_width_list = list(zip(points_iterator, width_iterator)) N = len(point_width_list) first_point, first_width = point_width_list[0] next_point, next_width = point_width_list[1] delta = next_point - first_point theta = np.arctan2(delta.y, delta.x) first_high_point = first_point + 0.5 * first_width * \ pya.DPoint(cos(theta + pi / 2), sin(theta + pi / 2)) first_low_point = first_point + 0.5 * first_width * \ pya.DPoint(cos(theta - pi / 2), sin(theta - pi / 2)) points_high.append(first_high_point) points_low.append(first_low_point) for i in range(1, N - 1): prev_point, prev_width = point_width_list[i - 1] point, width = point_width_list[i] next_point, next_width = point_width_list[i + 1] delta_prev = point - prev_point delta_next = next_point - point theta_prev = np.arctan2(delta_prev.y, delta_prev.x) theta_next = np.arctan2(delta_next.y, delta_next.x) next_point_high = (next_point + 0.5 * next_width * pya.DPoint(cos(theta_next + pi / 2), sin(theta_next + pi / 2))) next_point_low = (next_point + 0.5 * next_width * pya.DPoint(cos(theta_next - pi / 2), sin(theta_next - pi / 2))) forward_point_high = (point + 0.5 * width * pya.DPoint(cos(theta_next + pi / 2), sin(theta_next + pi / 2))) forward_point_low = (point + 0.5 * width * pya.DPoint(cos(theta_next - pi / 2), sin(theta_next - pi / 2))) prev_point_high = (prev_point + 0.5 * prev_width * pya.DPoint(cos(theta_prev + pi / 2), sin(theta_prev + pi / 2))) prev_point_low = (prev_point + 0.5 * prev_width * pya.DPoint(cos(theta_prev - pi / 2), sin(theta_prev - pi / 2))) backward_point_high = (point + 0.5 * width * pya.DPoint(cos(theta_prev + pi / 2), sin(theta_prev + pi / 2))) backward_point_low = (point + 0.5 * width * pya.DPoint(cos(theta_prev - pi / 2), sin(theta_prev - pi / 2))) # High point decision next_high_edge = pya.DEdge(forward_point_high, next_point_high) prev_high_edge = pya.DEdge(backward_point_high, prev_point_high) if next_high_edge.crossed_by(prev_high_edge): intersect_point = next_high_edge.crossing_point(prev_high_edge) points_high.append(intersect_point) else: if width * (1 - cos_angle(delta_next, delta_prev)) > dbu: points_high.append(backward_point_high) points_high.append(forward_point_high) else: points_high.append((backward_point_high + forward_point_high) * 0.5) # Low point decision next_low_edge = pya.DEdge(forward_point_low, next_point_low) prev_low_edge = pya.DEdge(backward_point_low, prev_point_low) if next_low_edge.crossed_by(prev_low_edge): intersect_point = next_low_edge.crossing_point(prev_low_edge) points_low.append(intersect_point) else: if width * (1 - cos_angle(delta_next, delta_prev)) > dbu: points_low.append(backward_point_low) points_low.append(forward_point_low) else: points_low.append((backward_point_low + forward_point_low) * 0.5) last_point, last_width = point_width_list[-1] point, width = point_width_list[-2] delta = last_point - point theta = np.arctan2(delta.y, delta.x) final_high_point = last_point + 0.5 * last_width * \ pya.DPoint(cos(theta + pi / 2), sin(theta + pi / 2)) final_low_point = last_point + 0.5 * last_width * \ pya.DPoint(cos(theta - pi / 2), sin(theta - pi / 2)) if (final_high_point - points_high[-1]) * delta > 0: points_high.append(final_high_point) if (final_low_point - points_low[-1]) * delta > 0: points_low.append(final_low_point) # Append point only if change in direction is less than 120 degrees. def smooth_append(point_list, point): if point_list is None: print(point) if len(point_list) < 1: point_list.append(point) return point_list elif len(point_list) < 2: curr_edge = point - point_list[-1] if norm(curr_edge) > dbu: point_list.append(point) return point_list curr_edge = point - point_list[-1] if norm(curr_edge) > dbu: prev_edge = point_list[-1] - point_list[-2] if cos_angle(curr_edge, prev_edge) > cos(120 / 180 * pi): point_list.append(point) return point_list polygon_points = points_low + list(reversed(points_high)) polygon_points = list(reduce(smooth_append, polygon_points, list())) poly = pya.DPolygon(polygon_points) cell.shapes(layer).insert(poly)
def waveguide_dpolygon(points_list, width, dbu, smooth=True): """ Returns a polygon outlining a waveguide. This was updated over many iterations of failure. It can be used for both smooth optical waveguides or DC metal traces with corners. It is better than klayout's Path because it can have varying width. Args: points_list: list of pya.DPoint (at least 2 points) width (microns): constant or list. If list, then it has to have the same length as points dbu: dbu: typically 0.001, only used for accuracy calculations. smooth: tries to smooth final polygons to avoid very sharp edges (greater than 130 deg) Returns: polygon DPoints """ if len(points_list) < 2: raise NotImplementedError("ERROR: points_list too short") return def norm(self): return sqrt(self.x**2 + self.y**2) try: if len(width) == len(points_list): width_iterator = iter(width) elif len(width) == 2: # assume width[0] is initial width and # width[1] is final width # interpolate with points_list L = curve_length(points_list) distance = 0 widths_list = [width[0]] widths_func = lambda t: (1 - t) * width[0] + t * width[1] old_point = points_list[0] for point in points_list[1:]: distance += norm(point - old_point) old_point = point widths_list.append(widths_func(distance / L)) width_iterator = iter(widths_list) else: width_iterator = repeat(width[0]) except TypeError: width_iterator = repeat(width) finally: points_iterator = iter(points_list) points_low = list() points_high = list() def cos_angle(point1, point2): cos_angle = point1 * point2 / norm(point1) / norm(point2) # ensure it's between -1 and 1 (nontrivial numerically) if abs(cos_angle) > 1: return cos_angle / abs(cos_angle) else: return cos_angle def sin_angle(point1, point2): return sin(np.arccos(cos_angle(point1, point2))) point_width_list = list(zip(points_iterator, width_iterator)) N = len(point_width_list) first_point, first_width = point_width_list[0] next_point, next_width = point_width_list[1] delta = next_point - first_point theta = np.arctan2(delta.y, delta.x) first_high_point = first_point + 0.5 * first_width * \ pya.DPoint(cos(theta + pi / 2), sin(theta + pi / 2)) first_low_point = first_point + 0.5 * first_width * \ pya.DPoint(cos(theta - pi / 2), sin(theta - pi / 2)) points_high.append(first_high_point) points_low.append(first_low_point) for i in range(1, N - 1): prev_point, prev_width = point_width_list[i - 1] point, width = point_width_list[i] next_point, next_width = point_width_list[i + 1] delta_prev = point - prev_point delta_next = next_point - point theta_prev = np.arctan2(delta_prev.y, delta_prev.x) theta_next = np.arctan2(delta_next.y, delta_next.x) next_point_high = (next_point + 0.5 * next_width * pya.DPoint(cos(theta_next + pi / 2), sin(theta_next + pi / 2))) next_point_low = (next_point + 0.5 * next_width * pya.DPoint(cos(theta_next - pi / 2), sin(theta_next - pi / 2))) forward_point_high = (point + 0.5 * width * pya.DPoint(cos(theta_next + pi / 2), sin(theta_next + pi / 2))) forward_point_low = (point + 0.5 * width * pya.DPoint(cos(theta_next - pi / 2), sin(theta_next - pi / 2))) prev_point_high = (prev_point + 0.5 * prev_width * pya.DPoint(cos(theta_prev + pi / 2), sin(theta_prev + pi / 2))) prev_point_low = (prev_point + 0.5 * prev_width * pya.DPoint(cos(theta_prev - pi / 2), sin(theta_prev - pi / 2))) backward_point_high = (point + 0.5 * width * pya.DPoint(cos(theta_prev + pi / 2), sin(theta_prev + pi / 2))) backward_point_low = (point + 0.5 * width * pya.DPoint(cos(theta_prev - pi / 2), sin(theta_prev - pi / 2))) fix_angle = lambda theta: ((theta + pi) % (2 * pi)) - pi # High point decision next_high_edge = pya.DEdge(forward_point_high, next_point_high) prev_high_edge = pya.DEdge(backward_point_high, prev_point_high) if next_high_edge.crossed_by(prev_high_edge): intersect_point = next_high_edge.crossing_point(prev_high_edge) points_high.append(intersect_point) else: cos_dd = cos_angle(delta_next, delta_prev) if width * (1 - cos_dd) > dbu and fix_angle(theta_next - theta_prev) < 0: points_high.append(backward_point_high) points_high.append(forward_point_high) else: points_high.append((backward_point_high + forward_point_high) * 0.5) # Low point decision next_low_edge = pya.DEdge(forward_point_low, next_point_low) prev_low_edge = pya.DEdge(backward_point_low, prev_point_low) if next_low_edge.crossed_by(prev_low_edge): intersect_point = next_low_edge.crossing_point(prev_low_edge) points_low.append(intersect_point) else: cos_dd = cos_angle(delta_next, delta_prev) if width * (1 - cos_dd) > dbu and fix_angle(theta_next - theta_prev) > 0: points_low.append(backward_point_low) points_low.append(forward_point_low) else: points_low.append((backward_point_low + forward_point_low) * 0.5) last_point, last_width = point_width_list[-1] point, width = point_width_list[-2] delta = last_point - point theta = np.arctan2(delta.y, delta.x) final_high_point = last_point + 0.5 * last_width * \ pya.DPoint(cos(theta + pi / 2), sin(theta + pi / 2)) final_low_point = last_point + 0.5 * last_width * \ pya.DPoint(cos(theta - pi / 2), sin(theta - pi / 2)) if (final_high_point - points_high[-1]) * delta > 0: points_high.append(final_high_point) if (final_low_point - points_low[-1]) * delta > 0: points_low.append(final_low_point) # Append point only if change in direction is less than 130 degrees. def smooth_append(point_list, point): if len(point_list) < 1: point_list.append(point) return point_list elif len(point_list) < 2: curr_edge = point - point_list[-1] if norm(curr_edge) >= dbu: point_list.append(point) return point_list curr_edge = point - point_list[-1] if norm(curr_edge) >= dbu: prev_edge = point_list[-1] - point_list[-2] if norm(prev_edge) * abs(sin_angle(curr_edge + prev_edge, prev_edge)) > dbu: if smooth: # avoid corners when smoothing if cos_angle(curr_edge, prev_edge) > cos(130 / 180 * pi): point_list.append(point) else: # edge case when there is prev_edge is small and # needs to be deleted to get rid of the corner if norm(curr_edge) > norm(prev_edge): point_list[-1] = point else: point_list.append(point) # avoid unnecessary points else: point_list[-1] = point return point_list if debug and False: print("Points to be smoothed:") for point, width in point_width_list: print(point, width) smooth_points_high = list(reduce(smooth_append, points_high, list())) smooth_points_low = list(reduce(smooth_append, points_low, list())) # smooth_points_low = points_low # polygon_dpoints = points_high + list(reversed(points_low)) # polygon_dpoints = list(reduce(smooth_append, polygon_dpoints, list())) polygon_dpoints = smooth_points_high + list(reversed(smooth_points_low)) return DSimplePolygon(polygon_dpoints)
def _get_region_cell_port(region,brush,layerlist,boxx,boxy,offsetx=0,offsety=0,deltaangle=0,absx=None,absy=None,portbrushs=None,transmissionlines=None,crossoverLayerList=False):#added crossoverLayerList '''先把图像逆时针转deltaangle角度后沿着平直截取''' # if deltaangle>46 or deltaangle<-46:raise RuntimeError('deltaangle more than 45 degree') if absx==None or absy==None: _pb=[region.bbox()] if type(brush)!=type(None): painter=CavityPainter(brush) painter.Run(lambda painter:painter._Straight(10)+painter._Straight(-10)) _pb.append(painter.Output_Region().bbox()) _pbr=pya.Region() for ii in _pb:_pbr.insert(ii) pc=_pbr.bbox().center() else: pc=pya.DPoint(absx,absy) pc=pya.DPoint(pc.x+offsetx,pc.y+offsety) def tr_to(obj,itr=False): trs=[pya.DCplxTrans(1,-deltaangle,False,pc.x,pc.y)] if itr:trs=[pya.ICplxTrans.from_dtrans(tr) for tr in trs] for tr in trs: obj=obj.transformed(tr) return obj def tr_back(obj,itr=False): trs=[pya.DCplxTrans(1,0,False,-pc.x,-pc.y), pya.DCplxTrans(1,deltaangle,False,0,0)] if itr:trs=[pya.ICplxTrans.from_dtrans(tr) for tr in trs] for tr in trs: obj=obj.transformed(tr) return obj box=tr_to(pya.Box(-boxx/2,-boxy/2,boxx/2,boxy/2),itr=True) # _,inregion=Interactive.cut(layerlist=layerlist,layermod='in',box=box,mergeanddraw=False) inregion=tr_back(inregion,itr=True) outregion=pya.Region(pya.Box(-boxx/2,-boxy/2,boxx/2,boxy/2)) if type(brush)!=type(None): painter=CavityPainter(tr_back(brush)) painter.Run(lambda painter:painter._Straight(-boxx-boxy)) painter.Run(lambda painter:painter.Straight(2*boxx+2*boxy)) inregion=inregion+painter.Output_Region() #计算端口 ports=[] edges=[ #左,上,右,下 pya.DEdge(-boxx/2,-boxy/2,-boxx/2,+boxy/2), pya.DEdge(-boxx/2,+boxy/2,+boxx/2,+boxy/2), pya.DEdge(+boxx/2,+boxy/2,+boxx/2,-boxy/2), pya.DEdge(+boxx/2,-boxy/2,-boxx/2,-boxy/2) ] # if type(brush)!=type(None): br=painter.brush pt=pya.DPoint(br.centerx,br.centery) angle=br.angle edge=pya.DEdge(pt.x,pt.y,pt.x-(2*boxx+2*boxy)*cos(angle/180*pi),pt.y-(2*boxx+2*boxy)*sin(angle/180*pi)) ports.extend([ee.crossing_point(edge) for ee in edges if ee.crossed_by(edge)]) if transmissionlines!=None: for transmissionline in transmissionlines: for info in transmissionline: cpts=info[0] pt0=cpts[0] for pt in cpts[1:]: edge=tr_back(pya.DEdge(pt0,pt)) pt0=pt ports.extend([ee.crossing_point(edge) for ee in edges if ee.crossed_by(edge)]) if portbrushs!=None: portbrushs=[tr_back(brush) for brush in portbrushs] ports.extend([pya.DPoint(brush.centerx,brush.centery) for brush in portbrushs]) ports=[[pt.x,pt.y] for pt in ports if abs(pt.x)<boxx/2+10 and abs(pt.y)<boxy/2+10] final_region,cell=Interactive._merge_and_draw(outregion,inregion,pya.CplxTrans(1,-deltaangle,False,pc.x,pc.y)) if crossoverLayerList!=False: final_region,cell=Interactive._merge_and_draw(outregion,inregion,pya.CplxTrans(1,-deltaangle,False,pc.x,pc.y),False) ######################added crossover_region_list########### # crossover_region_list=[] # if crossoverLayerList!=False: #for layerlist in crossoverLayerList: # _,crossover_inregion=Interactive.cut(layerlist=layerlist,layermod='in',box=box,mergeanddraw=False)#[1] #crossover_region_list.append(Interactive._merge_and_draw(outregion,crossover_inregion,None,False)[0]) # final_region,cell=Interactive._merge_and_draw(outregion,crossover_inregion,None,False) #if crossoverLayerList!=False: # final_region=[] # final_region=crossover_region return final_region,cell,ports
def constructors3(self, pointoutl, pointinl, pointinr, pointoutr): self.edgeout = pya.DEdge(pointoutl, pointoutr) self.edgein = pya.DEdge(pointinl, pointinr)
def constructors2(self, edgeout=pya.DEdge(0, 20000 / 2, 0, -20000 / 2), edgein=pya.DEdge(0, 0, 0, 0)): self.edgeout = edgeout self.edgein = edgein
def _link_scan_angles(brush1, brush2, spts): ''' 扫角度 ''' deltaangle, maxlength, boundAngle, gridAngle, extendlength, turningr = Interactive._link_define_utils( ) #起点 angles = [boundAngle(brush1.angle)] pts = [pya.DPoint(brush1.centerx, brush1.centery)] edges = [ pya.DEdge(pts[0].x, pts[0].y, pts[0].x + maxlength * cos(angles[0] / 180 * pi), pts[0].y + maxlength * sin(angles[0] / 180 * pi)) ] das = [] #这四个数组最终长度: n个角度, n+1个点, n条边, n-1个角度变化 #经过的点 for ii in range(1, len(spts)): pt = spts[ii] pt0 = spts[ii - 1] angle0 = angles[-1] edge0 = edges[-1] angle = gridAngle(atan2(pt.y - pt0.y, pt.x - pt0.x) / pi * 180) da = boundAngle(angle0 - angle) # 默认的右转是顺时针, 计算出的逆时针的角度要取反 if (da == 0): continue if (da == 180): IO.warning.warning("paintlib.Interactive.link", "Error : Turn 180 degrees", pya.MessageBox.Ok) return edge = pya.DEdge(pt.x + maxlength * cos(angle / 180 * pi), pt.y + maxlength * sin(angle / 180 * pi), pt.x - maxlength * cos(angle / 180 * pi), pt.y - maxlength * sin(angle / 180 * pi)) if not edge.crossed_by(edge0): if len(das) == 0: continue print('point ', ii) print(angle) print(angle0) IO.warning.warning( "paintlib.Interactive.link", "Error : Invalid path leads to no crossing point", pya.MessageBox.Ok) return angles.append(angle) das.append(da) pts.append(edge.crossing_point(edge0)) edges.append(edge) #终点 if True: angle0 = angles[-1] edge0 = edges[-1] angle = boundAngle(brush2.angle + 180) pt = pya.DPoint(brush2.centerx, brush2.centery) _angle = gridAngle(angle) if (_angle == angle0 and len(das) > 0): # 规整化后与终点平行, 放弃最后一个点, 从而不再平行 angles.pop() das.pop() pts.pop() edges.pop() angle0 = angles[-1] edge0 = edges[-1] da = boundAngle(angle0 - angle) _da = boundAngle(angle0 - _angle) if (_da == 180): IO.warning.warning("paintlib.Interactive.link", "Error : Turn 180 degrees", pya.MessageBox.Ok) return lastpt = pt edge = pya.DEdge(pt.x, pt.y, pt.x - maxlength * cos(angle / 180 * pi), pt.y - maxlength * sin(angle / 180 * pi)) if (angle == angle0 and len(das) == 0): # 只有起点和终点且平行 dis = edge0.distance(pt) if abs(dis) < 10: # 直连无需转弯 pass else: # 需转弯, 此处多生成两个点和两个角度, 如果dis小于2-sqrt(2)的转弯半径, 生成路径时会报错 pt0 = pts[-1] dse = pt0.distance(pt) dp = sqrt(dse**2 - dis**2) l1 = (dp - dis) / 2 if dis < 0: das.extend([-45, 45]) angles.extend([angle + 45, angle]) else: das.extend([45, -45]) angles.extend([angle - 45, angle]) pt1 = pya.DPoint(pt0.x + l1 * cos(angle / 180 * pi), pt0.y + l1 * sin(angle / 180 * pi)) pt2 = pya.DPoint(pt.x - l1 * cos(angle / 180 * pi), pt.y - l1 * sin(angle / 180 * pi)) pts.extend([pt1, pt2]) edges.extend([pya.DEdge(pt1, pt2), edge]) else: angles.append(angle) das.append(da) if not edge.crossed_by(edge0): print('brush2') print(angle) print(angle0) IO.warning.warning( "paintlib.Interactive.link", "Error : Invalid path leads to no crossing point", pya.MessageBox.Ok) return pts.append(edge.crossing_point(edge0)) edges.append(edge) pts.append(lastpt) return angles, pts, edges, das
def link(brush1=None, brush2=None): ''' 输入两个CavityBrush作为参数, 并点击图中的一个路径, 生成一个连接两个brush的路径的函数 缺省时会在Interactive.searchr内搜索最近的brush 第二个brush可为None, 此时取path的终点作为路径终点 ''' deltaangle = Interactive.deltaangle maxlength = Interactive.maxlength spts = Interactive._pts_path_selected() if spts == False: return if brush1 == None: brush1 = Interactive._get_nearest_brush(spts[0].x, spts[0].y) if brush2 == None: brush2 = Interactive._get_nearest_brush(spts[-1].x, spts[-1].y) if not isinstance(brush1, CavityBrush): pya.MessageBox.warning("paintlib.Interactive.link", "Argument 1 must be CavityBrush", pya.MessageBox.Ok) return if not isinstance(brush2, CavityBrush) and brush2 != None: pya.MessageBox.warning("paintlib.Interactive.link", "Argument 2 must be CavityBrush or None", pya.MessageBox.Ok) return angles = [brush1.angle] pts = [pya.DPoint(brush1.centerx, brush1.centery)] edges = [ pya.DEdge(pts[0].x, pts[0].y, pts[0].x + maxlength * cos(angles[0] / 180 * pi), pts[0].y + maxlength * sin(angles[0] / 180 * pi)) ] das = [] lastpt = None for ii in range(1, len(spts)): pt = spts[ii] pt0 = spts[ii - 1] angle0 = angles[-1] edge0 = edges[-1] angle = atan2(pt.y - pt0.y, pt.x - pt0.x) / pi * 180 angle = round(angle / deltaangle) * deltaangle angle = 0 if angle == 360.0 else angle if (angle == angle0): continue da = -((angle + 3600 - angle0) % 360) if (da == -180): pya.MessageBox.warning("paintlib.Interactive.link", "Error : Turn 180 degrees", pya.MessageBox.Ok) return if (da < -180): da = 360 + da lastpt = [pt.x, pt.y] angles.append(angle) edge = pya.DEdge(pt.x + maxlength * cos(angle / 180 * pi), pt.y + maxlength * sin(angle / 180 * pi), pt.x - maxlength * cos(angle / 180 * pi), pt.y - maxlength * sin(angle / 180 * pi)) das.append(da) if not edge.crossed_by(edge0): print('point ', ii) print(angle) print(angle0) pya.MessageBox.warning( "paintlib.Interactive.link", "Error : Invalid path leads to no crossing point", pya.MessageBox.Ok) return pts.append(edge.crossing_point(edge0)) edges.append(edge) if (brush2 != None): angle0 = angles[-1] edge0 = edges[-1] angle = brush2.angle + 180 pt = pya.DPoint(brush2.centerx, brush2.centery) _angle = round(angle / deltaangle) * deltaangle _angle = 0 if _angle == 360.0 else _angle if (_angle == angle0): angles.pop() das.pop() pts.pop() edges.pop() angle0 = angles[-1] edge0 = edges[-1] da = -((angle + 3600 - angle0) % 360) _da = -((_angle + 3600 - angle0) % 360) if (_da == -180): pya.MessageBox.warning("paintlib.Interactive.link", "Error : Turn 180 degrees", pya.MessageBox.Ok) return if (da < -180): da = 360 + da lastpt = [pt.x, pt.y] edge = pya.DEdge(pt.x, pt.y, pt.x - maxlength * cos(angle / 180 * pi), pt.y - maxlength * sin(angle / 180 * pi)) angles.append(angle) das.append(da) if not edge.crossed_by(edge0): print('brush2') print(angle) print(angle0) pya.MessageBox.warning( "paintlib.Interactive.link", "Error : Invalid path leads to no crossing point", pya.MessageBox.Ok) return pts.append(edge.crossing_point(edge0)) edges.append(edge) pts.append(pya.DPoint(lastpt[0], lastpt[1])) ss = Interactive._generatepath(pts, das) print('##################################') print(ss) print('##################################') Interactive._show_path(brush1, ss)