Sistema completo de packaging para distribuir ViBe3 Physics:
**Core - ResourcePack:**
- source/resource_pack.{h,cpp}: Clase para empaquetar/desempaquetar recursos
- Header binario "VBE3" con índice de archivos
- Encriptación XOR simple para ofuscar contenido
- Checksums para verificar integridad
**Integración en Texture:**
- source/external/texture.cpp: Carga desde pack con fallback a disco
- Método estático Texture::initResourceSystem()
- 1. Intenta cargar desde resources.pack
- 2. Si falla, carga desde carpeta data/ (modo desarrollo)
**Herramienta de empaquetado:**
- tools/pack_resources.cpp: Herramienta CLI para generar .pack
- tools/README.md: Documentación actualizada para ViBe3
- make pack_tool: Compila herramienta
- make resources.pack: Genera pack desde data/
**Sistema de releases (Makefile):**
- Makefile: Adaptado de Coffee Crisis a ViBe3 Physics
- Targets: windows_release, macos_release, linux_release
- APP_SOURCES actualizado con archivos de ViBe3
- Variables: TARGET_NAME=vibe3_physics, APP_NAME="ViBe3 Physics"
- Elimina carpeta config (no usada en ViBe3)
**Recursos de release:**
- release/vibe3.rc: Resource file para Windows (icono)
- release/Info.plist: Bundle info para macOS (.app)
- release/icon.{ico,icns,png}: Iconos multiplataforma
- release/frameworks/SDL3.xcframework: Framework macOS
- release/SDL3.dll: DLL Windows
- release/create_icons.py: Script generador de iconos
**Resultado:**
- resources.pack generado (5 recursos, ~1.3KB)
- Compila correctamente con CMake
- Listo para make windows_release / macos_release
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
El threshold anterior (SHAPE_NEAR_THRESHOLD * scale_factor = 80px)
era demasiado estricto. Las pelotas con spring physics oscilan
constantemente y nunca alcanzaban el 75% de convergencia requerido,
impidiendo que el logo se formara completamente.
Cambios:
- defines.h: Nueva constante LOGO_CONVERGENCE_DISTANCE = 20.0px
- engine.cpp: Usar threshold fijo de 20px (en lugar de 80px)
Resultado: Las pelotas se consideran "convergidas" con más facilidad,
permitiendo que el logo alcance el 75-100% threshold y se forme
antes de ser interrumpido.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implementa sistema adaptativo que evita interrupciones prematuras
en resoluciones altas. El timing ahora se ajusta según convergencia
de partículas en lugar de usar intervalos fijos.
Cambios:
- Ball: getDistanceToTarget() para medir distancia a objetivo
- Engine: shape_convergence_, logo_convergence_threshold_ y tiempos escalados
- defines.h: LOGO_CONVERGENCE_MIN/MAX (75-100%)
- updateShape(): Cálculo de % de pelotas convergidas
- toggleShapeMode(): Genera threshold aleatorio al entrar en LOGO
- setState(): Escala logo_min/max_time con resolución (base 720p)
- updateDemoMode(): Dispara cuando (tiempo>=MIN AND convergencia>=threshold) OR tiempo>=MAX
Funcionamiento:
1. Al entrar a SHAPE en LOGO: threshold random 75-100%, tiempos escalados con altura
2. Cada frame: calcula % pelotas cerca de objetivo (shape_convergence_)
3. Dispara acción cuando: (tiempo>=MIN AND convergencia>=threshold) OR tiempo>=MAX
4. Resultado: En 720p funciona como antes, en 1440p espera convergencia real
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## 1. Sistema de Estados AppMode (MANUAL/DEMO/DEMO_LITE/LOGO)
**engine.h:**
- Creado enum AppMode con 4 estados mutuamente excluyentes
- Reemplazadas 4 flags booleanas por 2 variables de estado:
* current_app_mode_: Modo actual
* previous_app_mode_: Para restaurar al salir de LOGO
- Añadido método setState() para gestión centralizada
**engine.cpp:**
- Implementado setState() con configuración automática de timers
- Actualizado updateDemoMode() para usar current_app_mode_
- Actualizado handleEvents() para teclas D/L/K con setState()
- Actualizadas todas las referencias a flags antiguas (8 ubicaciones)
- enterLogoMode/exitLogoMode usan setState()
**Comportamiento:**
- Teclas D/L/K ahora desactivan otros modos automáticamente
- Al salir de LOGO vuelve al modo previo (DEMO/DEMO_LITE/MANUAL)
## 2. Ajuste Ratio DEMO:LOGO = 6:1
**defines.h:**
- Probabilidad DEMO→LOGO: 15% → 5% (más raro)
- Probabilidad DEMO_LITE→LOGO: 10% → 3%
- Probabilidad salir de LOGO: 25% → 60% (sale rápido)
- Intervalos LOGO: 4-8s → 3-5s (más corto que DEMO)
**Resultado:** DEMO pasa 6x más tiempo activo que LOGO
## 3. Fix PNG_SHAPE no hace flip en modo LOGO
**Bugs encontrados:**
1. next_idle_time_ inicializado a 5.0s (hardcoded) > intervalos LOGO (3-5s)
2. toggleShapeMode() recrea PNG_SHAPE → pierde is_logo_mode_=true
**Soluciones:**
**png_shape.cpp (constructor):**
- Inicializa next_idle_time_ con PNG_IDLE_TIME_MIN/MAX (no hardcoded)
**png_shape.h:**
- Añadidos includes: defines.h, <cstdlib>
- Flag is_logo_mode_ para distinguir MANUAL vs LOGO
- Expandido setLogoMode() para recalcular next_idle_time_ con rangos apropiados
- PNG_IDLE_TIME_MIN_LOGO/MAX_LOGO: 2.5-4.5s (ajustable en defines.h)
**engine.cpp (toggleShapeMode):**
- Detecta si vuelve a SHAPE en modo LOGO con PNG_SHAPE
- Restaura setLogoMode(true) después de recrear instancia
**defines.h:**
- PNG_IDLE_TIME_MIN/MAX = 0.5-2.0s (modo MANUAL)
- PNG_IDLE_TIME_MIN_LOGO/MAX_LOGO = 2.5-4.5s (modo LOGO)
**Resultado:** PNG_SHAPE ahora hace flip cada 2.5-4.5s en modo LOGO (visible antes de toggles)
## 4. Nuevas Texturas
**data/balls/big.png:** 16x16px (añadida)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cambios:
1. **PNG_SHAPE pivoteo sutil** (similar a WAVE_GRID):
- Añadidas variables tilt_x_ y tilt_y_ en png_shape.h
- Actualización continua de tilt en update()
- Aplicación de pivoteo en getPoint3D() con:
* Cálculo correcto de logo_size para normalización
* Normalización a rango [-1, 1] usando logo_size * 0.5
* Amplitudes 0.15 y 0.1 (matching WAVE_GRID)
* z_tilt proporcional al tamaño del logo
- Fix crítico: usar z_base en lugar de z fijo (línea 390)
2. **Eliminación de debug output**:
- Removidos 13 std::cout de png_shape.cpp
- Removidos 2 std::cout de engine.cpp (Logo Mode)
- Consola ahora limpia sin mensajes [PNG_SHAPE]
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
MODO LOGO (Easter Egg):
- Modo especial que muestra logo JAILGAMES como "marca de agua"
- Activación manual: tecla K (perpetuo, no sale automáticamente)
- Auto-salto desde DEMO/DEMO_LITE (15%/10% probabilidad, ≥500 pelotas)
- Configuración fija: PNG_SHAPE + tiny texture + MONOCHROME + 120% escala + 5000 pelotas
- Sistema de 5 acciones variadas con probabilidades ajustadas:
* SHAPE→PHYSICS gravedad ON (50%) - caída dramática
* SHAPE→PHYSICS gravedad OFF (50%) - ver rotaciones sin caer
* PHYSICS→SHAPE (60%) - reconstruir logo y mostrar rotaciones
* PHYSICS: forzar gravedad ON (20%) - caer mientras da vueltas
* PHYSICS: forzar gravedad OFF (20%) - flotar mientras da vueltas
- Intervalos 4-8s (aumentado para completar ciclos de rotación PNG_SHAPE)
- Textos informativos suprimidos en Logo Mode
- Corrección cambio de textura: actualiza texture_ y setTexture() en pelotas
- PNG_SHAPE idle reducido a 0.5-2s para animación más dinámica
MEJORAS FÍSICAS GLOBALES:
- Impulso automático al quitar gravedad si >50% pelotas en superficie
- Usa isOnSurface() para detectar pelotas quietas (DEMO/DEMO_LITE/LOGO)
- Evita que quitar gravedad con pelotas paradas no haga nada visible
SISTEMA AUTOMÁTICO DE CURSOR:
- Importado mouse.h/mouse.cpp desde Coffee Crisis Arcade Edition
- Auto-oculta cursor tras 3s de inactividad (namespace Mouse)
- Reaparece inmediatamente al mover ratón
- Funciona en todos los modos (ventana, fullscreen F3, real fullscreen F4)
- Eliminadas llamadas manuales SDL_ShowCursor/HideCursor
- Soluciona bug: cursor visible al iniciar con argumento -f
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
REFACTORING:
- Movido array de escenarios desde engine.h a defines.h
- Nombre más descriptivo: test_ → BALL_COUNT_SCENARIOS
- Ahora es constexpr y accesible globalmente
MEJORA PNG_SHAPE:
- Priorizar calidad 2D sobre profundidad 3D
- Reducir capas AGRESIVAMENTE hasta 1 (antes se detenía en 3)
- Condiciones más estrictas: < total (antes < total * 0.8)
- Vértices activados hasta 150 pelotas (antes 100)
FILOSOFÍA NUEVA:
1. Reducir capas hasta 1 (llenar bien el texto en 2D)
2. Si no alcanza: filas alternas en relleno
3. Si no alcanza: cambiar a bordes
4. Si no alcanza: filas alternas en bordes
5. Último recurso: vértices
RESULTADO ESPERADO:
- 500 pelotas: RELLENO completo 1 capa (texto lleno, sin 3D)
- 100 pelotas: BORDES completos 1 capa (todo visible)
- 50 pelotas: VÉRTICES (esqueleto visible)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Sistema de optimización en 3 niveles para cualquier cantidad de pelotas:
Nivel 1 - Cambio de modo:
- Si RELLENO y pocas pelotas → switch a BORDES
- Reduce de ~22K puntos a ~4.5K puntos
Nivel 2 - Reducción de capas:
- Si aún insuficiente → dividir capas a la mitad
- 15 capas → 7 capas → 3 capas → 1 capa
- Reduce profundidad pero mantiene forma visible
Nivel 3 - Sampling de píxeles:
- Si aún insuficiente → tomar cada N píxeles
- Sampling 1/2, 1/3, 1/4... hasta 1/10
- Funciona en BORDES o RELLENO
- Mantiene forma general con menos detalle
Resultado:
- Con 1 pelota: Funciona (1 píxel visible) ✅
- Con 10 pelotas: Forma reconocible ✅
- Con 100 pelotas: Forma clara ✅
- Con 1000+ pelotas: Relleno completo ✅
Output informativo:
[PNG_SHAPE] Paso 1: Cambiando RELLENO → BORDES
[PNG_SHAPE] Paso 2: Reduciendo capas a 3
[PNG_SHAPE] Paso 3: Aplicando sampling 1/4
[PNG_SHAPE] === CONFIGURACIÓN FINAL ===
[PNG_SHAPE] Modo: BORDES (optimizado)
[PNG_SHAPE] Píxeles 2D: 75 (sampling 1/4)
[PNG_SHAPE] Capas: 3
[PNG_SHAPE] Ratio: 1.33 pelotas/punto ✅🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problema:
- PNG_USE_EDGES_ONLY = false usa ~22,000 puntos 3D
- Con 1, 10 o 100 pelotas, no hay suficientes para formar el logo
- Resultado: logo invisible o mal formado
Solución:
- Detectar automáticamente si num_pelotas < total_puntos / 2
- Si hay pocas pelotas → cambiar automáticamente a BORDES
- Bordes usa ~300 puntos × 15 capas = ~4,500 puntos 3D
- Mucho mejor ratio para pocos sprites
Implementación:
- generatePoints() verifica ratio pelotas/puntos
- Si insuficiente: llama detectEdges() y regenera
- getPoint3D() usa edge_points_ si están disponibles
- Mensajes informativos en consola
Ahora funciona:
Escenario 1 (1 pelota) → Auto-switch a bordes ✅
Escenario 2 (10 pelotas) → Auto-switch a bordes ✅
Escenario 5 (1000 pelotas) → Usa relleno completo ✅
Escenario 6+ (10K+ pelotas) → Usa relleno completo ✅
Output de debug muestra:
[PNG_SHAPE] Advertencia: Solo X pelotas para Y puntos
[PNG_SHAPE] Cambiando automáticamente a BORDES...
[PNG_SHAPE] Modo: BORDES/RELLENO
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problema:
- Al usar -w/-h, la ventana se creaba correcta
- Pero el renderizado interno seguía usando SCREEN_WIDTH/HEIGHT (320x240)
- Resultado: ventana grande con área de juego pequeña en esquina
Solución:
- Añadidas variables base_screen_width/height_
- Guardan resolución configurada por CLI (o default)
- current_screen_* ahora se inicializa con valores base
- toggleRealFullscreen() restaura a resolución base, no constantes
Cambios:
- engine.h: Añadir base_screen_width/height_
- engine.cpp: Inicializar con valores CLI
- engine.cpp: Usar base_* al salir de fullscreen real
Ahora funciona:
./vibe3_physics -w 1920 -h 1080 # Renderiza en 1920x1080 ✅
./vibe3_physics # Renderiza en 1280x720 ✅🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Nuevas Características:
- PNG_SHAPE (tecla O): Logo JAILGAMES desde PNG 1-bit
- Extrusión 2D con detección de bordes/relleno configurable
- Rotación "legible": 90% frente, 10% volteretas aleatorias
- 15 capas de extrusión con relleno completo (22K+ puntos 3D)
- Fix: Z forzado a máximo cuando está de frente (brillante)
- Excluido de DEMO/DEMO_LITE (logo especial)
- Sistema de texturas dinámicas
- Carga automática desde data/balls/*.png
- normal.png siempre primero, resto alfabético
- Tecla N cicla entre todas las texturas encontradas
- Display dinámico del nombre (uppercase)
- Física mejorada para figuras 3D
- Constantes SHAPE separadas de ROTOBALL
- SHAPE_SPRING_K=800 (+167% rigidez vs ROTOBALL)
- SHAPE_DAMPING_NEAR=150 (+88% absorción)
- Pelotas mucho más "pegadas" durante rotaciones
- applyRotoBallForce() acepta parámetros personalizados
Archivos:
- NEW: source/shapes/png_shape.{h,cpp}
- NEW: data/shapes/jailgames.png
- NEW: data/balls/{normal,small,tiny}.png
- MOD: defines.h (constantes PNG + SHAPE physics)
- MOD: engine.cpp (carga dinámica texturas + física SHAPE)
- MOD: ball.{h,cpp} (parámetros física configurables)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- **CylinderShape**: Añadido sistema de tumbling ocasional
- Volteretas de 90° en eje X cada 3-5 segundos
- Interpolación suave con ease-in-out (1.5s)
- Rotación Y continua + tumbling X ocasional = más dinámico
- **WaveGridShape**: Reemplazada rotación por pivoteo sutil
- Eliminada rotación completa en eje Y
- Pivoteo en esquinas (oscilación lenta 0.3/0.5 rad/s)
- Grid paralelo a pantalla con efecto "sábana ondeando"
- Ondas sinusoidales + pivoteo = movimiento más orgánico
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes:
1. F5 ahora cicla correctamente entre 3 modos de escalado:
- INTEGER: Escalado entero con barras negras (píxel perfecto)
- LETTERBOX: Zoom hasta llenar una dimensión
- STRETCH: Estirar pantalla completa
2. Artefactos de renderizado en barras negras resueltos:
- SDL_RenderClear() ahora usa color negro
- Barras letterbox/integer se muestran negras correctamente
3. Texto duplicado de tema resuelto:
- Durante LERP, verifica tema actual Y destino
- Evita mostrar segunda línea si text_ es nombre de tema
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Funcionalidad:
- Tecla F5 alterna entre escalado INTEGER y STRETCH
- Solo activo en modo fullscreen F3 (no aplica en F4)
- INTEGER: Mantiene aspecto 4:3 con bandas negras
- STRETCH: Estira imagen a pantalla completa
- Texto informativo: 'SCALING: INTEGER' o 'SCALING: STRETCH'
Implementación:
- Variable integer_scaling_enabled_ (true por defecto)
- toggleIntegerScaling() cambia SDL_RendererLogicalPresentation
- Solo funciona si fullscreen_enabled_ == true
- Ignora la tecla si no estás en modo F3
README actualizado:
- Añadida tecla F5 en controles de ventana
- Actualizada descripción de F3
- Nueva característica en lista principal
Comportamiento:
- Por defecto: INTEGER (mantiene aspecto)
- Presionar F5: Cambia a STRETCH (pantalla completa)
- Presionar F5 otra vez: Vuelve a INTEGER
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cambios:
- toggleFullscreen(): Oculta cursor al entrar, muestra al salir
- toggleRealFullscreen(): Oculta cursor al entrar, muestra al salir
- Usa SDL3 API correcta: SDL_HideCursor() y SDL_ShowCursor()
Comportamiento:
- F3 (fullscreen normal): Cursor oculto
- F4 (real fullscreen): Cursor oculto
- Salir de cualquier fullscreen: Cursor visible de nuevo
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Características actuales actualizadas:
- 6 temas visuales (antes decía 5)
- Transiciones LERP añadidas
- Hot-swap de sprites con tecla N
- 8 Figuras 3D completas (ya no pendientes)
- Paletas 8-24 colores según tema
Controles actualizados:
- Todas las figuras marcadas como implementadas (Q/W/E/R/T/Y/U/I)
- Añadida tecla N para cambio de sprites
- MONOCHROME corregido: todas las pelotas blancas (no tonos de gris)
Temas de colores:
- MONOCHROME: 'Todas las pelotas blancas puras' (antes '8 tonos de gris')
- Añadida sección 'Sistema de Sprites Dinámico'
- Añadida sección 'Controles de Temas' con LERP
Figuras 3D:
- Documentadas las 8 figuras completas (SPHERE, WAVE_GRID, HELIX, TORUS, CUBE, CYLINDER, ICOSAHEDRON, ATOM)
- Eliminada sección 'Figuras Futuras (Pendientes)'
- Descripciones técnicas de cada figura
Detalles técnicos corregidos:
- Resolución: 320x240 (antes 640x360)
- Escala por defecto: x3 (antes x2)
- Tamaño pelota: Dinámico 10x10 o 6x6 (antes fijo 10x10)
- Física: Rebotes 0.30-0.95 (más preciso)
- Añadidos temas y figuras 3D
Nuevas secciones técnicas:
- Sistema de Transiciones LERP (completa explicación con código)
- Sistema de Hot-Swap de Sprites (desafío técnico y solución)
- Texturas disponibles en data/
Estructura del proyecto:
- Añadido ball_small.png en data/
- Actualizada carpeta shapes/ con 8 figuras
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cambios en Estado Actual:
- Marcar CYLINDER y WAVE_GRID con advertencias de mejora
- Actualizar Temas Visuales: 6/6 completadas
- Añadir sección de Sistemas de Presentación
Nueva sección: Mejorar Animaciones de Figuras 3D
- CYLINDER: Rotación multi-eje con tumbling periódico
- WAVE_GRID: Vista frontal paralela con pivoteo sutil
- Implementación técnica con rotation_mode y estados
Historial de Cambios:
- Documentar logros de sesión actual (MONOCHROME, LERP, hot-swap)
- Añadir sesiones anteriores para contexto
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Sistema de múltiples texturas:
- Carga ball.png (10x10) y ball_small.png (6x6) al inicio
- Variable current_ball_size_ obtiene tamaño desde texture->getWidth()
- Eliminar constante BALL_SIZE hardcoded
Cambio de tamaño con ajuste de posiciones:
- updateBallSizes() ajusta pos según gravedad y superficie
- DOWN: mueve Y hacia abajo si crece
- UP: mueve Y hacia arriba si crece
- LEFT/RIGHT: mueve X correspondiente
- Solo ajusta pelotas en superficie (isOnSurface())
Ball class actualizada:
- Constructor recibe ball_size como parámetro
- updateSize(new_size): actualiza hitbox y sprite
- setTexture(texture): cambia textura del sprite
- setPosition() usa setRotoBallScreenPosition()
Sprite class:
- Añadido setTexture() inline para hot-swap
Tecla N:
- Cicla entre texturas disponibles
- Actualiza todas las pelotas sin reiniciar física
- Texto informativo "SPRITE: NORMAL" / "SPRITE: SMALL"
Fix bug initBalls():
- Ahora usa current_ball_size_ en constructor
- Pelotas nuevas tienen tamaño correcto según textura activa
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Características:
- Sistema LERP para interpolar colores de fondo y sprites
- Transiciones de 0.5 segundos sin interrumpir física
- Variables de estado: target_theme, transitioning, transition_progress
- getInterpolatedColor() para colores en tiempo real
- Actualización automática de colores al finalizar transición
- setColor() añadido a Ball class
- Teclas B y Numpad 1-6 activan transiciones suaves
- Ya no reinicia pelotas al cambiar tema
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fondo negro degradado (similar a NEON)
- 8 tonos de gris: blanco puro a gris muy oscuro
- Estética minimalista monocromática
- Ciclo con tecla B incluye nuevo tema
- Actualizado README con documentación
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Nueva clase AtomShape con núcleo central + 3 órbitas
- Núcleo: esfera pequeña con distribución Fibonacci
- Órbitas: planos inclinados con electrones animados
- Rotación global + rotación orbital independiente
- Modelo atómico clásico de Bohr
- Compatible con física spring-damper y z-sorting
✅ TODAS LAS 8 FIGURAS 3D IMPLEMENTADAS:
Q-Sphere, W-WaveGrid, E-Helix, R-Torus, T-Cube, Y-Cylinder, U-Icosahedron, I-Atom
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Nueva clase IcosahedronShape con 12 vértices golden ratio
- Vértices basados en 3 rectángulos áureos ortogonales
- Subdivisión de caras para más de 12 puntos
- Rotación triple simultánea (X, Y, Z)
- Proyección a esfera circunscrita
- Compatible con física spring-damper y z-sorting
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Nueva clase CylinderShape con ecuaciones paramétricas
- Distribución uniforme en anillos y circunferencia
- Rotación simple en eje Y
- Dimensiones: radius=0.25, height=0.5 (proporción altura)
- Compatible con física spring-damper y z-sorting
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Nueva clase TorusShape con ecuaciones paramétricas
- Distribución uniforme en anillos y puntos por anillo
- Rotación triple simultánea (X, Y, Z)
- Radios: major=0.25, minor=0.12 (proporción altura)
- Compatible con física spring-damper y z-sorting
- Escalable con Numpad +/-
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Nueva clase WaveGridShape con ecuaciones de onda 2D
- Grid adaptativo según número de pelotas (1-N puntos)
- Ecuación: z = A*sin(kx*x + phase)*cos(ky*y + phase)
- Rotación lenta en Y + animación de fase rápida
- Compatible con física spring-damper y z-sorting
- Escalable con Numpad +/-
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Nueva clase HelixShape con ecuaciones paramétricas
- Distribución uniforme en 3 vueltas completas
- Rotación en eje Y + animación de fase vertical
- Pitch ajustado a 0.25 para evitar clipping (180px altura total)
- Compatible con física spring-damper y z-sorting
- Escalable con Numpad +/-
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
NUEVAS CARACTERÍSTICAS:
- Zoom por profundidad Z: Escala sprites según distancia (0.5x-1.5x)
- Toggle con Numpad / (KP_DIVIDE) para activar/desactivar perspectiva
- Fix transición figura→física: Reset automático de depth_scale a 1.0
- Texto informativo: "DEPTH ZOOM ON/OFF"
IMPLEMENTACIÓN TÉCNICA:
- Ball class: Nueva variable depth_scale_ (0.5-1.5)
- Ball class: Getters/setters getDepthScale() / setDepthScale()
- Engine::addSpriteToBatch(): Parámetro scale con valor defecto 1.0
- Engine::addSpriteToBatch(): Cálculo de vértices escalados centrados
- Engine::updateShape(): Cálculo depth_scale = 0.5 + z_normalized * 1.0
- Engine::render(): Pasa depth_scale al batch en modo SHAPE
- Engine::toggleShapeMode(): Reset depth_scale en salida de figura
- Engine: Variable depth_zoom_enabled_ (true por defecto)
- Batch rendering: Mantiene performance (sin llamadas individuales)
EFECTO VISUAL:
- Pelotas lejanas (Z-): Pequeñas (50%) y oscuras
- Pelotas medias (Z=0): Normales (100%) y brillo medio
- Pelotas cercanas (Z+): Grandes (150%) y brillantes
- Perspectiva 3D realista combinada con Z-sorting
CONTROLES:
- Numpad /: Toggle zoom por profundidad (solo en modo SHAPE)
- Por defecto: ACTIVADO para máximo realismo 3D
README ACTUALIZADO:
- Añadida tecla KP_/ a tabla de controles
- Actualizada sección "Características Técnicas"
- Añadida línea "Zoom por profundidad" en características
- Actualizada sección "Uso" con control de perspectiva
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problema:
- Radio fijo de 80px funcionaba bien en 320x240
- En F4 fullscreen (1920x1080), radio de 360px era correcto visualmente
- PERO las fuerzas físicas (spring_k, damping) seguían siendo para 80px
- Resultado: pelotas nunca llegaban a pegarse en resoluciones altas
Solución:
1. Radio proporcional a altura de pantalla (ROTOBALL_RADIUS_FACTOR = 0.333)
2. Escalar TODAS las constantes de física proporcionalmente al radio
3. Fórmula: scale = sphere_radius / BASE_RADIUS (80px)
Cambios técnicos:
- defines.h: ROTOBALL_RADIUS → ROTOBALL_RADIUS_FACTOR (0.333)
- engine.cpp: Calcular radius dinámicamente en generate/update
- ball.h: applyRotoBallForce() ahora recibe sphere_radius
- ball.cpp: Escalar spring_k, damping_base, damping_near, near_threshold, max_force
Resultado:
- 320x240: Radio 80px, scale=1.0 (idéntico a antes)
- 640x480: Radio 160px, scale=2.0 (fuerzas 2x)
- 1920x1080: Radio 360px, scale=4.5 (fuerzas 4.5x)
Comportamiento físico IDÉNTICO en todas las resoluciones ✅🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cambios principales:
- Fix: Pelotas ahora caen correctamente al salir de RotoBall (resetear on_surface_/stopped_)
- Fix: Al cambiar escenario en RotoBall, desactivar modo figura primero
- Feature: Al entrar en RotoBall, gravedad se desactiva automáticamente
- Feature: Tecla G desde RotoBall → Sale a física SIN gravedad (pelotas flotan)
- Feature: Tecla C desde RotoBall → Sale a física CON gravedad (pelotas caen)
- Feature: Cursores desde RotoBall → Sale a física CON gravedad + cambio dirección
- Feature: Cursores desde física sin gravedad → Reactiva gravedad automáticamente
Nuevos métodos:
- Ball::enableGravityIfDisabled() - Reactiva solo si está desactivada
- Ball::forceGravityOn() - Fuerza activación
- Ball::forceGravityOff() - Fuerza desactivación
- Engine::toggleRotoBallMode(bool force_gravity_on_exit) - Control de gravedad al salir
Lógica de controles desde RotoBall:
- C: Figura OFF → Física CON gravedad (caen)
- ↑↓←→: Figura OFF → Física CON gravedad + dirección (caen hacia dirección)
- G: Figura OFF → Física SIN gravedad (flotan)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Solucionar problema de renderizado donde pelotas oscuras (fondo) se
pintaban sobre pelotas claras (frente) debido al orden fijo del vector.
## Problema Resuelto
**Antes:**
- Renderizado en orden del vector: Ball[0], Ball[1], ..., Ball[N]
- Orden aleatorio respecto a profundidad Z
- Pelotas oscuras (lejos) se pintaban sobre claras (cerca)
- Resultado visual incorrecto (inversión de profundidad)
**Ahora:**
- Ordenamiento por depth_brightness antes de renderizar
- Painter's Algorithm: Fondo primero, frente último
- Pelotas oscuras (Z bajo) → Pelotas claras (Z alto)
- Renderizado 3D correcto con oclusión visual apropiada
## Implementación
### Algoritmo (engine.cpp::render())
```cpp
// Solo en modo RotoBall:
1. Crear vector de índices [0, 1, 2, ..., N]
2. Ordenar índices por depth_brightness (menor primero)
- depth_brightness bajo = lejos/oscuro = fondo
- depth_brightness alto = cerca/brillante = frente
3. Renderizar en orden: índices ordenados
4. Resultado: Fondo → Frente (oclusión correcta)
```
### Complejidad
- **Tiempo:** O(n log n) por std::sort cada frame
- **Espacio:** O(n) para vector de índices
- **Impacto:** Imperceptible hasta ~10K pelotas
- 1K pelotas: ~0.01ms
- 10K pelotas: ~0.15ms
- 100K pelotas: ~2ms (ligera bajada FPS)
### Optimización
- Solo ordena en modo RotoBall
- Modo física: orden normal (sin overhead)
- Vector balls_ no se modifica (física estable)
- Usa vector de índices temporal
## Resultado Visual
✅ Pelotas del fondo (oscuras) quedan detrás
✅ Pelotas del frente (brillantes) quedan delante
✅ Efecto 3D realista con profundidad correcta
✅ Oclusión visual apropiada (Painter's Algorithm)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Incrementar constantes de damping para lograr amortiguamiento crítico
y eliminar oscilaciones durante la convergencia a la esfera RotoBall.
## Cambios
**defines.h - Nuevos valores:**
- `ROTOBALL_DAMPING_BASE`: 15.0 → 35.0 (+133%)
- Amortiguamiento crítico calculado: c ≈ 2*√(k*m) = 2*√(300*1) ≈ 34.64
- `ROTOBALL_DAMPING_NEAR`: 50.0 → 80.0 (+60%)
- Absorción rápida cuando están cerca del punto
## Problema Resuelto
**Antes (subdamped):**
- Las pelotas oscilaban varias veces antes de estabilizarse
- Sobrepasaban el punto destino repetidamente
- Convergencia lenta con "rebotes" visuales
**Ahora (critically damped):**
- Las pelotas convergen directamente sin oscilar
- Se "pegan" suavemente a la esfera
- Absorción rápida y visualmente limpia
## Teoría
Sistema masa-resorte-amortiguador:
- Subdamped (c < 2√km): Oscila antes de estabilizar
- Critically damped (c = 2√km): Converge rápido sin oscilar
- Overdamped (c > 2√km): Converge muy lento
Valores ajustados para estar en el punto crítico, logrando
la convergencia más rápida posible sin oscilación.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reemplazar interpolación lineal artificial por sistema de fuerzas físicamente
realista usando Ley de Hooke con amortiguación variable.
## Cambios Principales
### Sistema de Física Implementado
**Fuerza de Resorte (Hooke's Law):**
```cpp
F_spring = k * (target - position)
F_damping = c * velocity
F_total = F_spring - F_damping
acceleration = F_total / mass
```
**Constantes (defines.h):**
- `ROTOBALL_SPRING_K = 300.0f`: Rigidez del resorte
- `ROTOBALL_DAMPING_BASE = 15.0f`: Amortiguación lejos del punto
- `ROTOBALL_DAMPING_NEAR = 50.0f`: Amortiguación cerca (estabilización)
- `ROTOBALL_NEAR_THRESHOLD = 5.0f`: Distancia considerada "cerca"
- `ROTOBALL_MAX_FORCE = 1000.0f`: Límite de seguridad
### Nuevas Funciones (Ball class)
- `enableRotoBallAttraction(bool)`: Activa/desactiva atracción física
- `applyRotoBallForce(target_x, target_y, deltaTime)`: Aplica fuerza de resorte
### Comportamiento Físico
**Al entrar (PHYSICS → ROTOBALL):**
1. Pelotas mantienen velocidad actual (vx, vy)
2. Fuerza de atracción las acelera hacia puntos en esfera rotante
3. Amortiguación variable evita oscilaciones infinitas
4. Convergen al punto con aceleración natural
**Durante RotoBall:**
- Punto destino rota constantemente
- Fuerza se recalcula cada frame hacia posición rotada
- Pelotas "persiguen" su punto móvil
- Efecto: Convergencia orgánica con ligera oscilación
**Al salir (ROTOBALL → PHYSICS):**
1. Atracción se desactiva
2. Pelotas conservan velocidad tangencial actual
3. Gravedad vuelve a aplicarse
4. Caen con la inercia que traían de la esfera
### Archivos Modificados
- `defines.h`: 5 nuevas constantes físicas
- `ball.h/cpp`: Sistema de resorte completo
- `engine.cpp`: Enable/disable atracción en toggle, updateRotoBall() usa física
- `CLAUDE.md`: Documentación técnica completa
## Ventajas del Sistema
✅ Física realista con conservación de momento
✅ Transición orgánica (no artificial)
✅ Inercia preservada entrada/salida
✅ Amortiguación automática (no oscila infinito)
✅ Constantes ajustables para tuning
✅ Performance: O(1) por pelota
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Añadido modo alternativo de simulación que transforma las pelotas en una
esfera 3D rotante proyectada en 2D, inspirado en efectos clásicos de demoscene.
## Características Principales
- **Algoritmo Fibonacci Sphere**: Distribución uniforme de puntos en esfera 3D
- **Rotación dual**: Matrices de rotación en ejes X e Y simultáneos
- **Profundidad Z simulada**: Color modulation según distancia (oscuro=lejos, brillante=cerca)
- **Transición suave**: Interpolación de 1.5s desde física a esfera
- **Sin sprites adicionales**: Usa SDL_SetTextureColorMod para profundidad
- **Performance optimizado**: >60 FPS con 100,000 pelotas
## Implementación Técnica
### Nuevos Archivos/Cambios:
- `defines.h`: Enum SimulationMode + constantes RotoBall (radio, velocidades, brillo)
- `ball.h/cpp`: Soporte 3D (pos_3d, target_2d, depth_brightness, setters)
- `engine.h/cpp`: Lógica completa RotoBall (generate, update, toggle)
- `generateRotoBallSphere()`: Fibonacci sphere algorithm
- `updateRotoBall()`: Rotación 3D + proyección ortográfica
- `toggleRotoBallMode()`: Cambio entre PHYSICS/ROTOBALL
- `README.md`: Documentación completa del modo
- `CLAUDE.md`: Detalles técnicos y algoritmos
## Parámetros Configurables (defines.h)
```cpp
ROTOBALL_RADIUS = 80.0f; // Radio de la esfera
ROTOBALL_ROTATION_SPEED_Y = 1.5f; // Velocidad rotación eje Y (rad/s)
ROTOBALL_ROTATION_SPEED_X = 0.8f; // Velocidad rotación eje X (rad/s)
ROTOBALL_TRANSITION_TIME = 1.5f; // Tiempo de transición (segundos)
ROTOBALL_MIN_BRIGHTNESS = 50; // Brillo mínimo fondo (0-255)
ROTOBALL_MAX_BRIGHTNESS = 255; // Brillo máximo frente (0-255)
```
## Uso
- **Tecla C**: Alternar entre modo física y modo RotoBall
- Compatible con todos los temas de colores
- Funciona con 1-100,000 pelotas
- Debug display muestra "MODE PHYSICS" o "MODE ROTOBALL"
## Performance
- Batch rendering: Una sola llamada SDL_RenderGeometry
- Fibonacci sphere recalculada por frame (O(n) predecible)
- Color mod CPU-side sin overhead GPU
- Delta time independiente del framerate
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Añadir getter isStopped() en Ball para detectar pelotas quietas
- Sistema de temporizador de 5 segundos que detecta cuando todas las pelotas están paradas
- Auto-reinicio aleatorio cuando se cumple el tiempo de inactividad:
* Escenario aleatorio usando test_.size() (1 a 100,000 pelotas)
* Tema aleatorio usando sizeof(themes_) (5 temas disponibles)
* Reset inteligente del temporizador si alguna pelota se mueve
- Integración no intrusiva en update() del bucle principal
- Usa infraestructura existente (SDL_GetTicks, initBalls, rand)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Renombrar pushUpBalls() → pushBallsAwayFromGravity() con lógica direccional
- ESPACIO ahora impulsa en dirección opuesta a gravedad actual:
* Gravedad DOWN → impulsa ARRIBA (comportamiento original)
* Gravedad UP → impulsa ABAJO
* Gravedad LEFT → impulsa DERECHA
* Gravedad RIGHT → impulsa IZQUIERDA
- Corregir modVel() para aplicar impulso horizontal a todas las pelotas
- Actualizar documentación de controles en README.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Refactorizar ThemeColors para usar std::vector<Color> en lugar de array fijo
- Cada tema puede tener cualquier cantidad de colores (8, 12, 24, etc.)
- Expandir tema RGB a 24 colores del círculo cromático (cada 15°)
- Añadir función initializeThemes() para configuración dinámica
- Mantener temas originales con 8 colores cada uno
- Tema RGB ahora incluye transiciones suaves por todo el espectro
Paleta RGB matemáticamente perfecta:
- 6 colores primarios: R, Y, G, C, B, M (cada 60°)
- 18 colores intermedios: transiciones cada 15°
- Cobertura completa del círculo cromático 0°-345°
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Añadido nuevo tema RGB con fondo blanco puro y colores matemáticos
- Actualizado README.md con controles actuales y nuevas características
- Reorganizada documentación de controles por categorías
- Corregida información obsoleta (resolución, temas, problemas)
- Añadido control KP_5 para selección directa del tema RGB
- Mejorada visibilidad del texto adaptando colores por tema
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>