facett-graphview/examples/world_mega_still.rs
//! **world_mega_still** — ONE gigantic composite still of the whole code-universe graph:
//! a dense high-res RASTER of the node/edge cloud (tiled on the GPU past the 2048² cap and
//! stitched) with a crisp vector TEXT/label layer (repo labels at cluster centroids, a
//! title, a colour↔repo legend, node/edge counts, a timestamp) burned OVER it into ONE
//! image. Reuses the shared instanced GPU cloud + the world galaxy layout + categorical
//! colour; the text is rasterised + composited by pure-Rust resvg/usvg/tiny_skia.
//!
//! Outputs `<base>.png` (the huge composite) + `<base>.svg` (the scalable text layer).
//!
//! FORMAT: PNG, not JPEG2000 — there is NO pure-Rust JPEG2000 encoder on crates.io (every
//! JP2 crate binds C OpenJPEG, forbidden here). PNG is the lossless pure-Rust raster; the
//! text also stays crisp/scalable in the separate `.svg`. No C imaging tool is shelled out.
//!
//! ```bash
//! cargo run -p facett-graphview --features gpu --example world_mega_still --release -- \
//! 100000 8960 ~/git/workspace_nordisk/scratch/world-mega-still
//! ```
use std::time::Instant;
use facett_graphview::world::{layout_world, LayoutBudget, Lod, WorldGraph};
use facett_graphview::world_still::{
burn_svg_over_rgba, render_world_still_mega, world_text_svg, MegaLabels,
};
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let target: usize = args.first().and_then(|s| s.parse().ok()).unwrap_or(100_000);
// Mega edge in px. Rounded down to a multiple of the tile so tiling is clean.
let mega_req: u32 = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(8960);
let base = args
.get(2)
.cloned()
.unwrap_or_else(|| format!("{}/git/workspace_nordisk/scratch/world-mega-still", std::env::var("HOME").unwrap_or_default()));
let dark = !args.iter().any(|a| a == "light");
// Tiling: interior 1792 + 128 halo per side = 2048 (the downlevel max texture). The
// halo keeps bloom seamless across tile seams.
let tile: u32 = 1792;
let halo: u32 = 128;
let tiles_side = (mega_req.max(tile) as f32 / tile as f32).round().max(1.0) as u32;
let mega = tile * tiles_side; // clean multiple of the tile
let edge_cap = 400_000usize;
let t0 = Instant::now();
let w = WorldGraph::synthetic(target, Lod::Functions, true);
let build_ms = t0.elapsed().as_secs_f64() * 1e3;
let t1 = Instant::now();
let (pos, lane) = layout_world(&w, &LayoutBudget::default());
let layout_ms = t1.elapsed().as_secs_f64() * 1e3;
// 1) Dense RASTER (tiled GPU cloud, stitched).
let t2 = Instant::now();
let Some(mut raster) = render_world_still_mega(&w, &pos, mega, mega, dark, edge_cap, tile, halo)
else {
eprintln!("world_mega_still: NO usable GPU adapter — the mega/still path builds but needs a device here");
std::process::exit(2);
};
let render_ms = t2.elapsed().as_secs_f64() * 1e3;
// 2) Crisp vector TEXT layer (SVG), kept as a scalable sidecar too.
let opts = MegaLabels {
title: "facett — the code-universe graph".into(),
dark,
node_count: w.n,
edge_count: w.edges.len(),
synthetic: w.synthetic_flag(),
timestamp: format!("rendered {} · lane={}", today(), lane.key()),
max_repo_labels: 120,
};
let svg = world_text_svg(&w, &pos, raster.width, raster.height, &opts);
let svg_path = format!("{base}.svg");
std::fs::write(&svg_path, &svg).expect("write SVG text layer");
// 3) BURN IN — composite the crisp text OVER the raster, flatten to ONE image.
let t3 = Instant::now();
let burned = burn_svg_over_rgba(&mut raster.rgba, raster.width, raster.height, &svg);
let burn_ms = t3.elapsed().as_secs_f64() * 1e3;
// 4) Save the composite. PNG — the honest pure-Rust high-quality raster (no JP2 in Rust).
let png_path = format!("{base}.png");
image::save_buffer(
&png_path,
&raster.rgba,
raster.width,
raster.height,
image::ColorType::Rgba8,
)
.expect("write composite PNG");
let png_bytes = std::fs::metadata(&png_path).map(|m| m.len()).unwrap_or(0);
println!(
"world_mega_still: target={target} nodes={} edges={} lane={} synthetic={}\n \
build={build_ms:.1}ms layout={layout_ms:.1}ms render={render_ms:.1}ms burn={burn_ms:.1}ms\n \
raster {}x{} in {} tiles ({}px interior + {}px halo) · scene {} nodes + {} edges\n \
text burned in: {} · format: PNG (pure Rust — NO pure-Rust JPEG2000 encoder exists)\n \
composite → {png_path} ({:.1} MB)\n text layer → {svg_path}",
w.n, w.edges.len(), lane.key(), w.synthetic_flag(),
raster.width, raster.height, raster.tiles, tile, halo,
raster.nodes, raster.edges, burned,
png_bytes as f64 / 1e6,
);
}
/// A tiny date stamp (UTC yyyy-mm-dd) with no chrono dep — from the system clock.
fn today() -> String {
let secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let days = secs / 86_400;
// Civil-from-days (Howard Hinnant's algorithm).
let z = days as i64 + 719_468;
let era = z.div_euclid(146_097);
let doe = z - era * 146_097;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
format!("{y:04}-{m:02}-{d:02}")
}