def background(self): """ Returns ------- res : Box The extended box including the ruler. """ x1 = self._line_dbu.p1.x y1 = self._line_dbu.p1.y x2 = self._line_dbu.p2.x y2 = self._line_dbu.p2.y if x2 < x1: x1, x2 = x2, x1 if y2 < y1: y1, y2 = y2, y1 x1 -= self._extend y1 -= self._extend x2 += self._extend y2 += self._extend return Box( Point(x1 - self._delta * 5, y1 - self._delta * 5), Point(x2 + self._delta * 5, y2 + self._delta * 5), )
def _setup(self, p1, p2): """ Parameters ---------- p1 : Point first point of the ruler p2 : Point second point of the ruler """ # locate the layout app = Application.instance() view = app.main_window().current_view() # LayoutView if not view: MessageBox.critical( "Error", "No view open for creating the cross-" "section from", MessageBox.b_ok(), ) return False cv = view.cellview(view.active_cellview_index()) # CellView if not cv.is_valid(): MessageBox.critical("Error", "The selected layout is not valid", MessageBox.b_ok()) return False self._cv = cv # CellView self._layout = cv.layout() # Layout self._dbu = self._layout.dbu self._cell = cv.cell_index # int # get the start and end points in database units and micron p1_dbu = Point.from_dpoint(p1 * (1.0 / self._dbu)) p2_dbu = Point.from_dpoint(p2 * (1.0 / self._dbu)) self._line_dbu = Edge(p1_dbu, p2_dbu) # Edge describing the ruler # initialize height and depth self._extend = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu self._delta = 10 self._height = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu self._depth = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu self._below = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu info(' XSG._dbu is: {}'.format(self._dbu)) info(' XSG._extend is: {}'.format(self._extend)) info(' XSG._delta is: {}'.format(self._delta)) info(' XSG._height is: {}'.format(self._height)) info(' XSG._depth is: {}'.format(self._depth)) info(' XSG._below is: {}'.format(self._below)) return True
def layer(self, layer_spec): """ Fetches an input layer from the original layout. Parameters ---------- layer_spec : str Returns ------- ld : LayerData """ ld = LayoutData([], self) # empty # collect shapes from the corresponding layer into ld._polygons ld.load(self._layout, self._cell, self._line_dbu.bbox().enlarge( Point(self._extend, self._extend)), layer_spec) return ld
def planarize(self, into=[], downto=[], less=None, to=None, **kwargs): """Planarization """ if not into: raise ValueError("'planarize' requires an 'into' argument") into = make_iterable(into) for i in into: # should be MaterialData @@@ if not isinstance(i, MaterialData3D): raise TypeError("'planarize' method: 'into' expects " "a material parameter or an array " "of such") downto = make_iterable(downto) for i in downto: # should be MaterialData @@@ if not isinstance(i, MaterialData3D): raise TypeError("'planarize' method: 'downto' expects " "a material parameter or an array " "of such") if less is not None: less = int_floor(0.5 + float(less) / self.dbu) if to is not None: to = int_floor(0.5 + float(to) / self.dbu) if downto: downto_data = [] for d in downto: if len(downto_data) == 0: downto_data = d.data else: downto_data = self._lp.boolean_p2p(d.data, downto_data, LP.ModeOr) # determine upper bound of material if downto_data: raise NotImplementedError('downto not implemented yet') for p in downto_data: yt = p.bbox().top yb = p.bbox().bottom to = to or yt if not self._flipped: to = max([to, yt, yb]) else: to = min([to, yt, yb]) elif into and not to: raise NotImplementedError('into and not to not implemented yet') # determine upper bound of our material for i in into: for p in i.data: yt = p.bbox().top yb = p.bbox().bottom to = to or yt if not self._flipped: to = max([to, yt, yb]) else: to = min([to, yt, yb]) if to: less = less or 0 if self._flipped: removed_box = MaterialLayer( LayoutData([ Polygon( self._box_dbu.enlarged( Point(self._extend, self._extend))) ], self), -(self.depth_dbu + self.below_dbu), (to + less) + self.depth_dbu + self.below_dbu) else: removed_box = MaterialLayer( LayoutData([ Polygon( self._box_dbu.enlarged( Point(self._extend, self._extend))) ], self), to - less, self.height_dbu - (to - less)) rem = MaterialData3D([], self, self._delta) for i in into: rem.add(i.and_([removed_box])) i.sub([removed_box]) self.air().add(rem) self.air().close_gaps()
def _setup(self): # locate the layout app = Application.instance() view = app.main_window().current_view() # LayoutView if not view: MessageBox.critical( "Error", "No view open for creating the cross " "section from", MessageBox.b_ok()) return False # locate the (single) ruler rulers = [] n_rulers = 0 for a in view.each_annotation(): # Use only rulers with "plain line" style # print(a.style) # print(Annotation.StyleLine) # if a.style == Annotation.StyleLine: rulers.append(a) n_rulers += 1 # if n_rulers == 0 or n_rulers >= 2: # MessageBox.info("No rulers", # "Number of rulers is not equal to one. " # "Will be exporting the whole layout", # pya.MessageBox.b_ok()) # if n_rulers == 1: # MessageBox.info( # "Box export", "One ruler is present for the cross " # "section line. Will be exporting only shapes in the box", # pya.MessageBox.b_ok()) cv = view.cellview(view.active_cellview_index()) # CellView if not cv.is_valid(): MessageBox.critical("Error", "The selected layout is not valid", MessageBox.b_ok()) return False self._cv = cv # CellView self._layout = cv.layout() # Layout self._dbu = self._layout.dbu self._cell = cv.cell_index # int if n_rulers == 1: # get the start and end points in database units and micron p1_dbu = Point.from_dpoint(rulers[0].p1 * (1.0 / self._dbu)) p2_dbu = Point.from_dpoint(rulers[0].p2 * (1.0 / self._dbu)) self._box_dbu = Box(p1_dbu, p2_dbu) # box describing the ruler else: # TODO: choose current cell, not top cell top_cell = self._layout.top_cell() p1_dbu = (top_cell.bbox().p1 * (1.0 / self._dbu)).dup() p1_dbu = top_cell.bbox().p1.dup() p2_dbu = (top_cell.bbox().p2 * (1.0 / self._dbu)).dup() p2_dbu = top_cell.bbox().p2.dup() self._box_dbu = Box(p1_dbu, p2_dbu) # box describing the top cell info('XSG._box_dbu to be used is: {}'.format(self._box_dbu)) # create a new layout for the output cv = app.main_window().create_layout(1) cell = cv.layout().add_cell("XSECTION") self._target_view = app.main_window().current_view() self._target_view.select_cell(cell, 0) self._target_layout = cv.layout() self._target_layout.dbu = self._dbu self._target_cell = cell # initialize height and depth self._extend = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu self._delta = 10 self._height = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu self._depth = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu self._below = int_floor(2.0 / self._dbu + 0.5) # 2 um in dbu info(' XSG._dbu is: {}'.format(self._dbu)) info(' XSG._extend is: {}'.format(self._extend)) info(' XSG._delta is: {}'.format(self._delta)) info(' XSG._height is: {}'.format(self._height)) info(' XSG._depth is: {}'.format(self._depth)) info(' XSG._below is: {}'.format(self._below)) return True
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