5 blocs de millora: reorganitzacio de fitxers, limpieza de codi mort,
eliminacio de duplicitats Engine/ShapeManager, encapsulacio de
getBallsMutable(), i completar StateManager Phase 9.
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>
- Corregir nombre de archivos DMG usando TARGET_NAME en lugar de TARGET_FILE
- Agregar limpieza de volúmenes montados antes de crear DMG
- Eliminar creación manual de enlace Applications (create-dmg lo hace con --app-drop-link)
- Mejorar manejo de errores eliminando || true y agregando verificación de éxito
- Limpiar archivos temporales rw.*.dmg después de crear DMG
Esto resuelve el problema donde el DMG final tenía prefijo "rw.XXXXX" y no se
podía abrir correctamente debido a conflictos con volúmenes montados y doble
creación del enlace a Applications.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Añadir instalación automática de create-dmg vía Homebrew si no está presente
- Reemplazar hdiutil por create-dmg para generar DMG con diseño profesional
- Configurar ventana de DMG: 720x300px con iconos de 96x96px
- Posicionar iconos centrados: Applications, .app, LICENSE, README.md
- Aplicar mejoras a ambas versiones: Intel y Apple Silicon
- Eliminar referencia obsoleta a tmp.dmg
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <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 tools/Makefile con soporte multiplataforma (Windows/Linux/macOS)
- Añadir targets: pack_tool, resource_pack, test_pack, clean, help
- Mejorar Makefile raíz con target force_resource_pack
- Integrar regeneración automática de resources.pack en todos los releases
- Los releases siempre generan un resources.pack actualizado
🤖 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 crítico detectado:**
- resources.pack del 5 oct (14 días antiguo, 1.3 KB)
- Archivos en data/ modificados recientemente (logo.png: 18 oct)
- make *_release NO regeneraba resources.pack automáticamente
- Releases distribuían resources.pack obsoleto
**Causa:**
Makefile línea 108 solo dependía de $(PACK_TOOL):
```makefile
resources.pack: $(PACK_TOOL) # ← Faltaban archivos de data/
```
Result: Make decía "up to date" aunque data/ tuviera cambios
**Solución:**
Añadido wildcard con todos los archivos de data/ como dependencias:
```makefile
DATA_FILES := $(shell find data -type f 2>/dev/null)
resources.pack: $(PACK_TOOL) $(DATA_FILES) # ← Ahora detecta cambios
```
**Verificación:**
✅ Regeneración completa: 1.3 KB → 3.7 MB (16 recursos)
✅ Detección de cambios: touch data/logo.png → regenera automáticamente
✅ Eficiencia: Si no hay cambios → "up to date" (no regenera)
✅ tools/pack_resources: +x permisos de ejecución
**Impacto:**
Todas las recetas *_release ahora regeneran resources.pack si hay cambios
🤖 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>
**Problemas corregidos:**
1. APP_SOURCES ahora usa wildcards automáticos (como CMakeLists.txt)
- Detecta automáticamente todos los .cpp en subdirectorios
- Faltaban 16 archivos: themes, state, input, scene, shapes_mgr, boids_mgr, text, ui, app_logo, logo_scaler
- Elimina mantenimiento manual de lista de archivos
2. macOS release ahora copia SDL3_ttf.xcframework correctamente
- Añadido a Contents/Frameworks en el .app
- Añadido a carpeta Frameworks temporal
3. Windows DLLs: línea mantenida para futuro uso
🤖 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>