Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-graph3d/tests/rabbit3d_snapshot.rs

//! **Screenshot PROOF (LAW #4)** — the **3D znippy rabbit logo**.
//!
//! The extruded rabbit ([`facett_core::rabbit::rabbit_mesh`]) is rendered by
//! [`RabbitLogo3D`] through egui_kittest's wgpu backend at a **fixed clock** (so
//! the spin is deterministic), and the framebuffer is asserted to be:
//!   1. **non-blank** — a real lit solid drew (not a black pane), and
//!   2. **lit with depth** — many distinct shades (the `normal·light` shading +
//!      depth-sorted faces produce a gradient, not one flat colour),
//!
//! then the golden PNG is committed so the 3D mascot is visibly a lit logo.
//!
//! This drives the **CPU painter** path (the one the glow demo + most hosts use);
//! egui's wgpu backend rasterises its tessellated, depth-sorted, shaded triangles
//! on a real GPU, so the proof is a genuine GPU render of the lit 3D logo. The
//! optional `wgpu`-feature GPU pipeline shares the same mesh + shading model.
//!
//! Run: `cargo test -p facett-graph3d --test rabbit3d_snapshot`
//! Golden: `facett-graph3d/tests/snapshots/rabbit_3d.png`.

use egui_kittest::Harness;

use facett_core::Facet;
use facett_graph3d::RabbitLogo3D;

/// `(content_ratio, distinct_shades)` of the image. `content_ratio` = fraction of
/// pixels far from the modal (≈background) colour; `distinct_shades` = number of
/// non-background colour buckets present (a flat-coloured blob → tiny; a lit,
/// depth-shaded solid → many).
fn measure(img: &image::RgbaImage) -> (f64, usize) {
    let mut hist = std::collections::HashMap::<u32, u32>::new();
    for p in img.pixels() {
        let key = ((p.0[0] as u32 >> 3) << 10) | ((p.0[1] as u32 >> 3) << 5) | (p.0[2] as u32 >> 3);
        *hist.entry(key).or_default() += 1;
    }
    let (modal, _) = hist.iter().max_by_key(|(_, c)| **c).map(|(k, c)| (*k, *c)).unwrap();
    let (mr, mg, mb) = (((modal >> 10) & 0x1f) as i32, ((modal >> 5) & 0x1f) as i32, (modal & 0x1f) as i32);
    let total = (img.width() * img.height()) as usize;
    let mut content = 0usize;
    let mut shades = std::collections::HashSet::<u32>::new();
    for p in img.pixels() {
        let (r, g, b) = ((p.0[0] >> 3) as i32, (p.0[1] >> 3) as i32, (p.0[2] >> 3) as i32);
        if (r - mr).abs() + (g - mg).abs() + (b - mb).abs() > 3 {
            content += 1;
            shades.insert(((r as u32) << 10) | ((g as u32) << 5) | b as u32);
        }
    }
    (content as f64 / total as f64, shades.len())
}

#[test]
fn rabbit_3d_renders_lit_with_depth_and_saves_golden() {
    // Inject-assert the mesh first: a real extruded solid, deterministically.
    let logo = RabbitLogo3D::new("rabbit3d").with_clock(1.2);
    let s = logo.state_json();
    assert!(s["triangles"].as_u64().unwrap() > 50, "real extruded mesh");
    assert!(s["vertices"].as_u64().unwrap() > 100);

    let mut view = RabbitLogo3D::new("rabbit3d").with_clock(1.2); // fixed spin → stable
    let mut harness = Harness::builder().with_size(egui::vec2(720.0, 720.0)).build_ui(move |ui| {
        facett_core::set_theme(ui.ctx(), facett_core::Theme::sci_fi());
        ui.heading("znippy rabbit — 3D extruded logo (lit, auto-rotate)");
        view.ui(ui);
    });
    harness.run_steps(3);

    let img_out = harness.render().expect("wgpu render produced an image");
    let (ratio, shades) = measure(&img_out);
    eprintln!("[rabbit-3d] content ratio {ratio:.4}, distinct shades {shades}");
    // Non-blank: the lit solid fills a visible region.
    assert!(ratio > 0.05, "the 3D logo must fill a visible region (got {ratio:.4})");
    // Lit + depth: many shades, not a single flat fill (proves the lighting +
    // depth-sorted faces actually shaded the solid).
    assert!(shades >= 8, "the lit, depth-shaded logo must show many shades (got {shades})");

    let out_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/snapshots");
    std::fs::create_dir_all(&out_dir).unwrap();
    let path = out_dir.join("rabbit_3d.png");
    image::DynamicImage::ImageRgba8(img_out).save(&path).expect("save PNG");
    let bytes = std::fs::metadata(&path).unwrap().len();
    assert!(bytes > 5_000, "golden PNG must be non-trivial (got {bytes} bytes)");
    eprintln!("facett-graph3d rabbit-3d snapshot → {} ({bytes} bytes)", path.display());
    // Feed the matrix: ok iff the lit 3D logo rendered non-blank with depth shades.
    #[cfg(feature = "testmatrix")]
    {
        let tris = s["triangles"].as_u64().unwrap_or(0) as usize;
        facett_core::testmatrix::emit(
            "RabbitLogo3D",
            "renders_lit_with_depth",
            ratio > 0.05 && shades >= 8 && tris > 50,
            &format!("content_ratio={ratio:.4} shades={shades} triangles={tris}"),
        );
    }
}