Source code for simvx.core.debug

"""Debug & diagnostic system for SimVX.

Static singleton (like Input) for data collection + DebugNode for rendering.

Usage:
    from simvx.core import Debug

    # Add to scene tree for overlay rendering
    root.add_child(Debug.create_node())

    # Toggle with F3 in-game, or programmatically:
    Debug.enabled = True
"""


from __future__ import annotations

from __future__ import annotations

from ..engine import Input, Node
from ..input.enums import Key  # noqa: I001

__all__ = ["Debug", "DebugNode"]


class _LazyAttr:
    """Descriptor for lazy-initialized class attributes."""

    def __init__(self, factory):
        self._factory = factory
        self._attr = f"_{factory.__name__}"

    def __set_name__(self, owner, name):
        self._attr = f"_{name}"

    def __get__(self, obj, objtype=None):
        val = getattr(objtype, self._attr, None)
        if val is None:
            val = self._factory()
            setattr(objtype, self._attr, val)
        return val


def _make_profiler():
    from .profiler import FrameProfiler

    return FrameProfiler()


def _make_inspector():
    from .ui_inspector import UIInspector

    return UIInspector()


def _make_overlay():
    from .overlay import DebugOverlay

    return DebugOverlay()


[docs] class Debug: """Static debug controller. Disabled by default — zero cost when off.""" enabled: bool = False profiler = _LazyAttr(_make_profiler) ui_inspector = _LazyAttr(_make_inspector) overlay = _LazyAttr(_make_overlay)
[docs] @classmethod def toggle(cls): cls.enabled = not cls.enabled
[docs] @classmethod def create_node(cls) -> DebugNode: """Factory for the debug overlay rendering node.""" return DebugNode(name="DebugOverlay")
[docs] class DebugNode(Node): """Add to scene tree to render debug overlay. F3 toggles, F4 cycles features.""" _cycle_index: int = 0 _CYCLE = [ {"show_profiler": True}, {"show_profiler": True, "show_bounds": True}, {"show_profiler": True, "show_bounds": True, "show_labels": True}, {"show_profiler": True, "show_bounds": True, "show_labels": True, "show_event_log": True}, ]
[docs] def process(self, dt: float): if Input.is_key_just_pressed(Key.F3): Debug.toggle() if Input.is_key_just_pressed(Key.F4) and Debug.enabled: self._cycle_index = (self._cycle_index + 1) % (len(self._CYCLE) + 1) overlay = Debug.overlay # Reset all overlay.show_profiler = False overlay.show_bounds = False overlay.show_labels = False overlay.show_event_log = False overlay.show_hit_target = False # Apply cycle preset if self._cycle_index < len(self._CYCLE): for k, v in self._CYCLE[self._cycle_index].items(): setattr(overlay, k, v)
[docs] def draw(self, renderer): if not Debug.enabled: return tree = self.tree Debug.overlay.draw_overlay(renderer, tree, Debug.profiler, Debug.ui_inspector)