From 723bb6d1987735becbe2a4357edf3a584d501c3a Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sat, 4 Oct 2025 14:02:02 +0200 Subject: [PATCH] =?UTF-8?q?A=C3=B1adir=20par=C3=A1metro=20-z/--zoom=20con?= =?UTF-8?q?=20validaci=C3=B3n=20inteligente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defaults correctos (sin CLI): - Resolución: 320x240 - Zoom: 3 - Ventana resultante: 960x720 Nuevas funcionalidades: - Parámetro -z/--zoom para especificar zoom de ventana - Si se pasan -w/-h sin -z: zoom automático = 1 - Validación de resolución vs pantalla - Validación de zoom vs max_zoom calculado Lógica de validación: 1. Si resolución > pantalla → reset a 320x240 zoom 3 2. Calcular max_zoom = min(screen_w/width, screen_h/height) 3. Si zoom > max_zoom → ajustar a max_zoom 4. Si CLI con -w/-h pero sin -z → zoom = 1 (auto) Ejemplos: ./vibe3_physics # 320x240 zoom 3 ✅ ./vibe3_physics -w 1920 -h 1080 # 1920x1080 zoom 1 ✅ ./vibe3_physics -w 640 -h 480 -z 2 # 640x480 zoom 2 (1280x960) ✅ ./vibe3_physics -w 9999 -h 9999 # Reset a default (warning) ✅ Archivos: - defines.h: Renombrar WINDOW_ZOOM → DEFAULT_WINDOW_ZOOM - main.cpp: Añadir parsing -z/--zoom - engine.h: initialize() acepta zoom - engine.cpp: Validación + advertencias informativas 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- source/defines.h | 7 +- source/engine.cpp | 379 +++++++++++++++++++++++++++------------------- source/engine.h | 272 ++++++++++++++++----------------- source/main.cpp | 27 +++- 4 files changed, 381 insertions(+), 304 deletions(-) diff --git a/source/defines.h b/source/defines.h index 823a33c..7e7000c 100644 --- a/source/defines.h +++ b/source/defines.h @@ -5,9 +5,10 @@ // Configuración de ventana y pantalla constexpr char WINDOW_CAPTION[] = "vibe3_physics"; -constexpr int SCREEN_WIDTH = 320; // Ancho de la pantalla lógica (píxeles) -constexpr int SCREEN_HEIGHT = 240; // Alto de la pantalla lógica (píxeles) -constexpr int WINDOW_ZOOM = 3; // Zoom inicial de la ventana +// 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() // Configuración de zoom dinámico de ventana diff --git a/source/engine.cpp b/source/engine.cpp index 8414302..07fb794 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -8,31 +8,31 @@ #include // for SDL_GetTicks #include // for SDL_CreateWindow, SDL_DestroyWindow, SDL_GetDisplayBounds -#include // for std::min, std::max, std::sort -#include // for sqrtf, acosf, cosf, sinf (funciones matemáticas) -#include // for rand, srand -#include // for strlen -#include // for time -#include // for cout -#include // for string -#include // for path operations +#include // for std::min, std::max, std::sort +#include // for sqrtf, acosf, cosf, sinf (funciones matemáticas) +#include // for rand, srand +#include // for strlen +#include // for time +#include // for path operations +#include // for cout +#include // for string #ifdef _WIN32 #include // for GetModuleFileName #endif -#include "ball.h" // for Ball -#include "external/dbgtxt.h" // for dbg_init, dbg_print -#include "external/texture.h" // for Texture -#include "shapes/sphere_shape.h" // for SphereShape -#include "shapes/cube_shape.h" // for CubeShape -#include "shapes/helix_shape.h" // for HelixShape -#include "shapes/wave_grid_shape.h" // for WaveGridShape -#include "shapes/torus_shape.h" // for TorusShape -#include "shapes/cylinder_shape.h" // for CylinderShape -#include "shapes/icosahedron_shape.h" // for IcosahedronShape -#include "shapes/atom_shape.h" // for AtomShape -#include "shapes/png_shape.h" // for PNGShape +#include "ball.h" // for Ball +#include "external/dbgtxt.h" // for dbg_init, dbg_print +#include "external/texture.h" // for Texture +#include "shapes/atom_shape.h" // for AtomShape +#include "shapes/cube_shape.h" // for CubeShape +#include "shapes/cylinder_shape.h" // for CylinderShape +#include "shapes/helix_shape.h" // for HelixShape +#include "shapes/icosahedron_shape.h" // for IcosahedronShape +#include "shapes/png_shape.h" // for PNGShape +#include "shapes/sphere_shape.h" // for SphereShape +#include "shapes/torus_shape.h" // for TorusShape +#include "shapes/wave_grid_shape.h" // for WaveGridShape // Función auxiliar para obtener la ruta del directorio del ejecutable std::string getExecutableDirectory() { @@ -48,25 +48,64 @@ std::string getExecutableDirectory() { } // Implementación de métodos públicos -bool Engine::initialize(int width, int height, bool fullscreen) { +bool Engine::initialize(int width, int height, int zoom, bool fullscreen) { bool success = true; - // Usar parámetros o valores por defecto - int window_width = (width > 0) ? width : SCREEN_WIDTH * WINDOW_ZOOM; - int window_height = (height > 0) ? height : SCREEN_HEIGHT * WINDOW_ZOOM; - int logical_width = (width > 0) ? width : SCREEN_WIDTH; - int logical_height = (height > 0) ? height : SCREEN_HEIGHT; + // Obtener resolución de pantalla para validación + if (!SDL_Init(SDL_INIT_VIDEO)) { + std::cout << "¡SDL no se pudo inicializar! Error de SDL: " << SDL_GetError() << std::endl; + return false; + } - // Guardar resolución base (configurada por CLI) + int num_displays = 0; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + const auto *dm = (displays && num_displays > 0) ? SDL_GetCurrentDisplayMode(displays[0]) : nullptr; + + int screen_w = dm ? dm->w : 1920; // Fallback si falla + int screen_h = dm ? dm->h - WINDOW_DECORATION_HEIGHT : 1080; + + if (displays) SDL_free(displays); + + // Usar parámetros o valores por defecto + int logical_width = (width > 0) ? width : DEFAULT_SCREEN_WIDTH; + int logical_height = (height > 0) ? height : DEFAULT_SCREEN_HEIGHT; + int window_zoom = (zoom > 0) ? zoom : DEFAULT_WINDOW_ZOOM; + + // VALIDACIÓN 1: Si resolución > pantalla → reset a default + if (logical_width > screen_w || logical_height > screen_h) { + std::cout << "Advertencia: Resolución " << logical_width << "x" << logical_height + << " excede pantalla " << screen_w << "x" << screen_h + << ". Usando default " << DEFAULT_SCREEN_WIDTH << "x" << DEFAULT_SCREEN_HEIGHT << "\n"; + logical_width = DEFAULT_SCREEN_WIDTH; + logical_height = DEFAULT_SCREEN_HEIGHT; + window_zoom = DEFAULT_WINDOW_ZOOM; // Reset zoom también + } + + // VALIDACIÓN 2: Calcular max_zoom y ajustar si es necesario + int max_zoom = std::min(screen_w / logical_width, screen_h / logical_height); + if (window_zoom > max_zoom) { + std::cout << "Advertencia: Zoom " << window_zoom << " excede máximo " << max_zoom + << " para " << logical_width << "x" << logical_height << ". Ajustando a " << max_zoom << "\n"; + window_zoom = max_zoom; + } + + // Si se especificaron parámetros CLI y zoom no se especificó, usar zoom=1 + if ((width > 0 || height > 0) && zoom == 0) { + window_zoom = 1; + } + + // Calcular tamaño de ventana + int window_width = logical_width * window_zoom; + int window_height = logical_height * window_zoom; + + // Guardar resolución base (configurada por CLI o default) base_screen_width_ = logical_width; base_screen_height_ = logical_height; current_screen_width_ = logical_width; current_screen_height_ = logical_height; - if (!SDL_Init(SDL_INIT_VIDEO)) { - std::cout << "¡SDL no se pudo inicializar! Error de SDL: " << SDL_GetError() << std::endl; - success = false; - } else { + // SDL ya inicializado arriba para validación + { // Crear ventana principal (fullscreen si se especifica) Uint32 window_flags = SDL_WINDOW_OPENGL; if (fullscreen) { @@ -197,7 +236,7 @@ void Engine::calculateDeltaTime() { last_frame_time_ = current_time; // Limitar delta time para evitar saltos grandes (pausa larga, depuración, etc.) - if (delta_time_ > 0.05f) { // Máximo 50ms (20 FPS mínimo) + if (delta_time_ > 0.05f) { // Máximo 50ms (20 FPS mínimo) delta_time_ = 1.0f / 60.0f; // Fallback a 60 FPS } } @@ -217,7 +256,7 @@ void Engine::update() { // Bifurcar actualización según modo activo if (current_mode_ == SimulationMode::PHYSICS) { // Modo física normal: actualizar física de cada pelota - for (auto &ball : balls_) { + for (auto& ball : balls_) { ball->update(delta_time_); // Pasar delta time a cada pelota } } else if (current_mode_ == SimulationMode::SHAPE) { @@ -613,10 +652,9 @@ void Engine::render() { } // Ordenar índices por profundidad Z (menor primero = fondo primero) - std::sort(render_order.begin(), render_order.end(), - [this](size_t a, size_t b) { - return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness(); - }); + std::sort(render_order.begin(), render_order.end(), [this](size_t a, size_t b) { + return balls_[a]->getDepthBrightness() < balls_[b]->getDepthBrightness(); + }); // Renderizar en orden de profundidad (fondo → frente) for (size_t idx : render_order) { @@ -626,8 +664,7 @@ void Engine::render() { float depth_scale = balls_[idx]->getDepthScale(); // Mapear brightness de 0-1 a rango MIN-MAX - float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * - (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f; + float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * (ROTOBALL_MAX_BRIGHTNESS - ROTOBALL_MIN_BRIGHTNESS)) / 255.0f; // Aplicar factor de brillo al color int r_mod = static_cast(color.r * brightness_factor); @@ -639,7 +676,7 @@ void Engine::render() { } else { // MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad) size_t idx = 0; - for (auto &ball : balls_) { + for (auto& ball : balls_) { SDL_FRect pos = ball->getPosition(); Color color = getInterpolatedColor(idx); // Usar color interpolado (LERP) addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b, 1.0f); @@ -665,7 +702,7 @@ void Engine::render() { const ThemeColors& target = themes_[static_cast(target_theme_)]; if (text_ != current.name_es && text_ != target.name_es) { int theme_text_width = static_cast(strlen(current.name_es) * 8); // 8 píxeles por carácter - int theme_x = (current_screen_width_ - theme_text_width) / 2; // Centrar horizontalmente + int theme_x = (current_screen_width_ - theme_text_width) / 2; // Centrar horizontalmente // Texto del nombre del tema con el mismo color dbg_print(theme_x, 24, current.name_es, current.text_color_r, current.text_color_g, current.text_color_b); @@ -676,7 +713,7 @@ void Engine::render() { if (show_debug_) { // Mostrar contador de FPS en esquina superior derecha int fps_text_width = static_cast(fps_text_.length() * 8); // 8 píxeles por carácter - int fps_x = current_screen_width_ - fps_text_width - 8; // 8 píxeles de margen + int fps_x = current_screen_width_ - fps_text_width - 8; // 8 píxeles de margen dbg_print(fps_x, 8, fps_text_.c_str(), 255, 255, 0); // Amarillo para distinguir // Mostrar estado V-Sync en esquina superior izquierda @@ -701,7 +738,7 @@ void Engine::render() { // Línea 4: Coeficiente de rebote (loss) float loss_val = balls_[0]->getLossCoefficient(); std::string loss_text = "LOSS " + std::to_string(loss_val).substr(0, 4); // Solo 2 decimales - dbg_print(8, 48, loss_text.c_str(), 255, 0, 255); // Magenta para debug + dbg_print(8, 48, loss_text.c_str(), 255, 0, 255); // Magenta para debug // Línea 5: Dirección de gravedad std::string gravity_dir_text = "GRAVITY " + gravityDirectionToString(current_gravity_); @@ -742,15 +779,15 @@ void Engine::initBalls(int value) { // Crear las bolas según el escenario for (int i = 0; i < test_.at(value); ++i) { - const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -) + const int SIGN = ((rand() % 2) * 2) - 1; // Genera un signo aleatorio (+ o -) // Calcular spawn zone: margen a cada lado, zona central para spawn const int margin = static_cast(current_screen_width_ * BALL_SPAWN_MARGIN); const int spawn_zone_width = current_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 + 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 // Seleccionar color de la paleta del tema actual - ThemeColors &theme = themes_[static_cast(current_theme_)]; + ThemeColors& theme = themes_[static_cast(current_theme_)]; int color_index = rand() % theme.ball_colors.size(); // Cantidad variable de colores por tema const Color COLOR = theme.ball_colors[color_index]; // Generar factor de masa aleatorio (0.7 = ligera, 1.3 = pesada) @@ -776,7 +813,7 @@ void Engine::setText() { } void Engine::pushBallsAwayFromGravity() { - for (auto &ball : balls_) { + for (auto& ball : balls_) { const int SIGNO = ((rand() % 2) * 2) - 1; const float LATERAL = (((rand() % 20) + 10) * 0.1f) * SIGNO; const float MAIN = ((rand() % 40) * 0.1f) + 5; @@ -787,7 +824,7 @@ void Engine::pushBallsAwayFromGravity() { vx = LATERAL; vy = -MAIN; break; - case GravityDirection::UP: // Impulsar ABAJO + case GravityDirection::UP: // Impulsar ABAJO vx = LATERAL; vy = MAIN; break; @@ -795,7 +832,7 @@ void Engine::pushBallsAwayFromGravity() { vx = MAIN; vy = LATERAL; break; - case GravityDirection::RIGHT: // Impulsar IZQUIERDA + case GravityDirection::RIGHT: // Impulsar IZQUIERDA vx = -MAIN; vy = LATERAL; break; @@ -805,32 +842,32 @@ void Engine::pushBallsAwayFromGravity() { } void Engine::switchBallsGravity() { - for (auto &ball : balls_) { + for (auto& ball : balls_) { ball->switchGravity(); } } void Engine::enableBallsGravityIfDisabled() { - for (auto &ball : balls_) { + for (auto& ball : balls_) { ball->enableGravityIfDisabled(); } } void Engine::forceBallsGravityOn() { - for (auto &ball : balls_) { + for (auto& ball : balls_) { ball->forceGravityOn(); } } void Engine::forceBallsGravityOff() { - for (auto &ball : balls_) { + for (auto& ball : balls_) { ball->forceGravityOff(); } } void Engine::changeGravityDirection(GravityDirection direction) { current_gravity_ = direction; - for (auto &ball : balls_) { + for (auto& ball : balls_) { ball->setGravityDirection(direction); ball->applyRandomLateralPush(); // Aplicar empuje lateral aleatorio } @@ -873,9 +910,9 @@ void Engine::toggleRealFullscreen() { if (real_fullscreen_enabled_) { // Obtener resolución del escritorio int num_displays = 0; - SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + SDL_DisplayID* displays = SDL_GetDisplays(&num_displays); if (displays != nullptr && num_displays > 0) { - const auto *dm = SDL_GetCurrentDisplayMode(displays[0]); + const auto* dm = SDL_GetCurrentDisplayMode(displays[0]); if (dm != nullptr) { // Cambiar a resolución nativa del escritorio current_screen_width_ = dm->w; @@ -903,7 +940,7 @@ void Engine::toggleRealFullscreen() { // Restaurar ventana normal SDL_SetWindowFullscreen(window_, false); - SDL_SetWindowSize(window_, base_screen_width_ * WINDOW_ZOOM, base_screen_height_ * WINDOW_ZOOM); + SDL_SetWindowSize(window_, base_screen_width_ * DEFAULT_WINDOW_ZOOM, base_screen_height_ * DEFAULT_WINDOW_ZOOM); // Restaurar presentación lógica base SDL_SetRenderLogicalPresentation(renderer_, base_screen_width_, base_screen_height_, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); @@ -966,11 +1003,16 @@ void Engine::toggleIntegerScaling() { std::string Engine::gravityDirectionToString(GravityDirection direction) const { switch (direction) { - case GravityDirection::DOWN: return "DOWN"; - case GravityDirection::UP: return "UP"; - case GravityDirection::LEFT: return "LEFT"; - case GravityDirection::RIGHT: return "RIGHT"; - default: return "UNKNOWN"; + case GravityDirection::DOWN: + return "DOWN"; + case GravityDirection::UP: + return "UP"; + case GravityDirection::LEFT: + return "LEFT"; + case GravityDirection::RIGHT: + return "RIGHT"; + default: + return "UNKNOWN"; } } @@ -983,8 +1025,8 @@ void Engine::renderGradientBackground() { if (transitioning_) { // Interpolar entre tema actual y tema destino - ThemeColors ¤t = themes_[static_cast(current_theme_)]; - ThemeColors &target = themes_[static_cast(target_theme_)]; + ThemeColors& current = themes_[static_cast(current_theme_)]; + ThemeColors& target = themes_[static_cast(target_theme_)]; top_r = lerp(current.bg_top_r, target.bg_top_r, transition_progress_); top_g = lerp(current.bg_top_g, target.bg_top_g, transition_progress_); @@ -995,7 +1037,7 @@ void Engine::renderGradientBackground() { bottom_b = lerp(current.bg_bottom_b, target.bg_bottom_b, transition_progress_); } else { // Sin transición: usar tema actual directamente - ThemeColors &theme = themes_[static_cast(current_theme_)]; + ThemeColors& theme = themes_[static_cast(current_theme_)]; top_r = theme.bg_top_r; top_g = theme.bg_top_g; top_b = theme.bg_top_b; @@ -1087,20 +1129,20 @@ void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g, int Engine::calculateMaxWindowZoom() const { // Obtener información del display usando el método de Coffee Crisis int num_displays = 0; - SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + SDL_DisplayID* displays = SDL_GetDisplays(&num_displays); if (displays == nullptr || num_displays == 0) { return WINDOW_ZOOM_MIN; // Fallback si no se puede obtener } // Obtener el modo de display actual - const auto *dm = SDL_GetCurrentDisplayMode(displays[0]); + const auto* dm = SDL_GetCurrentDisplayMode(displays[0]); if (dm == nullptr) { SDL_free(displays); return WINDOW_ZOOM_MIN; } // Calcular zoom máximo usando la fórmula de Coffee Crisis - const int MAX_ZOOM = std::min(dm->w / SCREEN_WIDTH, (dm->h - WINDOW_DECORATION_HEIGHT) / SCREEN_HEIGHT); + const int MAX_ZOOM = std::min(dm->w / base_screen_width_, (dm->h - WINDOW_DECORATION_HEIGHT) / base_screen_height_); SDL_free(displays); @@ -1120,12 +1162,12 @@ void Engine::setWindowZoom(int new_zoom) { // Obtener posición actual del centro de la ventana int current_x, current_y; SDL_GetWindowPosition(window_, ¤t_x, ¤t_y); - int current_center_x = current_x + (SCREEN_WIDTH * current_window_zoom_) / 2; - int current_center_y = current_y + (SCREEN_HEIGHT * current_window_zoom_) / 2; + int current_center_x = current_x + (base_screen_width_ * current_window_zoom_) / 2; + int current_center_y = current_y + (base_screen_height_ * current_window_zoom_) / 2; // Calcular nuevo tamaño - int new_width = SCREEN_WIDTH * new_zoom; - int new_height = SCREEN_HEIGHT * new_zoom; + int new_width = base_screen_width_ * new_zoom; + int new_height = base_screen_height_ * new_zoom; // Calcular nueva posición (centrada en el punto actual) int new_x = current_center_x - new_width / 2; @@ -1162,80 +1204,117 @@ void Engine::zoomOut() { void Engine::initializeThemes() { // SUNSET: Naranjas, rojos, amarillos, rosas (8 colores) themes_[0] = { - "SUNSET", "ATARDECER", // Nombres (inglés, español) - 255, 140, 60, // Color texto: naranja cálido - 180.0f / 255.0f, 140.0f / 255.0f, 100.0f / 255.0f, // Fondo superior (naranja suave) - 40.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior (púrpura oscuro) - {{255, 140, 0}, {255, 69, 0}, {255, 215, 0}, {255, 20, 147}, {255, 99, 71}, {255, 165, 0}, {255, 192, 203}, {220, 20, 60}} - }; + "SUNSET", + "ATARDECER", // Nombres (inglés, español) + 255, + 140, + 60, // Color texto: naranja cálido + 180.0f / 255.0f, + 140.0f / 255.0f, + 100.0f / 255.0f, // Fondo superior (naranja suave) + 40.0f / 255.0f, + 20.0f / 255.0f, + 60.0f / 255.0f, // Fondo inferior (púrpura oscuro) + {{255, 140, 0}, {255, 69, 0}, {255, 215, 0}, {255, 20, 147}, {255, 99, 71}, {255, 165, 0}, {255, 192, 203}, {220, 20, 60}}}; // OCEAN: Azules, turquesas, blancos (8 colores) themes_[1] = { - "OCEAN", "OCEANO", // Nombres (inglés, español) - 80, 200, 255, // Color texto: azul océano - 100.0f / 255.0f, 150.0f / 255.0f, 200.0f / 255.0f, // Fondo superior (azul cielo) - 20.0f / 255.0f, 40.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior (azul marino) - {{0, 191, 255}, {0, 255, 255}, {32, 178, 170}, {176, 224, 230}, {70, 130, 180}, {0, 206, 209}, {240, 248, 255}, {64, 224, 208}} - }; + "OCEAN", + "OCEANO", // Nombres (inglés, español) + 80, + 200, + 255, // Color texto: azul océano + 100.0f / 255.0f, + 150.0f / 255.0f, + 200.0f / 255.0f, // Fondo superior (azul cielo) + 20.0f / 255.0f, + 40.0f / 255.0f, + 80.0f / 255.0f, // Fondo inferior (azul marino) + {{0, 191, 255}, {0, 255, 255}, {32, 178, 170}, {176, 224, 230}, {70, 130, 180}, {0, 206, 209}, {240, 248, 255}, {64, 224, 208}}}; // NEON: Cian, magenta, verde lima, amarillo vibrante (8 colores) themes_[2] = { - "NEON", "NEON", // Nombres (inglés, español) - 255, 60, 255, // Color texto: magenta brillante - 20.0f / 255.0f, 20.0f / 255.0f, 40.0f / 255.0f, // Fondo superior (negro azulado) - 0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior (negro) - {{0, 255, 255}, {255, 0, 255}, {50, 205, 50}, {255, 255, 0}, {255, 20, 147}, {0, 255, 127}, {138, 43, 226}, {255, 69, 0}} - }; + "NEON", + "NEON", // Nombres (inglés, español) + 255, + 60, + 255, // Color texto: magenta brillante + 20.0f / 255.0f, + 20.0f / 255.0f, + 40.0f / 255.0f, // Fondo superior (negro azulado) + 0.0f / 255.0f, + 0.0f / 255.0f, + 0.0f / 255.0f, // Fondo inferior (negro) + {{0, 255, 255}, {255, 0, 255}, {50, 205, 50}, {255, 255, 0}, {255, 20, 147}, {0, 255, 127}, {138, 43, 226}, {255, 69, 0}}}; // FOREST: Verdes, marrones, amarillos otoño (8 colores) themes_[3] = { - "FOREST", "BOSQUE", // Nombres (inglés, español) - 100, 255, 100, // Color texto: verde natural - 144.0f / 255.0f, 238.0f / 255.0f, 144.0f / 255.0f, // Fondo superior (verde claro) - 101.0f / 255.0f, 67.0f / 255.0f, 33.0f / 255.0f, // Fondo inferior (marrón tierra) - {{34, 139, 34}, {107, 142, 35}, {154, 205, 50}, {255, 215, 0}, {210, 180, 140}, {160, 82, 45}, {218, 165, 32}, {50, 205, 50}} - }; + "FOREST", + "BOSQUE", // Nombres (inglés, español) + 100, + 255, + 100, // Color texto: verde natural + 144.0f / 255.0f, + 238.0f / 255.0f, + 144.0f / 255.0f, // Fondo superior (verde claro) + 101.0f / 255.0f, + 67.0f / 255.0f, + 33.0f / 255.0f, // Fondo inferior (marrón tierra) + {{34, 139, 34}, {107, 142, 35}, {154, 205, 50}, {255, 215, 0}, {210, 180, 140}, {160, 82, 45}, {218, 165, 32}, {50, 205, 50}}}; // RGB: Círculo cromático con 24 puntos (cada 15°) - Ultra precisión matemática themes_[4] = { - "RGB", "RGB", // Nombres (inglés, español) - 100, 100, 100, // Color texto: gris oscuro (contraste con fondo blanco) - 1.0f, 1.0f, 1.0f, // Fondo superior (blanco puro) - 1.0f, 1.0f, 1.0f, // Fondo inferior (blanco puro) - sin degradado + "RGB", + "RGB", // Nombres (inglés, español) + 100, + 100, + 100, // Color texto: gris oscuro (contraste con fondo blanco) + 1.0f, + 1.0f, + 1.0f, // Fondo superior (blanco puro) + 1.0f, + 1.0f, + 1.0f, // Fondo inferior (blanco puro) - sin degradado { - {255, 0, 0}, // 0° - Rojo puro - {255, 64, 0}, // 15° - Rojo-Naranja - {255, 128, 0}, // 30° - Naranja - {255, 191, 0}, // 45° - Naranja-Amarillo - {255, 255, 0}, // 60° - Amarillo puro - {191, 255, 0}, // 75° - Amarillo-Verde claro - {128, 255, 0}, // 90° - Verde-Amarillo - {64, 255, 0}, // 105° - Verde claro-Amarillo - {0, 255, 0}, // 120° - Verde puro - {0, 255, 64}, // 135° - Verde-Cian claro - {0, 255, 128}, // 150° - Verde-Cian - {0, 255, 191}, // 165° - Verde claro-Cian - {0, 255, 255}, // 180° - Cian puro - {0, 191, 255}, // 195° - Cian-Azul claro - {0, 128, 255}, // 210° - Azul-Cian - {0, 64, 255}, // 225° - Azul claro-Cian - {0, 0, 255}, // 240° - Azul puro - {64, 0, 255}, // 255° - Azul-Magenta claro - {128, 0, 255}, // 270° - Azul-Magenta - {191, 0, 255}, // 285° - Azul claro-Magenta - {255, 0, 255}, // 300° - Magenta puro - {255, 0, 191}, // 315° - Magenta-Rojo claro - {255, 0, 128}, // 330° - Magenta-Rojo - {255, 0, 64} // 345° - Magenta claro-Rojo - } - }; + {255, 0, 0}, // 0° - Rojo puro + {255, 64, 0}, // 15° - Rojo-Naranja + {255, 128, 0}, // 30° - Naranja + {255, 191, 0}, // 45° - Naranja-Amarillo + {255, 255, 0}, // 60° - Amarillo puro + {191, 255, 0}, // 75° - Amarillo-Verde claro + {128, 255, 0}, // 90° - Verde-Amarillo + {64, 255, 0}, // 105° - Verde claro-Amarillo + {0, 255, 0}, // 120° - Verde puro + {0, 255, 64}, // 135° - Verde-Cian claro + {0, 255, 128}, // 150° - Verde-Cian + {0, 255, 191}, // 165° - Verde claro-Cian + {0, 255, 255}, // 180° - Cian puro + {0, 191, 255}, // 195° - Cian-Azul claro + {0, 128, 255}, // 210° - Azul-Cian + {0, 64, 255}, // 225° - Azul claro-Cian + {0, 0, 255}, // 240° - Azul puro + {64, 0, 255}, // 255° - Azul-Magenta claro + {128, 0, 255}, // 270° - Azul-Magenta + {191, 0, 255}, // 285° - Azul claro-Magenta + {255, 0, 255}, // 300° - Magenta puro + {255, 0, 191}, // 315° - Magenta-Rojo claro + {255, 0, 128}, // 330° - Magenta-Rojo + {255, 0, 64} // 345° - Magenta claro-Rojo + }}; // MONOCHROME: Fondo negro degradado, sprites blancos monocromáticos (8 tonos grises) themes_[5] = { - "MONOCHROME", "MONOCROMO", // Nombres (inglés, español) - 200, 200, 200, // Color texto: gris claro - 20.0f / 255.0f, 20.0f / 255.0f, 20.0f / 255.0f, // Fondo superior (gris muy oscuro) - 0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior (negro) + "MONOCHROME", + "MONOCROMO", // Nombres (inglés, español) + 200, + 200, + 200, // Color texto: gris claro + 20.0f / 255.0f, + 20.0f / 255.0f, + 20.0f / 255.0f, // Fondo superior (gris muy oscuro) + 0.0f / 255.0f, + 0.0f / 255.0f, + 0.0f / 255.0f, // Fondo inferior (negro) { {255, 255, 255}, // Blanco puro - todas las pelotas del mismo color {255, 255, 255}, @@ -1244,9 +1323,7 @@ void Engine::initializeThemes() { {255, 255, 255}, {255, 255, 255}, {255, 255, 255}, - {255, 255, 255} - } - }; + {255, 255, 255}}}; } void Engine::startThemeTransition(ColorTheme new_theme) { @@ -1284,8 +1361,7 @@ Color Engine::getInterpolatedColor(size_t ball_index) const { return { static_cast(lerp(static_cast(current_color.r), static_cast(target_color.r), transition_progress_)), static_cast(lerp(static_cast(current_color.g), static_cast(target_color.g), transition_progress_)), - static_cast(lerp(static_cast(current_color.b), static_cast(target_color.b), transition_progress_)) - }; + static_cast(lerp(static_cast(current_color.b), static_cast(target_color.b), transition_progress_))}; } // Sistema de Modo DEMO (auto-play) @@ -1321,9 +1397,7 @@ void Engine::performDemoAction(bool is_lite) { if (is_lite) { // DEMO LITE: Solo física/figuras - TOTAL_WEIGHT = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE - + DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS - + DEMO_LITE_WEIGHT_IMPULSE; + TOTAL_WEIGHT = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE + DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS + DEMO_LITE_WEIGHT_IMPULSE; random_value = rand() % TOTAL_WEIGHT; // Cambiar dirección gravedad (25%) @@ -1344,9 +1418,7 @@ void Engine::performDemoAction(bool is_lite) { // Activar figura 3D (25%) accumulated_weight += DEMO_LITE_WEIGHT_SHAPE; if (random_value < accumulated_weight) { - ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, - ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, - ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE}; + ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE}; int shape_index = rand() % 9; activateShape(shapes[shape_index]); return; @@ -1368,10 +1440,7 @@ void Engine::performDemoAction(bool is_lite) { } else { // DEMO COMPLETO: Todas las acciones - TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE - + DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME - + DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM - + DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE; + TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE + DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME + DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM + DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE; random_value = rand() % TOTAL_WEIGHT; // Cambiar dirección gravedad (10%) @@ -1392,9 +1461,7 @@ void Engine::performDemoAction(bool is_lite) { // Activar figura 3D (20%) accumulated_weight += DEMO_WEIGHT_SHAPE; if (random_value < accumulated_weight) { - ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, - ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, - ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE}; + ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE}; int shape_index = rand() % 9; activateShape(shapes[shape_index]); return; @@ -1489,9 +1556,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) { } } else { // Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial) - ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, - ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, - ShapeType::ICOSAHEDRON, ShapeType::ATOM}; + ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM}; activateShape(shapes[rand() % 8]); } @@ -1527,9 +1592,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) { } } else { // Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial) - ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, - ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, - ShapeType::ICOSAHEDRON, ShapeType::ATOM}; + ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX, ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER, ShapeType::ICOSAHEDRON, ShapeType::ATOM}; activateShape(shapes[rand() % 8]); // 5. Profundidad (solo si estamos en figura) @@ -1780,9 +1843,7 @@ void Engine::updateShape() { // Aplicar fuerza de atracción física hacia el punto rotado // Usar constantes SHAPE (mayor pegajosidad que ROTOBALL) float shape_size = scale_factor * 80.0f; // 80px = radio base - balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_, - SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR, - SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE); + balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_, SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR, SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE); // Calcular brillo según profundidad Z para renderizado // Normalizar Z al rango de la figura (asumiendo simetría ±shape_size) diff --git a/source/engine.h b/source/engine.h index 451c285..d919cad 100644 --- a/source/engine.h +++ b/source/engine.h @@ -1,166 +1,166 @@ #pragma once -#include // for SDL_Event -#include // for SDL_Renderer -#include // for Uint64 -#include // for SDL_Window +#include // for SDL_Event +#include // for SDL_Renderer +#include // for Uint64 +#include // for SDL_Window -#include // for array -#include // for unique_ptr, shared_ptr -#include // for string -#include // for vector +#include // for array +#include // for unique_ptr, shared_ptr +#include // for string +#include // for vector -#include "defines.h" // for GravityDirection, ColorTheme, ShapeType -#include "ball.h" // for Ball +#include "ball.h" // for Ball +#include "defines.h" // for GravityDirection, ColorTheme, ShapeType #include "external/texture.h" // for Texture -#include "shapes/shape.h" // for Shape (interfaz polimórfica) +#include "shapes/shape.h" // for Shape (interfaz polimórfica) class Engine { -public: - // Interfaz pública - bool initialize(int width = 0, int height = 0, bool fullscreen = false); - void run(); - void shutdown(); + public: + // Interfaz pública + bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false); + void run(); + void shutdown(); -private: - // Recursos SDL - SDL_Window* window_ = nullptr; - SDL_Renderer* renderer_ = nullptr; - std::shared_ptr texture_ = nullptr; // Textura activa actual - std::vector> textures_; // Todas las texturas disponibles - std::vector texture_names_; // Nombres de texturas (sin extensión) - size_t current_texture_index_ = 0; // Índice de textura activa - int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico, se actualiza desde texture) + private: + // Recursos SDL + SDL_Window* window_ = nullptr; + SDL_Renderer* renderer_ = nullptr; + std::shared_ptr texture_ = nullptr; // Textura activa actual + std::vector> textures_; // Todas las texturas disponibles + std::vector texture_names_; // Nombres de texturas (sin extensión) + size_t current_texture_index_ = 0; // Índice de textura activa + int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico, se actualiza desde texture) - // Estado del simulador - std::vector> balls_; - std::array test_ = {1, 10, 100, 500, 1000, 10000, 50000, 100000}; - GravityDirection current_gravity_ = GravityDirection::DOWN; - int scenario_ = 0; - bool should_exit_ = false; + // Estado del simulador + std::vector> balls_; + std::array test_ = {1, 10, 100, 500, 1000, 10000, 50000, 100000}; + GravityDirection current_gravity_ = GravityDirection::DOWN; + int scenario_ = 0; + bool should_exit_ = false; - // Sistema de timing - Uint64 last_frame_time_ = 0; - float delta_time_ = 0.0f; + // Sistema de timing + Uint64 last_frame_time_ = 0; + float delta_time_ = 0.0f; - // UI y debug - bool show_debug_ = false; - bool show_text_ = true; + // UI y debug + bool show_debug_ = false; + bool show_text_ = true; - // Sistema de zoom dinámico - int current_window_zoom_ = WINDOW_ZOOM; - std::string text_; - int text_pos_ = 0; - Uint64 text_init_time_ = 0; + // Sistema de zoom dinámico + int current_window_zoom_ = DEFAULT_WINDOW_ZOOM; + std::string text_; + int text_pos_ = 0; + Uint64 text_init_time_ = 0; - // FPS y V-Sync - Uint64 fps_last_time_ = 0; - int fps_frame_count_ = 0; - int fps_current_ = 0; - std::string fps_text_ = "FPS: 0"; - bool vsync_enabled_ = true; - std::string vsync_text_ = "VSYNC ON"; - bool fullscreen_enabled_ = false; - bool real_fullscreen_enabled_ = false; - ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5) + // FPS y V-Sync + Uint64 fps_last_time_ = 0; + int fps_frame_count_ = 0; + int fps_current_ = 0; + std::string fps_text_ = "FPS: 0"; + bool vsync_enabled_ = true; + std::string vsync_text_ = "VSYNC ON"; + bool fullscreen_enabled_ = false; + bool real_fullscreen_enabled_ = false; + ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5) - // Resolución base (configurada por CLI o default) - int base_screen_width_ = SCREEN_WIDTH; - int base_screen_height_ = SCREEN_HEIGHT; + // Resolución base (configurada por CLI o default) + int base_screen_width_ = DEFAULT_SCREEN_WIDTH; + int base_screen_height_ = DEFAULT_SCREEN_HEIGHT; - // Resolución dinámica actual (cambia en fullscreen real) - int current_screen_width_ = SCREEN_WIDTH; - int current_screen_height_ = SCREEN_HEIGHT; + // Resolución dinámica actual (cambia en fullscreen real) + int current_screen_width_ = DEFAULT_SCREEN_WIDTH; + int current_screen_height_ = DEFAULT_SCREEN_HEIGHT; - // Sistema de temas - ColorTheme current_theme_ = ColorTheme::SUNSET; - ColorTheme target_theme_ = ColorTheme::SUNSET; // Tema destino para transición - bool transitioning_ = false; // ¿Estamos en transición? - float transition_progress_ = 0.0f; // Progreso de 0.0 a 1.0 - float transition_duration_ = 0.5f; // Duración en segundos + // Sistema de temas + ColorTheme current_theme_ = ColorTheme::SUNSET; + ColorTheme target_theme_ = ColorTheme::SUNSET; // Tema destino para transición + bool transitioning_ = false; // ¿Estamos en transición? + float transition_progress_ = 0.0f; // Progreso de 0.0 a 1.0 + float transition_duration_ = 0.5f; // Duración en segundos - // Estructura de tema de colores - struct ThemeColors { - const char* name_en; // Nombre en inglés (para debug) - const char* name_es; // Nombre en español (para display) - int text_color_r, text_color_g, text_color_b; // Color del texto del tema - float bg_top_r, bg_top_g, bg_top_b; - float bg_bottom_r, bg_bottom_g, bg_bottom_b; - std::vector ball_colors; - }; + // Estructura de tema de colores + struct ThemeColors { + const char* name_en; // Nombre en inglés (para debug) + const char* name_es; // Nombre en español (para display) + int text_color_r, text_color_g, text_color_b; // Color del texto del tema + float bg_top_r, bg_top_g, bg_top_b; + float bg_bottom_r, bg_bottom_g, bg_bottom_b; + std::vector ball_colors; + }; - // Temas de colores definidos - ThemeColors themes_[6]; // 6 temas: SUNSET, OCEAN, NEON, FOREST, RGB, MONOCHROME + // Temas de colores definidos + ThemeColors themes_[6]; // 6 temas: SUNSET, OCEAN, NEON, FOREST, RGB, MONOCHROME - // Sistema de Figuras 3D (polimórfico) - SimulationMode current_mode_ = SimulationMode::PHYSICS; - ShapeType current_shape_type_ = ShapeType::SPHERE; // Tipo de figura actual - ShapeType last_shape_type_ = ShapeType::SPHERE; // Última figura para toggle F - std::unique_ptr active_shape_; // Puntero polimórfico a figura activa - float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-) - bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado + // Sistema de Figuras 3D (polimórfico) + SimulationMode current_mode_ = SimulationMode::PHYSICS; + ShapeType current_shape_type_ = ShapeType::SPHERE; // Tipo de figura actual + ShapeType last_shape_type_ = ShapeType::SPHERE; // Última figura para toggle F + std::unique_ptr active_shape_; // Puntero polimórfico a figura activa + float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-) + bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado - // Sistema de Modo DEMO (auto-play) - bool demo_mode_enabled_ = false; // ¿Está activo el modo demo completo? - bool demo_lite_enabled_ = false; // ¿Está activo el modo demo lite? - float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción - float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos) + // Sistema de Modo DEMO (auto-play) + bool demo_mode_enabled_ = false; // ¿Está activo el modo demo completo? + bool demo_lite_enabled_ = false; // ¿Está activo el modo demo lite? + float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción + float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos) - // Batch rendering - std::vector batch_vertices_; - std::vector batch_indices_; + // Batch rendering + std::vector batch_vertices_; + std::vector batch_indices_; - // Métodos principales del loop - void calculateDeltaTime(); - void update(); - void handleEvents(); - void render(); + // Métodos principales del loop + void calculateDeltaTime(); + void update(); + void handleEvents(); + void render(); - // Métodos auxiliares - void initBalls(int value); - void setText(); - void pushBallsAwayFromGravity(); - void switchBallsGravity(); - void enableBallsGravityIfDisabled(); - void forceBallsGravityOn(); - void forceBallsGravityOff(); - void changeGravityDirection(GravityDirection direction); - void toggleVSync(); - void toggleFullscreen(); - void toggleRealFullscreen(); - void toggleIntegerScaling(); - std::string gravityDirectionToString(GravityDirection direction) const; - void initializeThemes(); + // Métodos auxiliares + void initBalls(int value); + void setText(); + void pushBallsAwayFromGravity(); + void switchBallsGravity(); + void enableBallsGravityIfDisabled(); + void forceBallsGravityOn(); + void forceBallsGravityOff(); + void changeGravityDirection(GravityDirection direction); + void toggleVSync(); + void toggleFullscreen(); + void toggleRealFullscreen(); + void toggleIntegerScaling(); + std::string gravityDirectionToString(GravityDirection direction) const; + void initializeThemes(); - // Sistema de Modo DEMO - void updateDemoMode(); - void performDemoAction(bool is_lite); - void randomizeOnDemoStart(bool is_lite); - void toggleGravityOnOff(); + // Sistema de Modo DEMO + void updateDemoMode(); + void performDemoAction(bool is_lite); + void randomizeOnDemoStart(bool is_lite); + void toggleGravityOnOff(); - // Sistema de transiciones LERP - float lerp(float a, float b, float t) const { return a + (b - a) * t; } - Color getInterpolatedColor(size_t ball_index) const; // Obtener color interpolado durante transición - void startThemeTransition(ColorTheme new_theme); + // Sistema de transiciones LERP + float lerp(float a, float b, float t) const { return a + (b - a) * t; } + Color getInterpolatedColor(size_t ball_index) const; // Obtener color interpolado durante transición + void startThemeTransition(ColorTheme new_theme); - // Sistema de cambio de sprites dinámico - void switchTexture(); // Cambia a siguiente textura disponible - void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño + // Sistema de cambio de sprites dinámico + void switchTexture(); // Cambia a siguiente textura disponible + void updateBallSizes(int old_size, int new_size); // Ajusta posiciones al cambiar tamaño - // Sistema de zoom dinámico - int calculateMaxWindowZoom() const; - void setWindowZoom(int new_zoom); - void zoomIn(); - void zoomOut(); + // Sistema de zoom dinámico + int calculateMaxWindowZoom() const; + void setWindowZoom(int new_zoom); + void zoomIn(); + void zoomOut(); - // Rendering - void renderGradientBackground(); - void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f); + // Rendering + void renderGradientBackground(); + void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f); - // Sistema de Figuras 3D - void toggleShapeMode(bool force_gravity_on_exit = true); // Toggle PHYSICS ↔ última figura (tecla F) - void activateShape(ShapeType type); // Activar figura específica (teclas Q/W/E/R/Y/U/I) - void updateShape(); // Actualizar figura activa - void generateShape(); // Generar puntos de figura activa - void clampShapeScale(); // Limitar escala para evitar clipping + // Sistema de Figuras 3D + void toggleShapeMode(bool force_gravity_on_exit = true); // Toggle PHYSICS ↔ última figura (tecla F) + void activateShape(ShapeType type); // Activar figura específica (teclas Q/W/E/R/Y/U/I) + void updateShape(); // Actualizar figura activa + void generateShape(); // Generar puntos de figura activa + void clampShapeScale(); // Limitar escala para evitar clipping }; diff --git a/source/main.cpp b/source/main.cpp index dffd297..73e2fc3 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -6,19 +6,23 @@ void printHelp() { std::cout << "ViBe3 Physics - Simulador de físicas avanzadas\n"; std::cout << "\nUso: vibe3_physics [opciones]\n\n"; std::cout << "Opciones:\n"; - std::cout << " -w, --width Ancho de resolución (default: 1280)\n"; - std::cout << " -h, --height Alto de resolución (default: 720)\n"; + std::cout << " -w, --width Ancho de resolución (default: 320)\n"; + std::cout << " -h, --height Alto de resolución (default: 240)\n"; + std::cout << " -z, --zoom Zoom de ventana (default: 3)\n"; std::cout << " -f, --fullscreen Modo pantalla completa\n"; std::cout << " --help Mostrar esta ayuda\n\n"; std::cout << "Ejemplos:\n"; - std::cout << " vibe3_physics # 1280x720 ventana\n"; - std::cout << " vibe3_physics -w 1920 -h 1080 # 1920x1080 ventana\n"; - std::cout << " vibe3_physics -w 1920 -h 1080 -f # 1920x1080 fullscreen\n"; + std::cout << " vibe3_physics # 320x240 zoom 3 (ventana 960x720)\n"; + std::cout << " vibe3_physics -w 1920 -h 1080 # 1920x1080 zoom 1 (auto)\n"; + std::cout << " vibe3_physics -w 640 -h 480 -z 2 # 640x480 zoom 2 (ventana 1280x960)\n"; + std::cout << " vibe3_physics -w 1920 -h 1080 -f # 1920x1080 fullscreen\n\n"; + std::cout << "Nota: Si resolución > pantalla, se usa default. Zoom se ajusta automáticamente.\n"; } int main(int argc, char* argv[]) { int width = 0; int height = 0; + int zoom = 0; bool fullscreen = false; // Parsear argumentos @@ -48,6 +52,17 @@ int main(int argc, char* argv[]) { std::cerr << "Error: -h/--height requiere un valor\n"; return -1; } + } else if (strcmp(argv[i], "-z") == 0 || strcmp(argv[i], "--zoom") == 0) { + if (i + 1 < argc) { + zoom = atoi(argv[++i]); + if (zoom < 1) { + std::cerr << "Error: Zoom mínimo es 1\n"; + return -1; + } + } else { + std::cerr << "Error: -z/--zoom requiere un valor\n"; + return -1; + } } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fullscreen") == 0) { fullscreen = true; } else { @@ -59,7 +74,7 @@ int main(int argc, char* argv[]) { Engine engine; - if (!engine.initialize(width, height, fullscreen)) { + if (!engine.initialize(width, height, zoom, fullscreen)) { std::cout << "¡Error al inicializar el engine!" << std::endl; return -1; }