- Afegir GpuShaderPreset i ShaderManager per carregar shaders des de data/shaders/
- Implementar preset ntsc-md-rainbows (2 passos: encode + decode MAME NTSC)
- Render loop multi-pass per shaders externs (targets intermedis R16G16B16A16_FLOAT)
- cycleShader(): cicla OFF→PostFX natius→shaders externs amb tecla X
- --shader <nom> per arrancar directament amb un preset extern
- CMake auto-descubreix i compila data/shaders/**/*.vert/.frag → .spv
- HUD F1 mostra 'Shader: <nom>' quan hi ha shader extern actiu
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HUD (F1) afegeix línia PostFX: OFF o PostFX: <preset> [V:x.xx C:x.xx S:x.xx]
- applyPostFXPreset reaaplica overrides de CLI per preservar-los en ciclar amb X
- setPostFXParamOverrides guarda els valors en membres privats per persistència
- --postfx sense valor ja no dona error i utilitza complet (preset 3) per defecte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Nou: shaders/sprite.vert|frag, postfx.vert|frag, ball.vert (GLSL)
- Nou: cmake/spv_to_header.cmake — converteix .spv → uint8_t C header
- CMakeLists.txt: bloc non-Apple troba glslc, compila GLSL → SPIRV en
build-time i genera headers embeguts a build/generated_shaders/
- gpu_context.cpp: MSL|METALLIB en Apple, SPIRV en la resta
- gpu_pipeline.cpp: createShaderSPIRV() + branques #ifdef __APPLE__
per sprite/ball/postfx pipelines
- Corregeix crash a engine.cpp:821 (Windows/Linux) causat per pipelines
null quan init() fallava en no trobar suport MSL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Eliminar guarda !fullscreen_enabled_ de toggleIntegerScaling(): F6 ara
cicla i notifica en mode ventana i fullscreen per igual
- Pass 2: afegir viewport+scissor SDL_GPU condicionat a fullscreen_enabled_
per als tres modes (INTEGER pixel-perfect centrat, LETTERBOX aspect-ratio,
STRETCH pantalla completa)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- X cicla 4 efectes (Vinyeta/Scanlines/Cromàtica/Complet), sempre activa PostFX
- F5 fa toggle PostFX on/off mantenint l'efecte seleccionat
- F6 hereta el toggle d'integer scaling (abans F5)
- Arrencada per defecte sense postprocés (tot a 0)
- --postfx <vinyeta|scanlines|cromatica|complet> per activar des de CLI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Elimina el kernel Metal O(N²) de boids en GPU que causava GPU timeout
a macOS amb >50K boles, arrossegant WindowServer fins al crash.
- Elimina gpu_boid_buffer.hpp/cpp (GpuBoidBuffer, BallComputeData, BoidParams)
- Elimina kBoidComputeMSL i kBallComputeVertMSL de gpu_pipeline
- Elimina boid_compute_pipeline_ i ball_compute_pipeline_
- Elimina use_gpu_boids_, boid_params_, ball_screen_uniforms_ de Engine
- Elimina syncAndExitGpuBoids() i tot el compute dispatch de render()
- Mode BOIDS ara usa sempre boid_manager_ (CPU, spatial hash O(N))
i renderitza via gpu_ball_buffer_ instanced (mateix path que PHYSICS)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Afegir handlePostFXCycle() amb 5 presets (vinyeta/scanlines/cromàtica/complet/off)
i tecla X per ciclar-los (input_handler + engine.hpp)
- Augmentar MAX_SPRITES de 65536 a 200000 i afegir guard d'overflow a pushQuad()
- Netejar textures/objectes UI abans de reinicialitzar (AppLogo::initialize,
UIManager::initialize) per evitar leaks en toggleRealFullscreen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bug 2: moure shape_manager_->updateScreenSize() fora del bloc
condicional SHAPE a les dues branques de toggleRealFullscreen(),
de manera que ShapeManager sempre té les dimensions correctes quan
s'activa una figura després d'entrar en fullscreen
- Bug text: passar base_screen_width_/height_ com a dimensions lògiques
a ui_manager_->initialize() en recreateOffscreenTexture(), evitant
que calculateFontSize() s'avaluï amb la resolució nativa de fullscreen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bloque 2: eliminar codi mort comentat (shape_manager, engine)
Bloque 3: Engine shape methods com thin wrappers a ShapeManager;
eliminar estat duplicat de shapes en Engine
Bloque 4: encapsular getBallsMutable() amb helpers a SceneManager
(enableShapeAttractionAll, resetDepthScalesAll)
Bloque 5: StateManager Phase 9 - tota la logica DEMO/LOGO
implementada directament amb refs a SceneManager,
ThemeManager i ShapeManager; eliminar callbacks a Engine.
Acoplament Engine<->StateManager passa a unidireccional.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app_logo.hpp/cpp i logo_scaler.hpp/cpp moguts a source/ui/
- spatial_grid.hpp/cpp mogut a source/boids_mgr/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problemas resueltos:
- En 640x360, el overlay generaba textura enorme con letras grandes
- El cálculo de font size usaba dimensiones físicas (con zoom aplicado)
en lugar de dimensiones lógicas (resolución interna)
- No había límite máximo de ancho para el overlay
- Padding fijo de 25px era excesivo en pantallas pequeñas
Cambios realizados:
1. UIManager: Usar dimensiones lógicas para calcular font size
- Nuevo parámetro logical_width/logical_height en initialize()
- calculateFontSize() ahora usa altura lógica sin zoom
- Escalado híbrido: proporcional en extremos, escalonado en rango medio
- Para 640x360: 10px (antes 18px con zoom 2x)
- Para 640x480: 12px (antes 24px con zoom 2x)
2. HelpOverlay: Agregar límites máximos de dimensiones
- Box width limitado al 95% del ancho físico
- Box height limitado al 90% de la altura física
- Padding dinámico: 25px para >=600px, escalado para menores
- Para 360px altura: padding de 15px (antes 25px fijo)
3. Engine: Pasar dimensiones lógicas a UIManager
- initialize() ahora recibe current_screen_width/height
Resultado:
- 640x360: Overlay compacto con fuente 10px que cabe en pantalla
- 640x480: Overlay con fuente 12px (tamaño apropiado)
- Tamaño de fuente consistente independiente del zoom de ventana
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Solucionar bug donde las pelotas aparecían en el centro sin formar
la figura geométrica al entrar en modo DEMO con SimulationMode::SHAPE.
## Problema
Al randomizar el estado en modo DEMO, si se elegía una figura:
1. Se configuraba el modo SHAPE
2. Se llamaba a changeScenario() que creaba pelotas en el centro
3. NO se llamaba a generateShape() para calcular los puntos de la figura
4. Resultado: pelotas amontonadas en el centro sin formar figura
## Solución
Reordenar operaciones en executeRandomizeOnDemoStart():
1. Decidir PRIMERO el modo (PHYSICS o SHAPE) antes de changeScenario
2. Si SHAPE: configurar figura manualmente sin generar puntos
3. Llamar a changeScenario() con el modo ya establecido
4. Después de changeScenario(), generar figura y activar atracción
Cambios adicionales:
- Arreglar warning de formato %zu en textrenderer.cpp (MinGW)
- Usar %lu con cast para size_t en logs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problema:
- Las fuentes TTF no se renderizaban (error "Text has zero width")
- Ocurría tanto al cargar desde resources.pack como desde disco
- El buffer de memoria se liberaba inmediatamente después de crear
el SDL_IOStream, pero SDL_ttf necesita acceder a esos datos
durante toda la vida de la fuente
Solución:
- Añadido campo font_data_buffer_ para mantener los datos en memoria
- Modificado init() y reinitialize() para NO liberar el buffer
inmediatamente después de cargar la fuente
- Modificado cleanup() para liberar el buffer cuando se cierre la fuente
- Añadidos logs de debug para confirmar la carga desde ResourceManager
Archivos modificados:
- source/text/textrenderer.h: Añadido campo font_data_buffer_
- source/text/textrenderer.cpp: Correcciones en init(), reinitialize()
y cleanup()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Crear ResourceManager singleton para gestión centralizada de recursos
- Separar lógica de ResourcePack de la clase Texture
- Adaptar TextRenderer para cargar fuentes TTF desde pack
- Adaptar LogoScaler para cargar imágenes PNG desde pack
- Actualizar main.cpp y engine.cpp para usar ResourceManager
- Regenerar resources.pack con fuentes y logos incluidos
Fixes:
- Resuelve error de carga de fuentes desde disco
- Resuelve error de carga de logos (can't fopen)
- Implementa fallback automático a disco si no existe pack
- Todas las clases ahora pueden cargar recursos desde pack
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problema 1: Símbolos no definidos de SDL_ttf** (CRÍTICO)
- Error: "Undefined symbols: _TTF_Init, _TTF_OpenFont, etc."
- Causa: LDFLAGS solo incluía -lSDL3 (faltaba -lSDL3_ttf)
- Solución: Añadido -lSDL3_ttf a LDFLAGS para Unix/macOS (línea 81)
- Afecta: Linux, macOS y otros sistemas Unix
**Problema 2: Mismatch class/struct SDL_Renderer** (WARNING)
- Warning: "class 'SDL_Renderer' was previously declared as a struct"
- Causa: ui_manager.h:7 declaraba "class SDL_Renderer"
- SDL3 lo declara como "struct SDL_Renderer" (SDL_render.h:119)
- Solución: Cambiado class → struct en ui_manager.h:7
- Evita warnings y potenciales errores de linker en MSVC
**Resultado:**
✅ make macos_release completa exitosamente
✅ DMG creado: vibe3_physics-2025-10-19-macos-apple-silicon.dmg (17.9 MB)
✅ Sin errores de enlazado, solo warnings menores de versión macOS
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Cambios organizativos:**
- Archivos .md movidos de raíz a .claude/ (BOIDS_ROADMAP, CLAUDE, REFACTOR_*, ROADMAP, RULES)
- .claude/ ya está en .gitignore, archivos de sesión no versionados
**Nuevos recursos para release:**
- Añadido release/frameworks/SDL3_ttf.xcframework/ para macOS
- Añadidos release/SDL3.dll y release/SDL3_ttf.dll para Windows (forzado con -f)
**Configuración:**
- defines.h: APPLOGO_DISPLAY_INTERVAL 120→90 segundos (logo aparece más frecuente)
- defines.h: Ajustes de formato/indentación (sin cambios funcionales)
**Makefile windows_release:**
- Comandos Unix reemplazados por Windows CMD nativos:
- rm -rf → if exist + rmdir /S /Q
- mkdir -p → mkdir
- cp -f → copy /Y
- rm -f → if exist + del /Q
- Ahora funciona en Windows CMD sin necesitar Git Bash/MSYS2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problema:**
Cuando se iniciaba con `-m demo`, `-m demo-lite` o `-m logo`, se llamaba
a `setState()` directamente, que es un método de bajo nivel que solo
cambia el estado interno SIN ejecutar las acciones de configuración.
**Resultado del bug:**
- `-m demo`: NO aleatorizaba (tema default, primer escenario)
- `-m demo-lite`: NO aleatorizaba física
- `-m logo`: NO configuraba tema, PNG_SHAPE, ni pelotas pequeñas
**Arquitectura correcta:**
- `setState()` = Método primitivo bajo nivel (solo cambia estado)
- `toggleDemoMode()` = Método alto nivel (setState + randomize)
- `toggleDemoLiteMode()` = Método alto nivel (setState + randomize)
- `enterLogoMode()` = Método alto nivel (setState + configuración completa)
**Solución implementada:**
En lugar de llamar a setState() directamente, usar los métodos de
alto nivel que ejecutan las acciones de configuración:
```cpp
if (initial_mode == AppMode::DEMO) {
state_manager_->toggleDemoMode(...); // Entra a DEMO + randomiza
}
else if (initial_mode == AppMode::DEMO_LITE) {
state_manager_->toggleDemoLiteMode(...); // Entra a DEMO_LITE + randomiza
}
else if (initial_mode == AppMode::LOGO) {
state_manager_->enterLogoMode(...); // Entra a LOGO + configura todo
}
```
**Archivos modificados:**
- source/engine.cpp (líneas 249-263):
- Reemplazado setState() por toggleDemoMode/toggleDemoLiteMode/enterLogoMode
- Agregados comentarios explicativos
**Resultado esperado:**
- ✅ `-m demo` → Aleatoriza todo como si pulsaras D
- ✅ `-m demo-lite` → Aleatoriza física como si pulsaras Shift+D
- ✅ `-m logo` → Configura tema/PNG_SHAPE/pelotas como si pulsaras K
- ✅ Comportamiento consistente entre CLI y teclas
- ✅ Arquitectura correcta: alto nivel para acciones, bajo nivel para estado
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**1. Eliminado logging de debug del FADE_OUT:**
- Removido log de timer/delta_time/progress (FADE_OUT inicial)
- Removido log de alpha1/alpha2
- Removido log de animaciones (ZOOM, ELASTIC, SPIRAL, BOUNCE)
- Removido log de completado de FADE_OUT
- Consola limpia en modo producción
**2. Fix centro del logo en animación ZOOM_ONLY:**
**Problema:**
- Centro del logo se calculaba basándose en width/height escalados
- Cuando scale cambiaba (1.2 → 1.0), corner_x/corner_y se movían
- Resultado: logo se desplazaba lateralmente durante zoom
**Solución:**
- Calcular esquina BASE (sin escala): corner_x_base, corner_y_base
- Calcular centro FIJO basándose en base_width/base_height
- Calcular width/height escalados DESPUÉS (solo para vértices)
- Resultado: centro permanece fijo, zoom crece/decrece alrededor del centro
**Archivos modificados:**
- source/app_logo.cpp:
- Líneas 343-347: Eliminado log FADE_OUT inicial
- Línea 347: Eliminado log completado
- Líneas 365-366: Eliminado log alphas
- Líneas 381-383: Eliminado log ZOOM
- Líneas 396-398: Eliminado log ELASTIC
- Líneas 414-417: Eliminado log SPIRAL
- Líneas 444-446: Eliminado log BOUNCE
- Líneas 609-625: Reordenado cálculo de centro (FIJO) y tamaño (ESCALADO)
**Resultado esperado:**
- Sin spam en consola
- Animación ZOOM perfectamente centrada en esquina inferior derecha
- Logo crece/decrece sin desplazamiento lateral
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problema:**
- SDL_SetTextureAlphaMod() no funciona correctamente con SDL_RenderGeometry()
- El alpha de los vértices (hardcodeado a 1.0) overrideaba el alpha de textura
- Resultado: fade invisible o instantáneo a pesar de valores correctos
**Solución:**
- Eliminar SDL_SetTextureAlphaMod()
- Convertir alpha de 0-255 a 0.0-1.0 (alpha_normalized)
- Aplicar alpha_normalized directamente al canal alpha de los 4 vértices
- Ahora SDL_RenderGeometry respeta el fade correctamente
**Archivos modificados:**
- source/app_logo.cpp:
- Línea 630: Crear alpha_normalized en lugar de SetTextureAlphaMod
- Líneas 669, 680, 691, 702: Aplicar alpha_normalized a vértices
**Resultado esperado:**
- Fade visible y suave durante 2 segundos completos
- Logo 2 con retraso de 0.25s como esperado
- Sincronización perfecta entre animación y fade
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Log de timer_, delta_time, progress en cada frame
- Log de alpha1/alpha2 calculados
- Log de valores de animación por tipo (ZOOM/ELASTIC/SPIRAL/BOUNCE)
- Log de ease_t1 en ROTATE_SPIRAL para diagnosticar desincronización
- Log cuando FADE_OUT se completa
Propósito: Diagnosticar por qué el fade parece instantáneo
y desincronizado con la animación (serie en lugar de paralelo).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Restaura fade lineal para el alpha de los logos, eliminando el easing
que hacía la transición casi imperceptible. Renombra constante para
mayor claridad sobre lo que controla.
Problema identificado:
- El alpha usaba easeInOutQuad, acelerando la transición en el medio
- Con 2 segundos, el easing hacía que el logo pareciera aparecer/
desaparecer instantáneamente sin fade visible
- El usuario reportó "el logo termina y desaparece directamente"
Solución implementada:
1. **Fade lineal restaurado**:
- FADE_IN: alpha aumenta linealmente 0→255
- FADE_OUT: alpha disminuye linealmente 255→0
- Progreso visible y constante durante toda la duración
2. **Constante renombrada**:
- `APPLOGO_FADE_DURATION` → `APPLOGO_ANIMATION_DURATION`
- Nombre más claro: controla duración de toda la animación
- Actualizado valor a 2.0 segundos (configurable por usuario)
3. **Animaciones mantienen easing**:
- Zoom, rotación, squash, etc. siguen usando sus easings
- Solo el alpha es lineal para fade visible
Confirmaciones:
✅ Sistema time-based: usa delta_time correctamente
✅ Blend mode configurado: SDL_BLENDMODE_BLEND en todas las texturas
✅ Alpha se aplica: SDL_SetTextureAlphaMod en renderizado
Resultado con APPLOGO_ANIMATION_DURATION = 2.0s:
- t=0.0s → Alpha=0 (invisible)
- t=0.5s → Alpha=64 (25% visible)
- t=1.0s → Alpha=127 (50% visible)
- t=1.5s → Alpha=191 (75% visible)
- t=2.0s → Alpha=255 (100% visible)
Nota: El logo solo se muestra en modos DEMO/DEMO_LITE/LOGO,
no en SANDBOX. Para probar: ./vibe3_physics --mode demo
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Permite arrancar directamente en modo DEMO, DEMO_LITE, LOGO o SANDBOX
mediante argumentos de línea de comandos, eliminando necesidad de
cambiar manualmente el modo después del arranque.
Nuevos argumentos:
- `-m, --mode <mode>` - Establece modo inicial
- `sandbox` - Control manual (default)
- `demo` - Auto-play completo (figuras + temas + colisiones)
- `demo-lite` - Auto-play simple (solo física/figuras)
- `logo` - Modo logo (easter egg con convergencia)
Ejemplos de uso:
```bash
# Arrancar en modo DEMO
./vibe3_physics --mode demo
./vibe3_physics -m demo
# Arrancar en DEMO_LITE (solo física)
./vibe3_physics -m demo-lite
# Arrancar directo en LOGO
./vibe3_physics --mode logo
# Combinar con otros argumentos
./vibe3_physics -w 1920 -h 1080 --mode demo
./vibe3_physics -F -m demo-lite # Fullscreen + DEMO_LITE
```
Implementación:
1. main.cpp: Parsing de argumento --mode con validación
2. engine.h: Nuevo parámetro `initial_mode` en initialize()
3. engine.cpp: Aplicación del modo vía StateManager::setState()
Si no se especifica --mode, se usa SANDBOX (comportamiento actual).
El modo se aplica después de inicializar StateManager, garantizando
que todos los componentes estén listos antes del cambio de estado.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implementa sistema de sincronización elegante entre logos con efecto de
"seguimiento" y fade suavizado para eliminar desincronización visual.
Cambios principales:
1. **Animación sincronizada**: Ambos logos usan la MISMA animación
- Eliminadas 4 variables independientes (logo1/logo2 × entry/exit)
- Una sola variable `current_animation_` compartida
- Misma animación para entrada y salida (simetría)
2. **Retraso de Logo 2**: 0.25 segundos detrás de Logo 1
- Logo 1 empieza en t=0.00s
- Logo 2 empieza en t=0.25s
- Efecto visual de "eco" o "seguimiento"
3. **Alpha independiente con retraso**:
- `logo1_alpha_` y `logo2_alpha_` separados
- Logo 2 aparece/desaparece más tarde visualmente
4. **Easing en alpha** (NUEVO):
- Aplicado `easeInOutQuad()` al fade de alpha
- Elimina problema de "logo deformado esperando a desvanecerse"
- Sincronización visual perfecta entre animación y fade
- Curva suave: lento al inicio, rápido en medio, lento al final
Comportamiento resultante:
FADE_IN:
- t=0.00s: Logo 1 empieza (alpha con easing)
- t=0.25s: Logo 2 empieza (alpha con easing + retraso)
- t=0.50s: Logo 1 completamente visible
- t=0.75s: Logo 2 completamente visible
FADE_OUT:
- t=0.00s: Logo 1 empieza a desaparecer (misma animación)
- t=0.25s: Logo 2 empieza a desaparecer
- t=0.50s: Logo 1 completamente invisible
- t=0.75s: Logo 2 completamente invisible
Archivos modificados:
- source/defines.h: +APPLOGO_LOGO2_DELAY
- source/app_logo.h: Reestructuración de variables de animación/alpha
- source/app_logo.cpp: Implementación de retraso + easing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implementa pre-escalado de alta calidad para eliminar artefactos de
escalado dinámico de SDL y mejorar la nitidez visual de los logos.
Características:
- 4 texturas pre-escaladas (2 logos × 2 resoluciones: base + nativa)
- Detección automática de resolución nativa del monitor
- Switching dinámico entre texturas al cambiar resolución (F4)
- Renderizado 1:1 sin escalado adicional (máxima calidad)
- Algoritmo Mitchell en espacio sRGB (balance calidad/velocidad)
- Todo en RAM, sin archivos temporales
Archivos nuevos:
- source/external/stb_image_resize2.h: Biblioteca de escalado stb
- source/logo_scaler.h/cpp: Clase helper para pre-escalado
Cambios en AppLogo:
- Reemplazadas shared_ptr<Texture> por SDL_Texture* raw pointers
- initialize(): Pre-escala logos a 2 resoluciones al inicio
- updateScreenSize(): Cambia entre texturas según resolución
- render(): Simplificado, siempre usa renderWithGeometry()
- ~AppLogo(): Libera 4 texturas SDL manualmente
El sistema detecta la resolución nativa al inicio y crea versiones
optimizadas. Al presionar F4, cambia automáticamente a la textura
nativa para calidad perfecta en fullscreen.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implementación de sistema de 2 logos superpuestos con animaciones completamente independientes:
**Nuevas características:**
- Dos logos superpuestos (logo1.png + logo2.png) con animaciones independientes
- 4 tipos de animación: ZOOM_ONLY, ELASTIC_STICK, ROTATE_SPIRAL, BOUNCE_SQUASH
- Aleatorización independiente para entrada y salida de cada logo
- 256 combinaciones posibles (4×4 entrada × 4×4 salida)
**Ajuste de tamaño y posición:**
- Nueva constante APPLOGO_HEIGHT_PERCENT (40%) - altura del logo respecto a pantalla
- Nueva constante APPLOGO_PADDING_PERCENT (10%) - padding desde esquina inferior-derecha
- Logo anclado a esquina en lugar de centrado en cuadrante
- Valores fácilmente ajustables mediante constantes en defines.h
**Cambios técnicos:**
- Variables duplicadas para logo1 y logo2 (scale, squash, stretch, rotation)
- Variables compartidas para sincronización (state, timer, alpha)
- renderWithGeometry() acepta parámetro logo_index (1 o 2)
- Logo1 renderizado primero (fondo), Logo2 encima (overlay)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implementación de deformación elástica con vértices para el logo:
FADE IN (0.5s):
- Scale: 120% → 100% con easing elástico (bounce)
- Squash Y: 0.6 → 1.0 con easing back (aplastamiento)
- Stretch X: compensación automática
- Efecto: logo se "pega" aplastándose y rebotando
FADE OUT (0.5s):
- Scale: 100% → 120% (aceleración cuadrática)
- Squash Y: 1.0 → 1.3 (estiramiento vertical)
- Stretch X: 1.0 → 0.8 (compresión horizontal)
- Rotación: 0° → ~5.7° (torsión sutil)
- Efecto: logo se "despega" estirándose y girando
Características técnicas:
- Enum AppLogoAnimationType (ZOOM_ONLY / ELASTIC_STICK)
- Renderizado con SDL_RenderGeometry para deformaciones
- Funciones de easing: easeOutElastic() y easeOutBack()
- Transformación de vértices con rotación y escala 2D
- Actualmente fijo en ELASTIC_STICK para testing
Limpieza adicional:
- Eliminado dbgtxt.h (no utilizado)
- Removidos SDL_Log de debug en HelpOverlay
- Comentada variable no usada en ShapeManager
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Nuevo sistema AppLogo que muestra el logo cada 20 segundos por 5 segundos
- Fade in/out suave de 0.5 segundos con alpha blending
- Máquina de estados: HIDDEN → FADE_IN → VISIBLE → FADE_OUT
- Logo posicionado en cuadrante inferior derecho (1/4 de pantalla)
- Añadido método setAlpha() a Texture para control de transparencia
- Habilitado SDL_BLENDMODE_BLEND en todas las texturas
- Filtrado LINEAR para suavizado del logo escalado
- Desactivado automáticamente en modo SANDBOX
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problema:
- Al cambiar de escenario (teclas 1-8) en modo BOIDS, la gravedad
se reseteaba a 720 en lugar de mantenerse en 0
- SceneManager::changeScenario() reinicializa bolas con gravedad default
- Esto rompía el invariante: "modo BOIDS = gravedad OFF siempre"
Solución:
- Añadido check en Engine::changeScenario() para forzar gravedad OFF
después del cambio de escenario si estamos en modo BOIDS
- Mantiene consistencia con el comportamiento de SHAPE mode
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**1. Bordes como obstáculos (no más wrapping):**
- Implementada fuerza de repulsión cuando boids se acercan a bordes
- Nueva regla: Boundary Avoidance (evitar bordes)
- Fuerza proporcional a cercanía (0% en margen, 100% en colisión)
- Constantes: BOID_BOUNDARY_MARGIN (50px), BOID_BOUNDARY_WEIGHT (7200 px/s²)
**2. Variables ajustables en runtime:**
- Añadidas 11 variables miembro en BoidManager (inicializadas con defines.h)
- Permite modificar comportamiento sin recompilar
- Variables: radios (separation/alignment/cohesion), weights, speeds, boundary
- Base para futuras herramientas de debug/tweaking visual
**3. Fix tecla G (BOIDS → PHYSICS):**
- Corregido: toggleBoidsMode() ahora acepta parámetro force_gravity_on
- handleGravityToggle() pasa explícitamente false para preservar inercia
- Transición BOIDS→PHYSICS ahora mantiene gravedad OFF correctamente
**Implementación:**
- defines.h: +2 constantes (BOUNDARY_MARGIN, BOUNDARY_WEIGHT)
- boid_manager.h: +11 variables miembro ajustables
- boid_manager.cpp:
- Constructor inicializa variables
- Todas las funciones usan variables en lugar de constantes
- applyBoundaries() completamente reescrito (repulsión vs wrapping)
- engine.h/cpp: toggleBoidsMode() con parámetro opcional
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>