fix: Preservar SimulationMode y mejorar Debug HUD

CAMBIOS:
- Debug HUD reorganizado en layout de 2 columnas (LEFT/RIGHT, sin centro)
- Añadidos getters públicos en Engine para info de sistema
- changeScenario() ahora preserva el SimulationMode actual
- Inicialización de pelotas según modo (PHYSICS/SHAPE/BOIDS)
- Eliminada duplicación de logo_entered_manually_ (ahora en StateManager)

ARCHIVOS MODIFICADOS:
- engine.h: Añadidos 8 getters públicos para UIManager
- engine.cpp: changeScenario() pasa current_mode_ a SceneManager
- scene_manager.h: changeScenario() acepta parámetro SimulationMode
- scene_manager.cpp: Inicialización según modo (RULES.md líneas 23-26)
- ui_manager.h: render() acepta Engine* y renderDebugHUD() actualizado
- ui_manager.cpp: Debug HUD con columnas LEFT (sistema) y RIGHT (física)

REGLAS.md IMPLEMENTADO:
 Líneas 23-26: Inicialización diferenciada por modo
  - PHYSICS: Top, 75% distribución central en X, velocidades aleatorias
  - SHAPE: Centro de pantalla, sin velocidad inicial
  - BOIDS: Posiciones y velocidades aleatorias
 Líneas 88-96: Debug HUD con información de sistema completa

BUGS CORREGIDOS:
- Fix: Cambiar escenario (1-8) en FIGURE ya no resetea a PHYSICS 
- Fix: Las pelotas se inicializan correctamente según el modo activo
- Fix: AppMode movido de centro a izquierda en Debug HUD

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-16 09:52:33 +02:00
parent 3d26bfc6fa
commit 8c2a8857fc
6 changed files with 239 additions and 101 deletions

View File

@@ -340,10 +340,14 @@ void Engine::update() {
// Gravedad y física
void Engine::handleGravityToggle() {
// Si estamos en modo boids, salir a modo física primero
// Si estamos en modo boids, salir a modo física CON GRAVEDAD OFF
// Según RULES.md: "BOIDS a PHYSICS: Pulsando la tecla G: Gravedad OFF"
if (current_mode_ == SimulationMode::BOIDS) {
toggleBoidsMode(); // Esto cambia a PHYSICS y activa gravedad
return; // La notificación ya se muestra en toggleBoidsMode
toggleBoidsMode(); // Cambiar a PHYSICS (preserva inercia, gravedad ya está OFF desde activateBoids)
// NO llamar a forceBallsGravityOff() porque aplica impulsos que destruyen la inercia de BOIDS
// La gravedad ya está desactivada por BoidManager::activateBoids() y se mantiene al salir
showNotificationForAction("Modo Física - Gravedad Off");
return;
}
// Si estamos en modo figura, salir a modo física SIN GRAVEDAD
@@ -504,13 +508,14 @@ void Engine::switchTexture() {
// Escenarios (número de pelotas)
void Engine::changeScenario(int scenario_id, const char* notification_text) {
// Resetear modo SHAPE si está activo
// Pasar el modo actual al SceneManager para inicialización correcta
scene_manager_->changeScenario(scenario_id, current_mode_);
// Si estamos en modo SHAPE, regenerar la figura con nuevo número de pelotas
if (current_mode_ == SimulationMode::SHAPE) {
current_mode_ = SimulationMode::PHYSICS;
active_shape_.reset();
generateShape();
}
scene_manager_->changeScenario(scenario_id);
showNotificationForAction(notification_text);
}
@@ -695,7 +700,7 @@ void Engine::render() {
*/
// Renderizar UI (debug HUD, texto obsoleto, notificaciones) - delegado a UIManager
ui_manager_->render(renderer_, scene_manager_.get(), current_mode_, state_manager_->getCurrentMode(),
ui_manager_->render(renderer_, this, scene_manager_.get(), current_mode_, state_manager_->getCurrentMode(),
active_shape_.get(), shape_convergence_,
physical_window_width_, physical_window_height_, current_screen_width_);
@@ -769,7 +774,7 @@ void Engine::toggleRealFullscreen() {
// Reinicar la escena con nueva resolución
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
scene_manager_->changeScenario(scene_manager_->getCurrentScenario());
scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_);
// Actualizar tamaño de pantalla para boids (wrapping boundaries)
boid_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
@@ -794,7 +799,7 @@ void Engine::toggleRealFullscreen() {
// Reinicar la escena con resolución original
scene_manager_->updateScreenSize(current_screen_width_, current_screen_height_);
scene_manager_->changeScenario(scene_manager_->getCurrentScenario());
scene_manager_->changeScenario(scene_manager_->getCurrentScenario(), current_mode_);
}
}
@@ -1154,7 +1159,7 @@ void Engine::performLogoAction(bool logo_waiting_for_flip) {
// Solo salir automáticamente si la entrada a LOGO fue automática (desde DEMO)
// No salir si el usuario entró manualmente con tecla K
// Probabilidad de salir: 60% en cada acción → sale rápido (relación DEMO:LOGO = 6:1)
if (!logo_entered_manually_ && rand() % 100 < 60) {
if (!state_manager_->getLogoEnteredManually() && rand() % 100 < 60) {
state_manager_->exitLogoMode(true); // Volver a DEMO/DEMO_LITE
}
}
@@ -1313,7 +1318,7 @@ void Engine::executeDemoAction(bool is_lite) {
// Escenarios válidos: índices 1, 2, 3, 4, 5 (10, 100, 500, 1000, 10000 pelotas)
int valid_scenarios[] = {1, 2, 3, 4, 5};
int new_scenario = valid_scenarios[rand() % 5];
scene_manager_->changeScenario(new_scenario);
scene_manager_->changeScenario(new_scenario, current_mode_);
return;
}
@@ -1398,7 +1403,7 @@ void Engine::executeRandomizeOnDemoStart(bool is_lite) {
// 1. Escenario (excluir índices 0, 6, 7)
int valid_scenarios[] = {1, 2, 3, 4, 5};
int new_scenario = valid_scenarios[rand() % 5];
scene_manager_->changeScenario(new_scenario);
scene_manager_->changeScenario(new_scenario, current_mode_);
// 2. Tema (elegir entre TODOS los 15 temas)
int random_theme_index = rand() % 15;
@@ -1463,7 +1468,7 @@ void Engine::executeEnterLogoMode(size_t ball_count) {
// Verificar mínimo de pelotas
if (static_cast<int>(ball_count) < LOGO_MODE_MIN_BALLS) {
// Ajustar a 5000 pelotas automáticamente
scene_manager_->changeScenario(5); // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS)
scene_manager_->changeScenario(5, current_mode_); // Escenario 5000 pelotas (índice 5 en BALL_COUNT_SCENARIOS)
}
// Guardar estado previo (para restaurar al salir)

View File

@@ -87,6 +87,16 @@ class Engine {
void executeEnterLogoMode(size_t ball_count);
void executeExitLogoMode();
// === Getters públicos para UIManager (Debug HUD) ===
bool getVSyncEnabled() const { return vsync_enabled_; }
bool getFullscreenEnabled() const { return fullscreen_enabled_; }
bool getRealFullscreenEnabled() const { return real_fullscreen_enabled_; }
ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; }
int getCurrentScreenWidth() const { return current_screen_width_; }
int getCurrentScreenHeight() const { return current_screen_height_; }
int getBaseScreenWidth() const { return base_screen_width_; }
int getBaseScreenHeight() const { return base_screen_height_; }
private:
// === Componentes del sistema (Composición) ===
std::unique_ptr<InputHandler> input_handler_; // Manejo de entradas SDL
@@ -168,9 +178,9 @@ class Engine {
float logo_target_flip_percentage_ = 0.0f; // % de flip a esperar (0.2-0.8)
int logo_current_flip_count_ = 0; // Flips observados hasta ahora
// Control de entrada manual vs automática a LOGO MODE
// Determina si LOGO debe salir automáticamente o esperar input del usuario
bool logo_entered_manually_ = false; // true si se activó con tecla K, false si automático desde DEMO
// NOTA: logo_entered_manually_ fue eliminado de Engine (duplicado)
// Ahora se obtiene de StateManager con state_manager_->getLogoEnteredManually()
// Esto evita desincronización entre Engine y StateManager
// Estado previo antes de entrar a Logo Mode (para restaurar al salir)
// Guardado por executeEnterLogoMode(), restaurado por executeExitLogoMode()

View File

@@ -22,8 +22,8 @@ void SceneManager::initialize(int scenario, std::shared_ptr<Texture> texture, Th
theme_manager_ = theme_manager;
current_ball_size_ = texture_->getWidth();
// Crear bolas iniciales
changeScenario(scenario_);
// Crear bolas iniciales (siempre en modo PHYSICS al inicio)
changeScenario(scenario_, SimulationMode::PHYSICS);
}
void SceneManager::update(float delta_time) {
@@ -33,7 +33,7 @@ void SceneManager::update(float delta_time) {
}
}
void SceneManager::changeScenario(int scenario_id) {
void SceneManager::changeScenario(int scenario_id, SimulationMode mode) {
// Guardar escenario
scenario_ = scenario_id;
@@ -45,14 +45,49 @@ void SceneManager::changeScenario(int scenario_id) {
// Crear las bolas según el escenario
for (int i = 0; i < BALL_COUNT_SCENARIOS[scenario_id]; ++i) {
const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -)
float X, VX, VY;
// Calcular spawn zone: margen a cada lado, zona central para spawn
const int margin = static_cast<int>(screen_width_ * BALL_SPAWN_MARGIN);
const int spawn_zone_width = screen_width_ - (2 * margin);
const float X = (rand() % spawn_zone_width) + margin; // Posición inicial en X
const float VX = (((rand() % 20) + 10) * 0.1f) * SIGN; // Velocidad en X
const float VY = ((rand() % 60) - 30) * 0.1f; // Velocidad en Y
// Inicialización según SimulationMode (RULES.md líneas 23-26)
switch (mode) {
case SimulationMode::PHYSICS: {
// PHYSICS: Parte superior, 75% distribución central en X
const int SIGN = ((rand() % 2) * 2) - 1;
const int margin = static_cast<int>(screen_width_ * BALL_SPAWN_MARGIN);
const int spawn_zone_width = screen_width_ - (2 * margin);
X = (rand() % spawn_zone_width) + margin;
VX = (((rand() % 20) + 10) * 0.1f) * SIGN;
VY = ((rand() % 60) - 30) * 0.1f;
break;
}
case SimulationMode::SHAPE: {
// SHAPE: Centro de pantalla, sin velocidad inicial
X = screen_width_ / 2.0f;
VX = 0.0f;
VY = 0.0f;
break;
}
case SimulationMode::BOIDS: {
// BOIDS: Posiciones aleatorias, velocidades aleatorias
const int SIGN_X = ((rand() % 2) * 2) - 1;
const int SIGN_Y = ((rand() % 2) * 2) - 1;
X = static_cast<float>(rand() % screen_width_);
VX = (((rand() % 40) + 10) * 0.1f) * SIGN_X; // 1.0 - 5.0 px/frame
VY = (((rand() % 40) + 10) * 0.1f) * SIGN_Y;
break;
}
default:
// Fallback a PHYSICS por seguridad
const int SIGN = ((rand() % 2) * 2) - 1;
const int margin = static_cast<int>(screen_width_ * BALL_SPAWN_MARGIN);
const int spawn_zone_width = screen_width_ - (2 * margin);
X = (rand() % spawn_zone_width) + margin;
VX = (((rand() % 20) + 10) * 0.1f) * SIGN;
VY = ((rand() % 60) - 30) * 0.1f;
break;
}
// Seleccionar color de la paleta del tema actual (delegado a ThemeManager)
int random_index = rand();

View File

@@ -51,8 +51,9 @@ class SceneManager {
/**
* @brief Cambia el número de bolas según escenario
* @param scenario_id Índice del escenario (0-7 para 10 a 50,000 bolas)
* @param mode Modo de simulación actual (afecta inicialización)
*/
void changeScenario(int scenario_id);
void changeScenario(int scenario_id, SimulationMode mode);
/**
* @brief Actualiza textura y tamaño de todas las bolas

View File

@@ -5,6 +5,7 @@
#include "../ball.h" // for Ball
#include "../defines.h" // for TEXT_DURATION, NOTIFICATION_DURATION, AppMode, SimulationMode
#include "../engine.h" // for Engine (info de sistema)
#include "../scene/scene_manager.h" // for SceneManager
#include "../shapes/shape.h" // for Shape
#include "../text/textrenderer.h" // for TextRenderer
@@ -96,6 +97,7 @@ void UIManager::update(Uint64 current_time, float delta_time) {
}
void UIManager::render(SDL_Renderer* renderer,
const Engine* engine,
const SceneManager* scene_manager,
SimulationMode current_mode,
AppMode current_app_mode,
@@ -115,7 +117,7 @@ void UIManager::render(SDL_Renderer* renderer,
// Renderizar debug HUD si está activo
if (show_debug_) {
renderDebugHUD(scene_manager, current_mode, current_app_mode,
renderDebugHUD(engine, scene_manager, current_mode, current_app_mode,
active_shape, shape_convergence);
}
@@ -167,7 +169,8 @@ void UIManager::setTextObsolete(const std::string& text, int pos, int current_sc
// === Métodos privados ===
void UIManager::renderDebugHUD(const SceneManager* scene_manager,
void UIManager::renderDebugHUD(const Engine* engine,
const SceneManager* scene_manager,
SimulationMode current_mode,
AppMode current_app_mode,
const Shape* active_shape,
@@ -175,92 +178,171 @@ void UIManager::renderDebugHUD(const SceneManager* scene_manager,
// Obtener altura de línea para espaciado dinámico
int line_height = text_renderer_debug_->getTextHeight();
int margin = 8; // Margen constante en píxeles físicos
int current_y = margin; // Y inicial en píxeles físicos
// Mostrar contador de FPS en esquina superior derecha
// ===========================
// COLUMNA LEFT (Sistema)
// ===========================
int left_y = margin;
// AppMode (antes estaba centrado, ahora va a la izquierda)
std::string appmode_text;
SDL_Color appmode_color = {255, 255, 255, 255}; // Blanco por defecto
if (current_app_mode == AppMode::LOGO) {
appmode_text = "AppMode: LOGO";
appmode_color = {255, 128, 0, 255}; // Naranja
} else if (current_app_mode == AppMode::DEMO) {
appmode_text = "AppMode: DEMO";
appmode_color = {255, 165, 0, 255}; // Naranja
} else if (current_app_mode == AppMode::DEMO_LITE) {
appmode_text = "AppMode: DEMO LITE";
appmode_color = {255, 200, 0, 255}; // Amarillo-naranja
} else {
appmode_text = "AppMode: SANDBOX";
appmode_color = {0, 255, 128, 255}; // Verde claro
}
text_renderer_debug_->printAbsolute(margin, left_y, appmode_text.c_str(), appmode_color);
left_y += line_height;
// SimulationMode
std::string simmode_text;
if (current_mode == SimulationMode::PHYSICS) {
simmode_text = "SimMode: PHYSICS";
} else if (current_mode == SimulationMode::SHAPE) {
if (active_shape) {
simmode_text = std::string("SimMode: SHAPE (") + active_shape->getName() + ")";
} else {
simmode_text = "SimMode: SHAPE";
}
} else if (current_mode == SimulationMode::BOIDS) {
simmode_text = "SimMode: BOIDS";
}
text_renderer_debug_->printAbsolute(margin, left_y, simmode_text.c_str(), {0, 255, 255, 255}); // Cian
left_y += line_height;
// V-Sync
text_renderer_debug_->printAbsolute(margin, left_y, vsync_text_.c_str(), {0, 255, 255, 255}); // Cian
left_y += line_height;
// Modo de escalado (INTEGER/LETTERBOX/STRETCH o WINDOWED si no está en fullscreen)
std::string scaling_text;
if (engine->getFullscreenEnabled() || engine->getRealFullscreenEnabled()) {
ScalingMode scaling = engine->getCurrentScalingMode();
if (scaling == ScalingMode::INTEGER) {
scaling_text = "Scaling: INTEGER";
} else if (scaling == ScalingMode::LETTERBOX) {
scaling_text = "Scaling: LETTERBOX";
} else if (scaling == ScalingMode::STRETCH) {
scaling_text = "Scaling: STRETCH";
}
} else {
scaling_text = "Scaling: WINDOWED";
}
text_renderer_debug_->printAbsolute(margin, left_y, scaling_text.c_str(), {255, 255, 0, 255}); // Amarillo
left_y += line_height;
// Resolución física (píxeles reales de la ventana)
std::string phys_res_text = "Physical: " + std::to_string(physical_window_width_) + "x" + std::to_string(physical_window_height_);
text_renderer_debug_->printAbsolute(margin, left_y, phys_res_text.c_str(), {255, 128, 255, 255}); // Magenta claro
left_y += line_height;
// Resolución lógica (resolución interna del renderizador)
std::string logic_res_text = "Logical: " + std::to_string(engine->getCurrentScreenWidth()) + "x" + std::to_string(engine->getCurrentScreenHeight());
text_renderer_debug_->printAbsolute(margin, left_y, logic_res_text.c_str(), {255, 128, 255, 255}); // Magenta claro
left_y += line_height;
// Display refresh rate (obtener de SDL)
std::string refresh_text;
int num_displays = 0;
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
if (displays && num_displays > 0) {
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
if (dm) {
refresh_text = "Refresh: " + std::to_string(static_cast<int>(dm->refresh_rate)) + " Hz";
} else {
refresh_text = "Refresh: N/A";
}
SDL_free(displays);
} else {
refresh_text = "Refresh: N/A";
}
text_renderer_debug_->printAbsolute(margin, left_y, refresh_text.c_str(), {255, 255, 128, 255}); // Amarillo claro
left_y += line_height;
// Tema actual (delegado a ThemeManager)
std::string theme_text = std::string("Theme: ") + theme_manager_->getCurrentThemeNameEN();
text_renderer_debug_->printAbsolute(margin, left_y, theme_text.c_str(), {128, 255, 255, 255}); // Cian claro
left_y += line_height;
// ===========================
// COLUMNA RIGHT (Primera pelota)
// ===========================
int right_y = margin;
// FPS counter (esquina superior derecha)
int fps_text_width = text_renderer_debug_->getTextWidthPhysical(fps_text_.c_str());
int fps_x = physical_window_width_ - fps_text_width - margin;
text_renderer_debug_->printAbsolute(fps_x, current_y, fps_text_.c_str(), {255, 255, 0, 255}); // Amarillo
text_renderer_debug_->printAbsolute(fps_x, right_y, fps_text_.c_str(), {255, 255, 0, 255}); // Amarillo
right_y += line_height;
// Mostrar estado V-Sync en esquina superior izquierda
text_renderer_debug_->printAbsolute(margin, current_y, vsync_text_.c_str(), {0, 255, 255, 255}); // Cian
current_y += line_height;
// Debug: Mostrar valores de la primera pelota (si existe)
// Info de la primera pelota (si existe)
const Ball* first_ball = scene_manager->getFirstBall();
if (first_ball != nullptr) {
// Línea 1: Gravedad
int grav_int = static_cast<int>(first_ball->getGravityForce());
std::string grav_text = "Gravedad: " + std::to_string(grav_int);
text_renderer_debug_->printAbsolute(margin, current_y, grav_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height;
// Posición X, Y
SDL_FRect pos = first_ball->getPosition();
std::string pos_text = "Pos: (" + std::to_string(static_cast<int>(pos.x)) + ", " + std::to_string(static_cast<int>(pos.y)) + ")";
int pos_width = text_renderer_debug_->getTextWidthPhysical(pos_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - pos_width - margin, right_y, pos_text.c_str(), {255, 128, 128, 255}); // Rojo claro
right_y += line_height;
// Línea 2: Velocidad Y
// Velocidad X
int vx_int = static_cast<int>(first_ball->getVelocityX());
std::string vx_text = "VelX: " + std::to_string(vx_int);
int vx_width = text_renderer_debug_->getTextWidthPhysical(vx_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - vx_width - margin, right_y, vx_text.c_str(), {128, 255, 128, 255}); // Verde claro
right_y += line_height;
// Velocidad Y
int vy_int = static_cast<int>(first_ball->getVelocityY());
std::string vy_text = "Velocidad Y: " + std::to_string(vy_int);
text_renderer_debug_->printAbsolute(margin, current_y, vy_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height;
std::string vy_text = "VelY: " + std::to_string(vy_int);
int vy_width = text_renderer_debug_->getTextWidthPhysical(vy_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - vy_width - margin, right_y, vy_text.c_str(), {128, 255, 128, 255}); // Verde claro
right_y += line_height;
// Línea 3: Estado superficie
std::string surface_text = first_ball->isOnSurface() ? "Superficie: Sí" : "Superficie: No";
text_renderer_debug_->printAbsolute(margin, current_y, surface_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height;
// Fuerza de gravedad
int grav_int = static_cast<int>(first_ball->getGravityForce());
std::string grav_text = "Gravity: " + std::to_string(grav_int);
int grav_width = text_renderer_debug_->getTextWidthPhysical(grav_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - grav_width - margin, right_y, grav_text.c_str(), {255, 255, 128, 255}); // Amarillo claro
right_y += line_height;
// Línea 4: Coeficiente de rebote (loss)
// Estado superficie
std::string surface_text = first_ball->isOnSurface() ? "Surface: YES" : "Surface: NO";
int surface_width = text_renderer_debug_->getTextWidthPhysical(surface_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - surface_width - margin, right_y, surface_text.c_str(), {255, 200, 128, 255}); // Naranja claro
right_y += line_height;
// Coeficiente de rebote (loss)
float loss_val = first_ball->getLossCoefficient();
std::string loss_text = "Rebote: " + std::to_string(loss_val).substr(0, 4);
text_renderer_debug_->printAbsolute(margin, current_y, loss_text.c_str(), {255, 0, 255, 255}); // Magenta
current_y += line_height;
std::string loss_text = "Loss: " + std::to_string(loss_val).substr(0, 4);
int loss_width = text_renderer_debug_->getTextWidthPhysical(loss_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - loss_width - margin, right_y, loss_text.c_str(), {255, 128, 255, 255}); // Magenta
right_y += line_height;
// Línea 5: Dirección de gravedad
std::string gravity_dir_text = "Dirección: " + gravityDirectionToString(static_cast<int>(scene_manager->getCurrentGravity()));
text_renderer_debug_->printAbsolute(margin, current_y, gravity_dir_text.c_str(), {255, 255, 0, 255}); // Amarillo
current_y += line_height;
// Dirección de gravedad
std::string gravity_dir_text = "Dir: " + gravityDirectionToString(static_cast<int>(scene_manager->getCurrentGravity()));
int dir_width = text_renderer_debug_->getTextWidthPhysical(gravity_dir_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - dir_width - margin, right_y, gravity_dir_text.c_str(), {128, 255, 255, 255}); // Cian claro
right_y += line_height;
}
// Debug: Mostrar tema actual (delegado a ThemeManager)
std::string theme_text = std::string("Tema: ") + theme_manager_->getCurrentThemeNameEN();
text_renderer_debug_->printAbsolute(margin, current_y, theme_text.c_str(), {255, 255, 128, 255}); // Amarillo claro
current_y += line_height;
// Debug: Mostrar modo de simulación actual
std::string mode_text;
if (current_mode == SimulationMode::PHYSICS) {
mode_text = "Modo: Física";
} else if (active_shape) {
mode_text = std::string("Modo: ") + active_shape->getName();
} else {
mode_text = "Modo: Forma";
}
text_renderer_debug_->printAbsolute(margin, current_y, mode_text.c_str(), {0, 255, 128, 255}); // Verde claro
current_y += line_height;
// Debug: Mostrar convergencia en modo LOGO (solo cuando está activo)
// Convergencia en modo LOGO (solo cuando está activo) - Parte inferior derecha
if (current_app_mode == AppMode::LOGO && current_mode == SimulationMode::SHAPE) {
int convergence_percent = static_cast<int>(shape_convergence * 100.0f);
std::string convergence_text = "Convergencia: " + std::to_string(convergence_percent) + "%";
text_renderer_debug_->printAbsolute(margin, current_y, convergence_text.c_str(), {255, 128, 0, 255}); // Naranja
current_y += line_height;
}
// Debug: Mostrar modo DEMO/LOGO activo (siempre visible cuando debug está ON)
// FIJO en tercera fila (no se mueve con otros elementos del HUD)
int fixed_y = margin + (line_height * 2); // Tercera fila fija
if (current_app_mode == AppMode::LOGO) {
const char* logo_text = "Modo Logo";
int logo_text_width = text_renderer_debug_->getTextWidthPhysical(logo_text);
int logo_x = (physical_window_width_ - logo_text_width) / 2;
text_renderer_debug_->printAbsolute(logo_x, fixed_y, logo_text, {255, 128, 0, 255}); // Naranja
} else if (current_app_mode == AppMode::DEMO) {
const char* demo_text = "Modo Demo";
int demo_text_width = text_renderer_debug_->getTextWidthPhysical(demo_text);
int demo_x = (physical_window_width_ - demo_text_width) / 2;
text_renderer_debug_->printAbsolute(demo_x, fixed_y, demo_text, {255, 165, 0, 255}); // Naranja
} else if (current_app_mode == AppMode::DEMO_LITE) {
const char* lite_text = "Modo Demo Lite";
int lite_text_width = text_renderer_debug_->getTextWidthPhysical(lite_text);
int lite_x = (physical_window_width_ - lite_text_width) / 2;
text_renderer_debug_->printAbsolute(lite_x, fixed_y, lite_text, {255, 200, 0, 255}); // Amarillo-naranja
std::string convergence_text = "Convergence: " + std::to_string(convergence_percent) + "%";
int conv_width = text_renderer_debug_->getTextWidthPhysical(convergence_text.c_str());
text_renderer_debug_->printAbsolute(physical_window_width_ - conv_width - margin, right_y, convergence_text.c_str(), {255, 128, 0, 255}); // Naranja
right_y += line_height;
}
}

View File

@@ -11,6 +11,7 @@ class ThemeManager;
class TextRenderer;
class Notifier;
class HelpOverlay;
class Engine;
enum class SimulationMode;
enum class AppMode;
@@ -59,6 +60,7 @@ class UIManager {
/**
* @brief Renderiza todos los elementos UI
* @param renderer Renderizador SDL3
* @param engine Puntero a Engine (para info de sistema)
* @param scene_manager SceneManager (para info de debug)
* @param current_mode Modo de simulación actual (PHYSICS/SHAPE)
* @param current_app_mode Modo de aplicación (SANDBOX/DEMO/LOGO)
@@ -69,6 +71,7 @@ class UIManager {
* @param current_screen_width Ancho lógico de pantalla (para texto centrado)
*/
void render(SDL_Renderer* renderer,
const Engine* engine,
const SceneManager* scene_manager,
SimulationMode current_mode,
AppMode current_app_mode,
@@ -136,13 +139,15 @@ class UIManager {
private:
/**
* @brief Renderiza HUD de debug (solo si show_debug_ == true)
* @param engine Puntero a Engine (para info de sistema)
* @param scene_manager SceneManager (para info de pelotas)
* @param current_mode Modo de simulación (PHYSICS/SHAPE)
* @param current_app_mode Modo de aplicación (SANDBOX/DEMO/LOGO)
* @param active_shape Figura 3D activa (puede ser nullptr)
* @param shape_convergence % de convergencia en LOGO mode
*/
void renderDebugHUD(const SceneManager* scene_manager,
void renderDebugHUD(const Engine* engine,
const SceneManager* scene_manager,
SimulationMode current_mode,
AppMode current_app_mode,
const Shape* active_shape,