def is_cell_outside(cell_test: Device, cell_ref: Device, tolerance: float = 0): ''' Checks whether cell_test is not overlap with cell_ref. Parameters: --------- cell_test : Device cell_ref : Device tolerance : float (optional) Returns: True (if strictly cell_tot>=cell_test+cell_ref) False (otherwise). Note: if tolerance is not zero, then function returns True if cell_tot>=cell_test+cell_ref-tol in all direction ''' if tolerance == 0: return _is_cell_outside(cell_test, cell_ref) else: shifts_set = np.array([[1, 1], [-1, -1], [1, -1], [-1, 1]]) * tolerance for shift in shifts_set: cell_test.move(destination=shift) if not _is_cell_outside(cell_test, cell_ref): cell_test.move(destination=-shift) return False else: cell_test.move(destination=-shift) else: return True
def route_manhattan90(port1, port2, bendType='circular', layer=0, radius=20): #this is a subroutine of route_manhattan() and should not be used by itself. Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] #create placeholder ports based on the imaginary coordinates we created Total.add_port(name='t1', midpoint=[0, 0], orientation=0, width=width) #CHECK THIS #first quadrant target, route upward if (p2[1] > p1[1]) & (p2[0] > p1[0]): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=-90, width=width) if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff, 0]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) R2 = route_basic(port1=b1.ports[2], port2=Total.ports['t2'], layer=layer) r1 = Total.add_ref(R1) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #fourth quadrant target, route downward if (p2[1] < p1[1]) & (p2[0] > p1[0]): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=90, width=width) if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff, 0]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) R2 = route_basic(port1=b1.ports[2], port2=Total.ports['t2'], layer=layer) r1 = Total.add_ref(R1) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) Total.rotate(angle=port1.orientation, center=p1) Total.move(origin=Total.ports['t1'], destination=port1) return Total
def route_manhattan180(port1, port2, bendType='circular', layer=0, radius=20): #this is a subroutine of route_manhattan() and should not be used by itself. Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] #create placeholder ports based on the imaginary coordinates we created Total.add_port(name='t1', midpoint=[0, 0], orientation=0, width=width) if (port1.orientation != port2.orientation): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=180, width=width) else: Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=0, width=width) if port1.orientation == port2.orientation: #first quadrant target if (p2[1] > p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0], 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #second quadrant target if (p2[1] > p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b2.ports[2], port2=Total.ports['t2'], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=b1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #third quadrant target if (p2[1] < p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b2.ports[2], port2=Total.ports['t2'], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=b1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #fourth quadrant target if (p2[1] < p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0], 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #other port orientations are not supported: elif np.round(np.abs(np.mod(port1.orientation - port2.orientation, 360)), 3) != 180: raise ValueError( '[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)' ) #otherwise, they are 180 degrees apart: else: #first quadrant target if (p2[1] > p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff * 2, 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #second quadrant target if (p2[1] > p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) B3 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=180, theta=-90) B4 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') B3 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=180, direction='cw') B4 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b3 = Total.add_ref(B3) b4 = Total.add_ref(B4) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 4]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) b3.connect(port=b3.ports[1], destination=b2.ports[2]) b3.move([p2[0] - p1[0], 0]) R2 = route_basic(port1=b2.ports[2], port2=b3.ports[1], layer=layer) r2 = Total.add_ref(R2) b4.connect(port=b4.ports[1], destination=b3.ports[2]) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b4.ports[2]) #third quadrant target if (p2[1] < p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) B3 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-180, theta=90) B4 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') B3 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-180, direction='ccw') B4 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b3 = Total.add_ref(B3) b4 = Total.add_ref(B4) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 4]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) b3.connect(port=b3.ports[1], destination=b2.ports[2]) b3.move([p2[0] - p1[0], 0]) R2 = route_basic(port1=b2.ports[2], port2=b3.ports[1], layer=layer) r2 = Total.add_ref(R2) b4.connect(port=b4.ports[1], destination=b3.ports[2]) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b4.ports[2]) #fourth quadrant target if (p2[1] < p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff * 2, 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) Total.rotate(angle=port1.orientation, center=p1) Total.move(origin=Total.ports['t1'], destination=port1) return Total
def route_basic(port1, port2, path_type='sine', width_type='straight', width1=None, width2=None, num_path_pts=99, layer=0): # Assuming they're both Ports for now point_a = np.array(port1.midpoint) if width1 is None: width1 = port1.width point_b = np.array(port2.midpoint) if width2 is None: width2 = port2.width if round(abs(mod(port1.orientation - port2.orientation, 360)), 3) != 180: raise ValueError( '[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)' ) orientation = port1.orientation separation = point_b - point_a # Vector drawn from A to B distance = norm(separation) # Magnitude of vector from A to B rotation = np.arctan2( separation[1], separation[0]) * 180 / pi # Rotation of vector from A to B angle = rotation - orientation # If looking out along the normal of ``a``, the angle you would have to look to see ``b`` forward_distance = distance * cos(angle * pi / 180) lateral_distance = distance * sin(angle * pi / 180) # Create a path assuming starting at the origin and setting orientation = 0 # use the "connect" function later to move the path to the correct location xf = forward_distance yf = lateral_distance if path_type == 'straight': curve_fun = lambda t: [xf * t, yf * t] curve_deriv_fun = lambda t: [xf + t * 0, t * 0] if path_type == 'sine': curve_fun = lambda t: [xf * t, yf * (1 - cos(t * pi)) / 2] curve_deriv_fun = lambda t: [xf + t * 0, yf * (sin(t * pi) * pi) / 2] #if path_type == 'semicircle': # def semicircle(t): # t = np.array(t) # x,y = np.zeros(t.shape), np.zeros(t.shape) # ii = (0 <= t) & (t <= 0.5) # jj = (0.5 < t) & (t <= 1) # x[ii] = (cos(-pi/2 + t[ii]*pi/2))*xf # y[ii] = (sin(-pi/2 + t[ii]*pi/2)+1)*yf*2 # x[jj] = (cos(pi*3/2 - t[jj]*pi)+2)*xf/2 # y[jj] = (sin(pi*3/2 - t[jj]*pi)+1)*yf/2 # return x,y # curve_fun = semicircle # curve_deriv_fun = None if width_type == 'straight': width_fun = lambda t: (width2 - width1) * t + width1 if width_type == 'sine': width_fun = lambda t: (width2 - width1) * (1 - cos(t * pi) ) / 2 + width1 route_path = gdspy.Path(width=width1, initial_point=(0, 0)) route_path.parametric(curve_fun, curve_deriv_fun, number_of_evaluations=num_path_pts, max_points=199, final_width=width_fun, final_distance=None) route_path_polygons = route_path.polygons # Make the route path into a Device with ports, and use "connect" to move it # into the proper location D = Device() D.add_polygon(route_path_polygons, layer=layer) p1 = D.add_port(name=1, midpoint=(0, 0), width=width1, orientation=180) p2 = D.add_port(name=2, midpoint=[forward_distance, lateral_distance], width=width2, orientation=0) D.info['length'] = route_path.length D.rotate(angle=180 + port1.orientation - p1.orientation, center=p1.midpoint) D.move(origin=p1, destination=port1) return D
def text(text='abcd', size=10, justify='left', layer=0, font="DEPLOF"): """ Creates geometries of text Parameters ---------- text : str Text string to be written. size : int or float Size of the text justify : {'left', 'right', 'center'} Justification of the text. layer : int, array-like[2], or set Specific layer(s) to put polygon geometry on. font: str Font face to use. Default DEPLOF does not require additional libraries, otherwise freetype will be used to load fonts. Font can be given either by name (e.g. "Times New Roman"), or by file path. OTF or TTF fonts are supported. Returns ------- t : Device A Device containing the text geometry. """ t = Device('text') xoffset = 0 yoffset = 0 face = font if face == "DEPLOF": scaling = size / 1000 for line in text.split('\n'): l = Device(name='textline') for c in line: ascii_val = ord(c) if c == ' ': xoffset += 500 * scaling elif (33 <= ascii_val <= 126) or (ascii_val == 181): for poly in _glyph[ascii_val]: xpts = np.array(poly)[:, 0] * scaling ypts = np.array(poly)[:, 1] * scaling l.add_polygon([xpts + xoffset, ypts + yoffset], layer=layer) xoffset += (_width[ascii_val] + _indent[ascii_val]) * scaling else: valid_chars = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~µ' warnings.warn('[PHIDL] text(): Warning, some characters ignored, no geometry for character "%s" with ascii value %s. ' \ 'Valid characters: %s' % (chr(ascii_val), ascii_val, valid_chars)) t.add_ref(l) yoffset -= 1500 * scaling xoffset = 0 else: from .font import _get_font_by_name, _get_font_by_file, _get_glyph # Load the font # If we've passed a valid file, try to load that, otherwise search system fonts font = None if (face.endswith(".otf") or face.endswith(".ttf")) and os.path.exists(face): font = _get_font_by_file(face) else: try: font = _get_font_by_name(face) except ValueError: pass if font is None: raise ValueError(( '[PHIDL] Failed to find font: "%s". ' + 'Try specifying the exact (full) path to the .ttf or .otf file. ' + 'Otherwise, it might be resolved by rebuilding the matplotlib font cache' ) % (face)) # Render each character for line in text.split('\n'): l = Device('textline') xoffset = 0 for letter in line: letter_dev = Device("letter") letter_template, advance_x = _get_glyph(font, letter) for poly in letter_template.polygons: letter_dev.add_polygon(poly.polygons, layer=layer) ref = l.add_ref(letter_dev) ref.move(destination=(xoffset, 0)) ref.magnification = size xoffset += size * advance_x ref = t.add_ref(l) ref.move(destination=(0, yoffset)) yoffset -= size justify = justify.lower() for l in t.references: if justify == 'left': pass if justify == 'right': l.xmax = 0 if justify == 'center': l.move(origin=l.center, destination=(0, 0), axis='x') t.flatten() return t