gioser

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 660Markdown 302TOML 157RON 312382 Definitions

⎇ c5a232cf24788ab75a850ffe81eca5a7af50e2f8 llimphi-voxel/src/mueble.rs Rust · 1571 lines · 51.9 KB History Who touched what raw

chore: refresco desde el monorepo — 27 crates nuevos (anim, lottie, svg, mesh, image, layer, test, wire-view, widgets rag-sidebar/router/lazy-list/rive-button) y sync de los 95 existentes 93253cc22 · Sergio ·

1//! **Muebles y objetos de casa**: un catálogo de modelitos voxel paramétricos
2//! (mesa, silla, cama, estante, lámpara, alfombra, cofre, estufa, sofá, maceta,
3//! barril, escritorio) que se **estampan** en una escena con [`stamp_mueble`].
4//!
5//! A diferencia de la [`Forma`](crate::Forma) de flora (un solo color), un mueble
6//! combina **varios materiales** por pieza (madera + acero + tela…) y lleva el
7//! **brillo** de cada material en el alpha del voxel, así el acero de un cofre o la
8//! pantalla de una lámpara **glasean** con el mismo ray-march que las gemas. Se
9//! orientan con `facing` (0=+Z, 1=+X, 2=−Z, 3=−X).
10
11use serde::{Deserialize, Serialize};
12
13use crate::worldgen::Material;
14use llimphi_3d::VoxelGrid;
15
16/// Catálogo de muebles y objetos de casa.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
18pub enum Mueble {
19    /// Mesa: tapa de madera sobre cuatro patas.
20    Mesa,
21    /// Silla: asiento, respaldo y patas de madera.
22    Silla,
23    /// Cama: bastidor de madera, colchón claro y almohada.
24    Cama,
25    /// Estante: librero de madera con libros de colores.
26    Estante,
27    /// Lámpara de pie: base y poste de acero, pantalla cálida.
28    Lampara,
29    /// Alfombra: losa plana coloreada.
30    Alfombra,
31    /// Cofre: caja de madera con herrajes de acero.
32    Cofre,
33    /// Estufa: cuerpo de ladrillo con boca de fuego.
34    Estufa,
35    /// Sofá: bloque tapizado con brazos y respaldo.
36    Sofa,
37    /// Maceta: tiesto de cerámica con tierra y una plantita.
38    Maceta,
39    /// Barril: duela de madera con aros de acero.
40    Barril,
41    /// Escritorio: tapa de madera con un bloque de cajones.
42    Escritorio,
43    // --- Plantas de interior ---
44    /// Ficus: árbol de interior en maceta (copa frondosa).
45    Ficus,
46    /// Palma de interior en maceta.
47    Palma,
48    /// Suculenta en maceta baja.
49    Suculenta,
50    /// Bonsái en maceta plana.
51    Bonsai,
52    /// Planta colgante: cesta a altura con enredaderas que caen.
53    Colgante,
54    /// Cactus de interior en maceta.
55    Nopal,
56    // --- Decoración de pared ---
57    /// Cuadro: marco con lienzo coloreado, montado a altura.
58    Cuadro,
59    /// Espejo: marco con vidrio lustroso.
60    Espejo,
61    /// Reloj de pared: esfera clara con agujas.
62    Reloj,
63    /// Repisa flotante: estante con un par de objetos.
64    Repisa,
65    /// Aplique: brazo de acero con una luz cálida.
66    Aplique,
67    /// Tapiz: paño colgante de colores.
68    Tapiz,
69    // --- Elementos de casas y edificios modernos ---
70    /// Escalera: tramo de peldaños de hormigón.
71    Escalera,
72    /// Baranda: barandal de vidrio con montantes de acero.
73    Baranda,
74    /// Ventanal: paño de vidrio de piso a techo (muro cortina).
75    Ventanal,
76    /// Columna: pilar cuadrado de hormigón.
77    Columna,
78    /// Aire acondicionado: split blanco de pared.
79    Aire,
80    /// Panel solar: placa azul inclinada sobre soporte.
81    Solar,
82    /// Televisor: pantalla plana negra sobre soporte.
83    Tele,
84    /// Isla de cocina: mueble bajo con mesada de mármol.
85    Isla,
86    /// Ducha: mampara de vidrio con base y ducha de acero.
87    Ducha,
88    /// Placard: armario empotrado alto.
89    Placard,
90    /// Farol: poste de acero con luz cálida (ilumina).
91    Farol,
92    /// Pileta: espejo de agua con borde de cerámica.
93    Pileta,
94    // --- Elementos de ciudad ---
95    /// Semáforo: poste con luces roja/amarilla/verde.
96    Semaforo,
97    /// Banco de plaza: tablas sobre patas de acero.
98    Banco,
99    /// Fuente: pileta de piedra con chorro de agua.
100    Fuente,
101    /// Buzón azul sobre poste.
102    Buzon,
103    /// Papelera: tacho cilíndrico de acero.
104    Basura,
105    /// Parada de colectivo: marquesina de vidrio con banco.
106    Parada,
107    /// Hidrante: boca de incendio roja.
108    Hidrante,
109    /// Cabina telefónica de vidrio y techo azul.
110    Cabina,
111    /// Cartel: poste con letrero.
112    Cartel,
113    /// Auto: carrocería azul con ventanas de vidrio y ruedas.
114    Auto,
115    /// Estatua de mármol sobre pedestal.
116    Estatua,
117    /// Bolardo: poste bajo de hormigón.
118    Bolardo,
119    // --- Edificios grandes ---
120    /// Rascacielos: torre alta de vidrio y acero.
121    Rascacielos,
122    /// Edificio: bloque de departamentos de ladrillo con ventanas.
123    Edificio,
124    /// Torre: torre delgada de hormigón con antena.
125    Torre,
126    /// Fábrica: nave baja con chimenea de ladrillo.
127    Fabrica,
128    /// Iglesia: nave con campanario y cruz.
129    Iglesia,
130    /// Galpón: nave ancha con techo de zinc.
131    Galpon,
132    /// Silo: cilindro de acero con cúpula.
133    Silo,
134    /// Estacionamiento: losas y columnas en varios niveles.
135    Estacionamiento,
136    /// Farola: alumbrado público alto (ilumina).
137    Farola,
138    /// Contenedor: volquete metálico.
139    Contenedor,
140    /// Grúa: torre grúa con pluma y contrapeso.
141    Grua,
142    /// Puente: tablero con barandas y pilas.
143    Puente,
144    // --- Transporte ---
145    /// Avión: fuselaje con alas y cola.
146    Avion,
147    /// Helicóptero: cabina, cola y rotor.
148    Helicoptero,
149    /// Camioneta (pickup): cabina y caja abierta.
150    Camioneta,
151    /// Camión: cabina y caja de carga.
152    Camion,
153    /// Colectivo (bus): carrocería larga con ventanas.
154    Colectivo,
155    /// Taxi: auto amarillo con cartel.
156    Taxi,
157    /// Tren: locomotora sobre ruedas.
158    Tren,
159    /// Vagón: caja de tren sobre ruedas.
160    Vagon,
161    /// Moto: dos ruedas y manubrio.
162    Moto,
163    /// Bici: cuadro y dos ruedas.
164    Bici,
165    /// Barco: casco con cabina y mástil.
166    Barco,
167    /// Ambulancia: furgón blanco con cruz roja.
168    Ambulancia,
169}
170
171impl Mueble {
172    /// Todos los muebles (para ciclar en un editor).
173    pub const ALL: [Mueble; 72] = [
174        Mueble::Mesa,
175        Mueble::Silla,
176        Mueble::Cama,
177        Mueble::Estante,
178        Mueble::Lampara,
179        Mueble::Alfombra,
180        Mueble::Cofre,
181        Mueble::Estufa,
182        Mueble::Sofa,
183        Mueble::Maceta,
184        Mueble::Barril,
185        Mueble::Escritorio,
186        Mueble::Ficus,
187        Mueble::Palma,
188        Mueble::Suculenta,
189        Mueble::Bonsai,
190        Mueble::Colgante,
191        Mueble::Nopal,
192        Mueble::Cuadro,
193        Mueble::Espejo,
194        Mueble::Reloj,
195        Mueble::Repisa,
196        Mueble::Aplique,
197        Mueble::Tapiz,
198        Mueble::Escalera,
199        Mueble::Baranda,
200        Mueble::Ventanal,
201        Mueble::Columna,
202        Mueble::Aire,
203        Mueble::Solar,
204        Mueble::Tele,
205        Mueble::Isla,
206        Mueble::Ducha,
207        Mueble::Placard,
208        Mueble::Farol,
209        Mueble::Pileta,
210        Mueble::Semaforo,
211        Mueble::Banco,
212        Mueble::Fuente,
213        Mueble::Buzon,
214        Mueble::Basura,
215        Mueble::Parada,
216        Mueble::Hidrante,
217        Mueble::Cabina,
218        Mueble::Cartel,
219        Mueble::Auto,
220        Mueble::Estatua,
221        Mueble::Bolardo,
222        Mueble::Rascacielos,
223        Mueble::Edificio,
224        Mueble::Torre,
225        Mueble::Fabrica,
226        Mueble::Iglesia,
227        Mueble::Galpon,
228        Mueble::Silo,
229        Mueble::Estacionamiento,
230        Mueble::Farola,
231        Mueble::Contenedor,
232        Mueble::Grua,
233        Mueble::Puente,
234        Mueble::Avion,
235        Mueble::Helicoptero,
236        Mueble::Camioneta,
237        Mueble::Camion,
238        Mueble::Colectivo,
239        Mueble::Taxi,
240        Mueble::Tren,
241        Mueble::Vagon,
242        Mueble::Moto,
243        Mueble::Bici,
244        Mueble::Barco,
245        Mueble::Ambulancia,
246    ];
247
248    /// Nombre legible (español).
249    pub fn label(self) -> &'static str {
250        match self {
251            Mueble::Mesa => "mesa",
252            Mueble::Silla => "silla",
253            Mueble::Cama => "cama",
254            Mueble::Estante => "estante",
255            Mueble::Lampara => "lámpara",
256            Mueble::Alfombra => "alfombra",
257            Mueble::Cofre => "cofre",
258            Mueble::Estufa => "estufa",
259            Mueble::Sofa => "sofá",
260            Mueble::Maceta => "maceta",
261            Mueble::Barril => "barril",
262            Mueble::Escritorio => "escritorio",
263            Mueble::Ficus => "ficus",
264            Mueble::Palma => "palma",
265            Mueble::Suculenta => "suculenta",
266            Mueble::Bonsai => "bonsái",
267            Mueble::Colgante => "colgante",
268            Mueble::Nopal => "nopal",
269            Mueble::Cuadro => "cuadro",
270            Mueble::Espejo => "espejo",
271            Mueble::Reloj => "reloj",
272            Mueble::Repisa => "repisa",
273            Mueble::Aplique => "aplique",
274            Mueble::Tapiz => "tapiz",
275            Mueble::Escalera => "escalera",
276            Mueble::Baranda => "baranda",
277            Mueble::Ventanal => "ventanal",
278            Mueble::Columna => "columna",
279            Mueble::Aire => "aire",
280            Mueble::Solar => "panel solar",
281            Mueble::Tele => "televisor",
282            Mueble::Isla => "isla",
283            Mueble::Ducha => "ducha",
284            Mueble::Placard => "placard",
285            Mueble::Farol => "farol",
286            Mueble::Pileta => "pileta",
287            Mueble::Semaforo => "semáforo",
288            Mueble::Banco => "banco",
289            Mueble::Fuente => "fuente",
290            Mueble::Buzon => "buzón",
291            Mueble::Basura => "papelera",
292            Mueble::Parada => "parada",
293            Mueble::Hidrante => "hidrante",
294            Mueble::Cabina => "cabina",
295            Mueble::Cartel => "cartel",
296            Mueble::Auto => "auto",
297            Mueble::Estatua => "estatua",
298            Mueble::Bolardo => "bolardo",
299            Mueble::Rascacielos => "rascacielos",
300            Mueble::Edificio => "edificio",
301            Mueble::Torre => "torre",
302            Mueble::Fabrica => "fábrica",
303            Mueble::Iglesia => "iglesia",
304            Mueble::Galpon => "galpón",
305            Mueble::Silo => "silo",
306            Mueble::Estacionamiento => "estacionamiento",
307            Mueble::Farola => "farola",
308            Mueble::Contenedor => "contenedor",
309            Mueble::Grua => "grúa",
310            Mueble::Puente => "puente",
311            Mueble::Avion => "avión",
312            Mueble::Helicoptero => "helicóptero",
313            Mueble::Camioneta => "camioneta",
314            Mueble::Camion => "camión",
315            Mueble::Colectivo => "colectivo",
316            Mueble::Taxi => "taxi",
317            Mueble::Tren => "tren",
318            Mueble::Vagon => "vagón",
319            Mueble::Moto => "moto",
320            Mueble::Bici => "bici",
321            Mueble::Barco => "barco",
322            Mueble::Ambulancia => "ambulancia",
323        }
324    }
325
326    /// El mueble siguiente (cicla).
327    pub fn next(self) -> Mueble {
328        let i = Mueble::ALL.iter().position(|&m| m == self).unwrap_or(0);
329        Mueble::ALL[(i + 1) % Mueble::ALL.len()]
330    }
331
332    /// Si el mueble **emite luz**, devuelve `(altura sobre la base, color RGB,
333    /// alcance en voxels)` de su fuente puntual. `None` = no ilumina. Lo consume el
334    /// editor de interiores para sembrar `PointLight`s cálidas.
335    pub fn luz(self) -> Option<(f32, [f32; 3], f32)> {
336        match self {
337            Mueble::Lampara => Some((6.5, [1.55, 1.22, 0.72], 15.0)), // pantalla cálida
338            Mueble::Aplique => Some((5.5, [1.5, 1.16, 0.7], 11.0)),   // aplique de pared
339            Mueble::Estufa => Some((1.5, [1.6, 0.72, 0.28], 9.0)),    // boca de fuego
340            Mueble::Farol => Some((7.5, [1.4, 1.3, 1.0], 16.0)),      // farol de poste (luz fría)
341            Mueble::Farola => Some((10.5, [1.5, 1.42, 1.15], 20.0)),  // alumbrado público (alto)
342            _ => None,
343        }
344    }
345}
346
347/// Rota un desplazamiento `(dx, dz)` según `facing` (giros de 90°).
348#[inline]
349fn rot(dx: i32, dz: i32, facing: u8) -> (i32, i32) {
350    match facing & 3 {
351        0 => (dx, dz),
352        1 => (-dz, dx),
353        2 => (-dx, -dz),
354        _ => (dz, -dx),
355    }
356}
357
358/// Escribe un voxel del material `m` (con su brillo en el alpha), recortando fuera
359/// del grid.
360fn place(g: &mut VoxelGrid, x: i32, y: i32, z: i32, m: Material) {
361    if x < 0 || y < 0 || z < 0 {
362        return;
363    }
364    let dim = g.dim();
365    if (x as u32) < dim[0] && (y as u32) < dim[1] && (z as u32) < dim[2] {
366        g.set_rgba(x as u32, y as u32, z as u32, m.color(), VoxelGrid::shine_to_alpha(m.brillo()));
367    }
368}
369
370/// Hash barato de la posición de colocación.
371#[inline]
372fn hashxz(x: u32, z: u32) -> u32 {
373    let mut h = x.wrapping_mul(0x9E37_79B9).wrapping_add(z.wrapping_mul(0x85EB_CA77));
374    h ^= h >> 15;
375    h = h.wrapping_mul(0x2C1B_3C6D);
376    h ^ (h >> 13)
377}
378
379/// **Color de carrocería** de un vehículo, elegido de una paleta por la posición de
380/// colocación (determinista → una flota estampada sale VARIADA, del mismo modo que
381/// la flora varía por posición de mundo). No usa la semilla del mundo: los vehículos
382/// siguen siendo props colocables, pero cada uno toma su color de dónde cae.
383fn vehicle_body(x: u32, z: u32) -> Material {
384    const PAL: [Material; 6] = [
385        Material::Azul,
386        Material::Lava,
387        Material::Wheat,
388        Material::Leaf,
389        Material::Ceramica,
390        Material::Grava,
391    ];
392    PAL[(hashxz(x, z) % PAL.len() as u32) as usize]
393}
394
395/// **Estampa un mueble** con la base (centro del piso) en `(x, y, z)`, orientado
396/// según `facing`. Cada pieza combina sus materiales y lleva su brillo.
397pub fn stamp_mueble(g: &mut VoxelGrid, mueble: Mueble, x: u32, y: u32, z: u32, facing: u8) {
398    let (bx, by, bz) = (x as i32, y as i32, z as i32);
399    let body = vehicle_body(x, z); // color de carrocería (vehículos)
400    // Coloca en coordenadas LOCALES (dx a lo ancho, dz al fondo, dy hacia arriba),
401    // rotadas por facing alrededor de la base.
402    let mut put = |dx: i32, dy: i32, dz: i32, m: Material| {
403        let (rx, rz) = rot(dx, dz, facing);
404        place(g, bx + rx, by + dy, bz + rz, m);
405    };
406
407    // Paleta de lomos de libro / telas.
408    let books = [Material::Ladrillo, Material::Leaf, Material::Bloom, Material::Wheat];
409
410    match mueble {
411        Mueble::Mesa => {
412            for dx in -2..=2 {
413                for dz in -2..=2 {
414                    put(dx, 3, dz, Material::Madera); // tapa
415                }
416            }
417            for &(lx, lz) in &[(-2, -2), (2, -2), (-2, 2), (2, 2)] {
418                for dy in 0..3 {
419                    put(lx, dy, lz, Material::Madera); // patas
420                }
421            }
422        }
423
424        Mueble::Silla => {
425            for dx in -1..=1 {
426                for dz in -1..=1 {
427                    put(dx, 2, dz, Material::Madera); // asiento
428                }
429            }
430            for &(lx, lz) in &[(-1, -1), (1, -1), (-1, 1), (1, 1)] {
431                for dy in 0..2 {
432                    put(lx, dy, lz, Material::Madera); // patas
433                }
434            }
435            for dx in -1..=1 {
436                for dy in 3..=4 {
437                    put(dx, dy, -1, Material::Madera); // respaldo (al fondo)
438                }
439            }
440        }
441
442        Mueble::Cama => {
443            for dx in -1..=1 {
444                for dz in -2..=3 {
445                    put(dx, 1, dz, Material::Madera); // bastidor
446                    put(dx, 2, dz, Material::Yeso); // colchón
447                }
448            }
449            for dz in -2..=3 {
450                put(-2, 1, dz, Material::Madera);
451                put(2, 1, dz, Material::Madera);
452            }
453            for dx in -1..=1 {
454                put(dx, 3, 3, Material::Bloom); // almohada (cabecera +Z)
455            }
456        }
457
458        Mueble::Estante => {
459            for dy in 0..=6 {
460                put(-2, dy, 0, Material::Madera); // laterales
461                put(2, dy, 0, Material::Madera);
462            }
463            for &sy in &[0, 2, 4, 6] {
464                for dx in -2..=2 {
465                    put(dx, sy, 0, Material::Madera); // estantes
466                }
467            }
468            for (bi, &sy) in [1i32, 3, 5].iter().enumerate() {
469                for dx in -1..=1 {
470                    let m = books[((dx + bi as i32).rem_euclid(4)) as usize]; // libros
471                    put(dx, sy, 0, m);
472                }
473            }
474        }
475
476        Mueble::Lampara => {
477            for dx in -1..=1 {
478                for dz in -1..=1 {
479                    put(dx, 0, dz, Material::Acero); // base
480                }
481            }
482            for dy in 1..=5 {
483                put(0, dy, 0, Material::Acero); // poste
484            }
485            for dx in -1..=1 {
486                for dz in -1..=1 {
487                    put(dx, 6, dz, Material::Wheat); // pantalla cálida (lustrosa)
488                }
489            }
490            put(0, 7, 0, Material::Wheat);
491        }
492
493        Mueble::Alfombra => {
494            for dx in -2i32..=2 {
495                for dz in -3i32..=3 {
496                    // Borde de un color, centro de otro.
497                    let borde = dx.abs() == 2 || dz.abs() == 3;
498                    put(dx, 0, dz, if borde { Material::Bloom } else { Material::Ceramica });
499                }
500            }
501        }
502
503        Mueble::Cofre => {
504            for dx in -1..=1 {
505                for dz in -1..=1 {
506                    for dy in 0..=1 {
507                        put(dx, dy, dz, Material::Madera); // cuerpo
508                    }
509                    put(dx, 2, dz, Material::Madera); // tapa
510                }
511            }
512            for dx in -1..=1 {
513                put(dx, 0, -1, Material::Acero); // herrajes al frente
514                put(dx, 2, -1, Material::Acero);
515            }
516            put(0, 1, -1, Material::Acero); // cerradura
517        }
518
519        Mueble::Estufa => {
520            for dx in -1..=1 {
521                for dz in -1..=1 {
522                    for dy in 0..=2 {
523                        put(dx, dy, dz, Material::Hormigon); // cuerpo
524                    }
525                    put(dx, 3, dz, Material::Acero); // hornalla de acero
526                }
527            }
528            put(0, 1, -1, Material::Lava); // boca de fuego (al frente)
529            put(0, 0, -1, Material::Lava);
530        }
531
532        Mueble::Sofa => {
533            for dx in -2..=2 {
534                put(dx, 0, 0, Material::Madera); // base
535                put(dx, 0, 1, Material::Madera);
536                put(dx, 1, 0, Material::Bloom); // asiento (tela)
537                put(dx, 1, 1, Material::Bloom);
538                put(dx, 2, -1, Material::Bloom); // respaldo
539                put(dx, 3, -1, Material::Bloom);
540            }
541            for dz in -1..=1 {
542                put(-2, 1, dz, Material::Bloom); // brazos
543                put(-2, 2, dz, Material::Bloom);
544                put(2, 1, dz, Material::Bloom);
545                put(2, 2, dz, Material::Bloom);
546            }
547        }
548
549        Mueble::Maceta => {
550            for dx in -1i32..=1 {
551                for dz in -1i32..=1 {
552                    put(dx, 0, dz, Material::Ceramica); // tiesto
553                    if dx.abs() == 1 || dz.abs() == 1 {
554                        put(dx, 1, dz, Material::Ceramica); // borde
555                    }
556                }
557            }
558            put(0, 1, 0, Material::Tierra); // tierra
559            put(0, 2, 0, Material::Leaf); // plantita
560            put(0, 3, 0, Material::Leaf);
561            put(1, 2, 0, Material::Leaf);
562            put(-1, 2, 0, Material::Leaf);
563            put(0, 2, 1, Material::Leaf);
564        }
565
566        Mueble::Barril => {
567            for dy in 0..=3 {
568                let m = if dy == 0 || dy == 3 { Material::Acero } else { Material::Madera };
569                for dx in -1..=1 {
570                    for dz in -1..=1 {
571                        if dx * dx + dz * dz <= 2 {
572                            put(dx, dy, dz, m); // duela octogonal + aros
573                        }
574                    }
575                }
576            }
577            put(0, 4, 0, Material::Madera); // tapa
578        }
579
580        Mueble::Escritorio => {
581            for dx in -2..=2 {
582                for dz in -1..=1 {
583                    put(dx, 3, dz, Material::Madera); // tapa
584                }
585            }
586            // Bloque de cajones a un lado.
587            for dy in 0..=2 {
588                for dz in -1..=1 {
589                    put(-2, dy, dz, Material::Madera);
590                    put(-1, dy, dz, Material::Madera);
591                }
592            }
593            for dy in &[0, 2] {
594                put(-2, *dy, -1, Material::Acero); // tiradores
595            }
596            for dy in 0..3 {
597                put(2, dy, -1, Material::Madera); // pata del otro lado
598                put(2, dy, 1, Material::Madera);
599            }
600        }
601
602        // ---------------- Plantas de interior ----------------
603        Mueble::Ficus => {
604            for dx in -1..=1 {
605                for dz in -1..=1 {
606                    put(dx, 0, dz, Material::Ceramica); // maceta
607                }
608            }
609            for dy in 1..=5 {
610                put(0, dy, 0, Material::Madera); // tronco
611            }
612            for dx in -2..=2 {
613                for dy in 5..=8 {
614                    for dz in -2..=2 {
615                        if dx * dx + (dy - 6) * (dy - 6) + dz * dz <= 5 {
616                            put(dx, dy, dz, Material::Leaf); // copa frondosa
617                        }
618                    }
619                }
620            }
621        }
622
623        Mueble::Palma => {
624            for dx in -1..=1 {
625                for dz in -1..=1 {
626                    put(dx, 0, dz, Material::Ceramica);
627                }
628            }
629            for dy in 1..=6 {
630                put(0, dy, 0, Material::Madera); // tronco fino
631            }
632            let ty = 6;
633            put(0, ty + 1, 0, Material::Leaf);
634            for (dxi, dzi) in [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1)] {
635                for s in 1..=2 {
636                    put(dxi * s, ty + 1 - s, dzi * s, Material::Leaf); // frondas que caen
637                }
638            }
639        }
640
641        Mueble::Suculenta => {
642            for dx in -1..=1 {
643                for dz in -1..=1 {
644                    put(dx, 0, dz, Material::Ceramica); // maceta baja
645                }
646            }
647            put(0, 1, 0, Material::Leaf); // roseta
648            for (dxi, dzi) in [(1, 0), (-1, 0), (0, 1), (0, -1)] {
649                put(dxi, 1, dzi, Material::Leaf);
650            }
651            put(0, 2, 0, Material::Leaf);
652        }
653
654        Mueble::Bonsai => {
655            for dx in -1..=1 {
656                for dz in -1..=1 {
657                    put(dx, 0, dz, Material::Ceramica); // maceta plana ancha
658                }
659            }
660            put(0, 1, 0, Material::Madera); // tronco corto
661            put(0, 2, 0, Material::Madera);
662            put(1, 2, 0, Material::Madera);
663            for (dxi, dzi) in [(1, 0), (2, 0), (0, 1), (-1, 1)] {
664                put(dxi, 3, dzi, Material::Leaf); // copas plato
665            }
666            put(0, 3, 0, Material::Leaf);
667        }
668
669        Mueble::Colgante => {
670            // Cesta a altura + enredaderas que caen.
671            for dx in -1..=1 {
672                put(dx, 6, 0, Material::Ceramica);
673            }
674            for &dx in &[-1, 0, 1] {
675                for dy in 2..=5 {
676                    if (dx + dy) % 2 == 0 {
677                        put(dx, dy, 0, Material::Leaf); // hebras verdes
678                    }
679                }
680            }
681        }
682
683        Mueble::Nopal => {
684            for dx in -1..=1 {
685                for dz in -1..=1 {
686                    put(dx, 0, dz, Material::Ceramica);
687                }
688            }
689            for dy in 1..=4 {
690                put(0, dy, 0, Material::Cactus); // columna
691            }
692            put(1, 2, 0, Material::Cactus); // brazos
693            put(1, 3, 0, Material::Cactus);
694            put(-1, 3, 0, Material::Cactus);
695        }
696
697        // ---------------- Decoración de pared ----------------
698        Mueble::Cuadro => {
699            let (y0, y1) = (4i32, 7i32);
700            for dy in y0..=y1 {
701                for dx in -1i32..=1 {
702                    let borde = dx.abs() == 1 || dy == y0 || dy == y1;
703                    put(dx, dy, 0, if borde { Material::Madera } else { Material::Bloom });
704                }
705            }
706        }
707
708        Mueble::Espejo => {
709            let (y0, y1) = (4i32, 7i32);
710            for dy in y0..=y1 {
711                for dx in -1i32..=1 {
712                    let borde = dx.abs() == 1 || dy == y0 || dy == y1;
713                    put(dx, dy, 0, if borde { Material::Acero } else { Material::Vidrio });
714                }
715            }
716        }
717
718        Mueble::Reloj => {
719            let cy = 5i32;
720            for dx in -1i32..=1 {
721                for dy in (cy - 1)..=(cy + 1) {
722                    if dx.abs() + (dy - cy).abs() <= 2 {
723                        put(dx, dy, 0, Material::Yeso); // esfera
724                    }
725                }
726            }
727            put(0, cy, 0, Material::Pizarra); // centro/agujas
728            put(0, cy + 1, 0, Material::Pizarra);
729            put(1, cy, 0, Material::Madera); // marco
730            put(-1, cy, 0, Material::Madera);
731        }
732
733        Mueble::Repisa => {
734            for dx in -2..=2 {
735                put(dx, 5, 0, Material::Madera); // estante flotante
736            }
737            put(-1, 6, 0, Material::Bloom); // un libro
738            put(-1, 7, 0, Material::Leaf);
739            put(1, 6, 0, Material::Ceramica); // una maceta chica
740            put(1, 7, 0, Material::Leaf);
741        }
742
743        Mueble::Aplique => {
744            put(0, 4, 0, Material::Acero); // placa
745            put(0, 5, 0, Material::Acero);
746            put(0, 6, 0, Material::Acero);
747            put(0, 5, 1, Material::Acero); // brazo
748            put(0, 6, 1, Material::Wheat); // luz cálida (lustrosa)
749            put(0, 4, 1, Material::Wheat);
750        }
751
752        Mueble::Tapiz => {
753            for dy in 2..=7 {
754                for dx in -1..=1 {
755                    let m = if (dx + dy) % 2 == 0 { Material::Bloom } else { Material::Leaf };
756                    put(dx, dy, 0, m); // paño de colores
757                }
758            }
759        }
760
761        // ---------------- Casas y edificios modernos ----------------
762        Mueble::Escalera => {
763            for s in 0..7i32 {
764                for dx in -1..=1 {
765                    for k in 0..=s {
766                        put(dx, k, s, Material::Hormigon); // peldaños macizos que suben en +Z
767                    }
768                }
769            }
770        }
771
772        Mueble::Baranda => {
773            for dx in -2..=2 {
774                put(dx, 1, 0, Material::Vidrio); // paño de vidrio
775                put(dx, 2, 0, Material::Vidrio);
776                put(dx, 3, 0, Material::Acero); // pasamanos
777            }
778            for &dx in &[-2i32, 0, 2] {
779                for dy in 0..3 {
780                    put(dx, dy, 0, Material::Acero); // montantes
781                }
782            }
783        }
784
785        Mueble::Ventanal => {
786            for dy in 0..9 {
787                for dx in -1..=1 {
788                    put(dx, dy, 0, Material::Vidrio); // paño de piso a techo
789                }
790                put(-2, dy, 0, Material::Acero); // parantes
791                put(2, dy, 0, Material::Acero);
792            }
793            for dx in -2..=2 {
794                put(dx, 0, 0, Material::Acero); // marco inf/sup
795                put(dx, 8, 0, Material::Acero);
796            }
797        }
798
799        Mueble::Columna => {
800            for dy in 0..10 {
801                for dx in 0..=1 {
802                    for dz in 0..=1 {
803                        put(dx, dy, dz, Material::Hormigon); // pilar 2×2
804                    }
805                }
806            }
807        }
808
809        Mueble::Aire => {
810            for dx in -2..=2 {
811                for dy in 6..8 {
812                    put(dx, dy, 0, Material::Yeso); // caja del split
813                }
814                put(dx, 6, 0, Material::Acero); // rejilla
815            }
816        }
817
818        Mueble::Solar => {
819            for dx in -2..=2 {
820                for dz in 0..4 {
821                    put(dx, 2 + dz, dz, Material::Azul); // placa azul inclinada
822                }
823            }
824            for &dx in &[-2i32, 2] {
825                for dy in 0..2 {
826                    put(dx, dy, 0, Material::Acero); // patas
827                }
828            }
829            put(0, 0, 0, Material::Acero);
830        }
831
832        Mueble::Tele => {
833            for dx in -2..=2 {
834                for dy in 3..6 {
835                    put(dx, dy, 0, Material::Pizarra); // pantalla negra
836                }
837            }
838            for dy in 0..3 {
839                put(0, dy, 0, Material::Acero); // pie
840            }
841            put(-1, 0, 1, Material::Acero); // base
842            put(1, 0, 1, Material::Acero);
843        }
844
845        Mueble::Isla => {
846            for dx in -2..=2 {
847                for dz in -1..=1 {
848                    for dy in 0..2 {
849                        put(dx, dy, dz, Material::Yeso); // bajomesada
850                    }
851                    put(dx, 2, dz, Material::Marmol); // mesada de mármol (lustrosa)
852                }
853            }
854        }
855
856        Mueble::Ducha => {
857            for dx in -1..=1 {
858                for dz in -1..=1 {
859                    put(dx, 0, dz, Material::Ceramica); // base
860                }
861            }
862            for dy in 1..7 {
863                for dx in -1..=1 {
864                    put(dx, dy, -1, Material::Vidrio); // mampara fondo
865                }
866                for dz in -1..=1 {
867                    put(-1, dy, dz, Material::Vidrio); // mampara lateral
868                }
869            }
870            put(0, 6, -1, Material::Acero); // ducha
871            put(0, 5, -1, Material::Acero);
872        }
873
874        Mueble::Placard => {
875            for dx in -1..=1 {
876                for dy in 0..7 {
877                    for dz in -1..=0 {
878                        put(dx, dy, dz, Material::Madera); // cuerpo alto
879                    }
880                }
881            }
882            put(-1, 3, 0, Material::Acero); // manijas
883            put(1, 3, 0, Material::Acero);
884        }
885
886        Mueble::Farol => {
887            for dy in 0..7 {
888                put(0, dy, 0, Material::Acero); // poste
889            }
890            for dx in -1..=1 {
891                for dz in -1..=1 {
892                    put(dx, 7, dz, Material::Vidrio); // farol de vidrio
893                    put(dx, 9, dz, Material::Acero); // capucha
894                }
895            }
896            put(0, 8, 0, Material::Wheat); // luz
897        }
898
899        Mueble::Pileta => {
900            for dx in -3i32..=3 {
901                for dz in -2i32..=2 {
902                    let borde = dx.abs() == 3 || dz.abs() == 2;
903                    put(dx, 0, dz, if borde { Material::Ceramica } else { Material::Water });
904                }
905            }
906        }
907
908        // ---------------- Elementos de ciudad ----------------
909        Mueble::Semaforo => {
910            for dy in 0..6 {
911                put(0, dy, 0, Material::Acero); // poste
912            }
913            for dy in 5..9 {
914                put(0, dy, 0, Material::Pizarra); // caja
915            }
916            put(0, 8, 1, Material::Lava); // rojo
917            put(0, 7, 1, Material::Wheat); // amarillo
918            put(0, 6, 1, Material::Leaf); // verde
919            put(1, 7, 0, Material::Acero); // brazo
920        }
921
922        Mueble::Banco => {
923            for dx in -2..=2 {
924                put(dx, 1, 0, Material::Madera); // asiento
925                put(dx, 2, -1, Material::Madera); // respaldo
926            }
927            for &dx in &[-2i32, 2] {
928                put(dx, 0, 0, Material::Acero); // patas
929            }
930        }
931
932        Mueble::Fuente => {
933            for dx in -2i32..=2 {
934                for dz in -2i32..=2 {
935                    if dx * dx + dz * dz <= 4 {
936                        put(dx, 0, dz, Material::Hormigon); // pileta de piedra
937                        if dx * dx + dz * dz <= 2 {
938                            put(dx, 1, dz, Material::Water); // agua
939                        }
940                    }
941                }
942            }
943            put(0, 1, 0, Material::Hormigon); // columna
944            put(0, 2, 0, Material::Hormigon);
945            put(0, 3, 0, Material::Water); // chorro
946        }
947
948        Mueble::Buzon => {
949            put(0, 0, 0, Material::Acero); // poste
950            put(0, 1, 0, Material::Acero);
951            for dx in -1..=1 {
952                for dy in 2..4 {
953                    put(dx, dy, 0, Material::Azul); // caja azul
954                }
955            }
956            put(1, 3, 0, Material::Azul);
957        }
958
959        Mueble::Basura => {
960            for dy in 0..3 {
961                for dx in -1i32..=1 {
962                    for dz in -1i32..=1 {
963                        if dx * dx + dz * dz <= 2 {
964                            put(dx, dy, dz, Material::Acero); // tacho
965                        }
966                    }
967                }
968            }
969            for dx in -1i32..=1 {
970                for dz in -1i32..=1 {
971                    if dx * dx + dz * dz <= 2 {
972                        put(dx, 3, dz, Material::Pizarra); // tapa
973                    }
974                }
975            }
976        }
977
978        Mueble::Parada => {
979            for &(px, pz) in &[(-2i32, -1i32), (2, -1), (-2, 1), (2, 1)] {
980                for dy in 0..5 {
981                    put(px, dy, pz, Material::Acero); // columnas
982                }
983            }
984            for dx in -2..=2 {
985                for dz in -1..=1 {
986                    put(dx, 5, dz, Material::Vidrio); // techo de vidrio
987                }
988                for dy in 1..4 {
989                    put(dx, dy, -1, Material::Vidrio); // fondo de vidrio
990                }
991                put(dx, 1, 0, Material::Madera); // banco
992            }
993        }
994
995        Mueble::Hidrante => {
996            for dy in 0..3 {
997                put(0, dy, 0, Material::Lava); // cuerpo rojo
998            }
999            put(1, 1, 0, Material::Lava); // brazos
1000            put(-1, 1, 0, Material::Lava);
1001            put(0, 3, 0, Material::Lava); // tapa
1002        }
1003
1004        Mueble::Cabina => {
1005            for dy in 0..7 {
1006                for dx in -1..=1 {
1007                    put(dx, dy, -1, Material::Vidrio); // paredes de vidrio
1008                    put(dx, dy, 1, Material::Vidrio);
1009                }
1010                put(-1, dy, 0, Material::Vidrio);
1011                for &(cx, cz) in &[(-1i32, -1i32), (1, -1), (-1, 1), (1, 1)] {
1012                    put(cx, dy, cz, Material::Acero); // esquinas
1013                }
1014            }
1015            for dx in -1..=1 {
1016                for dz in -1..=1 {
1017                    put(dx, 7, dz, Material::Azul); // techo azul
1018                }
1019            }
1020        }
1021
1022        Mueble::Cartel => {
1023            for dy in 0..5 {
1024                put(0, dy, 0, Material::Acero); // poste
1025            }
1026            for dx in -1..=1 {
1027                for dy in 5..7 {
1028                    put(dx, dy, 0, Material::Yeso); // letrero blanco
1029                }
1030            }
1031            put(0, 6, 0, Material::Azul); // franja
1032        }
1033
1034        Mueble::Auto => {
1035            for dx in -3..=3 {
1036                for dz in -1..=1 {
1037                    put(dx, 1, dz, body); // carrocería (color por posición)
1038                }
1039            }
1040            for dx in -1..=1 {
1041                for dz in -1..=1 {
1042                    put(dx, 2, dz, Material::Vidrio); // habitáculo/ventanas
1043                }
1044            }
1045            for &(wx, wz) in &[(-2i32, -1i32), (2, -1), (-2, 1), (2, 1)] {
1046                put(wx, 0, wz, Material::Pizarra); // ruedas
1047            }
1048        }
1049
1050        Mueble::Estatua => {
1051            for dx in -1..=1 {
1052                for dz in -1..=1 {
1053                    put(dx, 0, dz, Material::Marmol); // pedestal
1054                    put(dx, 1, dz, Material::Marmol);
1055                }
1056            }
1057            for dy in 2..5 {
1058                put(0, dy, 0, Material::Marmol); // figura
1059            }
1060            put(1, 3, 0, Material::Marmol); // brazos
1061            put(-1, 3, 0, Material::Marmol);
1062        }
1063
1064        Mueble::Bolardo => {
1065            for dy in 0..4 {
1066                put(0, dy, 0, Material::Hormigon); // poste bajo
1067            }
1068            for &(dx, dz) in &[(1, 0), (-1, 0), (0, 1), (0, -1)] {
1069                put(dx, 0, dz, Material::Hormigon); // base ancha
1070            }
1071            put(0, 4, 0, Material::Acero); // tapa
1072        }
1073
1074        // ---------------- Edificios grandes ----------------
1075        Mueble::Rascacielos => {
1076            let h = 20i32;
1077            for dy in 0..h {
1078                for dx in -2..=1 {
1079                    for dz in -2..=1 {
1080                        let borde = dx == -2 || dx == 1 || dz == -2 || dz == 1;
1081                        if borde {
1082                            let esquina = (dx == -2 || dx == 1) && (dz == -2 || dz == 1);
1083                            let banda = dy % 4 == 0;
1084                            put(dx, dy, dz, if esquina || banda { Material::Acero } else { Material::Vidrio });
1085                        }
1086                    }
1087                }
1088            }
1089            for dx in -2..=1 {
1090                for dz in -2..=1 {
1091                    put(dx, h, dz, Material::Hormigon); // azotea
1092                }
1093            }
1094            put(0, h + 1, 0, Material::Acero);
1095            put(0, h + 2, 0, Material::Acero); // antena
1096        }
1097
1098        Mueble::Edificio => {
1099            let h = 12i32;
1100            for dy in 0..h {
1101                for dx in -2i32..=2 {
1102                    for dz in -2..=1 {
1103                        let borde = dx == -2 || dx == 2 || dz == -2 || dz == 1;
1104                        if borde {
1105                            let ventana = dy > 0 && dy % 3 != 0 && dx.rem_euclid(2) == 0;
1106                            put(dx, dy, dz, if ventana { Material::Vidrio } else { Material::Ladrillo });
1107                        }
1108                    }
1109                }
1110            }
1111            for dx in -2..=2 {
1112                for dz in -2..=1 {
1113                    put(dx, h, dz, Material::Hormigon); // azotea
1114                }
1115            }
1116        }
1117
1118        Mueble::Torre => {
1119            let h = 22i32;
1120            for dy in 0..h {
1121                for dx in 0..=1 {
1122                    for dz in 0..=1 {
1123                        put(dx, dy, dz, Material::Hormigon);
1124                    }
1125                }
1126            }
1127            let mut dy = 2;
1128            while dy < h {
1129                put(0, dy, 0, Material::Vidrio); // ventanas
1130                put(1, dy, 1, Material::Vidrio);
1131                dy += 3;
1132            }
1133            for k in 0..4 {
1134                put(0, h + k, 0, Material::Acero); // antena
1135            }
1136        }
1137
1138        Mueble::Fabrica => {
1139            for dy in 0..6 {
1140                for dx in -3..=2 {
1141                    for dz in -2..=1 {
1142                        if dx == -3 || dx == 2 || dz == -2 || dz == 1 {
1143                            put(dx, dy, dz, Material::Hormigon);
1144                        }
1145                    }
1146                }
1147            }
1148            for dx in -3..=2 {
1149                for dz in -2..=1 {
1150                    put(dx, 6, dz, Material::Acero); // techo de zinc
1151                }
1152            }
1153            for dy in 0..12 {
1154                put(2, dy, 1, Material::Ladrillo); // chimenea
1155            }
1156            put(2, 12, 1, Material::Pizarra);
1157        }
1158
1159        Mueble::Iglesia => {
1160            for dy in 0..6 {
1161                for dx in -2..=1 {
1162                    for dz in -3..=2 {
1163                        if dx == -2 || dx == 1 || dz == -3 || dz == 2 {
1164                            put(dx, dy, dz, Material::Yeso);
1165                        }
1166                    }
1167                }
1168            }
1169            for dx in -2..=1 {
1170                for dz in -3..=2 {
1171                    put(dx, 6, dz, Material::Teja); // techo
1172                }
1173            }
1174            for dy in 0..11 {
1175                for cdx in -2..=-1 {
1176                    for cdz in -3..=-2 {
1177                        put(cdx, dy, cdz, Material::Yeso); // campanario
1178                    }
1179                }
1180            }
1181            put(-2, 11, -3, Material::Teja);
1182            // cruz
1183            put(-2, 12, -3, Material::Acero);
1184            put(-2, 13, -3, Material::Acero);
1185            put(-3, 13, -3, Material::Acero);
1186            put(-1, 13, -3, Material::Acero);
1187        }
1188
1189        Mueble::Galpon => {
1190            for dy in 0..5 {
1191                for dx in -4..=3 {
1192                    for dz in -3..=2 {
1193                        if dx == -4 || dx == 3 || dz == -3 || dz == 2 {
1194                            put(dx, dy, dz, Material::Hormigon);
1195                        }
1196                    }
1197                }
1198            }
1199            for dx in -4..=3 {
1200                for dz in -3..=2 {
1201                    let slope = (dz + 3).min(2 - dz).min(3);
1202                    put(dx, 5 + slope, dz, Material::Acero); // techo de zinc a dos aguas
1203                }
1204            }
1205            for dx in -1..=1 {
1206                for dy in 0..4 {
1207                    put(dx, dy, 2, Material::Acero); // portón
1208                }
1209            }
1210        }
1211
1212        Mueble::Silo => {
1213            for dy in 0..14 {
1214                for dx in -1i32..=1 {
1215                    for dz in -1i32..=1 {
1216                        if dx * dx + dz * dz <= 2 {
1217                            put(dx, dy, dz, Material::Acero); // cilindro
1218                        }
1219                    }
1220                }
1221            }
1222            for dx in -1i32..=1 {
1223                for dz in -1i32..=1 {
1224                    if dx * dx + dz * dz <= 2 {
1225                        put(dx, 14, dz, Material::Yeso); // cúpula
1226                    }
1227                }
1228            }
1229            put(0, 15, 0, Material::Yeso);
1230        }
1231
1232        Mueble::Estacionamiento => {
1233            for lvl in 0..4 {
1234                let y = lvl * 3;
1235                for dx in -3..=2 {
1236                    for dz in -2..=1 {
1237                        put(dx, y, dz, Material::Hormigon); // losa
1238                    }
1239                }
1240                for &(cx, cz) in &[(-3, -2), (2, -2), (-3, 1), (2, 1), (0, -2), (0, 1)] {
1241                    for k in 1..3 {
1242                        put(cx, y + k, cz, Material::Hormigon); // columnas
1243                    }
1244                }
1245            }
1246        }
1247
1248        // ---------------- Más ciudad ----------------
1249        Mueble::Farola => {
1250            for dy in 0..10 {
1251                put(0, dy, 0, Material::Acero); // poste alto
1252            }
1253            for dx in 0..=2 {
1254                put(dx, 10, 0, Material::Acero); // brazo
1255            }
1256            put(2, 9, 0, Material::Wheat); // luminaria
1257        }
1258
1259        Mueble::Contenedor => {
1260            for dy in 0..2 {
1261                for dx in -2..=2 {
1262                    for dz in -1..=1 {
1263                        if dx == -2 || dx == 2 || dz == -1 || dz == 1 || dy == 0 {
1264                            put(dx, dy, dz, Material::Acero); // volquete
1265                        }
1266                    }
1267                }
1268            }
1269            for dx in -2..=2 {
1270                for dz in -1..=1 {
1271                    put(dx, 2, dz, Material::Pizarra); // tapa
1272                }
1273            }
1274        }
1275
1276        Mueble::Grua => {
1277            for dy in 0..18 {
1278                put(0, dy, 0, Material::Acero); // mástil
1279            }
1280            for dx in 0..=6 {
1281                put(dx, 17, 0, Material::Acero); // pluma
1282            }
1283            for dx in -3..=0 {
1284                put(dx, 17, 0, Material::Acero); // contrapluma
1285            }
1286            put(-3, 16, 0, Material::Hormigon); // contrapeso
1287            put(4, 16, 0, Material::Acero); // cable
1288            put(4, 15, 0, Material::Acero); // gancho
1289        }
1290
1291        Mueble::Puente => {
1292            for dx in -4..=4 {
1293                for dz in -1..=1 {
1294                    put(dx, 3, dz, Material::Hormigon); // tablero
1295                }
1296                put(dx, 4, -1, Material::Acero); // barandas
1297                put(dx, 4, 1, Material::Acero);
1298            }
1299            for &px in &[-3i32, 3] {
1300                for dy in 0..3 {
1301                    for dz in -1..=1 {
1302                        put(px, dy, dz, Material::Hormigon); // pilas
1303                    }
1304                }
1305            }
1306        }
1307
1308        // ---------------- Transporte ----------------
1309        Mueble::Avion => {
1310            for dx in -4..=4 {
1311                for dy in 1..=2 {
1312                    put(dx, dy, 0, Material::Yeso); // fuselaje
1313                }
1314            }
1315            put(4, 2, 0, Material::Vidrio); // cabina
1316            for dz in -3..=3 {
1317                put(0, 1, dz, body); // alas (color por posición)
1318            }
1319            put(-4, 3, 0, Material::Yeso); // cola vertical
1320            put(-4, 4, 0, Material::Yeso);
1321            put(-4, 3, 1, Material::Yeso); // cola horizontal
1322            put(-4, 3, -1, Material::Yeso);
1323            put(0, 0, 2, Material::Pizarra); // turbinas
1324            put(0, 0, -2, Material::Pizarra);
1325        }
1326
1327        Mueble::Helicoptero => {
1328            for dx in -1..=1 {
1329                for dz in -1..=1 {
1330                    for dy in 1..3 {
1331                        put(dx, dy, dz, body); // cabina
1332                    }
1333                }
1334            }
1335            put(1, 2, 0, Material::Vidrio); // burbuja
1336            for dx in -4..=-2 {
1337                put(dx, 2, 0, Material::Yeso); // cola
1338            }
1339            put(-4, 3, 0, Material::Pizarra); // rotor de cola
1340            for dx in -3..=3 {
1341                put(dx, 4, 0, Material::Pizarra); // rotor principal
1342            }
1343            for dz in -3..=3 {
1344                put(0, 4, dz, Material::Pizarra);
1345            }
1346            put(0, 3, 0, Material::Acero); // mástil
1347            for dx in -1..=1 {
1348                put(dx, 0, -1, Material::Acero); // patines
1349                put(dx, 0, 1, Material::Acero);
1350            }
1351        }
1352
1353        Mueble::Camioneta => {
1354            for dx in -3..=3 {
1355                for dz in -1..=1 {
1356                    put(dx, 1, dz, body); // chasis
1357                }
1358            }
1359            for dx in 1..=3 {
1360                for dz in -1..=1 {
1361                    put(dx, 2, dz, if dz == 0 && dx == 2 { Material::Vidrio } else { body }); // cabina
1362                }
1363            }
1364            for dx in -3..=0 {
1365                put(dx, 2, -1, body); // caja
1366                put(dx, 2, 1, body);
1367            }
1368            for &(wx, wz) in &[(-2i32, -1i32), (2, -1), (-2, 1), (2, 1)] {
1369                put(wx, 0, wz, Material::Pizarra); // ruedas
1370            }
1371        }
1372
1373        Mueble::Camion => {
1374            for dx in 2..=3 {
1375                for dz in -1..=1 {
1376                    for dy in 1..3 {
1377                        put(dx, dy, dz, body); // cabina
1378                    }
1379                }
1380            }
1381            put(3, 2, 0, Material::Vidrio);
1382            for dx in -4..=1 {
1383                for dz in -1..=1 {
1384                    for dy in 1..4 {
1385                        if dx == -4 || dz == -1 || dz == 1 || dy == 3 {
1386                            put(dx, dy, dz, Material::Yeso); // caja de carga
1387                        }
1388                    }
1389                }
1390            }
1391            for &(wx, wz) in &[(-3i32, -1i32), (0, -1), (3, -1), (-3, 1), (0, 1), (3, 1)] {
1392                put(wx, 0, wz, Material::Pizarra);
1393            }
1394        }
1395
1396        Mueble::Colectivo => {
1397            for dx in -5..=5 {
1398                for dz in -1..=1 {
1399                    for dy in 1..4 {
1400                        if dx == -5 || dx == 5 || dz == -1 || dz == 1 {
1401                            put(dx, dy, dz, if dy == 3 { Material::Vidrio } else { body });
1402                        }
1403                    }
1404                }
1405                for dz in -1..=1 {
1406                    put(dx, 4, dz, body); // techo
1407                }
1408            }
1409            for &wx in &[-4i32, -1, 4] {
1410                put(wx, 0, -1, Material::Pizarra);
1411                put(wx, 0, 1, Material::Pizarra);
1412            }
1413        }
1414
1415        Mueble::Taxi => {
1416            for dx in -3..=3 {
1417                for dz in -1..=1 {
1418                    put(dx, 1, dz, Material::Wheat); // amarillo
1419                }
1420            }
1421            for dx in -1..=1 {
1422                for dz in -1..=1 {
1423                    put(dx, 2, dz, if dz == 0 { Material::Vidrio } else { Material::Wheat });
1424                }
1425            }
1426            put(0, 3, 0, Material::Pizarra); // cartel de techo
1427            for &(wx, wz) in &[(-2i32, -1i32), (2, -1), (-2, 1), (2, 1)] {
1428                put(wx, 0, wz, Material::Pizarra);
1429            }
1430        }
1431
1432        Mueble::Tren => {
1433            for dx in -5..=5 {
1434                for dz in -1..=1 {
1435                    for dy in 1..3 {
1436                        put(dx, dy, dz, Material::Lava); // cuerpo rojo
1437                    }
1438                }
1439            }
1440            for dx in 3..=5 {
1441                for dz in -1..=1 {
1442                    put(dx, 3, dz, if dz == 0 { Material::Vidrio } else { Material::Lava }); // cabina
1443                }
1444            }
1445            put(-5, 3, 0, Material::Pizarra); // chimenea
1446            let mut wx = -4;
1447            while wx <= 4 {
1448                put(wx, 0, -1, Material::Pizarra);
1449                put(wx, 0, 1, Material::Pizarra);
1450                wx += 2;
1451            }
1452        }
1453
1454        Mueble::Vagon => {
1455            for dx in -4..=4 {
1456                for dz in -1..=1 {
1457                    for dy in 1..3 {
1458                        if dx == -4 || dx == 4 || dz == -1 || dz == 1 || dy == 2 {
1459                            put(dx, dy, dz, body);
1460                        }
1461                    }
1462                }
1463            }
1464            let mut wx = -3;
1465            while wx <= 3 {
1466                put(wx, 0, -1, Material::Pizarra);
1467                put(wx, 0, 1, Material::Pizarra);
1468                wx += 3;
1469            }
1470        }
1471
1472        Mueble::Moto => {
1473            put(1, 0, 0, Material::Pizarra); // rueda delantera
1474            put(1, 1, 0, Material::Pizarra);
1475            put(-1, 0, 0, Material::Pizarra); // rueda trasera
1476            put(-1, 1, 0, Material::Pizarra);
1477            put(0, 1, 0, body); // cuerpo
1478            put(0, 2, 0, body); // asiento
1479            put(1, 2, 0, Material::Acero); // manubrio
1480        }
1481
1482        Mueble::Bici => {
1483            put(1, 0, 0, Material::Pizarra); // rueda delantera
1484            put(1, 1, 0, Material::Pizarra);
1485            put(-1, 0, 0, Material::Pizarra); // rueda trasera
1486            put(-1, 1, 0, Material::Pizarra);
1487            put(0, 1, 0, Material::Acero); // cuadro
1488            put(0, 2, 0, Material::Acero); // asiento
1489            put(1, 3, 0, Material::Acero); // manubrio
1490        }
1491
1492        Mueble::Barco => {
1493            for dx in -4..=4 {
1494                put(dx, 1, 0, body); // casco superior
1495            }
1496            for dx in -3..=3 {
1497                put(dx, 0, 0, body); // casco inferior
1498            }
1499            for dx in -1..=1 {
1500                put(dx, 2, 0, Material::Yeso); // cabina
1501                put(dx, 3, 0, Material::Vidrio);
1502            }
1503            put(0, 4, 0, Material::Acero); // mástil
1504            put(0, 5, 0, Material::Acero);
1505        }
1506
1507        Mueble::Ambulancia => {
1508            for dx in -3..=3 {
1509                for dz in -1..=1 {
1510                    for dy in 1..4 {
1511                        if dx == -3 || dx == 3 || dz == -1 || dz == 1 || dy == 3 {
1512                            put(dx, dy, dz, Material::Yeso); // furgón blanco
1513                        }
1514                    }
1515                }
1516            }
1517            put(3, 2, 0, Material::Vidrio); // parabrisas
1518            put(0, 4, 0, Material::Lava); // baliza roja
1519            put(0, 2, 1, Material::Lava); // cruz roja
1520            put(0, 2, -1, Material::Lava);
1521            for &(wx, wz) in &[(-2i32, -1i32), (2, -1), (-2, 1), (2, 1)] {
1522                put(wx, 0, wz, Material::Pizarra);
1523            }
1524        }
1525    }
1526}
1527
1528#[cfg(test)]
1529mod tests {
1530    use super::*;
1531
1532    fn solids(g: &VoxelGrid, dim: [u32; 3]) -> usize {
1533        let mut n = 0;
1534        for x in 0..dim[0] {
1535            for y in 0..dim[1] {
1536                for z in 0..dim[2] {
1537                    if g.is_solid(x as i32, y as i32, z as i32) {
1538                        n += 1;
1539                    }
1540                }
1541            }
1542        }
1543        n
1544    }
1545
1546    #[test]
1547    fn cada_mueble_estampa_voxels() {
1548        let dim = [24u32, 24, 24];
1549        for m in Mueble::ALL {
1550            let mut g = VoxelGrid::new(dim);
1551            stamp_mueble(&mut g, m, 12, 2, 12, 0);
1552            assert!(solids(&g, dim) >= 5, "{} estampó muy poco", m.label());
1553        }
1554    }
1555
1556    #[test]
1557    fn facing_rota_la_silueta() {
1558        // La silla (asimétrica por el respaldo) cambia de huella al rotar.
1559        let dim = [24u32, 24, 24];
1560        let mut a = VoxelGrid::new(dim);
1561        let mut b = VoxelGrid::new(dim);
1562        stamp_mueble(&mut a, Mueble::Silla, 12, 2, 12, 0);
1563        stamp_mueble(&mut b, Mueble::Silla, 12, 2, 12, 1);
1564        // El respaldo cae en una celda distinta según la orientación. Base (12,2,12),
1565        // respaldo local (0, 3, -1): facing 0 → (12,5,11); facing 1 → (13,5,12).
1566        let back0 = a.is_solid(12, 5, 11); // -Z de la base
1567        let back1 = b.is_solid(13, 5, 12); // rotado a +X
1568        assert!(back0, "respaldo en facing 0");
1569        assert!(back1, "respaldo rotó con el facing 1");
1570    }
1571}