예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
    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')
예제 #6
0
    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")
예제 #7
0
    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
예제 #8
0
    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
예제 #9
0
    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