Source code for kqcircuits.util.gui_epr_preview

# This code is part of KQCircuits
# Copyright (C) 2026 IQM Finland Oy
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# https://www.gnu.org/licenses/gpl-3.0.html.
#
# The software distribution should follow IQM trademark policy for open-source software
# (meetiqm.com/iqm-open-source-trademark-policy). IQM welcomes contributions to the code.
# Please see our contribution agreements for individuals (meetiqm.com/iqm-individual-contributor-license-agreement)
# and organizations (meetiqm.com/iqm-organization-contributor-license-agreement).

"""Utility functions for EPR preview markers in the KLayout GUI."""

import importlib
import importlib.util

from kqcircuits.pya_resolver import pya


def _get_epr_instance_trans(layout_view):
    """Returns DCplxTrans of the single currently selected instance in the LayoutView.

    Returns None (without raising) if:
      - no instance is selected
      - more than one instance is selected

    Args:
        layout_view: the active pya.LayoutView
    """
    selected = list(layout_view.each_object_selected())
    if len(selected) != 1:
        return None
    return selected[0].inst().dcplx_trans


def _load_epr_module(element):
    """Dynamically imports and reloads the EPR module that corresponds to this element.

    Returns the module, or None if no EPR module exists for this element.

    Args:
        element: the Element instance whose EPR module should be loaded
    """
    library_name = element.__module__.split(".", maxsplit=1)[0]
    element_name = element.__module__.rsplit(".", maxsplit=1)[-1]
    epr_module_path = f"{library_name}.simulations.epr.{element_name}"
    if not importlib.util.find_spec(epr_module_path):
        return None
    epr_module = importlib.import_module(epr_module_path)
    importlib.reload(epr_module)
    return epr_module


[docs] def draw_epr_markers(element, layout_view): """Draw EPR markers (cross-section cuts and partition regions) into the active LayoutView.""" trans = _get_epr_instance_trans(layout_view) if trans is None: return epr_module = _load_epr_module(element) if epr_module is None: return if element._epr_cross_section_cut: draw_epr_cross_section_cuts(element, layout_view, trans, epr_module) draw_epr_partition_regions(element, layout_view, trans, epr_module)
[docs] def draw_epr_cross_section_cuts(element, layout_view, trans, epr_module): """Draw EPR correction cuts as persistent KLayout Markers. A line marker is drawn for each cut, plus text markers at both endpoints. Args: element: the Element instance layout_view: the active pya.LayoutView trans: DCplxTrans of the selected element instance epr_module: the loaded EPR module for this element """ assert hasattr( epr_module, "correction_cuts" ), f"No 'correction_cuts' function defined in EPR module for {type(element).__name__}" cuts = epr_module.correction_cuts(element) for cut_name, cut in cuts.items(): p1 = trans.trans(cut["p1"]) p2 = trans.trans(cut["p2"]) line_marker = pya.Marker() line_marker.set_path(pya.DPath([p1, p2], 0)) line_marker.line_width = 2 line_marker.color = 0xFF4500 # orange-red layout_view.add_marker(line_marker) for label, pt in [(f"{cut_name}_p1", p1), (f"{cut_name}_p2", p2)]: text_marker = pya.Marker() text_marker.set_text(pya.DText(label, pt.x, pt.y)) text_marker.color = 0xFF4500 layout_view.add_marker(text_marker)
[docs] def draw_epr_partition_regions(element, layout_view, trans, epr_module): """Draw EPR partition regions as persistent KLayout Markers. A filled polygon marker and a centred text marker are drawn for each enabled partition region. Args: element: the Element instance layout_view: the active pya.LayoutView trans: DCplxTrans of the selected element instance epr_module: the loaded EPR module for this element """ assert hasattr( epr_module, "partition_regions" ), f"No 'partition_regions' function defined in EPR module for {type(element).__name__}" partition_regions = epr_module.partition_regions(element) for pr in partition_regions: if not hasattr(element, f"_epr_part_reg_{pr.name}"): continue if not getattr(element, f"_epr_part_reg_{pr.name}"): continue region = pya.Region() if isinstance(pr.region, list): for r in pr.region: region += pya.Region(r.to_itype(element.layout.dbu)) elif isinstance(pr.region, pya.Region): region = pr.region else: region = pya.Region(pr.region.to_itype(element.layout.dbu)) for polygon in region.each(): dpoly = polygon.to_dtype(element.layout.dbu).transformed(trans) poly_marker = pya.Marker() poly_marker.set_polygon(dpoly) poly_marker.dither_pattern = 2 # crosshatch fill — makes region extent clearly visible poly_marker.line_width = 2 poly_marker.color = 0x1E90FF # dodger blue poly_marker.vertex_size = 4 layout_view.add_marker(poly_marker) center = region.bbox().to_dtype(element.layout.dbu).center() center_transformed = trans.trans(center) text_marker = pya.Marker() text_marker.set_text(pya.DText(pr.name, center_transformed.x, center_transformed.y)) text_marker.color = 0x1E90FF layout_view.add_marker(text_marker)