tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo (pin tawasuyu.git v0.3.0)
1//! **El jardín SDF**: las 30 formas de vegetación ([`Forma`](llimphi_voxel::Forma))
2//! armadas con primitivas analíticas ([`flora_sdf`](llimphi_voxel::flora_sdf)) — el
3//! ficus con sus raíces aéreas, la palmera con la fronda arqueada, el jacarandá en
4//! flor, la rosa por capas.
5//!
6//! ```text
7//! cargo run -p llimphi-voxel --example jardin_demo --release
8//! ```
9//! Salida: `/tmp/jardin_1.png` …, `/tmp/jardin_semillas.png` (la misma especie con
10//! cinco semillas: si salen iguales, el bosque es papel pintado) y
11//! `/tmp/jardin_viento.png`.
12
13use llimphi_voxel::{alto_flora, flora_sdf, Forma};
14
15#[path = "comun/mod.rs"]
16mod comun;
17
18const TW: u32 = 320;
19const TH: u32 = 300;
20const COLS: usize = 6;
21const POR_HOJA: usize = 24;
22
23fn main() {
24 let mut toma = comun::Toma::nueva();
25 let piso = toma.piso([116, 124, 106], [100, 110, 94]);
26 let dir = comun::dir();
27 let yaw: f32 = std::env::var("YAW").ok().and_then(|s| s.parse().ok()).unwrap_or(28.0);
28
29 println!("jardin_demo — {} formas", Forma::ALL.len());
30 let vacia = toma.casilla(&piso, Vec::new(), 12.0, comun::RADIO, yaw, TW, TH);
31 let mut casillas: Vec<(String, Vec<u8>)> = Vec::new();
32 let mut flacos = Vec::new();
33 for (i, f) in Forma::ALL.into_iter().enumerate() {
34 let unidad = flora_sdf(f, 1.0, 11, 0.0);
35 let (_, r1) = unidad.bound();
36 let alto = comun::RADIO / r1.max(1e-3);
37 let ser = flora_sdf(f, alto, 11, 0.15);
38 let (c, r) = ser.bound();
39 let n = ser.prims.len();
40 let posado = ser.posado(
41 [comun::DIM as f32 * 0.5, comun::PISO as f32, comun::DIM as f32 * 0.5],
42 0.0,
43 );
44 let px = toma.casilla(&piso, vec![posado], c[1], r, yaw, TW, TH);
45 let cov = comun::ocupacion(&px, &vacia);
46 if cov < 900 {
47 flacos.push(format!("{} ({cov} px)", f.label()));
48 }
49 println!(" {:<12} {:>3} prims {:>6} px {:.1} m", f.label(), n, cov, alto_flora(f));
50 casillas.push((format!("{}. {}", i + 1, f.label()), px));
51 }
52 for (h, grupo) in casillas.chunks(POR_HOJA).enumerate() {
53 let (px, w, hh) = toma.hoja(
54 &format!("jardín SDF — hoja {} de {}", h + 1, casillas.len().div_ceil(POR_HOJA)),
55 grupo,
56 COLS,
57 TW,
58 TH,
59 );
60 let out = format!("{dir}/jardin_{}.png", h + 1);
61 comun::png(&px, w, hh, &out);
62 println!("→ {out}");
63 }
64
65 // La semilla: cinco ficus y cinco palmeras que no son la misma planta.
66 let mut casillas = Vec::new();
67 for f in [Forma::Ficus, Forma::Palmera, Forma::Arbol] {
68 for s in 0..5u32 {
69 let unidad = flora_sdf(f, 1.0, s * 13 + 1, 0.0);
70 let (_, r1) = unidad.bound();
71 let ser = flora_sdf(f, comun::RADIO / r1.max(1e-3), s * 13 + 1, 0.0);
72 let (c, r) = ser.bound();
73 let posado = ser.posado(
74 [comun::DIM as f32 * 0.5, comun::PISO as f32, comun::DIM as f32 * 0.5],
75 0.0,
76 );
77 let px = toma.casilla(&piso, vec![posado], c[1], r, yaw, TW, TH);
78 casillas.push((format!("{} · semilla {s}", f.label()), px));
79 }
80 }
81 let (px, w, hh) = toma.hoja("jardín SDF — la semilla varía la planta", &casillas, 5, TW, TH);
82 let out = format!("{dir}/jardin_semillas.png");
83 comun::png(&px, w, hh, &out);
84 println!("→ {out}");
85
86 // El viento.
87 let mut casillas = Vec::new();
88 for f in [Forma::Palmera, Forma::Trigo, Forma::Cipres] {
89 for v in [0.0f32, 0.5, 1.0, 1.6] {
90 let unidad = flora_sdf(f, 1.0, 7, 0.0);
91 let (_, r1) = unidad.bound();
92 let ser = flora_sdf(f, comun::RADIO / r1.max(1e-3), 7, v);
93 let (c, r) = ser.bound();
94 let posado = ser.posado(
95 [comun::DIM as f32 * 0.5, comun::PISO as f32, comun::DIM as f32 * 0.5],
96 0.0,
97 );
98 let px = toma.casilla(&piso, vec![posado], c[1], r, 0.0, TW, TH);
99 casillas.push((format!("{} · viento {v:.1}", f.label()), px));
100 }
101 }
102 let (px, w, hh) = toma.hoja("jardín SDF — el viento dobla la copa", &casillas, 4, TW, TH);
103 let out = format!("{dir}/jardin_viento.png");
104 comun::png(&px, w, hh, &out);
105 println!("→ {out}");
106
107 if flacos.is_empty() {
108 println!("✓ las {} formas dibujaron píxeles de sobra", Forma::ALL.len());
109 } else {
110 println!("⚠ casi no se ven: {}", flacos.join(", "));
111 }
112}