def merge_layers_same_z(self, layers): """ Parameters ---------- layers : list of MaterialLayer Returns ------- res : list of MaterialLayer """ n_layers = len(layers) res_merged = [] for i, li in enumerate(layers): merged = MaterialLayer(li.mask, li.bottom, li.thickness) for j in range(i + 1, n_layers): lj = layers[j] if merged.is_z_same(lj): # perform OR operation on the LayoutData merged = MaterialLayer(merged.mask.or_(lj.mask), merged.bottom, merged.thickness) info(' Merged layers b = {}, t = {}'.format( merged.bottom, merged.top)) res_merged.append(merged) return res_merged
def __repr__(self): n_poly = self.n_poly s = '{} (n_polygons = {})'.format(self.__class__.__name__, n_poly) if n_poly > 0: s += ':' for pi in range(min(2, n_poly)): s += '\n {}'.format(self._polygons[pi]) return s
def __str__(self): n_poly = self.n_poly s = 'LayoutData (n_polygons = {})'.format(n_poly) if n_poly > 0: s += ':' for pi in range(min(2, n_poly)): s += '\n {}'.format(self._polygons[pi]) return s
def __str__(self): n_layers = self.n_layers s = 'MaterialData3D (n_layers = {}, delta = {})'.format( n_layers, self._delta) if n_layers > 0: s += ':' for li in range(min(5, n_layers)): s += '\n {}'.format(str(self._layers[li])) return s
def load(self, layout, cell, box, layer_spec): """ Load all shapes from the layer into self._polygons. The shapes are collected from layer defined by layer_spec. Only shapes touching the box are loaded. Box is effectively a ruler region. Parameters ---------- layout : Layout layout cell : int cell's index box : Box The box of the ruler, enlarged in both directions. Only shapes touching this box will be collected layer_spec : str layer to be used """ info('LD.load(..., box={}, layer_spec={})'.format(box, layer_spec)) ls = string_to_layer_info(layer_spec) # look up the layer index with a given layer_spec in the current layout layer_index = None for li in layout.layer_indices(): info(" li = {}".format(li)) if layout.get_info(li).is_equivalent(ls): info(" layer_index = {}".format(li)) layer_index = li break # collect polygons from the specified layer # all the shapes from the layout will be saved in self._polygons if layer_index is not None: info(" iterations:") shape_iter = layout.begin_shapes_touching(cell, layer_index, box) while not shape_iter.at_end(): shape = shape_iter.shape() if shape.is_polygon() or shape.is_path() or shape.is_box(): self._polygons.append( shape.polygon.transformed(shape_iter.itrans())) shape_iter.next() n_poly = self.n_poly info(' loaded polygon count: {}'.format(n_poly)) if n_poly > 0: info(' loaded polygons:') for pi in range(min(2, n_poly)): info(' {}'.format(self._polygons[pi])) info('LD.load()\n')
def make_mru(self, script): """ Save list of scripts script : str path to the script to be saved """ # Don't maintain MRU if an external list is provided global pyxs_scripts if pyxs_scripts: return # Make a new script list. New script goes first, ... scripts = [script] # ... the rest are taken from the existing list for a in self._mru_actions: if a.script != script: scripts.append(a.script) # make sure the list is filled to the same length while len(scripts) < len(self._mru_actions): scripts.append(None) # update list of actions for i in range(len(self._mru_actions)): self._mru_actions[i].script = scripts[i] # try to save the MRU list to $HOME/.klayout-xsection home = os.getenv("HOME", None) or os.getenv("HOMESHARE", None) if home: fn = home + "\\.klayout-pyxs-scripts" with open(fn, "w") as file: file.write("<pyxs>\n") for a in self._mru_actions: if a.script: file.write("<mru>{}</mru>\n".format(a.script)) file.write("</pyxs>\n")
def __init__(self): app = Application.instance() mw = app.main_window() def _on_triggered_callback(): """ Load pyxs script menu action. Load new .pyxs file and run it. """ view = Application.instance().main_window().current_view() if not view: raise UserWarning("No view open for running the pyxs script") filename = FileDialog.get_open_file_name( "Select cross-section script", "", "XSection Scripts (*.pyxs);;All Files (*)") # run the script and save it if filename.has_value(): self.run_script(filename.value()) self.make_mru(filename.value()) def _XSectionMRUAction_callback(script): """ *.pyxs menu action Load selected .pyxs file and run it. Parameters ---------- script : str """ self.run_script(script) self.make_mru(script) # Create pyxs submenu in Tools menu = mw.menu() if not menu.is_valid("tools_menu.pyxs3D_script_group"): menu.insert_separator("tools_menu.end", "pyxs3D_script_group") menu.insert_menu("tools_menu.end", "pyxs3D_script_submenu", "pyxs3D") # Create Load XSectionpy Script item in XSection (py) global pyxs_script_load_menuhandler pyxs_script_load_menuhandler = MenuHandler("Load pyxs script", _on_triggered_callback) menu.insert_item("tools_menu.pyxs3D_script_submenu.end", "pyxs3D_script_load", pyxs_script_load_menuhandler) menu.insert_separator("tools_menu.pyxs3D_script_submenu.end.end", "pyxs3D_script_mru_group") # Create list of existing pyxs scripts item in pyxs self._mru_actions = [] for i in range(N_PYXS_SCRIPTS_MAX): a = XSectionMRUAction(_XSectionMRUAction_callback) self._mru_actions.append(a) menu.insert_item("tools_menu.pyxs3D_script_submenu.end", "pyxs3D_script_mru{}".format(i), a) a.script = None # try to save the MRU list to $HOME/.klayout-processing-mru i = 0 home = os.getenv("HOME", None) or os.getenv("HOMESHARE", None) global pyxs_scripts if pyxs_scripts: for i, script in enumerate(pyxs_scripts.split(":")): if i < len(self._mru_actions): self._mru_actions[i].script = script elif home: fn = home + "\\.klayout-pyxs-scripts" try: with open(fn, "r") as file: for line in file.readlines(): match = re.match('<mru>(.*)<\/mru>', line) if match: if i < len(self._mru_actions): self._mru_actions[i].script = match.group(1) i += 1 except: pass
def __init__(self, menu_name='pyxs'): self._menu_name = menu_name app = Application.instance() mw = app.main_window() if mw is None: print('none') return def _on_triggered_callback(): """ Load pyxs script menu action. Load new .pyxs file and run it. """ view = Application.instance().main_window().current_view() if not view: MessageBox.critical( "Error", "No view open for creating the cross-" "section from", MessageBox.b_ok(), ) return None filename = FileDialog.get_open_file_name( "Select cross-section script", "", "XSection Scripts (*.pyxs);;All Files (*)", ) # run the script and save it if filename.has_value(): self.run_script(filename.value()) self.make_mru(filename.value()) def _XSectionMRUAction_callback(script): """ *.pyxs menu action Load selected .pyxs file and run it. Parameters ---------- script : str """ self.run_script(script) self.make_mru(script) # Create pyxs submenu in Tools menu = mw.menu() if not menu.is_valid("tools_menu.{}_script_group".format( self._menu_name)): menu.insert_separator("tools_menu.end", "{}_script_group".format(self._menu_name)) menu.insert_menu( "tools_menu.end", "{}_script_submenu".format(self._menu_name), self._menu_name, ) # Create Load XSection.py Script item in XSection (py) # global pyxs_script_load_menuhandler self.pyxs_script_load_menuhandler = MenuHandler( "Load pyxs script", _on_triggered_callback) menu.insert_item( "tools_menu.{}_script_submenu.end".format(self._menu_name), "{}_script_load".format(self._menu_name), self.pyxs_script_load_menuhandler, ) menu.insert_separator( "tools_menu.{}_script_submenu.end.end".format(self._menu_name), "{}_script_mru_group".format(self._menu_name), ) # Create list of existing pyxs scripts item in pyxs self._mru_actions = [] for i in range(N_PYXS_SCRIPTS_MAX): a = XSectionMRUAction(_XSectionMRUAction_callback) self._mru_actions.append(a) menu.insert_item( "tools_menu.{}_script_submenu.end".format(self._menu_name), "{}_script_mru{}".format(self._menu_name, i), a, ) a.script = None # try to save the MRU list to $HOME/.klayout-pyxs-scripts i = 0 home = os.getenv("HOME", None) or os.getenv("HOMESHARE", None) global pyxs_scripts if pyxs_scripts: for i, script in enumerate(pyxs_scripts.split(":")): if i < len(self._mru_actions): self._mru_actions[i].script = script elif home: fn = os.path.join(home, '.klayout-pyxs-scripts') try: with open(fn, "r") as file: for line in file.readlines(): match = re.match('<mru>(.*)<\/mru>', line) if match: if i < len(self._mru_actions): self._mru_actions[i].script = match.group(1) i += 1 except: pass
def produce_geom(self, method, xy, z, into, through, on, taper, bias, mode, buried): """ Parameters ---------- method : str xy : float extension z : float height into : list of MaterialData through : list of MaterialData on : list of MaterialData taper : float bias : float mode : str 'round|square|octagon' buried : Returns ------- d : list of Polygon """ info(' method={}, xy={}, z={},'.format(method, xy, z)) info(' into={}, through={}, on={},'.format(into, through, on)) info(' taper={}, bias={}, mode={}, buried={})'.format( taper, bias, mode, buried)) prebias = bias or 0.0 if xy < 0.0: # if size to be reduced, xy = -xy # prebias += xy # positive prebias if taper: d = z * math.tan(math.pi / 180.0 * taper) prebias += d - xy xy = d # determine the "into" material by joining the data of all "into" specs # or taking "air" if required. # into_data is a list of polygons from all `into` MaterialData # Finally we get a into_data, which is a list of Polygons if into: into_data = [] for i in into: if len(into_data) == 0: into_data = i.data else: into_data = self._ep.boolean_p2p(i.data, into_data, EP.ModeOr) else: # when deposit or grow is selected, into_data is self.air() into_data = self._xs.air().data info(' into_data = {}'.format(into_data)) # determine the "through" material by joining the data of all # "through" specs # through_data is a list of polygons from all `through` MaterialData # Finally we get a through_data, which is a list of Polygons if through: through_data = [] for i in through: if len(through_data) == 0: through_data = i.data else: through_data = self._ep.boolean_p2p( i.data, through_data, EP.ModeOr) info(' through_data = {}'.format(through_data)) # determine the "on" material by joining the data of all "on" specs # on_data is a list of polygons from all `on` MaterialData # Finally we get an on_data, which is a list of Polygons if on: on_data = [] for i in on: if len(on_data) == 0: on_data = i.data else: on_data = self._ep.boolean_p2p(i.data, on_data, EP.ModeOr) info(' on_data = {}'.format(on_data)) pi = int_floor(prebias / self._xs.dbu + 0.5) xyi = int_floor(xy / self._xs.dbu + 0.5) zi = int_floor(z / self._xs.dbu + 0.5) # calculate all edges without prebias and check if prebias # would remove edges if so reduce it mp = self._ep.size_p2p(self._mask_polygons, 0, 0, 2) for p in mp: box = p.bbox() if box.width() <= 2 * pi: pi = int_floor(box.width() / 2.0) - 1 xyi = pi mp = self._ep.size_p2p(self._mask_polygons, -pi, 0, 2) air_masked = self._ep.boolean_p2p(self._air_polygons, mp, EP.ModeAnd) me = (Edges(air_masked) if air_masked else Edges()) - \ (Edges(mp) if mp else Edges()) info('me after creation: {}'.format(me)) # in the "into" case determine the interface region between # self and into if into or through or on: if on: data = on_data elif through: data = through_data else: data = into_data info("data = {}".format(data)) me = (me & Edges(data)) if data else list() # if len(data) == 0: # me = [] # else: # me += Edges(data) info('type(me): {}'.format(type(me))) # list of Edge info('me before operation: {}'.format(me)) d = Region() if taper and xyi > 0: info(' case taper and xyi > 0') kernel_pts = list() kernel_pts.append(Point(-xyi, 0)) kernel_pts.append(Point(0, zi)) kernel_pts.append(Point(xyi, 0)) kernel_pts.append(Point(0, -zi)) kp = Polygon(kernel_pts) for e in me: d.insert(kp.minkowsky_sum(e, False)) elif xyi <= 0: info(' case xyi <= 0') # TODO: there is no way to do that with a Minkowsky sum currently # since polygons cannot be lines except through dirty tricks dz = Point(0, zi) for e in me: d.insert(Polygon([e.p1 - dz, e.p2 - dz, e.p2 + dz, e.p1 + dz])) elif mode in ('round', 'octagon'): info(' case round / octagon') # approximate round corners by 64 points for "round" and # 8 for "octagon" n = 64 if mode == 'round' else 8 da = 2.0 * math.pi / n rf = 1.0 / math.cos(da * 0.5) info(" n = {}, da = {}, rf = {}".format(n, da, rf)) kernel_pts = list() for i in range(n): kernel_pts.append( Point.from_dpoint( DPoint(xyi * rf * math.cos(da * (i + 0.5)), zi * rf * math.sin(da * (i + 0.5))))) info(' n kernel_pts: {}'.format(len(kernel_pts))) info(' kernel_pts: {}'.format(kernel_pts)) kp = Polygon(kernel_pts) for n, e in enumerate(me): d.insert(kp.minkowsky_sum(e, False)) if n > 0 and n % 10 == 0: d.merge() elif mode == 'square': kernel_pts = list() kernel_pts.append(Point(-xyi, -zi)) kernel_pts.append(Point(-xyi, zi)) kernel_pts.append(Point(xyi, zi)) kernel_pts.append(Point(xyi, -zi)) kp = SimplePolygon() kp.set_points(kernel_pts, True) # "raw" - don't optimize away for e in me: d.insert(kp.minkowsky_sum(e, False)) d.merge() info('d after merge: {}'.format(d)) if abs(buried or 0.0) > 1e-6: t = Trans(Point(0, -int_floor(buried / self._xs.dbu + 0.5))) d.transform(t) if through: d -= Region(through_data) d &= Region(into_data) poly = [p for p in d] return poly