scena 1.7.2

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
mod analysis;

use serde::{Deserialize, Serialize};

use crate::diagnostics::{Diagnostic, RendererStats};
use crate::scene::SceneInspectionReportV1;

use super::Renderer;

pub const VISIBILITY_DIAGNOSIS_SCHEMA_V1: &str = "scena.visibility_diagnosis.v1";

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VisibilityDiagnosisOptions {
    detail: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VisibilityDiagnosisReportV1 {
    pub schema: String,
    pub ok: bool,
    pub target: VisibilityDiagnosisTargetV1,
    pub reasons: Vec<VisibilityDiagnosisReasonV1>,
    pub fixes: Vec<VisibilityDiagnosisFixV1>,
    pub summary: VisibilityDiagnosisSummaryV1,
    #[serde(default)]
    pub evidence: Vec<VisibilityDiagnosisEvidenceV1>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct VisibilityDiagnosisTargetV1 {
    pub kind: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub handle: Option<u64>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VisibilityDiagnosisReasonV1 {
    pub code: String,
    pub severity: String,
    pub confidence: String,
    pub auto_fixable: bool,
    #[serde(default)]
    pub affected_handles: Vec<u64>,
    pub message: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VisibilityDiagnosisFixV1 {
    pub action: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub target_handle: Option<u64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub patch: Option<serde_json::Value>,
    pub risk: String,
    pub help: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct VisibilityDiagnosisSummaryV1 {
    pub visible_nodes: usize,
    pub hidden_nodes: usize,
    pub visible_drawables: usize,
    pub culled_objects: u64,
    pub not_prepared: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VisibilityDiagnosisEvidenceV1 {
    pub kind: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub handle: Option<u64>,
    pub message: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub value: Option<serde_json::Value>,
}

impl VisibilityDiagnosisOptions {
    pub const fn summary() -> Self {
        Self { detail: false }
    }

    pub const fn detail() -> Self {
        Self { detail: true }
    }

    pub const fn detail_enabled(&self) -> bool {
        self.detail
    }
}

impl Default for VisibilityDiagnosisOptions {
    fn default() -> Self {
        Self::summary()
    }
}

impl VisibilityDiagnosisReportV1 {
    pub fn from_inspection(
        inspection: &SceneInspectionReportV1,
        stats: RendererStats,
        target_handle: Option<u64>,
        options: VisibilityDiagnosisOptions,
        prepared: bool,
    ) -> Self {
        Self::from_inspection_with_diagnostics(
            inspection,
            stats,
            target_handle,
            options,
            prepared,
            &[],
        )
    }

    pub fn from_inspection_with_diagnostics(
        inspection: &SceneInspectionReportV1,
        stats: RendererStats,
        target_handle: Option<u64>,
        options: VisibilityDiagnosisOptions,
        prepared: bool,
        diagnostics: &[Diagnostic],
    ) -> Self {
        analysis::from_inspection(
            inspection,
            stats,
            target_handle,
            options,
            prepared,
            diagnostics,
        )
    }

    pub fn to_schema_json(&self) -> serde_json::Value {
        serde_json::to_value(self).expect("visibility diagnosis report is serializable")
    }
}

impl Renderer {
    pub fn diagnose_visibility(
        &self,
        inspection: &SceneInspectionReportV1,
        target_handle: Option<u64>,
        options: VisibilityDiagnosisOptions,
    ) -> VisibilityDiagnosisReportV1 {
        VisibilityDiagnosisReportV1::from_inspection_with_diagnostics(
            inspection,
            self.stats(),
            target_handle,
            options,
            self.prepared.is_some(),
            self.diagnostics(),
        )
    }

    pub fn diagnose_visibility_with_diagnostics(
        &self,
        inspection: &SceneInspectionReportV1,
        target_handle: Option<u64>,
        options: VisibilityDiagnosisOptions,
        diagnostics: &[Diagnostic],
    ) -> VisibilityDiagnosisReportV1 {
        VisibilityDiagnosisReportV1::from_inspection_with_diagnostics(
            inspection,
            self.stats(),
            target_handle,
            options,
            self.prepared.is_some(),
            diagnostics,
        )
    }
}