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//! **Seres del bioma (fauna procedural)**: un catálogo de modelitos voxel de
2//! animales (vaca, oveja, ciervo, zorro, oso, caballo…) que **pueblan el mundo
3//! por semilla**, como la flora y las casas. A diferencia de un [`Mueble`](crate::Mueble)
4//! (prop de interior, colocado a mano), un [`Bicho`] lo **esparce el bioma**: el
5//! campo `fauna` del [`Bioma`](crate::Bioma) dice qué animales y con qué densidad,
6//! y [`generate_window`](crate::Bioma::generate_window) los siembra parados sobre
7//! el terreno, orientados al azar.
8//!
9//! Los animales usan **color RGB directo** (no [`Material`](crate::worldgen::Material)):
10//! el rosa del cerdo o el naranja del zorro no viven en la paleta de terreno. Son
11//! mates (sin brillo). Se orientan con `facing` (0=+Z, 1=+X, 2=−Z, 3=−X).
12
13use serde::{Deserialize, Serialize};
14
15use llimphi_3d::VoxelGrid;
16
17/// Catálogo de seres/animales que pueblan un bioma.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum Bicho {
20 /// Vaca: cuerpo blanco con manchas negras, cabeza y cuernos.
21 Vaca,
22 /// Oveja: cuerpo lanudo blanco, cara oscura.
23 Oveja,
24 /// Cerdo: cuerpo rosado, hocico y colita.
25 Cerdo,
26 /// Gallina: cuerpo blanco chico, cresta roja y pico.
27 Gallina,
28 /// Ciervo: cuerpo marrón, patas largas y astas.
29 Ciervo,
30 /// Conejo: cuerpo chico con orejas largas.
31 Conejo,
32 /// Zorro: cuerpo naranja, pecho blanco y cola tupida.
33 Zorro,
34 /// Lobo: cuerpo gris alargado, orejas y cola.
35 Lobo,
36 /// Oso: cuerpo marrón voluminoso, cabeza redonda.
37 Oso,
38 /// Caballo: cuerpo marrón alto, cuello, crin y cola.
39 Caballo,
40 /// Pájaro: cuerpo diminuto con alas y pico (posado).
41 Pajaro,
42 /// Pez: cuerpo con aleta caudal (para el agua/orilla).
43 Pez,
44 /// Tortuga: caparazón verde abovedado, cabeza y patas.
45 Tortuga,
46 /// Rana: cuerpo verde agazapado, ojos saltones.
47 Rana,
48 /// Serpiente: cuerpo largo y bajo en zigzag, con cabeza.
49 Serpiente,
50 /// Mariposa: cuerpecito con cuatro alas coloridas (revolotea bajo).
51 Mariposa,
52 // --- Granja / pradera ---
53 /// Pato: cuerpo blanco chico, pico naranja (orilla/pradera).
54 Pato,
55 /// Cabra: cuerpo claro con cuernitos (montaña/granja).
56 Cabra,
57 /// Burro: cuerpo gris con orejas largas (granja).
58 Burro,
59 // --- Bosque ---
60 /// Ardilla: cuerpito con cola tupida enroscada (bosque).
61 Ardilla,
62 /// Búho: cuerpo compacto con ojos grandes, posado (bosque).
63 Buho,
64 /// Erizo: lomo de púas oscuras, cara clara (bosque).
65 Erizo,
66 // --- Desierto ---
67 /// Camello: cuerpo arena con joroba y cuello (desierto).
68 Camello,
69 /// Escorpión: cuerpo bajo con pinzas y cola en alto (desierto).
70 Escorpion,
71 /// Lagarto: cuerpo verde alargado y bajo (desierto/selva).
72 Lagarto,
73 // --- Nieve / agua fría ---
74 /// Pingüino: cuerpo negro con panza blanca, pico naranja (nieve).
75 Pinguino,
76 /// Foca: cuerpo gris tendido con aletas (nieve/agua).
77 Foca,
78 // --- Selva / sabana ---
79 /// Mono: cuerpo marrón con cola larga (selva).
80 Mono,
81 /// Loro: cuerpo verde con pecho rojo y pico (selva).
82 Loro,
83 /// Tigre: cuerpo naranja con rayas negras (selva).
84 Tigre,
85 /// Elefante: cuerpo gris grande con trompa y orejas (sabana).
86 Elefante,
87 /// Cocodrilo: cuerpo verde largo y bajo con fauces (selva/agua).
88 Cocodrilo,
89 // --- El bestiario SDF (2026-09-06) -------------------------------------
90 // Variantes SIEMPRE al final: `BichoUso` viaja en postcard, que numera por
91 // posición — una al medio corre a todas las de abajo y un mundo guardado
92 // cambia de fauna sin decir nada.
93 //
94 // Estas nacieron para la segunda técnica de graficación (`bicho_sdf`), que
95 // las arma de primitivas analíticas. En VOXEL no tienen estampa propia:
96 // `silueta_voxel` las manda a la de un pariente, que es el LOD lejano.
97 /// Perro: compañía, orejas caídas y cola tupida.
98 Perro,
99 /// Gato: lomo arqueado, orejas en punta y cola larga.
100 Gato,
101 /// Toro: cuello macizo, morrillo y cuernos en lira.
102 Toro,
103 /// Alce: cruz alta, hocico largo y astas palmeadas.
104 Alce,
105 /// Mapache: antifaz oscuro y cola anillada.
106 Mapache,
107 /// Ratón: orejas redondas grandes y cola pelada.
108 Raton,
109 /// Panda: blanco y negro, cara chata, orejas redondas.
110 Panda,
111 /// Koala: orejas mullidas enormes, sin cola.
112 Koala,
113 /// Canguro: bípedo, cola gruesa de contrapeso.
114 Canguro,
115 /// Llama: cuello largo lanudo (andes).
116 Llama,
117 /// Jirafa: cuello altísimo y manchas.
118 Jirafa,
119 /// León: melena alrededor del cuello y cola con penacho.
120 Leon,
121 /// Cebra: caballo a rayas.
122 Cebra,
123 /// Jaguar: felino manchado de la selva.
124 Jaguar,
125 /// Hipopótamo: cuerpo bajo y bocaza.
126 Hipopotamo,
127 /// Rinoceronte: cuerno nasal y piel de placas.
128 Rinoceronte,
129 /// Capibara: roedor grande y tranquilo de ribera.
130 Capibara,
131 /// Perezoso: brazos largos, cara serena, sin cola.
132 Perezoso,
133 /// Gorrión: pajarito pardo de ciudad.
134 Gorrion,
135 /// Cardenal: rojo con copete.
136 Cardenal,
137 /// Cuervo: negro entero, pico recto.
138 Cuervo,
139 /// Colibrí: pico aguja y alas en batido.
140 Colibri,
141 /// Guacamayo: rojo/azul/amarillo con cola larguísima.
142 Guacamayo,
143 /// Tucán: pico enorme naranja.
144 Tucan,
145 /// Quetzal: verde metálico con cola de dos plumas largas.
146 Quetzal,
147 /// Flamenco: zancudo rosado de cuello en S.
148 Flamenco,
149 /// Garza: zancuda blanca de pico largo.
150 Garza,
151 /// Cisne: cuello en S y pico naranja.
152 Cisne,
153 /// Ganso: cuello mediano, pico pala.
154 Ganso,
155 /// Pelícano: pico con bolsa.
156 Pelicano,
157 /// Águila: pico ganchudo y alas abiertas.
158 Aguila,
159 /// Cóndor: planeador enorme de collar blanco.
160 Condor,
161 /// Pavo real: rueda de ocelos y copete.
162 PavoReal,
163 /// Avestruz: patas largas, cuello pelado.
164 Avestruz,
165 /// Martín pescador: azul con pico largo y copete.
166 Martin,
167 /// Gallo: cresta roja y cola en abanico.
168 Gallo,
169 /// Boa: serpiente gruesa con rombos.
170 Boa,
171 /// Cobra: capucha desplegada y tercio delantero alzado.
172 Cobra,
173 /// Iguana: cresta dorsal de púas.
174 Iguana,
175 /// Camaleón: cuerpo alto y comprimido, ojos saltones.
176 Camaleon,
177 /// Salamandra: negra con manchas amarillas.
178 Salamandra,
179 /// Delfín: hocico y cola horizontal.
180 Delfin,
181 /// Ballena: cuerpo enorme y cola horizontal.
182 Ballena,
183 /// Tiburón: aleta dorsal alta y boca abajo.
184 Tiburon,
185 /// Pulpo: manto y ocho tentáculos.
186 Pulpo,
187 /// Abeja: rayas y alas de membrana.
188 Abeja,
189 /// Libélula: abdomen largo y cuatro alas.
190 Libelula,
191 /// Escarabajo: élitros duros con lunares.
192 Escarabajo,
193 /// Hormiga: tres segmentos y seis patas.
194 Hormiga,
195 /// Araña: ocho patas arqueadas.
196 Arana,
197 /// Cangrejo: caparazón plano, pinzas y ojos en pedúnculo.
198 Cangrejo,
199 /// Caracol: concha en espiral y cuernitos.
200 Caracol,
201 /// Luciérnaga: élitros oscuros y abdomen luminoso.
202 Luciernaga,
203}
204
205impl Bicho {
206 pub const ALL: [Bicho; 85] = [
207 Bicho::Vaca,
208 Bicho::Oveja,
209 Bicho::Cerdo,
210 Bicho::Gallina,
211 Bicho::Ciervo,
212 Bicho::Conejo,
213 Bicho::Zorro,
214 Bicho::Lobo,
215 Bicho::Oso,
216 Bicho::Caballo,
217 Bicho::Pajaro,
218 Bicho::Pez,
219 Bicho::Tortuga,
220 Bicho::Rana,
221 Bicho::Serpiente,
222 Bicho::Mariposa,
223 Bicho::Pato,
224 Bicho::Cabra,
225 Bicho::Burro,
226 Bicho::Ardilla,
227 Bicho::Buho,
228 Bicho::Erizo,
229 Bicho::Camello,
230 Bicho::Escorpion,
231 Bicho::Lagarto,
232 Bicho::Pinguino,
233 Bicho::Foca,
234 Bicho::Mono,
235 Bicho::Loro,
236 Bicho::Tigre,
237 Bicho::Elefante,
238 Bicho::Cocodrilo,
239 Bicho::Perro,
240 Bicho::Gato,
241 Bicho::Toro,
242 Bicho::Alce,
243 Bicho::Mapache,
244 Bicho::Raton,
245 Bicho::Panda,
246 Bicho::Koala,
247 Bicho::Canguro,
248 Bicho::Llama,
249 Bicho::Jirafa,
250 Bicho::Leon,
251 Bicho::Cebra,
252 Bicho::Jaguar,
253 Bicho::Hipopotamo,
254 Bicho::Rinoceronte,
255 Bicho::Capibara,
256 Bicho::Perezoso,
257 Bicho::Gorrion,
258 Bicho::Cardenal,
259 Bicho::Cuervo,
260 Bicho::Colibri,
261 Bicho::Guacamayo,
262 Bicho::Tucan,
263 Bicho::Quetzal,
264 Bicho::Flamenco,
265 Bicho::Garza,
266 Bicho::Cisne,
267 Bicho::Ganso,
268 Bicho::Pelicano,
269 Bicho::Aguila,
270 Bicho::Condor,
271 Bicho::PavoReal,
272 Bicho::Avestruz,
273 Bicho::Martin,
274 Bicho::Gallo,
275 Bicho::Boa,
276 Bicho::Cobra,
277 Bicho::Iguana,
278 Bicho::Camaleon,
279 Bicho::Salamandra,
280 Bicho::Delfin,
281 Bicho::Ballena,
282 Bicho::Tiburon,
283 Bicho::Pulpo,
284 Bicho::Abeja,
285 Bicho::Libelula,
286 Bicho::Escarabajo,
287 Bicho::Hormiga,
288 Bicho::Arana,
289 Bicho::Cangrejo,
290 Bicho::Caracol,
291 Bicho::Luciernaga,
292 ];
293
294 pub fn label(self) -> &'static str {
295 match self {
296 Bicho::Vaca => "vaca",
297 Bicho::Oveja => "oveja",
298 Bicho::Cerdo => "cerdo",
299 Bicho::Gallina => "gallina",
300 Bicho::Ciervo => "ciervo",
301 Bicho::Conejo => "conejo",
302 Bicho::Zorro => "zorro",
303 Bicho::Lobo => "lobo",
304 Bicho::Oso => "oso",
305 Bicho::Caballo => "caballo",
306 Bicho::Pajaro => "pájaro",
307 Bicho::Pez => "pez",
308 Bicho::Tortuga => "tortuga",
309 Bicho::Rana => "rana",
310 Bicho::Serpiente => "serpiente",
311 Bicho::Mariposa => "mariposa",
312 Bicho::Pato => "pato",
313 Bicho::Cabra => "cabra",
314 Bicho::Burro => "burro",
315 Bicho::Ardilla => "ardilla",
316 Bicho::Buho => "búho",
317 Bicho::Erizo => "erizo",
318 Bicho::Camello => "camello",
319 Bicho::Escorpion => "escorpión",
320 Bicho::Lagarto => "lagarto",
321 Bicho::Pinguino => "pingüino",
322 Bicho::Foca => "foca",
323 Bicho::Mono => "mono",
324 Bicho::Loro => "loro",
325 Bicho::Tigre => "tigre",
326 Bicho::Elefante => "elefante",
327 Bicho::Cocodrilo => "cocodrilo",
328 Bicho::Perro => "perro",
329 Bicho::Gato => "gato",
330 Bicho::Toro => "toro",
331 Bicho::Alce => "alce",
332 Bicho::Mapache => "mapache",
333 Bicho::Raton => "ratón",
334 Bicho::Panda => "panda",
335 Bicho::Koala => "koala",
336 Bicho::Canguro => "canguro",
337 Bicho::Llama => "llama",
338 Bicho::Jirafa => "jirafa",
339 Bicho::Leon => "león",
340 Bicho::Cebra => "cebra",
341 Bicho::Jaguar => "jaguar",
342 Bicho::Hipopotamo => "hipopótamo",
343 Bicho::Rinoceronte => "rinoceronte",
344 Bicho::Capibara => "capibara",
345 Bicho::Perezoso => "perezoso",
346 Bicho::Gorrion => "gorrión",
347 Bicho::Cardenal => "cardenal",
348 Bicho::Cuervo => "cuervo",
349 Bicho::Colibri => "colibrí",
350 Bicho::Guacamayo => "guacamayo",
351 Bicho::Tucan => "tucán",
352 Bicho::Quetzal => "quetzal",
353 Bicho::Flamenco => "flamenco",
354 Bicho::Garza => "garza",
355 Bicho::Cisne => "cisne",
356 Bicho::Ganso => "ganso",
357 Bicho::Pelicano => "pelícano",
358 Bicho::Aguila => "águila",
359 Bicho::Condor => "cóndor",
360 Bicho::PavoReal => "pavo real",
361 Bicho::Avestruz => "avestruz",
362 Bicho::Martin => "martín pescador",
363 Bicho::Gallo => "gallo",
364 Bicho::Boa => "boa",
365 Bicho::Cobra => "cobra",
366 Bicho::Iguana => "iguana",
367 Bicho::Camaleon => "camaleón",
368 Bicho::Salamandra => "salamandra",
369 Bicho::Delfin => "delfín",
370 Bicho::Ballena => "ballena",
371 Bicho::Tiburon => "tiburón",
372 Bicho::Pulpo => "pulpo",
373 Bicho::Abeja => "abeja",
374 Bicho::Libelula => "libélula",
375 Bicho::Escarabajo => "escarabajo",
376 Bicho::Hormiga => "hormiga",
377 Bicho::Arana => "araña",
378 Bicho::Cangrejo => "cangrejo",
379 Bicho::Caracol => "caracol",
380 Bicho::Luciernaga => "luciérnaga",
381 }
382 }
383
384 /// **La estampa VOXEL que le toca** a este bicho. Las 32 primeras tienen la
385 /// suya; las del bestiario SDF (2026-09-06) reusan la de un pariente de silueta
386 /// parecida — a la distancia en que se usa el voxel (LOD lejano, fauna esparcida
387 /// por el bioma) un jaguar y un tigre son el mismo puñado de voxels, y una
388 /// estampa a medias sería peor que la del pariente.
389 pub fn silueta_voxel(self) -> Bicho {
390 match self {
391 Bicho::Perro => Bicho::Zorro,
392 Bicho::Gato => Bicho::Zorro,
393 Bicho::Toro => Bicho::Vaca,
394 Bicho::Alce => Bicho::Ciervo,
395 Bicho::Mapache => Bicho::Zorro,
396 Bicho::Raton => Bicho::Conejo,
397 Bicho::Panda => Bicho::Oso,
398 Bicho::Koala => Bicho::Oso,
399 Bicho::Canguro => Bicho::Ciervo,
400 Bicho::Llama => Bicho::Oveja,
401 Bicho::Jirafa => Bicho::Caballo,
402 Bicho::Leon => Bicho::Oso,
403 Bicho::Cebra => Bicho::Caballo,
404 Bicho::Jaguar => Bicho::Tigre,
405 Bicho::Hipopotamo => Bicho::Oso,
406 Bicho::Rinoceronte => Bicho::Oso,
407 Bicho::Capibara => Bicho::Cerdo,
408 Bicho::Perezoso => Bicho::Mono,
409 Bicho::Gorrion => Bicho::Pajaro,
410 Bicho::Cardenal => Bicho::Pajaro,
411 Bicho::Cuervo => Bicho::Pajaro,
412 Bicho::Colibri => Bicho::Pajaro,
413 Bicho::Guacamayo => Bicho::Loro,
414 Bicho::Tucan => Bicho::Loro,
415 Bicho::Quetzal => Bicho::Loro,
416 Bicho::Flamenco => Bicho::Pato,
417 Bicho::Garza => Bicho::Pato,
418 Bicho::Cisne => Bicho::Pato,
419 Bicho::Ganso => Bicho::Pato,
420 Bicho::Pelicano => Bicho::Pato,
421 Bicho::Aguila => Bicho::Buho,
422 Bicho::Condor => Bicho::Buho,
423 Bicho::PavoReal => Bicho::Gallina,
424 Bicho::Avestruz => Bicho::Gallina,
425 Bicho::Martin => Bicho::Pajaro,
426 Bicho::Gallo => Bicho::Gallina,
427 Bicho::Boa => Bicho::Serpiente,
428 Bicho::Cobra => Bicho::Serpiente,
429 Bicho::Iguana => Bicho::Lagarto,
430 Bicho::Camaleon => Bicho::Lagarto,
431 Bicho::Salamandra => Bicho::Lagarto,
432 Bicho::Delfin => Bicho::Pez,
433 Bicho::Ballena => Bicho::Pez,
434 Bicho::Tiburon => Bicho::Pez,
435 Bicho::Pulpo => Bicho::Pez,
436 Bicho::Abeja => Bicho::Mariposa,
437 Bicho::Libelula => Bicho::Mariposa,
438 Bicho::Escarabajo => Bicho::Escorpion,
439 Bicho::Hormiga => Bicho::Escorpion,
440 Bicho::Arana => Bicho::Escorpion,
441 Bicho::Cangrejo => Bicho::Escorpion,
442 Bicho::Caracol => Bicho::Escorpion,
443 Bicho::Luciernaga => Bicho::Mariposa,
444 otro => otro,
445 }
446 }
447
448 pub fn next(self) -> Bicho {
449 let i = Bicho::ALL.iter().position(|&b| b == self).unwrap_or(0);
450 Bicho::ALL[(i + 1) % Bicho::ALL.len()]
451 }
452}
453
454/// **Uso de un ser en un bioma**: qué animal y con qué densidad `[0,1]` aparece
455/// (una candidata por parcela de la grilla de fauna; el bioma lo esparce por
456/// semilla → una fauna procedural, como la flora y las casas).
457#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
458pub struct BichoUso {
459 pub bicho: Bicho,
460 pub densidad: f32,
461}
462
463// Paleta cruda de fauna (colores que la paleta de terreno no tiene).
464const MARRON: [u8; 3] = [120, 84, 50];
465const MARRON_OSC: [u8; 3] = [86, 58, 34];
466const BLANCO: [u8; 3] = [230, 228, 222];
467const NEGRO: [u8; 3] = [42, 42, 46];
468const ROSA: [u8; 3] = [224, 152, 152];
469const NARANJA: [u8; 3] = [206, 108, 46];
470const GRIS: [u8; 3] = [132, 132, 138];
471const VERDE: [u8; 3] = [96, 156, 74];
472const VERDE_OSC: [u8; 3] = [64, 116, 56];
473const ROJO: [u8; 3] = [206, 72, 60];
474const AMARILLO: [u8; 3] = [224, 192, 92];
475const AZUL: [u8; 3] = [72, 116, 186];
476const TAN: [u8; 3] = [204, 172, 120];
477const MARRON_CLR: [u8; 3] = [158, 118, 76];
478const GRIS_OSC: [u8; 3] = [92, 94, 100];
479
480/// Hash barato de la posición → variedad de morfo (color/patrón) por dónde cae el
481/// animal, sin usar la semilla del mundo. Igual que la carrocería de un vehículo o
482/// el tinte del follaje: un rebaño estampado sale VARIADO, determinista por posición.
483#[inline]
484fn morph(x: u32, z: u32) -> u32 {
485 let mut h = x.wrapping_mul(0x9E37_79B9).wrapping_add(z.wrapping_mul(0x85EB_CA77));
486 h ^= h >> 15;
487 h = h.wrapping_mul(0x2C1B_3C6D);
488 (h ^ (h >> 13)) & 0xFFFF
489}
490
491/// Rota un desplazamiento `(dx, dz)` según `facing` (giros de 90°).
492#[inline]
493fn rot(dx: i32, dz: i32, facing: u8) -> (i32, i32) {
494 match facing & 3 {
495 0 => (dx, dz),
496 1 => (-dz, dx),
497 2 => (-dx, -dz),
498 _ => (dz, -dx),
499 }
500}
501
502/// **Detalle sub-voxel de un ser vivo** `(rugosidad, bisel)` — la gramática
503/// cuantizada que rompe la caja rectangular de los animales voxel. Rugosidad suave
504/// (grano de piel/pelaje, escala `VoxelGrid::DETALLE_RUG_MAX`) + bisel alto (redondeo
505/// orgánico de la silueta de sombreado): un ser vivo no es un ladrillo. Es la MISMA
506/// gramática de bisel/campo que el terreno (no una fidelidad distinta → sin valle),
507/// sólo con más redondeo por ser un cuerpo. `generate_window` lo aplica como pincel
508/// de detalle alrededor de cada `stamp_bicho`. El bisel sólo luce si el bisel GLOBAL
509/// de la escena está prendido (activa la isosuperficie); la rugosidad luce siempre.
510pub const DETALLE_SER_VIVO: (f32, f32) = (0.4, 0.85);
511
512/// **Estampa un ser** del `bicho` parado con las patas en `(x, y, z)` (base local
513/// `dy=0` = a ras del suelo), orientado por `facing`. `dz` crece hacia el frente
514/// (hocico), `dx` a lo ancho, `dy` hacia arriba.
515pub fn stamp_bicho(g: &mut VoxelGrid, bicho: Bicho, x: u32, y: u32, z: u32, facing: u8) {
516 // Las especies del bestiario SDF caen en la estampa de su pariente voxel.
517 let bicho = bicho.silueta_voxel();
518 let (bx, by, bz) = (x as i32, y as i32, z as i32);
519 let dim = g.dim();
520 let vh = morph(x, z); // morfo de color/patrón por posición (variedad de rebaño)
521 let mut put = |dx: i32, dy: i32, dz: i32, c: [u8; 3]| {
522 let (rx, rz) = rot(dx, dz, facing);
523 let (px, py, pz) = (bx + rx, by + dy, bz + rz);
524 if px >= 0 && py >= 0 && pz >= 0 && (px as u32) < dim[0] && (py as u32) < dim[1] && (pz as u32) < dim[2] {
525 g.set(px as u32, py as u32, pz as u32, c);
526 }
527 };
528
529 match bicho {
530 Bicho::Vaca => {
531 // Morfo de pelaje: holstein / marrón-blanca / negra.
532 let (base, mancha_c) = match vh % 3 {
533 0 => (BLANCO, NEGRO),
534 1 => (BLANCO, MARRON_CLR),
535 _ => (MARRON_CLR, NEGRO),
536 };
537 // Cuerpo (2 de ancho × 3 de largo × 2 de alto) sobre 4 patas.
538 for dx in -1i32..=0 {
539 for dz in -1i32..=1 {
540 for dy in 2..=3 {
541 // Manchas en algunas celdas, base en el resto.
542 let mancha = (dx + dz).rem_euclid(2) == 0 && dz != 1;
543 put(dx, dy, dz, if mancha { mancha_c } else { base });
544 }
545 }
546 }
547 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
548 put(lx, 0, lz, MARRON_OSC);
549 put(lx, 1, lz, MARRON_OSC);
550 }
551 // Cabeza al frente + cuernos.
552 put(0, 3, 2, ROSA);
553 put(-1, 3, 2, ROSA);
554 put(0, 4, 2, base);
555 put(-1, 4, 2, base);
556 put(1, 4, 2, [232, 228, 214]); // cuerno
557 put(-2, 4, 2, [232, 228, 214]); // cuerno
558 }
559 Bicho::Oveja => {
560 // Morfo de lana: blanca / beige / gris.
561 let lana = match vh % 3 {
562 0 => BLANCO,
563 1 => [214, 202, 176],
564 _ => [186, 186, 190],
565 };
566 // Cuerpo lanudo y mullido sobre patas oscuras.
567 for dx in -1..=0 {
568 for dz in -1..=1 {
569 for dy in 2..=3 {
570 put(dx, dy, dz, lana);
571 }
572 }
573 }
574 put(0, 4, 0, lana);
575 put(-1, 4, 0, lana);
576 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
577 put(lx, 0, lz, NEGRO);
578 put(lx, 1, lz, NEGRO);
579 }
580 // Cara oscura al frente.
581 put(0, 2, 2, NEGRO);
582 put(-1, 2, 2, NEGRO);
583 put(0, 3, 2, lana);
584 }
585 Bicho::Cerdo => {
586 // Cuerpo rosado rechoncho, patas cortas, hocico y colita.
587 for dx in -1..=0 {
588 for dz in -1..=1 {
589 put(dx, 1, dz, ROSA);
590 put(dx, 2, dz, ROSA);
591 }
592 }
593 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
594 put(lx, 0, lz, ROSA);
595 }
596 put(0, 1, 2, ROSA); // hocico
597 put(-1, 1, 2, ROSA);
598 put(0, 2, 2, ROSA);
599 put(-1, 2, 2, ROSA);
600 put(0, 2, 3, [232, 170, 170]); // trompa
601 put(-1, 1, -2, ROSA); // colita
602 }
603 Bicho::Gallina => {
604 // Morfo de plumaje: blanca / marrón / negra (con gallo rojizo).
605 let plum = match vh % 3 {
606 0 => BLANCO,
607 1 => MARRON_CLR,
608 _ => [70, 68, 74],
609 };
610 put(0, 1, 0, plum);
611 put(0, 1, -1, plum);
612 put(0, 2, 0, plum);
613 put(0, 2, 1, plum); // pecho/cabeza
614 put(0, 3, 1, ROJO); // cresta
615 put(0, 2, 2, AMARILLO); // pico
616 put(0, 0, 0, AMARILLO); // patas
617 put(0, 0, -1, AMARILLO);
618 put(0, 1, -2, plum); // cola
619 }
620 Bicho::Ciervo => {
621 // Patas largas, cuerpo marrón, cuello y cabeza al frente, astas.
622 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
623 put(lx, 0, lz, MARRON_OSC);
624 put(lx, 1, lz, MARRON_OSC);
625 }
626 for dx in -1..=0 {
627 for dz in -1..=1 {
628 put(dx, 2, dz, MARRON);
629 put(dx, 3, dz, MARRON);
630 }
631 }
632 // Cuello + cabeza.
633 put(0, 3, 2, MARRON);
634 put(0, 4, 2, MARRON);
635 put(0, 5, 2, MARRON);
636 put(0, 5, 3, MARRON); // hocico
637 // Astas.
638 put(0, 6, 2, MARRON_OSC);
639 put(1, 6, 2, MARRON_OSC);
640 put(-1, 6, 2, MARRON_OSC);
641 put(0, 6, 3, MARRON_OSC);
642 }
643 Bicho::Conejo => {
644 // Morfo de pelaje: blanco / marrón / gris.
645 let pelo = match vh % 3 {
646 0 => BLANCO,
647 1 => MARRON_CLR,
648 _ => GRIS,
649 };
650 // Cuerpo chico, orejas largas, cola de algodón.
651 put(0, 1, 0, pelo);
652 put(0, 1, -1, pelo);
653 put(0, 2, 0, pelo);
654 put(0, 2, 1, pelo); // cabeza al frente
655 put(0, 3, 1, pelo); // oreja
656 put(0, 4, 1, pelo);
657 put(0, 3, 0, pelo); // oreja 2
658 put(0, 4, 0, pelo);
659 put(0, 0, 0, pelo);
660 put(0, 0, -1, pelo);
661 put(0, 2, -1, [240, 240, 236]); // colita
662 }
663 Bicho::Zorro => {
664 // Cuerpo naranja, pecho/cola blancos, orejas puntiagudas.
665 for dz in -1..=1 {
666 put(0, 1, dz, NARANJA);
667 put(0, 2, dz, NARANJA);
668 }
669 put(0, 0, -1, MARRON_OSC); // patas
670 put(0, 0, 1, MARRON_OSC);
671 put(0, 2, 2, NARANJA); // cabeza
672 put(0, 3, 2, NARANJA); // oreja
673 put(0, 2, 3, BLANCO); // hocico
674 put(0, 1, 2, BLANCO); // pecho
675 put(0, 2, -2, BLANCO); // cola
676 put(0, 1, -2, NARANJA);
677 }
678 Bicho::Lobo => {
679 // Como el zorro pero gris y más largo/robusto.
680 for dz in -2..=1 {
681 put(0, 1, dz, GRIS);
682 put(0, 2, dz, GRIS);
683 }
684 for &lz in &[-1, 1] {
685 put(0, 0, lz, [96, 96, 102]);
686 }
687 put(0, 2, 2, GRIS); // cabeza
688 put(0, 3, 2, GRIS); // oreja
689 put(0, 2, 3, [110, 110, 116]); // hocico
690 put(0, 2, -3, GRIS); // cola
691 put(0, 3, -3, GRIS);
692 }
693 Bicho::Oso => {
694 // Cuerpo voluminoso (2×3), cabeza redonda con orejas.
695 for dx in -1..=0 {
696 for dz in -1..=1 {
697 for dy in 1..=3 {
698 put(dx, dy, dz, MARRON_OSC);
699 }
700 }
701 }
702 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
703 put(lx, 0, lz, MARRON_OSC);
704 }
705 put(0, 3, 2, MARRON); // cabeza
706 put(-1, 3, 2, MARRON);
707 put(0, 4, 2, MARRON);
708 put(-1, 4, 2, MARRON);
709 put(1, 4, 2, MARRON_OSC); // orejas
710 put(-2, 4, 2, MARRON_OSC);
711 }
712 Bicho::Caballo => {
713 // Morfo de capa: alazán / negro / bayo, con crin de contraste.
714 let (capa, crin) = match vh % 3 {
715 0 => (MARRON, MARRON_OSC),
716 1 => ([58, 54, 56], NEGRO),
717 _ => (TAN, MARRON_OSC),
718 };
719 // Alto: patas largas, lomo, cuello erguido, crin y cola.
720 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
721 put(lx, 0, lz, crin);
722 put(lx, 1, lz, capa);
723 put(lx, 2, lz, capa);
724 }
725 for dx in -1..=0 {
726 for dz in -1..=1 {
727 put(dx, 3, dz, capa);
728 }
729 }
730 // Cuello + cabeza.
731 put(0, 4, 1, capa);
732 put(0, 5, 1, capa);
733 put(0, 5, 2, capa); // testa
734 put(0, 4, 2, capa);
735 put(0, 6, 1, crin); // crin
736 put(0, 5, 0, crin);
737 put(-1, 4, -1, crin); // cola
738 put(-1, 3, -2, crin);
739 }
740 Bicho::Pajaro => {
741 // Diminuto: cuerpo azul, alas, pico, patitas.
742 put(0, 1, 0, AZUL);
743 put(0, 2, 0, AZUL); // cabeza
744 put(0, 2, 1, AMARILLO); // pico
745 put(1, 1, 0, [96, 140, 210]); // ala
746 put(-1, 1, 0, [96, 140, 210]);
747 put(0, 1, -1, AZUL); // cola
748 put(0, 0, 0, AMARILLO); // pata
749 }
750 Bicho::Pez => {
751 // Cuerpo lateral + aleta caudal (se coloca en/junto al agua).
752 put(0, 1, 0, AZUL);
753 put(0, 1, 1, AZUL);
754 put(0, 2, 0, [110, 150, 210]);
755 put(0, 1, 2, [110, 150, 210]); // morro
756 put(0, 1, -1, [150, 180, 220]); // cola
757 put(0, 2, -1, [150, 180, 220]);
758 put(0, 0, -1, [150, 180, 220]);
759 }
760 Bicho::Tortuga => {
761 // Caparazón verde abovedado + cabeza + 4 patas.
762 for dx in -1..=1 {
763 for dz in -1..=1 {
764 put(dx, 1, dz, VERDE_OSC);
765 }
766 }
767 put(0, 2, 0, VERDE); // cúpula del caparazón
768 put(0, 1, 2, [180, 200, 120]); // cabeza
769 for &(lx, lz) in &[(-1, -1), (1, -1), (-1, 1), (1, 1)] {
770 put(lx, 0, lz, [180, 200, 120]); // patas
771 }
772 }
773 Bicho::Rana => {
774 // Cuerpo verde agazapado, ojos saltones, patas dobladas.
775 put(0, 1, 0, VERDE);
776 put(-1, 1, 0, VERDE);
777 put(0, 1, 1, VERDE); // frente
778 put(-1, 1, 1, VERDE);
779 put(0, 2, 1, VERDE_OSC); // ojo
780 put(-1, 2, 1, VERDE_OSC);
781 put(0, 0, -1, VERDE); // pata trasera
782 put(-1, 0, -1, VERDE);
783 put(0, 0, 2, [200, 210, 140]); // pata delantera
784 }
785 Bicho::Serpiente => {
786 // Cuerpo largo y bajo en zigzag + cabeza.
787 let cuerpo = [(0, 0), (0, -1), (-1, -1), (-1, -2), (-2, -2), (-2, -3)];
788 for &(sx, sz) in &cuerpo {
789 put(sx, 1, sz, VERDE_OSC);
790 }
791 put(0, 1, 1, VERDE); // cabeza
792 put(0, 1, 2, VERDE);
793 put(0, 2, 1, ROJO); // lengua/ojo
794 }
795 Bicho::Mariposa => {
796 // Morfo de alas: naranja monarca / azul / amarilla.
797 let (ala1, ala2) = match vh % 3 {
798 0 => (AMARILLO, NARANJA),
799 1 => (AZUL, [140, 180, 230]),
800 _ => (AMARILLO, [230, 170, 60]),
801 };
802 put(0, 3, 0, NEGRO); // cuerpo
803 put(0, 2, 0, NEGRO);
804 put(1, 3, 0, ala1); // alas
805 put(1, 3, 1, ala2);
806 put(-1, 3, 0, ala1);
807 put(-1, 3, 1, ala2);
808 put(1, 2, 0, ala2);
809 put(-1, 2, 0, ala2);
810 }
811
812 // ---- Granja / pradera ----
813 Bicho::Pato => {
814 // Cuerpo blanco chico, cuello, pico naranja, cola.
815 put(0, 1, 0, BLANCO);
816 put(0, 1, -1, BLANCO);
817 put(-1, 1, 0, BLANCO);
818 put(0, 2, 1, BLANCO); // cuello/cabeza
819 put(0, 3, 1, BLANCO);
820 put(0, 3, 2, NARANJA); // pico
821 put(0, 2, -1, BLANCO); // cola
822 put(0, 0, 0, NARANJA); // patas
823 }
824 Bicho::Cabra => {
825 // Morfo: blanca / parda. Cuerpo, patas, cabeza barbada y cuernos.
826 let capa = if vh % 2 == 0 { [222, 216, 202] } else { MARRON_CLR };
827 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
828 put(lx, 0, lz, MARRON_OSC);
829 put(lx, 1, lz, capa);
830 }
831 for dx in -1..=0 {
832 for dz in -1..=1 {
833 put(dx, 2, dz, capa);
834 }
835 }
836 put(0, 2, 2, capa); // cabeza
837 put(0, 3, 2, capa);
838 put(0, 2, 3, capa); // hocico
839 put(0, 4, 2, [232, 228, 214]); // cuerno
840 put(0, 1, 2, [220, 214, 200]); // barbita
841 put(-1, 2, -2, capa); // cola corta
842 }
843 Bicho::Burro => {
844 // Gris, patas, lomo, cuello y orejas largas.
845 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
846 put(lx, 0, lz, GRIS_OSC);
847 put(lx, 1, lz, GRIS);
848 }
849 for dx in -1..=0 {
850 for dz in -1..=1 {
851 put(dx, 2, dz, GRIS);
852 }
853 }
854 put(0, 3, 1, GRIS); // cuello
855 put(0, 3, 2, GRIS); // cabeza
856 put(0, 4, 2, GRIS); // oreja larga
857 put(0, 5, 2, GRIS_OSC);
858 put(-1, 2, -2, GRIS_OSC); // cola
859 }
860
861 // ---- Bosque ----
862 Bicho::Ardilla => {
863 // Cuerpito naranja con cola tupida enroscada hacia arriba.
864 put(0, 1, 0, NARANJA);
865 put(0, 2, 0, NARANJA);
866 put(0, 2, 1, NARANJA); // cabeza
867 put(0, 3, 1, [222, 150, 90]); // oreja
868 put(0, 1, 1, [232, 190, 150]); // pecho claro
869 put(0, 0, 0, MARRON_OSC); // pata
870 put(0, 2, -1, NARANJA); // cola
871 put(0, 3, -1, [222, 150, 90]);
872 put(0, 4, -1, NARANJA);
873 put(0, 4, 0, [222, 150, 90]);
874 }
875 Bicho::Buho => {
876 // Compacto, posado, con ojos grandes y penachos.
877 for dy in 1..=3 {
878 put(0, dy, 0, MARRON);
879 put(-1, dy, 0, MARRON);
880 }
881 put(0, 3, 1, AMARILLO); // ojo
882 put(-1, 3, 1, AMARILLO);
883 put(0, 4, 0, MARRON_OSC); // penacho
884 put(-1, 4, 0, MARRON_OSC);
885 put(0, 2, 1, [206, 180, 140]); // pecho
886 put(-1, 2, 1, [206, 180, 140]);
887 put(0, 0, 0, AMARILLO); // garras
888 put(-1, 0, 0, AMARILLO);
889 }
890 Bicho::Erizo => {
891 // Cuerpo bajo con lomo de púas oscuras y carita clara.
892 for dx in -1..=0 {
893 for dz in -1..=1 {
894 put(dx, 1, dz, MARRON_OSC);
895 put(dx, 2, dz, [64, 52, 40]); // púas
896 }
897 }
898 put(0, 2, 2, [222, 198, 160]); // carita
899 put(-1, 2, 2, [222, 198, 160]);
900 put(0, 1, 3, NEGRO); // hocico
901 for &(lx, lz) in &[(-1, -1), (0, 1)] {
902 put(lx, 0, lz, [120, 96, 72]);
903 }
904 }
905
906 // ---- Desierto ----
907 Bicho::Camello => {
908 // Color arena, patas largas, joroba, cuello y cabeza.
909 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
910 put(lx, 0, lz, [176, 148, 104]);
911 put(lx, 1, lz, TAN);
912 put(lx, 2, lz, TAN);
913 }
914 for dx in -1..=0 {
915 for dz in -1..=1 {
916 put(dx, 3, dz, TAN);
917 }
918 }
919 put(0, 4, 0, TAN); // joroba
920 put(-1, 4, 0, TAN);
921 put(0, 4, 1, TAN); // cuello
922 put(0, 5, 2, TAN); // cabeza
923 put(0, 5, 3, [176, 148, 104]); // hocico
924 put(-1, 3, -2, TAN); // cola
925 }
926 Bicho::Escorpion => {
927 // Cuerpo bajo oscuro, pinzas al frente, cola curva con aguijón.
928 put(0, 1, 0, [70, 58, 40]);
929 put(0, 1, 1, [70, 58, 40]);
930 put(0, 1, 2, [86, 70, 48]); // pinzas
931 put(1, 1, 2, [86, 70, 48]);
932 put(-1, 1, 2, [86, 70, 48]);
933 put(0, 1, -1, [70, 58, 40]); // cola
934 put(0, 2, -1, [70, 58, 40]);
935 put(0, 2, 0, [70, 58, 40]);
936 put(0, 3, 0, ROJO); // aguijón
937 }
938 Bicho::Lagarto => {
939 // Cuerpo verde alargado y bajo, patitas, cabeza, cola larga.
940 for dz in -2..=1 {
941 put(0, 1, dz, VERDE_OSC);
942 }
943 put(0, 1, 2, VERDE); // cabeza
944 put(0, 1, -3, VERDE_OSC); // cola
945 put(0, 1, -4, [80, 130, 66]);
946 put(1, 0, 0, VERDE_OSC); // patas
947 put(-1, 0, 0, VERDE_OSC);
948 put(1, 0, -1, VERDE_OSC);
949 put(-1, 0, -1, VERDE_OSC);
950 }
951
952 // ---- Nieve / agua fría ----
953 Bicho::Pinguino => {
954 // De pie: espalda negra, panza blanca, pico y patas naranjas.
955 for dy in 1..=3 {
956 put(0, dy, 0, NEGRO); // espalda
957 }
958 put(0, 1, 1, BLANCO); // panza
959 put(0, 2, 1, BLANCO);
960 put(0, 4, 0, NEGRO); // cabeza
961 put(0, 4, 1, NARANJA); // pico
962 put(1, 2, 0, NEGRO); // aleta
963 put(-1, 2, 0, NEGRO);
964 put(0, 0, 1, NARANJA); // patas
965 }
966 Bicho::Foca => {
967 // Cuerpo gris tendido, cabeza levantada, aletas.
968 for dz in -2..=1 {
969 put(0, 1, dz, GRIS);
970 put(-1, 1, dz, GRIS);
971 }
972 put(0, 2, 1, GRIS); // cabeza
973 put(-1, 2, 1, GRIS);
974 put(0, 2, 2, [110, 112, 118]); // hocico
975 put(0, 1, -3, [110, 112, 118]); // cola/aleta
976 put(-1, 1, -3, [110, 112, 118]);
977 put(1, 1, 0, [150, 150, 156]); // aleta lateral
978 }
979
980 // ---- Selva / sabana ----
981 Bicho::Mono => {
982 // Cuerpo marrón sentado, cabeza clara, cola larga curva.
983 put(0, 1, 0, MARRON);
984 put(0, 2, 0, MARRON);
985 put(0, 2, 1, [176, 140, 100]); // cara clara
986 put(0, 3, 1, MARRON); // frente
987 put(0, 1, 1, MARRON); // rodillas
988 put(1, 2, 0, MARRON); // brazo
989 put(-1, 2, 0, MARRON);
990 put(0, 1, -1, MARRON); // cola
991 put(0, 2, -1, MARRON);
992 put(0, 2, -2, MARRON);
993 }
994 Bicho::Loro => {
995 // Cuerpo verde vivo, pecho rojo, pico y cola larga.
996 put(0, 1, 0, [70, 168, 72]);
997 put(0, 2, 0, [70, 168, 72]);
998 put(0, 3, 0, [70, 168, 72]); // cabeza
999 put(0, 3, 1, ROJO); // pico
1000 put(0, 1, 1, ROJO); // pecho
1001 put(1, 2, 0, [90, 190, 90]); // ala
1002 put(0, 1, -1, [220, 200, 60]); // cola
1003 put(0, 1, -2, [70, 168, 72]);
1004 put(0, 0, 0, [120, 96, 72]); // pata
1005 }
1006 Bicho::Tigre => {
1007 // Cuerpo naranja con rayas negras, patas, cabeza, cola.
1008 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
1009 put(lx, 0, lz, NARANJA);
1010 }
1011 for dx in -1i32..=0 {
1012 for dz in -1i32..=1 {
1013 // Rayas: bandas negras alternas a lo largo.
1014 let raya = dz.rem_euclid(2) == 0;
1015 put(dx, 1, dz, if raya { NEGRO } else { NARANJA });
1016 }
1017 }
1018 put(0, 2, 2, NARANJA); // cabeza
1019 put(-1, 2, 2, NARANJA);
1020 put(0, 1, 2, [232, 200, 150]); // hocico
1021 put(0, 2, -2, NARANJA); // cola
1022 put(0, 2, -3, NEGRO);
1023 }
1024 Bicho::Elefante => {
1025 // Grande: cuerpo gris (2×3×2), patas, trompa, orejas, colmillos.
1026 for dx in -1i32..=0 {
1027 for dz in -1..=1 {
1028 for dy in 2..=3 {
1029 put(dx, dy, dz, GRIS);
1030 }
1031 }
1032 }
1033 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
1034 put(lx, 0, lz, [110, 112, 118]);
1035 put(lx, 1, lz, GRIS);
1036 }
1037 put(0, 3, 2, GRIS); // cabeza
1038 put(-1, 3, 2, GRIS);
1039 put(0, 2, 2, GRIS);
1040 put(0, 1, 3, GRIS); // trompa
1041 put(0, 0, 3, [110, 112, 118]);
1042 put(1, 3, 1, [150, 150, 156]); // oreja
1043 put(-2, 3, 1, [150, 150, 156]);
1044 put(0, 1, 2, [232, 228, 214]); // colmillo
1045 }
1046 Bicho::Cocodrilo => {
1047 // Cuerpo verde largo y bajo, dorso escamado, fauces, cola.
1048 for dz in -3..=2 {
1049 put(0, 1, dz, VERDE_OSC);
1050 put(-1, 1, dz, VERDE_OSC);
1051 }
1052 // Dorso escamado (crestas alternas).
1053 for dz in -3i32..=1 {
1054 if dz.rem_euclid(2) == 0 {
1055 put(0, 2, dz, [72, 128, 60]);
1056 }
1057 }
1058 put(0, 1, 3, VERDE); // hocico
1059 put(-1, 1, 3, VERDE);
1060 put(0, 2, 3, NEGRO); // ojo/fauces
1061 put(0, 1, -4, VERDE_OSC); // cola
1062 put(0, 1, -5, [80, 130, 66]);
1063 put(1, 0, 0, VERDE_OSC); // patas
1064 put(-2, 0, 0, VERDE_OSC);
1065 }
1066 // Inalcanzable por construcción: `silueta_voxel` (arriba, primera línea de
1067 // esta función) manda toda especie del bestiario SDF a la estampa de un
1068 // pariente, y las 32 originales tienen la suya. Lo fija el test
1069 // `cada_bicho_estampa_voxels`, que recorre `Bicho::ALL` y exige voxels: una
1070 // especie nueva sin remapeo cae acá, no estampa nada, y el test se pone
1071 // rojo — que es mejor que un `unreachable!` reventando un mundo entero.
1072 _ => {}
1073 }
1074}
1075
1076#[cfg(test)]
1077mod tests {
1078 use super::*;
1079
1080 fn solids(g: &VoxelGrid, dim: [u32; 3]) -> usize {
1081 let mut n = 0;
1082 for x in 0..dim[0] {
1083 for y in 0..dim[1] {
1084 for z in 0..dim[2] {
1085 if g.is_solid(x as i32, y as i32, z as i32) {
1086 n += 1;
1087 }
1088 }
1089 }
1090 }
1091 n
1092 }
1093
1094 #[test]
1095 fn cada_bicho_estampa_voxels() {
1096 let dim = [20u32, 16, 20];
1097 for bicho in Bicho::ALL {
1098 let mut g = VoxelGrid::new(dim);
1099 stamp_bicho(&mut g, bicho, 10, 1, 10, 0);
1100 assert!(solids(&g, dim) >= 5, "{} estampó muy poco", bicho.label());
1101 }
1102 }
1103
1104 #[test]
1105 fn facing_rota_la_silueta() {
1106 let dim = [24u32, 16, 24];
1107 let mut a = VoxelGrid::new(dim);
1108 let mut b = VoxelGrid::new(dim);
1109 stamp_bicho(&mut a, Bicho::Ciervo, 12, 1, 12, 0);
1110 stamp_bicho(&mut b, Bicho::Ciervo, 12, 1, 12, 1);
1111 // Al rotar, la ocupación cambia (el hocico apunta a otro eje).
1112 let mut diff = 0;
1113 for x in 0..dim[0] {
1114 for y in 0..dim[1] {
1115 for z in 0..dim[2] {
1116 if a.is_solid(x as i32, y as i32, z as i32) != b.is_solid(x as i32, y as i32, z as i32) {
1117 diff += 1;
1118 }
1119 }
1120 }
1121 }
1122 assert!(diff > 0, "facing no rotó la silueta");
1123 }
1124}