//! **El jardín SDF**: las 30 formas de vegetación ([`Forma`](llimphi_voxel::Forma)) //! armadas con primitivas analíticas ([`flora_sdf`](llimphi_voxel::flora_sdf)) — el //! ficus con sus raíces aéreas, la palmera con la fronda arqueada, el jacarandá en //! flor, la rosa por capas. //! //! ```text //! cargo run -p llimphi-voxel --example jardin_demo --release //! ``` //! Salida: `/tmp/jardin_1.png` …, `/tmp/jardin_semillas.png` (la misma especie con //! cinco semillas: si salen iguales, el bosque es papel pintado) y //! `/tmp/jardin_viento.png`. use llimphi_voxel::{alto_flora, flora_sdf, Forma}; #[path = "comun/mod.rs"] mod comun; const TW: u32 = 320; const TH: u32 = 300; const COLS: usize = 6; const POR_HOJA: usize = 24; fn main() { let mut toma = comun::Toma::nueva(); let piso = toma.piso([116, 124, 106], [100, 110, 94]); let dir = comun::dir(); let yaw: f32 = std::env::var("YAW").ok().and_then(|s| s.parse().ok()).unwrap_or(28.0); println!("jardin_demo — {} formas", Forma::ALL.len()); let vacia = toma.casilla(&piso, Vec::new(), 12.0, comun::RADIO, yaw, TW, TH); let mut casillas: Vec<(String, Vec)> = Vec::new(); let mut flacos = Vec::new(); for (i, f) in Forma::ALL.into_iter().enumerate() { let unidad = flora_sdf(f, 1.0, 11, 0.0); let (_, r1) = unidad.bound(); let alto = comun::RADIO / r1.max(1e-3); let ser = flora_sdf(f, alto, 11, 0.15); let (c, r) = ser.bound(); let n = ser.prims.len(); let posado = ser.posado( [comun::DIM as f32 * 0.5, comun::PISO as f32, comun::DIM as f32 * 0.5], 0.0, ); let px = toma.casilla(&piso, vec![posado], c[1], r, yaw, TW, TH); let cov = comun::ocupacion(&px, &vacia); if cov < 900 { flacos.push(format!("{} ({cov} px)", f.label())); } println!(" {:<12} {:>3} prims {:>6} px {:.1} m", f.label(), n, cov, alto_flora(f)); casillas.push((format!("{}. {}", i + 1, f.label()), px)); } for (h, grupo) in casillas.chunks(POR_HOJA).enumerate() { let (px, w, hh) = toma.hoja( &format!("jardín SDF — hoja {} de {}", h + 1, casillas.len().div_ceil(POR_HOJA)), grupo, COLS, TW, TH, ); let out = format!("{dir}/jardin_{}.png", h + 1); comun::png(&px, w, hh, &out); println!("→ {out}"); } // La semilla: cinco ficus y cinco palmeras que no son la misma planta. let mut casillas = Vec::new(); for f in [Forma::Ficus, Forma::Palmera, Forma::Arbol] { for s in 0..5u32 { let unidad = flora_sdf(f, 1.0, s * 13 + 1, 0.0); let (_, r1) = unidad.bound(); let ser = flora_sdf(f, comun::RADIO / r1.max(1e-3), s * 13 + 1, 0.0); let (c, r) = ser.bound(); let posado = ser.posado( [comun::DIM as f32 * 0.5, comun::PISO as f32, comun::DIM as f32 * 0.5], 0.0, ); let px = toma.casilla(&piso, vec![posado], c[1], r, yaw, TW, TH); casillas.push((format!("{} · semilla {s}", f.label()), px)); } } let (px, w, hh) = toma.hoja("jardín SDF — la semilla varía la planta", &casillas, 5, TW, TH); let out = format!("{dir}/jardin_semillas.png"); comun::png(&px, w, hh, &out); println!("→ {out}"); // El viento. let mut casillas = Vec::new(); for f in [Forma::Palmera, Forma::Trigo, Forma::Cipres] { for v in [0.0f32, 0.5, 1.0, 1.6] { let unidad = flora_sdf(f, 1.0, 7, 0.0); let (_, r1) = unidad.bound(); let ser = flora_sdf(f, comun::RADIO / r1.max(1e-3), 7, v); let (c, r) = ser.bound(); let posado = ser.posado( [comun::DIM as f32 * 0.5, comun::PISO as f32, comun::DIM as f32 * 0.5], 0.0, ); let px = toma.casilla(&piso, vec![posado], c[1], r, 0.0, TW, TH); casillas.push((format!("{} · viento {v:.1}", f.label()), px)); } } let (px, w, hh) = toma.hoja("jardín SDF — el viento dobla la copa", &casillas, 4, TW, TH); let out = format!("{dir}/jardin_viento.png"); comun::png(&px, w, hh, &out); println!("→ {out}"); if flacos.is_empty() { println!("✓ las {} formas dibujaron píxeles de sobra", Forma::ALL.len()); } else { println!("⚠ casi no se ven: {}", flacos.join(", ")); } }