PNG_SHAPE: Auto-switch a bordes cuando hay pocas pelotas

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>
This commit is contained in:
2025-10-04 16:42:27 +02:00
parent 723bb6d198
commit 757bb9c525
2 changed files with 93 additions and 79 deletions

View File

@@ -7,9 +7,8 @@ constexpr char WINDOW_CAPTION[] = "vibe3_physics";
// Resolución por defecto (usada si no se especifica en CLI)
constexpr int DEFAULT_SCREEN_WIDTH = 320; // Ancho lógico por defecto (si no hay -w)
constexpr int DEFAULT_SCREEN_HEIGHT = 240; // Alto lógico por defecto (si no hay -h)
constexpr int DEFAULT_WINDOW_ZOOM = 3; // Zoom inicial de ventana (1x = sin zoom)
// BALL_SIZE eliminado: ahora se obtiene dinámicamente desde texture_->getWidth()
constexpr int DEFAULT_SCREEN_HEIGHT = 240; // Alto lógico por defecto (si no hay -h)
constexpr int DEFAULT_WINDOW_ZOOM = 3; // Zoom inicial de ventana (1x = sin zoom)
// Configuración de zoom dinámico de ventana
constexpr int WINDOW_ZOOM_MIN = 1; // Zoom mínimo (320x240)
@@ -64,22 +63,22 @@ enum class ColorTheme {
// Enum para tipo de figura 3D
enum class ShapeType {
NONE, // Sin figura (modo física pura)
SPHERE, // Esfera Fibonacci (antiguo RotoBall)
CUBE, // Cubo rotante
HELIX, // Espiral 3D
TORUS, // Toroide/donut
WAVE_GRID, // Malla ondeante
CYLINDER, // Cilindro rotante
ICOSAHEDRON, // Icosaedro D20
ATOM, // Átomo con órbitas
PNG_SHAPE // Forma cargada desde PNG 1-bit
NONE, // Sin figura (modo física pura)
SPHERE, // Esfera Fibonacci (antiguo RotoBall)
CUBE, // Cubo rotante
HELIX, // Espiral 3D
TORUS, // Toroide/donut
WAVE_GRID, // Malla ondeante
CYLINDER, // Cilindro rotante
ICOSAHEDRON, // Icosaedro D20
ATOM, // Átomo con órbitas
PNG_SHAPE // Forma cargada desde PNG 1-bit
};
// Enum para modo de simulación
enum class SimulationMode {
PHYSICS, // Modo física normal con gravedad
SHAPE // Modo figura 3D (Shape polimórfico)
PHYSICS, // Modo física normal con gravedad
SHAPE // Modo figura 3D (Shape polimórfico)
};
// Enum para modo de escalado en fullscreen (F5)
@@ -99,45 +98,45 @@ constexpr int ROTOBALL_MAX_BRIGHTNESS = 255; // Brillo máximo (frente, 0-
// Física de atracción para figuras 3D (sistema de resorte)
// ROTOBALL: Figura esfera rotante especial (modo C)
constexpr float ROTOBALL_SPRING_K = 300.0f; // Constante de rigidez del resorte (N/m)
constexpr float ROTOBALL_DAMPING_BASE = 35.0f; // Amortiguación base (amortiguamiento crítico ≈ 2*√k*m)
constexpr float ROTOBALL_DAMPING_NEAR = 80.0f; // Amortiguación cerca del punto (absorción rápida)
constexpr float ROTOBALL_NEAR_THRESHOLD = 5.0f; // Distancia "cerca" en píxeles
constexpr float ROTOBALL_MAX_FORCE = 1000.0f; // Fuerza máxima aplicable (evita explosiones)
constexpr float ROTOBALL_SPRING_K = 300.0f; // Constante de rigidez del resorte (N/m)
constexpr float ROTOBALL_DAMPING_BASE = 35.0f; // Amortiguación base (amortiguamiento crítico ≈ 2*√k*m)
constexpr float ROTOBALL_DAMPING_NEAR = 80.0f; // Amortiguación cerca del punto (absorción rápida)
constexpr float ROTOBALL_NEAR_THRESHOLD = 5.0f; // Distancia "cerca" en píxeles
constexpr float ROTOBALL_MAX_FORCE = 1000.0f; // Fuerza máxima aplicable (evita explosiones)
// SHAPE: Figuras 3D normales (Q/W/E/R/T/Y/U/I/O) - Mayor pegajosidad
constexpr float SHAPE_SPRING_K = 800.0f; // Rigidez alta (pelotas más "pegadas")
constexpr float SHAPE_DAMPING_BASE = 60.0f; // Amortiguación alta (menos rebote)
constexpr float SHAPE_DAMPING_NEAR = 150.0f; // Absorción muy rápida al llegar
constexpr float SHAPE_NEAR_THRESHOLD = 8.0f; // Umbral "cerca" más amplio
constexpr float SHAPE_MAX_FORCE = 2000.0f; // Permite fuerzas más fuertes
constexpr float SHAPE_SPRING_K = 800.0f; // Rigidez alta (pelotas más "pegadas")
constexpr float SHAPE_DAMPING_BASE = 60.0f; // Amortiguación alta (menos rebote)
constexpr float SHAPE_DAMPING_NEAR = 150.0f; // Absorción muy rápida al llegar
constexpr float SHAPE_NEAR_THRESHOLD = 8.0f; // Umbral "cerca" más amplio
constexpr float SHAPE_MAX_FORCE = 2000.0f; // Permite fuerzas más fuertes
// Configuración del Cubo (cubo 3D rotante)
constexpr float CUBE_SIZE_FACTOR = 0.25f; // Tamaño como proporción de altura (60/240 = 0.25)
constexpr float CUBE_ROTATION_SPEED_X = 0.5f; // Velocidad rotación eje X (rad/s)
constexpr float CUBE_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
constexpr float CUBE_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
constexpr float CUBE_SIZE_FACTOR = 0.25f; // Tamaño como proporción de altura (60/240 = 0.25)
constexpr float CUBE_ROTATION_SPEED_X = 0.5f; // Velocidad rotación eje X (rad/s)
constexpr float CUBE_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
constexpr float CUBE_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
// Configuración de Helix (espiral helicoidal 3D)
constexpr float HELIX_RADIUS_FACTOR = 0.25f; // Radio de la espiral (proporción de altura)
constexpr float HELIX_PITCH_FACTOR = 0.25f; // Separación vertical entre vueltas (proporción de altura)
constexpr float HELIX_NUM_TURNS = 3.0f; // Número de vueltas completas (1-5)
constexpr float HELIX_ROTATION_SPEED_Y = 1.2f; // Velocidad rotación eje Y (rad/s)
constexpr float HELIX_PHASE_SPEED = 0.5f; // Velocidad de animación vertical (rad/s)
constexpr float HELIX_RADIUS_FACTOR = 0.25f; // Radio de la espiral (proporción de altura)
constexpr float HELIX_PITCH_FACTOR = 0.25f; // Separación vertical entre vueltas (proporción de altura)
constexpr float HELIX_NUM_TURNS = 3.0f; // Número de vueltas completas (1-5)
constexpr float HELIX_ROTATION_SPEED_Y = 1.2f; // Velocidad rotación eje Y (rad/s)
constexpr float HELIX_PHASE_SPEED = 0.5f; // Velocidad de animación vertical (rad/s)
// Configuración de Wave Grid (malla ondeante 3D)
constexpr float WAVE_GRID_SIZE_FACTOR = 0.35f; // Tamaño del grid (proporción de altura)
constexpr float WAVE_GRID_AMPLITUDE = 0.15f; // Amplitud de las ondas (proporción de altura)
constexpr float WAVE_GRID_FREQUENCY = 3.0f; // Frecuencia de ondas (ciclos por grid)
constexpr float WAVE_GRID_PHASE_SPEED = 2.0f; // Velocidad de animación de ondas (rad/s)
constexpr float WAVE_GRID_ROTATION_SPEED_Y = 0.4f; // Velocidad rotación eje Y (rad/s)
constexpr float WAVE_GRID_SIZE_FACTOR = 0.35f; // Tamaño del grid (proporción de altura)
constexpr float WAVE_GRID_AMPLITUDE = 0.15f; // Amplitud de las ondas (proporción de altura)
constexpr float WAVE_GRID_FREQUENCY = 3.0f; // Frecuencia de ondas (ciclos por grid)
constexpr float WAVE_GRID_PHASE_SPEED = 2.0f; // Velocidad de animación de ondas (rad/s)
constexpr float WAVE_GRID_ROTATION_SPEED_Y = 0.4f; // Velocidad rotación eje Y (rad/s)
// Configuración de Torus (toroide/donut 3D)
constexpr float TORUS_MAJOR_RADIUS_FACTOR = 0.25f; // Radio mayor R (centro torus a centro tubo)
constexpr float TORUS_MINOR_RADIUS_FACTOR = 0.12f; // Radio menor r (grosor del tubo)
constexpr float TORUS_ROTATION_SPEED_X = 0.6f; // Velocidad rotación eje X (rad/s)
constexpr float TORUS_ROTATION_SPEED_Y = 0.9f; // Velocidad rotación eje Y (rad/s)
constexpr float TORUS_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
constexpr float TORUS_MAJOR_RADIUS_FACTOR = 0.25f; // Radio mayor R (centro torus a centro tubo)
constexpr float TORUS_MINOR_RADIUS_FACTOR = 0.12f; // Radio menor r (grosor del tubo)
constexpr float TORUS_ROTATION_SPEED_X = 0.6f; // Velocidad rotación eje X (rad/s)
constexpr float TORUS_ROTATION_SPEED_Y = 0.9f; // Velocidad rotación eje Y (rad/s)
constexpr float TORUS_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
// Configuración de Cylinder (cilindro 3D)
constexpr float CYLINDER_RADIUS_FACTOR = 0.25f; // Radio del cilindro (proporción de altura)
@@ -145,10 +144,10 @@ constexpr float CYLINDER_HEIGHT_FACTOR = 0.5f; // Altura del cilindro (propo
constexpr float CYLINDER_ROTATION_SPEED_Y = 1.0f; // Velocidad rotación eje Y (rad/s)
// Configuración de Icosahedron (icosaedro D20)
constexpr float ICOSAHEDRON_RADIUS_FACTOR = 0.30f; // Radio de la esfera circunscrita
constexpr float ICOSAHEDRON_ROTATION_SPEED_X = 0.4f; // Velocidad rotación eje X (rad/s)
constexpr float ICOSAHEDRON_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
constexpr float ICOSAHEDRON_ROTATION_SPEED_Z = 0.2f; // Velocidad rotación eje Z (rad/s)
constexpr float ICOSAHEDRON_RADIUS_FACTOR = 0.30f; // Radio de la esfera circunscrita
constexpr float ICOSAHEDRON_ROTATION_SPEED_X = 0.4f; // Velocidad rotación eje X (rad/s)
constexpr float ICOSAHEDRON_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
constexpr float ICOSAHEDRON_ROTATION_SPEED_Z = 0.2f; // Velocidad rotación eje Z (rad/s)
// Configuración de Atom (núcleo con órbitas electrónicas)
constexpr float ATOM_NUCLEUS_RADIUS_FACTOR = 0.08f; // Radio del núcleo central
@@ -163,33 +162,33 @@ constexpr float PNG_EXTRUSION_DEPTH_FACTOR = 0.12f; // Profundidad de extrusió
constexpr int PNG_NUM_EXTRUSION_LAYERS = 15; // Capas de extrusión (más capas = más pegajosidad)
constexpr bool PNG_USE_EDGES_ONLY = false; // true = solo bordes, false = relleno completo
// Rotación "legible" (texto de frente con volteretas ocasionales)
constexpr float PNG_IDLE_TIME_MIN = 3.0f; // Tiempo mínimo de frente (segundos)
constexpr float PNG_IDLE_TIME_MAX = 8.0f; // Tiempo máximo de frente (segundos)
constexpr float PNG_FLIP_SPEED = 3.0f; // Velocidad voltereta (rad/s)
constexpr float PNG_FLIP_DURATION = 1.5f; // Duración voltereta (segundos)
constexpr float PNG_IDLE_TIME_MIN = 3.0f; // Tiempo mínimo de frente (segundos)
constexpr float PNG_IDLE_TIME_MAX = 8.0f; // Tiempo máximo de frente (segundos)
constexpr float PNG_FLIP_SPEED = 3.0f; // Velocidad voltereta (rad/s)
constexpr float PNG_FLIP_DURATION = 1.5f; // Duración voltereta (segundos)
// Control manual de escala de figuras 3D (Numpad +/-)
constexpr float SHAPE_SCALE_MIN = 0.3f; // Escala mínima (30%)
constexpr float SHAPE_SCALE_MAX = 3.0f; // Escala máxima (300%)
constexpr float SHAPE_SCALE_STEP = 0.1f; // Incremento por pulsación
constexpr float SHAPE_SCALE_DEFAULT = 1.0f; // Escala por defecto (100%)
constexpr float SHAPE_SCALE_MIN = 0.3f; // Escala mínima (30%)
constexpr float SHAPE_SCALE_MAX = 3.0f; // Escala máxima (300%)
constexpr float SHAPE_SCALE_STEP = 0.1f; // Incremento por pulsación
constexpr float SHAPE_SCALE_DEFAULT = 1.0f; // Escala por defecto (100%)
// Configuración de Modo DEMO (auto-play completo)
constexpr float DEMO_ACTION_INTERVAL_MIN = 2.0f; // Tiempo mínimo entre acciones (segundos)
constexpr float DEMO_ACTION_INTERVAL_MAX = 6.0f; // Tiempo máximo entre acciones (segundos)
constexpr float DEMO_ACTION_INTERVAL_MIN = 2.0f; // Tiempo mínimo entre acciones (segundos)
constexpr float DEMO_ACTION_INTERVAL_MAX = 6.0f; // Tiempo máximo entre acciones (segundos)
// Pesos de probabilidad DEMO MODE (valores relativos, se normalizan)
constexpr int DEMO_WEIGHT_GRAVITY_DIR = 12; // Cambiar dirección gravedad (12%)
constexpr int DEMO_WEIGHT_GRAVITY_TOGGLE = 15; // Toggle gravedad ON/OFF (15%) - ¡Ver caer pelotas!
constexpr int DEMO_WEIGHT_SHAPE = 22; // Activar figura 3D (22%) - Construir figuras
constexpr int DEMO_WEIGHT_TOGGLE_PHYSICS = 18; // Toggle física ↔ figura (18%) - ¡Destruir figuras!
constexpr int DEMO_WEIGHT_REGENERATE_SHAPE = 10; // Re-generar misma figura (10%) - Reconstruir
constexpr int DEMO_WEIGHT_THEME = 12; // Cambiar tema de colores (12%)
constexpr int DEMO_WEIGHT_SCENARIO = 2; // Cambiar número de pelotas (2%) - MUY OCASIONAL
constexpr int DEMO_WEIGHT_IMPULSE = 6; // Aplicar impulso (SPACE) (6%)
constexpr int DEMO_WEIGHT_DEPTH_ZOOM = 1; // Toggle profundidad (1%)
constexpr int DEMO_WEIGHT_SHAPE_SCALE = 1; // Cambiar escala figura (1%)
constexpr int DEMO_WEIGHT_SPRITE = 1; // Cambiar sprite (1%)
constexpr int DEMO_WEIGHT_GRAVITY_DIR = 12; // Cambiar dirección gravedad (12%)
constexpr int DEMO_WEIGHT_GRAVITY_TOGGLE = 15; // Toggle gravedad ON/OFF (15%) - ¡Ver caer pelotas!
constexpr int DEMO_WEIGHT_SHAPE = 22; // Activar figura 3D (22%) - Construir figuras
constexpr int DEMO_WEIGHT_TOGGLE_PHYSICS = 18; // Toggle física ↔ figura (18%) - ¡Destruir figuras!
constexpr int DEMO_WEIGHT_REGENERATE_SHAPE = 10; // Re-generar misma figura (10%) - Reconstruir
constexpr int DEMO_WEIGHT_THEME = 12; // Cambiar tema de colores (12%)
constexpr int DEMO_WEIGHT_SCENARIO = 2; // Cambiar número de pelotas (2%) - MUY OCASIONAL
constexpr int DEMO_WEIGHT_IMPULSE = 6; // Aplicar impulso (SPACE) (6%)
constexpr int DEMO_WEIGHT_DEPTH_ZOOM = 1; // Toggle profundidad (1%)
constexpr int DEMO_WEIGHT_SHAPE_SCALE = 1; // Cambiar escala figura (1%)
constexpr int DEMO_WEIGHT_SPRITE = 1; // Cambiar sprite (1%)
// TOTAL: 100
// Configuración de Modo DEMO LITE (solo física/figuras)
@@ -197,11 +196,11 @@ constexpr float DEMO_LITE_ACTION_INTERVAL_MIN = 1.5f; // Más rápido que demo
constexpr float DEMO_LITE_ACTION_INTERVAL_MAX = 4.0f;
// Pesos de probabilidad DEMO LITE (solo física/figuras, sin cambios de escenario/tema)
constexpr int DEMO_LITE_WEIGHT_GRAVITY_DIR = 25; // Cambiar dirección gravedad (25%)
constexpr int DEMO_LITE_WEIGHT_GRAVITY_TOGGLE = 20;// Toggle gravedad ON/OFF (20%)
constexpr int DEMO_LITE_WEIGHT_SHAPE = 25; // Activar figura 3D (25%)
constexpr int DEMO_LITE_WEIGHT_TOGGLE_PHYSICS = 20;// Toggle física ↔ figura (20%)
constexpr int DEMO_LITE_WEIGHT_IMPULSE = 10; // Aplicar impulso (10%)
constexpr int DEMO_LITE_WEIGHT_GRAVITY_DIR = 25; // Cambiar dirección gravedad (25%)
constexpr int DEMO_LITE_WEIGHT_GRAVITY_TOGGLE = 20; // Toggle gravedad ON/OFF (20%)
constexpr int DEMO_LITE_WEIGHT_SHAPE = 25; // Activar figura 3D (25%)
constexpr int DEMO_LITE_WEIGHT_TOGGLE_PHYSICS = 20; // Toggle física ↔ figura (20%)
constexpr int DEMO_LITE_WEIGHT_IMPULSE = 10; // Aplicar impulso (10%)
// TOTAL: 100
constexpr float PI = 3.14159265358979323846f; // Constante PI
constexpr float PI = 3.14159265358979323846f; // Constante PI

View File

@@ -98,12 +98,27 @@ void PNGShape::generatePoints(int num_points, float screen_width, float screen_h
extrusion_depth_ = screen_height * PNG_EXTRUSION_DEPTH_FACTOR;
num_layers_ = PNG_NUM_EXTRUSION_LAYERS;
// Generar puntos según el enfoque
// Generar puntos según el enfoque configurado
generateExtrudedPoints();
// Debug: mostrar cantidad de puntos 2D detectados
// Calcular cuántos puntos 3D se necesitan
size_t num_2d_points = PNG_USE_EDGES_ONLY ? edge_points_.size() : filled_points_.size();
size_t total_3d_points = num_2d_points * num_layers_;
// ADAPTACIÓN AUTOMÁTICA: Si hay muy pocas pelotas, cambiar a bordes
if (!PNG_USE_EDGES_ONLY && num_points < static_cast<int>(total_3d_points) / 2) {
std::cout << "[PNG_SHAPE] Advertencia: Solo " << num_points << " pelotas para "
<< total_3d_points << " puntos (relleno).\n";
std::cout << "[PNG_SHAPE] Cambiando automáticamente a BORDES para mejor visualización.\n";
// Regenerar solo con bordes
detectEdges();
num_2d_points = edge_points_.size();
total_3d_points = num_2d_points * num_layers_;
}
// Debug: mostrar configuración final
std::cout << "[PNG_SHAPE] Modo: " << (PNG_USE_EDGES_ONLY ? "BORDES" : "RELLENO") << "\n";
std::cout << "[PNG_SHAPE] Detectados " << num_2d_points << " puntos 2D × "
<< num_layers_ << " capas = " << total_3d_points << " puntos 3D totales\n";
std::cout << "[PNG_SHAPE] Pelotas disponibles: " << num_points << "\n";
@@ -159,8 +174,8 @@ void PNGShape::update(float delta_time, float screen_width, float screen_height)
}
void PNGShape::getPoint3D(int index, float& x, float& y, float& z) const {
// Seleccionar puntos según configuración
const std::vector<Point2D>& points = PNG_USE_EDGES_ONLY ? edge_points_ : filled_points_;
// Seleccionar puntos según configuración (priorizar edges si fue auto-ajustado)
const std::vector<Point2D>& points = (!edge_points_.empty()) ? edge_points_ : filled_points_;
if (points.empty()) {
x = y = z = 0.0f;