morfeo 0.6.4 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,777 @@
1
+ // Segundo lote de piezas hechas en código: ciencia y cuerpo, accesorios de consola, servicios de casa y ciudad,
2
+ // vehículos y objetos de escuela. Son los huecos que quedaban al buscar en todas las bibliotecas libres.
3
+ // {tipo: 'modelo', forma: 'atomo'} {tipo: 'recurso', buscar: 'tanque de gas'}
4
+ // Cada pieza mide ~2 unidades, viene centrada y solo depende de `t`.
5
+ import React, {useMemo} from 'react';
6
+ import * as THREE from 'three';
7
+ import {ACERO, Aro, Aspas, C, COBRE, E, Helice, LATON, Mat, ORO, Tubo, Y, type M, type V3} from './objetos-ingenieria';
8
+
9
+ export const OBJETOS_CIENCIA = [
10
+ // ciencia y cuerpo
11
+ 'atomo', 'molecula', 'adn', 'celula', 'virus', 'cerebro', 'neurona', 'pulmon', 'ojo', 'diente', 'hueso', 'jeringa', 'matraz', 'telescopio', 'iman',
12
+ 'sol', 'tierra', 'volcan', 'tornado', 'iceberg', 'agujero_negro',
13
+ // consola y tecnología
14
+ 'disco', 'usb', 'tarjeta_sd', 'cable_hdmi', 'microfono', 'visor_vr', 'cargador', 'dron',
15
+ // casa y ciudad
16
+ 'tanque_gas', 'calentador_solar', 'termostato', 'detector_humo', 'camara_seguridad', 'antena_celular', 'presa', 'torre_enfriamiento', 'ascensor', 'fusible', 'fibra_optica', 'buzon',
17
+ // transporte
18
+ 'autobus', 'helicoptero', 'submarino', 'bicicleta',
19
+ // escuela y objetos
20
+ 'lapiz', 'regla', 'dado', 'ancla', 'paraguas',
21
+ ] as const;
22
+
23
+ /** Esfera aplastada o estirada. `s` = escala por eje. */
24
+ export const Ov: React.FC<{p?: V3; s: V3; rad?: number; c: string; m?: number; r?: number; e?: number; o?: number; rot?: V3}> = ({p = [0, 0, 0], s, rad = 1, rot, ...mat}) => (
25
+ <mesh position={p} scale={s} rotation={rot}>
26
+ <sphereGeometry args={[rad, 28, 18]} />
27
+ <Mat {...mat} />
28
+ </mesh>
29
+ );
30
+ /** Cono de pie. */
31
+ export const Cono: React.FC<{p?: V3; rad: number; h: number; c: string; m?: number; r?: number; e?: number; o?: number; rot?: V3; seg?: number}> = ({p = [0, 0, 0], rad, h, rot, seg = 28, ...mat}) => (
32
+ <mesh position={p} rotation={rot}>
33
+ <coneGeometry args={[rad, h, seg]} />
34
+ <Mat {...mat} />
35
+ </mesh>
36
+ );
37
+ export const azar = (n: number) => {
38
+ const x = Math.sin(n * 127.1) * 43758.5453;
39
+ return x - Math.floor(x);
40
+ };
41
+ /** Puntos repartidos sobre una esfera (espiral de Fibonacci). */
42
+ export const enEsfera = (n: number, rad: number): V3[] =>
43
+ Array.from({length: n}, (_, i) => {
44
+ const y = 1 - (i / (n - 1)) * 2;
45
+ const r = Math.sqrt(1 - y * y);
46
+ const a = i * 2.39996;
47
+ return [Math.cos(a) * r * rad, y * rad, Math.sin(a) * r * rad];
48
+ });
49
+
50
+ export const ObjetoCiencia: React.FC<{forma: string; t: number; opciones?: Record<string, any>} & M> = ({forma, t, opciones: o = {}, color}) => {
51
+ const giro = t * (o.velocidad ?? 1) * Math.PI * 2;
52
+ const k = (c: string) => color ?? c;
53
+ const pico = useMemo(() => enEsfera(26, 0.78), []);
54
+ switch (forma) {
55
+ // ------------------------------------------------------------ ciencia y cuerpo
56
+ case 'atomo':
57
+ return (
58
+ <group>
59
+ {[[0.12, 0.05, 0], [-0.1, 0.08, 0.05], [0, -0.12, 0.06], [0.03, 0.02, -0.13]].map((p, i) => (
60
+ <E key={i} p={p as V3} rad={0.17} c={i % 2 ? '#3D7BFF' : k('#E5484D')} r={0.3} />
61
+ ))}
62
+ {[0, 1, 2].map((i) => (
63
+ <group key={i} rotation={[Math.PI / 2, 0, 0]}>
64
+ <group rotation={[0, (i * Math.PI) / 3, 0]}>
65
+ <group rotation={[1.15, 0, 0]}>
66
+ <Aro rad={0.95} tubo={0.015} c="#C9D3E3" m={0.5} r={0.3} />
67
+ <E p={[Math.cos(giro * 0.5 + i * 2.1) * 0.95, Math.sin(giro * 0.5 + i * 2.1) * 0.95, 0]} rad={0.08} c="#FFD23F" e={0.6} r={0.2} />
68
+ </group>
69
+ </group>
70
+ </group>
71
+ ))}
72
+ </group>
73
+ );
74
+ case 'molecula':
75
+ return (
76
+ <group rotation={[0, 0, 0]}>
77
+ <E rad={0.55} c={k('#E5484D')} r={0.25} />
78
+ {[-1, 1].map((l) => (
79
+ <group key={l} rotation={[0, 0, l * 0.92]}>
80
+ <Y p={[0, -0.6, 0]} rad={0.08} h={0.6} c="#D9DEE6" r={0.4} />
81
+ <E p={[0, -0.95, 0]} rad={0.36} c="#F4F6F8" r={0.25} />
82
+ </group>
83
+ ))}
84
+ </group>
85
+ );
86
+ case 'adn':
87
+ return (
88
+ <group rotation={[0, giro * 0.15, 0.25]}>
89
+ {Array.from({length: 14}, (_, i) => {
90
+ const a = i * 0.55;
91
+ const y = -1.05 + i * 0.16;
92
+ return (
93
+ <group key={i} position={[0, y, 0]} rotation={[0, a, 0]}>
94
+ <E p={[0.42, 0, 0]} rad={0.1} c={k('#3D7BFF')} r={0.3} />
95
+ <E p={[-0.42, 0, 0]} rad={0.1} c="#E5484D" r={0.3} />
96
+ <Y eje="x" p={[0.2, 0, 0]} rad={0.035} h={0.4} c={['#FFD23F', '#3DDC84'][i % 2]} r={0.4} />
97
+ <Y eje="x" p={[-0.2, 0, 0]} rad={0.035} h={0.4} c={['#3DDC84', '#FFD23F'][i % 2]} r={0.4} />
98
+ </group>
99
+ );
100
+ })}
101
+ </group>
102
+ );
103
+ case 'celula':
104
+ return (
105
+ <group>
106
+ <Ov s={[1, 0.85, 0.9]} c={k('#7FD4C1')} r={0.2} o={0.45} />
107
+ <E p={[0.1, 0.05, 0]} rad={0.36} c="#7A4FD6" r={0.35} />
108
+ <E p={[0.16, 0.1, 0.2]} rad={0.14} c="#4B2FA0" r={0.35} />
109
+ {[[-0.5, 0.3, 0.2], [-0.45, -0.3, 0.1], [0.55, -0.35, 0.15], [0.5, 0.45, -0.1]].map((p, i) => (
110
+ <Ov key={i} p={p as V3} s={[0.2, 0.1, 0.1]} rot={[0, 0, i * 0.9]} c="#FF8A3D" r={0.4} />
111
+ ))}
112
+ {[[-0.1, -0.5, 0.3], [0.3, -0.1, 0.55], [-0.3, 0.5, -0.2]].map((p, i) => (
113
+ <E key={i} p={p as V3} rad={0.07} c="#FFD23F" r={0.4} />
114
+ ))}
115
+ </group>
116
+ );
117
+ case 'virus':
118
+ return (
119
+ <group rotation={[0, giro * 0.1, 0]}>
120
+ <E rad={0.7} c={k('#3DBE7B')} r={0.55} />
121
+ {pico.map((p, i) => (
122
+ <group key={i} position={p} quaternion={new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), new THREE.Vector3(...p).normalize())}>
123
+ <Y p={[0, 0.06, 0]} rad={0.035} h={0.22} c="#E5484D" r={0.5} seg={8} />
124
+ <E p={[0, 0.2, 0]} rad={0.08} c="#E5484D" r={0.45} />
125
+ </group>
126
+ ))}
127
+ </group>
128
+ );
129
+ case 'cerebro':
130
+ return (
131
+ <group>
132
+ {[-1, 1].map((l) => (
133
+ <group key={l} position={[l * 0.4, 0.12, 0]}>
134
+ <Ov s={[0.46, 0.6, 0.86]} c={k('#F2A0B0')} r={0.5} />
135
+ {/* surcos: cordones que serpentean sobre el hemisferio */}
136
+ {[-0.55, -0.3, -0.05, 0.2, 0.45].map((z, i) => {
137
+ const pts = Array.from({length: 7}, (_, n) => {
138
+ const a = -0.2 + (n / 6) * 2.6;
139
+ const zz = z + Math.sin(n * 1.7 + i * 2) * 0.09;
140
+ const esc = Math.sqrt(Math.max(0.05, 1 - (zz / 0.86) ** 2)) * 1.03;
141
+ return [l * Math.cos(a) * 0.46 * esc * (l > 0 ? 1 : 1), Math.sin(a) * 0.6 * esc, zz] as V3;
142
+ });
143
+ return <Tubo key={i} pts={pts} rad={0.045} c="#D9788C" r={0.55} />;
144
+ })}
145
+ </group>
146
+ ))}
147
+ <Ov p={[0, -0.42, -0.5]} s={[0.42, 0.26, 0.36]} c="#E88A9C" r={0.5} />
148
+ <Y p={[0, -0.72, -0.25]} rot={[0.3, 0, 0]} rad={[0.13, 0.09]} h={0.45} c="#F0C9B8" r={0.5} />
149
+ </group>
150
+ );
151
+ case 'neurona':
152
+ return (
153
+ <group position={[-0.5, 0.2, 0]}>
154
+ <E rad={0.34} c={k('#7A8CFF')} r={0.35} />
155
+ <E rad={0.15} p={[0.05, 0.05, 0.25]} c="#4B58C7" r={0.35} />
156
+ {[0.6, 1.5, 2.4, 3.3, 4.2, 5.2].map((a, i) => (
157
+ <Tubo key={i} pts={[[Math.cos(a) * 0.3, Math.sin(a) * 0.3, 0], [Math.cos(a + 0.2) * 0.6, Math.sin(a + 0.2) * 0.6, 0.05], [Math.cos(a - 0.15) * 0.85, Math.sin(a - 0.15) * 0.85, -0.05]]} rad={0.04} c={k('#7A8CFF')} r={0.4} />
158
+ ))}
159
+ <Tubo pts={[[0.3, -0.1, 0], [0.8, -0.3, 0], [1.3, -0.25, 0], [1.75, -0.5, 0]]} rad={0.05} c={k('#7A8CFF')} r={0.4} />
160
+ {[0.75, 1.1, 1.45].map((x, i) => (
161
+ <Ov key={x} p={[x, -0.29 - i * 0.02, 0]} s={[0.16, 0.1, 0.1]} c="#FFD23F" r={0.4} />
162
+ ))}
163
+ {[-0.3, 0, 0.3].map((d) => (
164
+ <E key={d} p={[1.9, -0.55 + d, 0]} rad={0.07} c="#FF8A3D" e={0.3 + 0.5 * Math.max(0, Math.sin(t * 5 + d * 4))} />
165
+ ))}
166
+ </group>
167
+ );
168
+ case 'pulmon':
169
+ return (
170
+ <group>
171
+ <Y p={[0, 0.75, 0]} rad={0.1} h={0.6} c="#F0D7CC" r={0.5} />
172
+ {[-1, 1].map((l) => (
173
+ <group key={l}>
174
+ <Y p={[l * 0.18, 0.4, 0]} rot={[0, 0, l * -0.9]} rad={0.07} h={0.42} c="#F0D7CC" r={0.5} />
175
+ <Ov p={[l * 0.52, -0.15, 0]} s={[0.42 * (1 + 0.04 * Math.sin(t * 2)), 0.85 * (1 + 0.03 * Math.sin(t * 2)), 0.4]} rot={[0, 0, l * 0.12]} c={k('#E8798A')} r={0.5} />
176
+ <Ov p={[l * 0.42, -0.6, 0.05]} s={[0.36, 0.42, 0.36]} c={k('#DE6B7D')} r={0.5} />
177
+ </group>
178
+ ))}
179
+ </group>
180
+ );
181
+ case 'ojo':
182
+ return (
183
+ <group rotation={[0, Math.sin(t * 1.3) * 0.25, 0]}>
184
+ <E rad={0.9} c="#F7F5F0" r={0.2} />
185
+ <Ov p={[0, 0, 0.62]} s={[0.46, 0.46, 0.3]} c={k('#3D8BD6')} r={0.25} />
186
+ <Ov p={[0, 0, 0.82]} s={[0.22, 0.22, 0.12]} c="#101114" r={0.2} />
187
+ <E p={[0.14, 0.16, 0.9]} rad={0.05} c="#FFFFFF" e={0.6} />
188
+ </group>
189
+ );
190
+ case 'diente':
191
+ return (
192
+ <group>
193
+ <Ov p={[0, 0.35, 0]} s={[0.68, 0.55, 0.55]} c={k('#FAF8F2')} r={0.2} />
194
+ {[-0.3, 0.3].map((x) => (
195
+ <E key={x} p={[x, 0.78, 0]} rad={0.24} c={k('#FAF8F2')} r={0.2} />
196
+ ))}
197
+ {[-1, 1].map((l) => (
198
+ <Cono key={l} p={[l * 0.3, -0.5, 0]} rot={[Math.PI, 0, l * 0.12]} rad={0.26} h={1.2} c="#F1EBDD" r={0.35} />
199
+ ))}
200
+ </group>
201
+ );
202
+ case 'hueso':
203
+ return (
204
+ <group rotation={[0, 0, 0.6]}>
205
+ <Y rad={0.15} h={1.5} c={k('#F4EFE3')} r={0.5} />
206
+ {[-1, 1].map((ly) => [-1, 1].map((lx) => <E key={`${ly}${lx}`} p={[lx * 0.17, ly * 0.82, 0]} rad={0.24} c={k('#F4EFE3')} r={0.5} />))}
207
+ </group>
208
+ );
209
+ case 'jeringa': {
210
+ const lleno = Math.max(0, Math.min(1, o.nivel ?? 0.6));
211
+ return (
212
+ <group rotation={[0, 0, -0.7]}>
213
+ <Y rad={0.24} h={1.3} c="#EAF4FF" r={0.1} o={0.35} />
214
+ <Y p={[0, -0.65 + 0.6 * lleno, 0]} rad={0.21} h={1.2 * lleno} c={k('#5AC8FA')} r={0.2} o={0.8} />
215
+ <Y p={[0, -0.6 + 1.2 * lleno, 0]} rad={0.22} h={0.1} c="#22242A" />
216
+ <Y p={[0, 0.25 + 0.6 * lleno, 0]} rad={0.05} h={1.3 - 1.2 * (1 - lleno) * 0} c="#DDE3EA" r={0.4} />
217
+ <Y p={[0, 0.95 + 0.6 * lleno, 0]} rad={0.26} h={0.06} c="#DDE3EA" r={0.4} />
218
+ <Y p={[0, 0.67, 0]} rad={0.36} h={0.05} c="#DDE3EA" r={0.4} />
219
+ <Y p={[0, -0.75, 0]} rad={[0.1, 0.05]} h={0.2} c="#3D7BFF" r={0.4} />
220
+ <Y p={[0, -1.15, 0]} rad={0.014} h={0.6} {...ACERO} />
221
+ </group>
222
+ );
223
+ }
224
+ case 'matraz': {
225
+ const c = k('#3DDC84');
226
+ return (
227
+ <group position={[0, -0.1, 0]}>
228
+ <Cono p={[0, -0.25, 0]} rad={0.85} h={1.3} c="#EAF4FF" r={0.08} o={0.28} />
229
+ <Y p={[0, -0.62, 0]} rad={[0.5, 0.82]} h={0.5} c={c} r={0.2} o={0.85} e={0.15} />
230
+ <Y p={[0, 0.65, 0]} rad={0.2} h={0.7} c="#EAF4FF" r={0.08} o={0.28} />
231
+ <Aro p={[0, 1.0, 0]} rot={[Math.PI / 2, 0, 0]} rad={0.21} tubo={0.035} c="#EAF4FF" r={0.1} o={0.5} />
232
+ {[0, 1, 2].map((i) => {
233
+ const f = (t * 0.5 + i / 3) % 1;
234
+ return <E key={i} p={[-0.2 + i * 0.2, -0.75 + f * 0.6, 0.1]} rad={0.05 + i * 0.012} c="#FFFFFF" o={0.7 * (1 - f)} />;
235
+ })}
236
+ </group>
237
+ );
238
+ }
239
+ case 'telescopio':
240
+ return (
241
+ <group>
242
+ <group position={[0, 0.35, 0]} rotation={[0, 0, 0.6]}>
243
+ <Y eje="x" rad={0.22} h={1.5} c={k('#F2F3F5')} r={0.35} />
244
+ <Y eje="x" p={[0.8, 0, 0]} rad={0.26} h={0.14} c="#22242A" r={0.4} />
245
+ <Y eje="x" p={[-0.9, 0, 0]} rad={0.12} h={0.35} c="#22242A" r={0.4} />
246
+ <Y eje="x" p={[0.1, 0.3, 0]} rad={0.06} h={0.6} c="#22242A" r={0.4} />
247
+ </group>
248
+ {[0, 1, 2].map((i) => (
249
+ <Y key={i} p={[Math.cos((i * Math.PI * 2) / 3) * 0.33, -0.5, Math.sin((i * Math.PI * 2) / 3) * 0.33]} rot={[Math.sin((i * Math.PI * 2) / 3) * 0.4, 0, -Math.cos((i * Math.PI * 2) / 3) * 0.4]} rad={0.035} h={1.4} {...ACERO} />
250
+ ))}
251
+ <E p={[0, 0.2, 0]} rad={0.1} c="#22242A" />
252
+ </group>
253
+ );
254
+ case 'iman':
255
+ return (
256
+ <group rotation={[0, 0, Math.PI]}>
257
+ <Aro p={[0, -0.2, 0]} rot={[0, 0, 0]} rad={0.6} tubo={0.22} arco={Math.PI} c={k('#D7372F')} r={0.35} />
258
+ {[-1, 1].map((l) => (
259
+ <group key={l}>
260
+ <Y p={[l * 0.6, -0.5, 0]} rad={0.22} h={0.6} c={k('#D7372F')} r={0.35} />
261
+ <Y p={[l * 0.6, -0.95, 0]} rad={0.22} h={0.3} c="#DDE3EA" m={0.7} r={0.3} />
262
+ </group>
263
+ ))}
264
+ </group>
265
+ );
266
+ case 'sol':
267
+ return (
268
+ <group>
269
+ <E rad={0.72} c={k('#FFC93C')} e={0.9} r={0.4} />
270
+ {Array.from({length: 12}, (_, i) => (
271
+ <group key={i} rotation={[0, 0, (i / 12) * Math.PI * 2 + giro * 0.05]}>
272
+ <Cono p={[0, 0.98 + 0.04 * Math.sin(t * 3 + i), 0]} rad={0.1} h={i % 2 ? 0.3 : 0.44} c="#FFB020" e={0.8} seg={10} />
273
+ </group>
274
+ ))}
275
+ </group>
276
+ );
277
+ case 'tierra':
278
+ return (
279
+ <group rotation={[0.3, giro * 0.08, 0.2]}>
280
+ <E rad={0.95} c={k('#2F7FD6')} r={0.45} />
281
+ {[[0.3, 0.5, 0.74, 0.42, 0.3], [-0.5, 0.2, 0.78, 0.3, 0.42], [0.1, -0.45, 0.83, 0.26, 0.34], [-0.75, -0.3, -0.5, 0.36, 0.3], [0.6, 0.1, -0.73, 0.4, 0.5], [0.1, 0.75, -0.57, 0.3, 0.22], [-0.2, -0.7, -0.6, 0.28, 0.24]].map(([x, y, z, a, b], i) => (
282
+ <Ov key={i} p={[x, y, z]} s={[a, b, a]} rad={0.5} c="#3DBE6A" r={0.6} />
283
+ ))}
284
+ <Ov p={[0, 0.93, 0]} s={[0.4, 0.12, 0.4]} rad={1} c="#F4F8FB" r={0.4} />
285
+ <Ov p={[0, -0.93, 0]} s={[0.36, 0.12, 0.36]} rad={1} c="#F4F8FB" r={0.4} />
286
+ </group>
287
+ );
288
+ case 'volcan':
289
+ return (
290
+ <group position={[0, -0.2, 0]}>
291
+ <Y p={[0, -0.2, 0]} rad={[0.32, 1.15]} h={1.4} c={k('#5A4A42')} r={0.9} seg={9} />
292
+ <Y p={[0, 0.48, 0]} rad={[0.26, 0.33]} h={0.08} c="#FF5A1F" e={0.9} seg={9} />
293
+ {[0.3, 1.6, 3.1, 4.5].map((a, i) => (
294
+ <Tubo key={i} pts={[[Math.cos(a) * 0.28, 0.46, Math.sin(a) * 0.28], [Math.cos(a) * 0.5, 0.05, Math.sin(a) * 0.5], [Math.cos(a + 0.2) * 0.75, -0.45, Math.sin(a + 0.2) * 0.75]]} rad={0.06} c="#FF5A1F" e={0.8} />
295
+ ))}
296
+ {[0, 1, 2, 3].map((i) => {
297
+ const f = (t * 0.35 + i / 4) % 1;
298
+ return <E key={i} p={[Math.sin(i * 2.3) * 0.25 * f, 0.65 + f * 0.9, Math.cos(i * 1.7) * 0.15 * f]} rad={0.18 + f * 0.2} c="#8C8782" r={1} o={0.65 * (1 - f)} />;
299
+ })}
300
+ </group>
301
+ );
302
+ case 'tornado':
303
+ return (
304
+ <group>
305
+ {Array.from({length: 9}, (_, i) => {
306
+ const y = -1 + i * 0.25;
307
+ const r = 0.14 + i * 0.1;
308
+ return <Aro key={i} p={[Math.sin(t * 2 + i * 0.6) * 0.06 * i * 0.3, y, Math.cos(t * 2 + i * 0.6) * 0.04]} rot={[Math.PI / 2, 0, giro * 0.6 + i]} rad={r} tubo={0.07 + i * 0.006} arco={Math.PI * 1.75} c={k('#9AA4B2')} r={0.9} o={0.85} />;
309
+ })}
310
+ </group>
311
+ );
312
+ case 'iceberg':
313
+ return (
314
+ <group>
315
+ <Cono p={[0, 0.45, 0]} rad={0.6} h={0.9} c={k('#EAF6FF')} r={0.35} seg={6} />
316
+ <Cono p={[0.35, 0.3, 0.1]} rad={0.35} h={0.6} c="#D6ECFA" r={0.35} seg={5} />
317
+ <Cono p={[0, -0.55, 0]} rot={[Math.PI, 0, 0]} rad={0.95} h={1.1} c="#8ED0F2" r={0.3} o={0.75} seg={6} />
318
+ <Y rad={1.2} h={0.02} c="#2F8FD6" r={0.15} o={0.45} seg={40} />
319
+ </group>
320
+ );
321
+ case 'agujero_negro':
322
+ return (
323
+ <group rotation={[1.15, 0, 0.25]}>
324
+ <E rad={0.5} c="#050507" r={1} />
325
+ {[0.7, 0.86, 1.02].map((r, i) => (
326
+ <Aro key={r} rot={[0, 0, giro * (0.2 - i * 0.05)]} rad={r} tubo={0.07 - i * 0.012} arco={Math.PI * 1.9} c={k(['#FFE9A8', '#FFB23F', '#FF6A1F'][i])} e={1 - i * 0.15} r={0.4} />
327
+ ))}
328
+ </group>
329
+ );
330
+ // ------------------------------------------------------------ consola y tecnología
331
+ case 'disco':
332
+ return (
333
+ <group rotation={[-0.5, giro * 0.2, 0]}>
334
+ <mesh>
335
+ <ringGeometry args={[0.16, 1, 64]} />
336
+ <meshStandardMaterial color={k('#D9DEE8')} metalness={0.95} roughness={0.12} side={THREE.DoubleSide} />
337
+ </mesh>
338
+ {[0.4, 0.62, 0.84].map((r, i) => (
339
+ <mesh key={r} position={[0, 0, 0.004]}>
340
+ <ringGeometry args={[r, r + 0.12, 64, 1, i * 1.3, 1.1]} />
341
+ <meshStandardMaterial color={['#7FD4FF', '#C79BFF', '#9BFFC4'][i]} metalness={0.9} roughness={0.15} transparent opacity={0.55} side={THREE.DoubleSide} />
342
+ </mesh>
343
+ ))}
344
+ <Aro rad={0.2} tubo={0.03} c="#F4F6F8" r={0.2} o={0.6} />
345
+ </group>
346
+ );
347
+ case 'usb':
348
+ return (
349
+ <group rotation={[0, 0, 0.5]}>
350
+ <C p={[0, -0.25, 0]} s={[0.7, 1.3, 0.26]} c={k('#22242A')} r={0.4} />
351
+ <C p={[0, 0.72, 0]} s={[0.55, 0.65, 0.2]} {...ACERO} />
352
+ {[-0.13, 0.13].map((x) => (
353
+ <C key={x} p={[x, 0.85, 0.105]} s={[0.12, 0.12, 0.01]} c="#15161A" />
354
+ ))}
355
+ <E p={[0, -0.7, 0.14]} rad={0.04} c="#3D7BFF" e={0.5 + 0.5 * Math.sin(t * 6)} />
356
+ </group>
357
+ );
358
+ case 'tarjeta_sd':
359
+ return (
360
+ <group rotation={[-0.3, 0, 0]}>
361
+ <C s={[1.3, 1.7, 0.08]} c={k('#22242A')} r={0.45} />
362
+ <C p={[0, 0.25, 0.045]} s={[1.0, 0.7, 0.005]} c="#E0352B" r={0.5} />
363
+ <C p={[0, -0.45, 0.045]} s={[1.0, 0.3, 0.005]} c="#F4F6F8" r={0.5} />
364
+ {Array.from({length: 7}, (_, i) => (
365
+ <C key={i} p={[-0.45 + i * 0.15, 0.62, -0.045]} s={[0.09, 0.36, 0.005]} {...ORO} />
366
+ ))}
367
+ </group>
368
+ );
369
+ case 'cable_hdmi':
370
+ return (
371
+ <group>
372
+ <Tubo pts={[[-0.9, -0.5, 0], [-0.5, 0.3, 0.1], [0.1, -0.3, -0.1], [0.55, 0.1, 0]]} rad={0.07} c="#1B1C22" r={0.6} />
373
+ <group position={[0.95, 0.42, 0]} rotation={[0, 0, 0.62]}>
374
+ <C s={[0.7, 0.5, 0.24]} c={k('#22242A')} r={0.45} />
375
+ <C p={[0.5, 0, 0]} s={[0.36, 0.4, 0.14]} {...ACERO} />
376
+ <C p={[0.69, 0, 0]} s={[0.01, 0.3, 0.07]} {...ORO} />
377
+ </group>
378
+ </group>
379
+ );
380
+ case 'microfono':
381
+ return (
382
+ <group>
383
+ <Ov p={[0, 0.7, 0]} s={[0.36, 0.42, 0.36]} c={k('#3A3D45')} m={0.7} r={0.5} />
384
+ {[0.55, 0.7, 0.85].map((y) => (
385
+ <Aro key={y} p={[0, y, 0]} rot={[Math.PI / 2, 0, 0]} rad={0.36 * Math.sqrt(1 - ((y - 0.7) / 0.42) ** 2)} tubo={0.015} {...ACERO} />
386
+ ))}
387
+ <Y p={[0, 0.3, 0]} rad={0.37} h={0.08} {...ACERO} />
388
+ <Y p={[0, -0.25, 0]} rad={[0.2, 0.15]} h={1.1} c="#22242A" r={0.45} />
389
+ <Y p={[0, -0.9, 0]} rad={0.06} h={0.3} {...ACERO} />
390
+ <Y p={[0, -1.05, 0]} rad={0.55} h={0.07} c="#22242A" r={0.45} />
391
+ </group>
392
+ );
393
+ case 'visor_vr':
394
+ return (
395
+ <group>
396
+ <C s={[1.7, 0.85, 0.7]} c={k('#F2F3F5')} r={0.35} />
397
+ <C p={[0, 0, 0.36]} s={[1.6, 0.75, 0.04]} c="#15161A" r={0.2} />
398
+ {[-0.4, 0.4].map((x) => (
399
+ <Y key={x} eje="z" p={[x, 0, 0.385]} rad={0.2} h={0.02} c="#2B6FD6" e={0.4} r={0.1} />
400
+ ))}
401
+ <Aro p={[0, 0, -0.75]} rot={[Math.PI / 2, 0, 0]} rad={0.85} tubo={0.06} arco={Math.PI} c="#22242A" r={0.6} />
402
+ </group>
403
+ );
404
+ case 'cargador':
405
+ return (
406
+ <group>
407
+ <C p={[0, 0.2, 0]} s={[1.0, 1.0, 0.6]} c={k('#F4F5F7')} r={0.35} />
408
+ {[-0.2, 0.2].map((x) => (
409
+ <C key={x} p={[x, 0.95, 0]} s={[0.05, 0.5, 0.22]} {...ACERO} />
410
+ ))}
411
+ <Tubo pts={[[0, -0.3, 0], [0.05, -0.7, 0.1], [0.5, -0.9, 0], [0.85, -0.6, -0.1]]} rad={0.05} c="#F4F5F7" r={0.45} />
412
+ <C p={[0.95, -0.5, -0.12]} rot={[0, 0, 0.8]} s={[0.3, 0.2, 0.1]} c="#DDE0E5" r={0.4} />
413
+ <E p={[0.35, 0.55, 0.31]} rad={0.035} c="#3DDC84" e={1} />
414
+ </group>
415
+ );
416
+ case 'dron':
417
+ return (
418
+ <group rotation={[0.25, 0, 0]} position={[0, Math.sin(t * 2.5) * 0.06, 0]}>
419
+ <C s={[0.7, 0.2, 0.5]} c={k('#22242A')} r={0.4} />
420
+ <Y eje="z" p={[0, -0.05, 0.28]} rad={0.08} h={0.1} c="#15161A" />
421
+ {[-1, 1].map((lx) =>
422
+ [-1, 1].map((lz) => (
423
+ <group key={`${lx}${lz}`}>
424
+ <C p={[lx * 0.5, 0, lz * 0.4]} rot={[0, lx * lz * -0.67, 0]} s={[0.75, 0.06, 0.08]} c="#3A3D45" r={0.5} />
425
+ <Y p={[lx * 0.82, 0.08, lz * 0.66]} rad={0.07} h={0.16} c="#3A3D45" />
426
+ <group position={[lx * 0.82, 0.18, lz * 0.66]} rotation={[Math.PI / 2, 0, 0]}>
427
+ <Aspas n={2} rad={0.34} giro={giro * 5 * lx * lz} c="#C9CDD4" ancho={0.14} />
428
+ </group>
429
+ </group>
430
+ )),
431
+ )}
432
+ </group>
433
+ );
434
+ // ------------------------------------------------------------ casa y ciudad
435
+ case 'tanque_gas':
436
+ return (
437
+ <group>
438
+ <Y eje="x" rad={0.55} h={1.4} c={k('#E9E6E0')} r={0.4} seg={36} />
439
+ {[-0.7, 0.7].map((x) => (
440
+ <Ov key={x} p={[x, 0, 0]} s={[0.3, 0.55, 0.55]} c={k('#E9E6E0')} r={0.4} />
441
+ ))}
442
+ <Y p={[0, 0.6, 0]} rad={0.12} h={0.16} {...LATON} />
443
+ <Y p={[0, 0.72, 0]} rad={0.16} h={0.05} c="#D7372F" r={0.4} />
444
+ {[-0.5, 0.5].map((x) => (
445
+ <C key={x} p={[x, -0.62, 0]} s={[0.12, 0.2, 0.8]} c="#6B727C" r={0.5} />
446
+ ))}
447
+ <C p={[0, 0, 0.552]} s={[0.5, 0.2, 0.01]} c="#D7372F" />
448
+ </group>
449
+ );
450
+ case 'calentador_solar':
451
+ return (
452
+ <group rotation={[0, -0.3, 0]}>
453
+ <Y eje="x" p={[0, 0.65, -0.45]} rad={0.26} h={1.7} c={k('#C9CDD4')} m={0.8} r={0.3} />
454
+ {Array.from({length: 8}, (_, i) => (
455
+ <Y key={i} p={[-0.7 + i * 0.2, 0, 0]} rot={[-0.75, 0, 0]} rad={0.055} h={1.5} c="#16233F" r={0.15} m={0.4} />
456
+ ))}
457
+ <C p={[0, -0.55, 0.52]} s={[1.7, 0.08, 0.12]} {...ACERO} />
458
+ {[-0.8, 0.8].map((x) => (
459
+ <C key={x} p={[x, -0.1, -0.5]} s={[0.05, 1.2, 0.05]} {...ACERO} />
460
+ ))}
461
+ </group>
462
+ );
463
+ case 'termostato':
464
+ return (
465
+ <group>
466
+ <Y eje="z" rad={0.9} h={0.22} c={k('#F4F5F7')} r={0.3} seg={48} />
467
+ <Y eje="z" p={[0, 0, 0.12]} rad={0.72} h={0.02} c="#15161A" r={0.15} seg={48} />
468
+ <Aro p={[0, 0, 0.13]} rot={[0, 0, Math.PI * 0.75]} rad={0.58} tubo={0.035} arco={Math.PI * 1.5 * (o.nivel ?? 0.6)} c="#FF8A3D" e={0.9} />
469
+ <C p={[0, 0.05, 0.135]} s={[0.4, 0.26, 0.005]} c="#F4F6F8" e={0.6} />
470
+ </group>
471
+ );
472
+ case 'detector_humo':
473
+ return (
474
+ <group rotation={[-0.6, 0, 0]}>
475
+ <Y eje="z" rad={0.9} h={0.22} c={k('#F4F5F7')} r={0.4} seg={48} />
476
+ <Y eje="z" p={[0, 0, 0.14]} rad={0.6} h={0.1} c="#ECEDEF" r={0.4} seg={48} />
477
+ {Array.from({length: 10}, (_, i) => (
478
+ <C key={i} p={[Math.cos((i / 10) * Math.PI * 2) * 0.72, Math.sin((i / 10) * Math.PI * 2) * 0.72, 0.115]} rot={[0, 0, (i / 10) * Math.PI * 2]} s={[0.16, 0.04, 0.01]} c="#9AA1AB" />
479
+ ))}
480
+ <E p={[0, 0.3, 0.2]} rad={0.045} c="#E0352B" e={Math.sin(t * 4) > 0.6 ? 1 : 0.1} />
481
+ </group>
482
+ );
483
+ case 'camara_seguridad':
484
+ return (
485
+ <group rotation={[0, -0.5, 0]}>
486
+ <C p={[-0.9, 0.5, 0]} s={[0.12, 0.7, 0.5]} c="#DDE0E5" r={0.4} />
487
+ <Tubo pts={[[-0.85, 0.5, 0], [-0.5, 0.55, 0], [-0.3, 0.3, 0]]} rad={0.06} c="#DDE0E5" r={0.4} />
488
+ <group rotation={[0, Math.sin(t * 0.8) * 0.35, -0.25]} position={[0.1, 0.05, 0]}>
489
+ <Y eje="x" rad={0.3} h={1.1} c={k('#F4F5F7')} r={0.35} />
490
+ <C p={[0.1, 0.3, 0]} s={[1.3, 0.05, 0.7]} c="#F4F5F7" r={0.35} />
491
+ <Y eje="x" p={[0.56, 0, 0]} rad={0.24} h={0.04} c="#15161A" r={0.1} />
492
+ <Y eje="x" p={[0.58, 0, 0]} rad={0.12} h={0.04} c="#1F3F8F" r={0.05} />
493
+ <E p={[0.57, 0.17, 0.12]} rad={0.03} c="#E0352B" e={1} />
494
+ </group>
495
+ </group>
496
+ );
497
+ case 'antena_celular':
498
+ return (
499
+ <group>
500
+ <Y rad={[0.05, 0.09]} h={2.3} {...ACERO} c={k(ACERO.c)} />
501
+ {[0, 1, 2].map((i) => (
502
+ <group key={i} rotation={[0, (i * Math.PI * 2) / 3, 0]}>
503
+ <C p={[0, 0.75, 0.2]} s={[0.18, 0.7, 0.08]} c="#F4F5F7" r={0.4} />
504
+ <C p={[0, 0.1, 0.16]} s={[0.14, 0.4, 0.06]} c="#F4F5F7" r={0.4} />
505
+ </group>
506
+ ))}
507
+ <Y p={[0, 0.35, 0]} rad={0.24} h={0.03} {...ACERO} />
508
+ <Y eje="z" p={[0.2, -0.3, 0.05]} rad={0.2} h={0.08} c="#DDE0E5" r={0.4} />
509
+ <E p={[0, 1.18, 0]} rad={0.05} c="#E0352B" e={Math.sin(t * 3) > 0 ? 1 : 0.1} />
510
+ </group>
511
+ );
512
+ case 'presa':
513
+ return (
514
+ <group rotation={[0, -0.6, 0]} position={[0, 0, 0]} scale={0.72}>
515
+ {/* cortina de concreto, más ancha abajo */}
516
+ <mesh rotation={[0, Math.PI / 2, 0]}>
517
+ <cylinderGeometry args={[0.18, 0.5, 1.5, 4, 1]} />
518
+ <meshStandardMaterial color={k('#C9C5BC')} roughness={0.9} flatShading />
519
+ </mesh>
520
+ <C p={[0, 0, 0]} s={[0.36, 1.5, 2.2]} c={k('#C9C5BC')} r={0.9} />
521
+ <C p={[0.32, -0.35, 0]} rot={[0, 0, 0.35]} s={[0.3, 1.2, 2.2]} c="#BDB9B0" r={0.9} />
522
+ {/* embalse (alto) y río (bajo) */}
523
+ <C p={[-0.85, 0.1, 0]} s={[1.4, 1.1, 2.2]} c="#2F8FD6" r={0.1} o={0.8} />
524
+ <C p={[1.0, -0.68, 0]} s={[1.2, 0.12, 0.9]} c="#4FB3FF" r={0.1} o={0.85} />
525
+ {/* compuertas con agua cayendo */}
526
+ {[-0.5, 0.5].map((z) => (
527
+ <group key={z}>
528
+ <C p={[0.2, 0.3, z]} s={[0.1, 0.3, 0.3]} c="#3A3D45" r={0.6} />
529
+ <C p={[0.46, -0.2 - ((t * 0.6) % 0.1), z]} rot={[0, 0, 0.35]} s={[0.04, 0.9, 0.24]} c="#DFF2FF" o={0.8} e={0.2} />
530
+ </group>
531
+ ))}
532
+ <C p={[0, -0.78, 0]} s={[3.4, 0.08, 2.3]} c="#6E6A60" r={0.95} />
533
+ </group>
534
+ );
535
+ case 'torre_enfriamiento':
536
+ return (
537
+ <group>
538
+ <Y p={[0, -0.55, 0]} rad={[0.55, 0.85]} h={1.0} c={k('#D9D6CE')} r={0.9} seg={36} />
539
+ <Y p={[0, 0.3, 0]} rad={[0.66, 0.55]} h={0.7} c={k('#D9D6CE')} r={0.9} seg={36} />
540
+ <Aro p={[0, 0.65, 0]} rot={[Math.PI / 2, 0, 0]} rad={0.66} tubo={0.03} c="#C73A32" r={0.6} />
541
+ {[0, 1, 2].map((i) => {
542
+ const f = (t * 0.25 + i / 3) % 1;
543
+ return <E key={i} p={[Math.sin(i * 2.1) * 0.2 * f, 0.8 + f * 0.7, 0]} rad={0.28 + f * 0.25} c="#F4F6F8" r={1} o={0.7 * (1 - f)} />;
544
+ })}
545
+ </group>
546
+ );
547
+ case 'ascensor': {
548
+ const y = Math.sin(t * (o.velocidad ?? 0.6)) * 0.45;
549
+ return (
550
+ <group>
551
+ {[-0.6, 0.6].map((x) => (
552
+ <C key={x} p={[x, 0, -0.5]} s={[0.06, 2.3, 0.06]} {...ACERO} />
553
+ ))}
554
+ <C p={[0, 1.12, -0.25]} s={[1.4, 0.08, 0.7]} {...ACERO} />
555
+ <Y p={[0, 0.6 + y / 2, 0]} rad={0.015} h={1.0 - y} c="#15161A" />
556
+ <group position={[0, -0.25 + y, 0]}>
557
+ <C s={[1.0, 1.1, 0.9]} c={k('#C9CDD4')} m={0.7} r={0.35} />
558
+ {[-0.24, 0.24].map((x) => (
559
+ <C key={x} p={[x, -0.03, 0.455]} s={[0.44, 0.95, 0.02]} c="#AEB6C2" m={0.8} r={0.25} />
560
+ ))}
561
+ <C p={[0, 0.5, 0.455]} s={[0.3, 0.07, 0.02]} c="#3DDC84" e={0.8} />
562
+ </group>
563
+ </group>
564
+ );
565
+ }
566
+ case 'fusible':
567
+ return (
568
+ <group rotation={[0, 0, 0.5]}>
569
+ <Y eje="x" rad={0.3} h={1.3} c="#EAF4FF" r={0.08} o={0.3} />
570
+ {[-0.75, 0.75].map((x) => (
571
+ <Y key={x} eje="x" p={[x, 0, 0]} rad={0.32} h={0.36} {...ACERO} />
572
+ ))}
573
+ {o.fundido ? (
574
+ <>
575
+ <Y eje="x" p={[-0.42, 0, 0]} rad={0.02} h={0.36} {...COBRE} />
576
+ <Y eje="x" p={[0.42, 0, 0]} rad={0.02} h={0.36} {...COBRE} />
577
+ <E rad={0.12} c="#3A3D45" o={0.5} />
578
+ </>
579
+ ) : (
580
+ <Y eje="x" rad={0.02} h={1.2} {...COBRE} />
581
+ )}
582
+ </group>
583
+ );
584
+ case 'fibra_optica':
585
+ return (
586
+ <group>
587
+ <Tubo pts={[[-1, -0.6, 0], [-0.5, -0.2, 0.1], [0, -0.1, 0]]} rad={0.16} c={k('#22242A')} r={0.6} />
588
+ {Array.from({length: 9}, (_, i) => {
589
+ const a = (i / 9) * Math.PI * 2;
590
+ const fin: V3 = [0.75 + azar(i) * 0.25, 0.25 + Math.sin(a) * 0.55, Math.cos(a) * 0.5];
591
+ return (
592
+ <group key={i}>
593
+ <Tubo pts={[[0, -0.1, 0], [0.4, 0.05 + Math.sin(a) * 0.2, Math.cos(a) * 0.2], fin]} rad={0.018} c="#DFF2FF" r={0.1} o={0.7} />
594
+ <E p={fin} rad={0.045} c={['#5AC8FA', '#FFD23F', '#FF6AD5'][i % 3]} e={0.6 + 0.4 * Math.sin(t * 5 + i)} />
595
+ </group>
596
+ );
597
+ })}
598
+ </group>
599
+ );
600
+ case 'buzon':
601
+ return (
602
+ <group>
603
+ <Y rad={0.06} h={1.2} p={[0, -0.6, 0]} c="#6A4C32" r={0.9} />
604
+ <C p={[0, 0.2, 0]} s={[0.7, 0.45, 1.2]} c={k('#D7372F')} r={0.4} />
605
+ <Y eje="z" p={[0, 0.42, 0]} rad={0.35} h={1.2} c={k('#D7372F')} r={0.4} />
606
+ <C p={[0, 0.3, 0.605]} s={[0.5, 0.55, 0.01]} c="#B92D26" r={0.4} />
607
+ <C p={[0.38, 0.55, 0.2]} s={[0.03, 0.4, 0.06]} c="#F2C230" r={0.4} />
608
+ <C p={[0.38, 0.72, 0.28]} s={[0.03, 0.14, 0.2]} c="#F2C230" r={0.4} />
609
+ </group>
610
+ );
611
+ // ------------------------------------------------------------ transporte
612
+ case 'autobus':
613
+ return (
614
+ <group rotation={[0, -0.5, 0]}>
615
+ <C p={[0, 0.1, 0]} s={[2.3, 0.95, 0.85]} c={k('#F2C230')} r={0.4} />
616
+ <C p={[0, 0.28, 0]} s={[2.1, 0.36, 0.87]} c="#1B2A44" r={0.15} />
617
+ {[-0.7, -0.2, 0.3].map((x) => (
618
+ <C key={x} p={[x, 0.28, 0]} s={[0.04, 0.4, 0.88]} c={k('#F2C230')} r={0.4} />
619
+ ))}
620
+ <C p={[1.13, 0.2, 0]} s={[0.06, 0.5, 0.75]} c="#1B2A44" r={0.15} />
621
+ <C p={[0, -0.33, 0]} s={[2.32, 0.1, 0.87]} c="#22242A" r={0.5} />
622
+ {[-0.72, 0.72].map((x) =>
623
+ [-1, 1].map((l) => <Y key={`${x}${l}`} eje="z" p={[x, -0.42, l * 0.38]} rad={0.2} h={0.14} c="#15161A" r={0.7} />),
624
+ )}
625
+ {[-1, 1].map((l) => (
626
+ <E key={l} p={[1.15, -0.15, l * 0.28]} rad={0.06} c="#FFF3C4" e={0.9} />
627
+ ))}
628
+ </group>
629
+ );
630
+ case 'helicoptero':
631
+ return (
632
+ <group rotation={[0, -0.6, 0]}>
633
+ <Ov s={[0.75, 0.5, 0.5]} c={k('#D7372F')} r={0.35} />
634
+ <Ov p={[0.38, 0.05, 0]} s={[0.4, 0.36, 0.42]} c="#9FD4FF" r={0.1} o={0.7} />
635
+ <Y eje="x" p={[-1.0, 0.08, 0]} rad={[0.05, 0.14]} h={1.1} c={k('#D7372F')} r={0.35} />
636
+ <C p={[-1.5, 0.3, 0]} s={[0.2, 0.45, 0.04]} c={k('#D7372F')} r={0.35} />
637
+ <group position={[-1.5, 0.2, 0.08]}>
638
+ <Aspas n={3} rad={0.24} giro={giro * 5} c="#22242A" ancho={0.2} />
639
+ </group>
640
+ <Y p={[0, 0.55, 0]} rad={0.04} h={0.2} {...ACERO} />
641
+ <group position={[0, 0.66, 0]} rotation={[Math.PI / 2, 0, 0]}>
642
+ <Aspas n={3} rad={1.15} giro={giro * 3} c="#22242A" ancho={0.07} />
643
+ </group>
644
+ {[-1, 1].map((l) => (
645
+ <group key={l}>
646
+ <Y eje="x" p={[0, -0.65, l * 0.35]} rad={0.03} h={1.2} {...ACERO} />
647
+ {[-0.3, 0.3].map((x) => (
648
+ <Y key={x} p={[x, -0.52, l * 0.3]} rad={0.02} h={0.28} {...ACERO} />
649
+ ))}
650
+ </group>
651
+ ))}
652
+ </group>
653
+ );
654
+ case 'submarino':
655
+ return (
656
+ <group rotation={[0, -0.5, 0]}>
657
+ <Ov s={[1.2, 0.42, 0.42]} c={k('#F2C230')} r={0.35} />
658
+ <C p={[0.1, 0.5, 0]} s={[0.5, 0.4, 0.26]} c={k('#F2C230')} r={0.35} />
659
+ <Y p={[0.2, 0.85, 0]} rad={0.03} h={0.4} {...ACERO} />
660
+ <C p={[0.26, 1.03, 0]} s={[0.14, 0.05, 0.05]} {...ACERO} />
661
+ {[-0.3, 0.15, 0.6].map((x) => (
662
+ <group key={x}>
663
+ <Y eje="z" p={[x, 0.05, 0.4 * Math.sqrt(1 - (x / 1.2) ** 2)]} rad={0.11} h={0.04} {...ACERO} />
664
+ <Y eje="z" p={[x, 0.05, 0.4 * Math.sqrt(1 - (x / 1.2) ** 2) + 0.015]} rad={0.08} h={0.03} c="#9FD4FF" r={0.1} />
665
+ </group>
666
+ ))}
667
+ <C p={[-1.0, 0, 0]} s={[0.3, 0.6, 0.04]} c="#D9A91A" r={0.4} />
668
+ <C p={[-1.0, 0, 0]} s={[0.3, 0.04, 0.6]} c="#D9A91A" r={0.4} />
669
+ <group position={[-1.22, 0, 0]} rotation={[0, Math.PI / 2, 0]}>
670
+ <Aspas n={4} rad={0.22} giro={giro * 2} c="#8E959F" ancho={0.4} />
671
+ </group>
672
+ </group>
673
+ );
674
+ case 'bicicleta':
675
+ return (
676
+ <group rotation={[0, -0.35, 0]}>
677
+ {[-0.7, 0.7].map((x) => (
678
+ <group key={x} position={[x, -0.35, 0]}>
679
+ <Aro rad={0.5} tubo={0.04} c="#15161A" r={0.7} />
680
+ <Aro rad={0.45} tubo={0.012} {...ACERO} />
681
+ <group rotation={[0, 0, -giro * 0.3]}>
682
+ {[0, 1, 2, 3].map((i) => (
683
+ <C key={i} rot={[0, 0, (i * Math.PI) / 4]} s={[0.9, 0.008, 0.008]} {...ACERO} />
684
+ ))}
685
+ </group>
686
+ </group>
687
+ ))}
688
+ {(
689
+ [
690
+ [[-0.7, -0.35, 0], [-0.25, 0.3, 0]],
691
+ [[-0.25, 0.3, 0], [0.45, 0.35, 0]],
692
+ [[0.45, 0.35, 0], [0.7, -0.35, 0]],
693
+ [[-0.25, 0.3, 0], [-0.05, -0.35, 0]],
694
+ [[-0.05, -0.35, 0], [0.45, 0.35, 0]],
695
+ [[-0.7, -0.35, 0], [-0.05, -0.35, 0]],
696
+ ] as [V3, V3][]
697
+ ).map((par, i) => (
698
+ <Tubo key={i} pts={[par[0], [(par[0][0] + par[1][0]) / 2, (par[0][1] + par[1][1]) / 2, 0], par[1]]} rad={0.035} c={k('#2B6FD6')} m={0.4} r={0.35} />
699
+ ))}
700
+ <C p={[-0.28, 0.46, 0]} s={[0.36, 0.06, 0.14]} c="#15161A" r={0.6} />
701
+ <Y p={[-0.26, 0.38, 0]} rad={0.025} h={0.16} {...ACERO} />
702
+ <Y p={[0.47, 0.5, 0]} rad={0.025} h={0.3} {...ACERO} />
703
+ <Y eje="z" p={[0.47, 0.65, 0]} rad={0.025} h={0.6} c="#15161A" />
704
+ <Y eje="z" p={[-0.05, -0.35, 0]} rad={0.1} h={0.04} {...ACERO} />
705
+ </group>
706
+ );
707
+ // ------------------------------------------------------------ escuela y objetos
708
+ case 'lapiz':
709
+ return (
710
+ <group rotation={[0, 0, -0.7]}>
711
+ <Y rad={0.16} h={1.5} seg={6} c={k('#F2C230')} r={0.45} />
712
+ <Cono p={[0, -0.95, 0]} rot={[Math.PI, 0, 0]} rad={0.16} h={0.4} seg={6} c="#E8C9A0" r={0.6} />
713
+ <Cono p={[0, -1.09, 0]} rot={[Math.PI, 0, 0]} rad={0.05} h={0.13} seg={8} c="#22242A" r={0.5} />
714
+ <Y p={[0, 0.82, 0]} rad={0.165} h={0.16} {...ACERO} />
715
+ <Y p={[0, 1.0, 0]} rad={0.15} h={0.22} c="#F28BA0" r={0.6} />
716
+ </group>
717
+ );
718
+ case 'regla':
719
+ return (
720
+ <group rotation={[0, 0, 0.35]}>
721
+ <C s={[2.3, 0.5, 0.04]} c={k('#F2C230')} r={0.4} o={0.9} />
722
+ {Array.from({length: 23}, (_, i) => (
723
+ <C key={i} p={[-1.1 + i * 0.1, 0.25 - (i % 5 === 0 ? 0.11 : 0.06), 0.022]} s={[0.012, i % 5 === 0 ? 0.22 : 0.12, 0.004]} c="#22242A" />
724
+ ))}
725
+ </group>
726
+ );
727
+ case 'dado': {
728
+ const caras: [V3, [number, number][]][] = [
729
+ [[0, 0, 0], [[0, 0]]],
730
+ [[0, Math.PI / 2, 0], [[-0.4, 0.4], [0.4, -0.4]]],
731
+ [[-Math.PI / 2, 0, 0], [[-0.4, 0.4], [0, 0], [0.4, -0.4]]],
732
+ [[Math.PI / 2, 0, 0], [[-0.4, 0.4], [0.4, 0.4], [-0.4, -0.4], [0.4, -0.4]]],
733
+ [[0, -Math.PI / 2, 0], [[-0.4, 0.4], [0.4, 0.4], [0, 0], [-0.4, -0.4], [0.4, -0.4]]],
734
+ [[0, Math.PI, 0], [[-0.4, 0.4], [0.4, 0.4], [-0.4, 0], [0.4, 0], [-0.4, -0.4], [0.4, -0.4]]],
735
+ ];
736
+ return (
737
+ <group rotation={[0.5, 0.6 + giro * 0.1, 0.2]}>
738
+ <C s={[1.5, 1.5, 1.5]} c={k('#F7F5F0')} r={0.3} />
739
+ {caras.map(([rot, pts], i) => (
740
+ <group key={i} rotation={rot}>
741
+ {pts.map(([x, y], j) => (
742
+ <Y key={j} eje="z" p={[x, y, 0.752]} rad={0.13} h={0.01} c="#15161A" />
743
+ ))}
744
+ </group>
745
+ ))}
746
+ </group>
747
+ );
748
+ }
749
+ case 'ancla':
750
+ return (
751
+ <group>
752
+ <Y rad={0.08} h={1.7} {...ACERO} c={k('#5A6472')} />
753
+ <Aro p={[0, 1.0, 0]} rad={0.17} tubo={0.05} {...ACERO} c={k('#5A6472')} />
754
+ <Y eje="x" p={[0, 0.6, 0]} rad={0.06} h={0.9} {...ACERO} c={k('#5A6472')} />
755
+ <Aro p={[0, -0.25, 0]} rot={[0, 0, Math.PI]} rad={0.7} tubo={0.08} arco={Math.PI} {...ACERO} c={k('#5A6472')} />
756
+ {[-1, 1].map((l) => (
757
+ <Cono key={l} p={[l * 0.72, -0.15, 0]} rot={[0, 0, l * 0.5]} rad={0.16} h={0.36} seg={4} c={k('#5A6472')} m={0.85} r={0.3} />
758
+ ))}
759
+ </group>
760
+ );
761
+ case 'paraguas':
762
+ return (
763
+ <group rotation={[0, 0, 0.25]}>
764
+ <mesh position={[0, 0.25, 0]}>
765
+ <sphereGeometry args={[1.05, 8, 8, 0, Math.PI * 2, 0, Math.PI * 0.42]} />
766
+ <meshStandardMaterial color={k('#E5484D')} roughness={0.6} side={THREE.DoubleSide} flatShading />
767
+ </mesh>
768
+ <Y p={[0, 0.2, 0]} rad={0.03} h={2.1} {...ACERO} />
769
+ <Y p={[0, 1.33, 0]} rad={[0.01, 0.04]} h={0.16} {...ACERO} />
770
+ <Aro p={[0.12, -0.85, 0]} rot={[0, 0, Math.PI]} rad={0.12} tubo={0.04} arco={Math.PI} c="#22242A" r={0.5} />
771
+ </group>
772
+ );
773
+ default:
774
+ return null;
775
+ }
776
+ };
777
+ void Helice;