From 3b0354da54cfa5985d6e64816a62f8c44449be3e Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 3 Dec 2025 17:40:27 +0100 Subject: [PATCH] afegit titol al TITOL --- CMakeLists.txt | 2 +- data/shapes/title/letra_a.shp | 10 ++ data/shapes/title/letra_c.shp | 9 ++ data/shapes/title/letra_exclamacion.shp | 10 ++ data/shapes/title/letra_i.shp | 9 ++ data/shapes/title/letra_k.shp | 9 ++ data/shapes/title/letra_n.shp | 9 ++ data/shapes/title/letra_o.shp | 10 ++ data/shapes/title/letra_r.shp | 10 ++ data/shapes/title/letra_t.shp | 9 ++ jailgames.svg | 15 -- sample.png | Bin 37413 -> 0 bytes source/core/defaults.hpp | 52 +++---- source/core/graphics/starfield.cpp | 20 +-- source/core/graphics/starfield.hpp | 92 ++++++------ source/core/rendering/line_renderer.cpp | 3 +- source/core/system/director.cpp | 6 +- source/core/system/global_events.cpp | 2 +- source/game/entities/enemic.cpp | 33 +++-- source/game/entities/enemic.hpp | 38 ++--- source/game/entities/nau.hpp | 4 +- source/game/escenes/escena_joc.cpp | 8 +- source/game/escenes/escena_joc.hpp | 14 +- source/game/escenes/escena_logo.hpp | 4 +- source/game/escenes/escena_titol.cpp | 186 +++++++++++++++++++++++- source/game/escenes/escena_titol.hpp | 32 +++- tools/svg_to_shp.py | 124 +++++++++++----- 27 files changed, 520 insertions(+), 200 deletions(-) create mode 100644 data/shapes/title/letra_a.shp create mode 100644 data/shapes/title/letra_c.shp create mode 100644 data/shapes/title/letra_exclamacion.shp create mode 100644 data/shapes/title/letra_i.shp create mode 100644 data/shapes/title/letra_k.shp create mode 100644 data/shapes/title/letra_n.shp create mode 100644 data/shapes/title/letra_o.shp create mode 100644 data/shapes/title/letra_r.shp create mode 100644 data/shapes/title/letra_t.shp delete mode 100644 jailgames.svg delete mode 100644 sample.png diff --git a/CMakeLists.txt b/CMakeLists.txt index d37fd20..446a1c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # CMakeLists.txt cmake_minimum_required(VERSION 3.10) -project(orni VERSION 0.3.1) +project(orni VERSION 0.4.0) # Info del proyecto set(PROJECT_LONG_NAME "Orni Attack") diff --git a/data/shapes/title/letra_a.shp b/data/shapes/title/letra_a.shp new file mode 100644 index 0000000..46f740a --- /dev/null +++ b/data/shapes/title/letra_a.shp @@ -0,0 +1,10 @@ +# letra_a.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 137.50 x 100.00 px + +name: letra_a +scale: 1.0 +center: 68.75, 50.00 + +polyline: 0.00,100.00 0.00,75.00 37.50,0.00 100.00,0.00 137.50,75.00 137.50,100.00 100.00,100.00 100.00,87.50 37.50,87.50 37.50,100.00 0.00,100.00 +polyline: 62.50,25.00 50.00,50.00 50.00,62.50 87.50,62.50 87.50,50.00 75.00,25.00 62.50,25.00 diff --git a/data/shapes/title/letra_c.shp b/data/shapes/title/letra_c.shp new file mode 100644 index 0000000..54389f6 --- /dev/null +++ b/data/shapes/title/letra_c.shp @@ -0,0 +1,9 @@ +# letra_c.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 137.50 x 100.00 px + +name: letra_c +scale: 1.0 +center: 68.75, 50.00 + +polyline: 12.50,100.00 0.00,87.50 0.00,12.50 12.50,0.00 125.00,0.00 137.50,12.50 137.50,37.50 100.00,37.50 100.00,25.00 37.50,25.00 37.50,75.00 100.00,75.00 100.00,62.50 137.50,62.50 137.50,87.50 125.00,100.00 12.50,100.00 diff --git a/data/shapes/title/letra_exclamacion.shp b/data/shapes/title/letra_exclamacion.shp new file mode 100644 index 0000000..5f27e11 --- /dev/null +++ b/data/shapes/title/letra_exclamacion.shp @@ -0,0 +1,10 @@ +# letra_exclamacion.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 37.51 x 100.00 px + +name: letra_exclamacion +scale: 1.0 +center: 18.75, 50.00 + +polyline: 0.00,62.50 0.00,0.00 37.51,0.00 37.51,62.50 0.00,62.50 +polyline: 0.00,100.00 0.00,75.00 37.51,75.00 37.51,100.00 0.00,100.00 diff --git a/data/shapes/title/letra_i.shp b/data/shapes/title/letra_i.shp new file mode 100644 index 0000000..f3677d4 --- /dev/null +++ b/data/shapes/title/letra_i.shp @@ -0,0 +1,9 @@ +# letra_i.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 37.50 x 100.00 px + +name: letra_i +scale: 1.0 +center: 18.75, 50.00 + +polyline: 0.00,0.00 37.50,0.00 37.50,100.00 0.00,100.00 0.00,0.00 diff --git a/data/shapes/title/letra_k.shp b/data/shapes/title/letra_k.shp new file mode 100644 index 0000000..9266eaa --- /dev/null +++ b/data/shapes/title/letra_k.shp @@ -0,0 +1,9 @@ +# letra_k.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 137.50 x 100.00 px + +name: letra_k +scale: 1.0 +center: 68.75, 50.00 + +polyline: 0.00,100.00 0.00,0.00 37.50,0.00 37.50,37.50 50.00,37.50 100.00,0.00 137.50,0.00 137.50,25.00 87.06,50.00 137.50,75.00 137.50,100.00 100.00,100.00 50.00,62.50 37.50,62.50 37.50,100.00 0.00,100.00 diff --git a/data/shapes/title/letra_n.shp b/data/shapes/title/letra_n.shp new file mode 100644 index 0000000..2ff6535 --- /dev/null +++ b/data/shapes/title/letra_n.shp @@ -0,0 +1,9 @@ +# letra_n.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 137.50 x 100.00 px + +name: letra_n +scale: 1.0 +center: 68.75, 50.00 + +polyline: 0.00,100.00 0.00,0.00 50.00,0.00 100.00,50.00 100.00,0.00 137.50,0.00 137.50,100.00 87.50,100.00 37.50,50.00 37.50,100.00 0.00,100.00 diff --git a/data/shapes/title/letra_o.shp b/data/shapes/title/letra_o.shp new file mode 100644 index 0000000..36226da --- /dev/null +++ b/data/shapes/title/letra_o.shp @@ -0,0 +1,10 @@ +# letra_o.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 137.50 x 100.00 px + +name: letra_o +scale: 1.0 +center: 68.75, 50.00 + +polyline: 12.50,100.00 0.00,87.50 0.00,12.50 12.50,0.00 125.00,0.00 137.50,12.50 137.50,87.50 125.00,100.00 12.50,100.00 +polyline: 100.00,25.00 37.50,25.00 37.50,75.00 100.00,75.00 100.00,25.00 diff --git a/data/shapes/title/letra_r.shp b/data/shapes/title/letra_r.shp new file mode 100644 index 0000000..96a86b3 --- /dev/null +++ b/data/shapes/title/letra_r.shp @@ -0,0 +1,10 @@ +# letra_r.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 137.50 x 100.00 px + +name: letra_r +scale: 1.0 +center: 68.75, 50.00 + +polyline: 0.00,100.00 0.00,0.00 125.00,0.00 137.50,12.50 137.50,62.50 125.00,62.50 137.50,75.00 137.50,100.00 100.00,100.00 100.00,75.00 37.50,75.00 37.50,100.00 0.00,100.00 +polyline: 37.50,50.00 100.00,50.00 100.00,25.00 37.50,25.00 37.50,50.00 diff --git a/data/shapes/title/letra_t.shp b/data/shapes/title/letra_t.shp new file mode 100644 index 0000000..616b976 --- /dev/null +++ b/data/shapes/title/letra_t.shp @@ -0,0 +1,9 @@ +# letra_t.shp +# Generado automáticamente desde jailgames.svg +# Dimensiones: 137.50 x 100.00 px + +name: letra_t +scale: 1.0 +center: 68.75, 50.00 + +polyline: 0.00,25.00 0.00,0.00 137.50,0.00 137.50,25.00 87.50,25.00 87.50,100.00 50.00,100.00 50.00,25.00 0.00,25.00 diff --git a/jailgames.svg b/jailgames.svg deleted file mode 100644 index 8b69eb2..0000000 --- a/jailgames.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/sample.png b/sample.png deleted file mode 100644 index 8c394a2c03f32578da5959e681bea5b70833f629..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37413 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 SYz;(!*Wvx<`?B{s{rDe>^*vPp diff --git a/source/core/defaults.hpp b/source/core/defaults.hpp index e19a5df..0394862 100644 --- a/source/core/defaults.hpp +++ b/source/core/defaults.hpp @@ -178,53 +178,53 @@ constexpr const char* LASER = "laser_shoot.wav"; // Disparo namespace Enemies { // Pentagon (esquivador - zigzag evasion) namespace Pentagon { -constexpr float VELOCITAT = 35.0f; // px/s (slightly slower) -constexpr float CANVI_ANGLE_PROB = 0.20f; // 20% per wall hit (frequent zigzag) -constexpr float CANVI_ANGLE_MAX = 1.0f; // Max random angle change (rad) -constexpr float DROTACIO_MIN = 0.5f; // Min visual rotation (rad/s) -constexpr float DROTACIO_MAX = 2.5f; // Max visual rotation (rad/s) +constexpr float VELOCITAT = 35.0f; // px/s (slightly slower) +constexpr float CANVI_ANGLE_PROB = 0.20f; // 20% per wall hit (frequent zigzag) +constexpr float CANVI_ANGLE_MAX = 1.0f; // Max random angle change (rad) +constexpr float DROTACIO_MIN = 0.5f; // Min visual rotation (rad/s) +constexpr float DROTACIO_MAX = 2.5f; // Max visual rotation (rad/s) constexpr const char* SHAPE_FILE = "enemy_pentagon.shp"; } // namespace Pentagon // Quadrat (perseguidor - tracks player) namespace Quadrat { -constexpr float VELOCITAT = 40.0f; // px/s (medium speed) -constexpr float TRACKING_STRENGTH = 0.5f; // Interpolation toward player (0.0-1.0) -constexpr float TRACKING_INTERVAL = 1.0f; // Seconds between angle updates -constexpr float DROTACIO_MIN = 0.2f; // Slow rotation +constexpr float VELOCITAT = 40.0f; // px/s (medium speed) +constexpr float TRACKING_STRENGTH = 0.5f; // Interpolation toward player (0.0-1.0) +constexpr float TRACKING_INTERVAL = 1.0f; // Seconds between angle updates +constexpr float DROTACIO_MIN = 0.2f; // Slow rotation constexpr float DROTACIO_MAX = 1.0f; constexpr const char* SHAPE_FILE = "enemy_square.shp"; } // namespace Quadrat // Molinillo (agressiu - fast straight lines, proximity spin-up) namespace Molinillo { -constexpr float VELOCITAT = 50.0f; // px/s (fastest) -constexpr float CANVI_ANGLE_PROB = 0.05f; // 5% per wall hit (rare direction change) -constexpr float CANVI_ANGLE_MAX = 0.3f; // Small angle adjustments -constexpr float DROTACIO_MIN = 2.0f; // Base rotation (rad/s) +constexpr float VELOCITAT = 50.0f; // px/s (fastest) +constexpr float CANVI_ANGLE_PROB = 0.05f; // 5% per wall hit (rare direction change) +constexpr float CANVI_ANGLE_MAX = 0.3f; // Small angle adjustments +constexpr float DROTACIO_MIN = 2.0f; // Base rotation (rad/s) constexpr float DROTACIO_MAX = 4.0f; constexpr float DROTACIO_PROXIMITY_MULTIPLIER = 3.0f; // Spin-up multiplier when near ship -constexpr float PROXIMITY_DISTANCE = 100.0f; // Distance threshold (px) +constexpr float PROXIMITY_DISTANCE = 100.0f; // Distance threshold (px) constexpr const char* SHAPE_FILE = "enemy_pinwheel.shp"; } // namespace Molinillo // Animation parameters (shared) namespace Animation { // Palpitation -constexpr float PALPITACIO_TRIGGER_PROB = 0.01f; // 1% chance per second -constexpr float PALPITACIO_DURACIO_MIN = 1.0f; // Min duration (seconds) -constexpr float PALPITACIO_DURACIO_MAX = 3.0f; // Max duration (seconds) -constexpr float PALPITACIO_AMPLITUD_MIN = 0.08f; // Min scale variation -constexpr float PALPITACIO_AMPLITUD_MAX = 0.20f; // Max scale variation -constexpr float PALPITACIO_FREQ_MIN = 1.5f; // Min frequency (Hz) -constexpr float PALPITACIO_FREQ_MAX = 3.0f; // Max frequency (Hz) +constexpr float PALPITACIO_TRIGGER_PROB = 0.01f; // 1% chance per second +constexpr float PALPITACIO_DURACIO_MIN = 1.0f; // Min duration (seconds) +constexpr float PALPITACIO_DURACIO_MAX = 3.0f; // Max duration (seconds) +constexpr float PALPITACIO_AMPLITUD_MIN = 0.08f; // Min scale variation +constexpr float PALPITACIO_AMPLITUD_MAX = 0.20f; // Max scale variation +constexpr float PALPITACIO_FREQ_MIN = 1.5f; // Min frequency (Hz) +constexpr float PALPITACIO_FREQ_MAX = 3.0f; // Max frequency (Hz) // Rotation acceleration -constexpr float ROTACIO_ACCEL_TRIGGER_PROB = 0.005f; // 0.5% chance per second -constexpr float ROTACIO_ACCEL_DURACIO_MIN = 3.0f; // Min transition time -constexpr float ROTACIO_ACCEL_DURACIO_MAX = 8.0f; // Max transition time -constexpr float ROTACIO_ACCEL_MULTIPLIER_MIN = 0.5f; // Min speed multiplier -constexpr float ROTACIO_ACCEL_MULTIPLIER_MAX = 2.5f; // Max speed multiplier +constexpr float ROTACIO_ACCEL_TRIGGER_PROB = 0.005f; // 0.5% chance per second +constexpr float ROTACIO_ACCEL_DURACIO_MIN = 3.0f; // Min transition time +constexpr float ROTACIO_ACCEL_DURACIO_MAX = 8.0f; // Max transition time +constexpr float ROTACIO_ACCEL_MULTIPLIER_MIN = 0.5f; // Min speed multiplier +constexpr float ROTACIO_ACCEL_MULTIPLIER_MAX = 2.5f; // Max speed multiplier } // namespace Animation } // namespace Enemies } // namespace Defaults diff --git a/source/core/graphics/starfield.cpp b/source/core/graphics/starfield.cpp index ad97e0e..9c0e729 100644 --- a/source/core/graphics/starfield.cpp +++ b/source/core/graphics/starfield.cpp @@ -17,10 +17,10 @@ Starfield::Starfield(SDL_Renderer* renderer, const Punt& punt_fuga, const SDL_FRect& area, int densitat) - : renderer_(renderer) - , punt_fuga_(punt_fuga) - , area_(area) - , densitat_(densitat) { + : renderer_(renderer), + punt_fuga_(punt_fuga), + area_(area), + densitat_(densitat) { // Carregar forma d'estrella shape_estrella_ = std::make_shared("data/shapes/star.shp"); @@ -84,9 +84,9 @@ void Starfield::inicialitzar_estrella(Estrella& estrella) { // Verificar si una estrella està fora de l'àrea bool Starfield::fora_area(const Estrella& estrella) const { return (estrella.posicio.x < area_.x || - estrella.posicio.x > area_.x + area_.w || - estrella.posicio.y < area_.y || - estrella.posicio.y > area_.y + area_.h); + estrella.posicio.x > area_.x + area_.w || + estrella.posicio.y < area_.y || + estrella.posicio.y > area_.y + area_.h); } // Calcular escala dinàmica segons distància del centre @@ -96,7 +96,7 @@ float Starfield::calcular_escala(const Estrella& estrella) const { // Interpolació lineal basada en distància del centre // distancia_centre: 0.0 (centre) → 1.0 (vora) return capa.escala_min + - (capa.escala_max - capa.escala_min) * estrella.distancia_centre; + (capa.escala_max - capa.escala_min) * estrella.distancia_centre; } // Calcular brightness dinàmica segons distància del centre @@ -104,8 +104,8 @@ float Starfield::calcular_brightness(const Estrella& estrella) const { // Interpolació lineal: estrelles properes (vora) més brillants // distancia_centre: 0.0 (centre, llunyanes) → 1.0 (vora, properes) return Defaults::Brightness::STARFIELD_MIN + - (Defaults::Brightness::STARFIELD_MAX - Defaults::Brightness::STARFIELD_MIN) * - estrella.distancia_centre; + (Defaults::Brightness::STARFIELD_MAX - Defaults::Brightness::STARFIELD_MIN) * + estrella.distancia_centre; } // Actualitzar posicions de les estrelles diff --git a/source/core/graphics/starfield.hpp b/source/core/graphics/starfield.hpp index 40b5df4..4109503 100644 --- a/source/core/graphics/starfield.hpp +++ b/source/core/graphics/starfield.hpp @@ -15,66 +15,66 @@ namespace Graphics { // Configuració per cada capa de profunditat struct CapaConfig { - float velocitat_base; // Velocitat base d'aquesta capa (px/s) - float escala_min; // Escala mínima prop del centre - float escala_max; // Escala màxima al límit de pantalla - int num_estrelles; // Nombre d'estrelles en aquesta capa + float velocitat_base; // Velocitat base d'aquesta capa (px/s) + float escala_min; // Escala mínima prop del centre + float escala_max; // Escala màxima al límit de pantalla + int num_estrelles; // Nombre d'estrelles en aquesta capa }; // Classe Starfield - camp d'estrelles animat amb efecte de profunditat class Starfield { - public: - // Constructor - // - renderer: SDL renderer - // - punt_fuga: punt d'origen/fuga des d'on surten les estrelles - // - area: rectangle on actuen les estrelles (SDL_FRect) - // - densitat: nombre total d'estrelles (es divideix entre capes) - Starfield(SDL_Renderer* renderer, - const Punt& punt_fuga, - const SDL_FRect& area, - int densitat = 150); + public: + // Constructor + // - renderer: SDL renderer + // - punt_fuga: punt d'origen/fuga des d'on surten les estrelles + // - area: rectangle on actuen les estrelles (SDL_FRect) + // - densitat: nombre total d'estrelles (es divideix entre capes) + Starfield(SDL_Renderer* renderer, + const Punt& punt_fuga, + const SDL_FRect& area, + int densitat = 150); - // Actualitzar posicions de les estrelles - void actualitzar(float delta_time); + // Actualitzar posicions de les estrelles + void actualitzar(float delta_time); - // Dibuixar totes les estrelles - void dibuixar(); + // Dibuixar totes les estrelles + void dibuixar(); - // Setters per ajustar paràmetres en temps real - void set_punt_fuga(const Punt& punt) { punt_fuga_ = punt; } + // Setters per ajustar paràmetres en temps real + void set_punt_fuga(const Punt& punt) { punt_fuga_ = punt; } - private: - // Estructura interna per cada estrella - struct Estrella { - Punt posicio; // Posició actual - float angle; // Angle de moviment (radians) - float distancia_centre; // Distància normalitzada del centre (0.0-1.0) - int capa; // Índex de capa (0=lluny, 1=mitjà, 2=prop) - }; + private: + // Estructura interna per cada estrella + struct Estrella { + Punt posicio; // Posició actual + float angle; // Angle de moviment (radians) + float distancia_centre; // Distància normalitzada del centre (0.0-1.0) + int capa; // Índex de capa (0=lluny, 1=mitjà, 2=prop) + }; - // Inicialitzar una estrella (nova o regenerada) - void inicialitzar_estrella(Estrella& estrella); + // Inicialitzar una estrella (nova o regenerada) + void inicialitzar_estrella(Estrella& estrella); - // Verificar si una estrella està fora de l'àrea - bool fora_area(const Estrella& estrella) const; + // Verificar si una estrella està fora de l'àrea + bool fora_area(const Estrella& estrella) const; - // Calcular escala dinàmica segons distància del centre - float calcular_escala(const Estrella& estrella) const; + // Calcular escala dinàmica segons distància del centre + float calcular_escala(const Estrella& estrella) const; - // Calcular brightness dinàmica segons distància del centre - float calcular_brightness(const Estrella& estrella) const; + // Calcular brightness dinàmica segons distància del centre + float calcular_brightness(const Estrella& estrella) const; - // Dades - std::vector estrelles_; - std::vector capes_; // Configuració de les 3 capes - std::shared_ptr shape_estrella_; - SDL_Renderer* renderer_; + // Dades + std::vector estrelles_; + std::vector capes_; // Configuració de les 3 capes + std::shared_ptr shape_estrella_; + SDL_Renderer* renderer_; - // Configuració - Punt punt_fuga_; // Punt d'origen de les estrelles - SDL_FRect area_; // Àrea activa - float radi_max_; // Distància màxima del centre al límit de pantalla - int densitat_; // Nombre total d'estrelles + // Configuració + Punt punt_fuga_; // Punt d'origen de les estrelles + SDL_FRect area_; // Àrea activa + float radi_max_; // Distància màxima del centre al límit de pantalla + int densitat_; // Nombre total d'estrelles }; } // namespace Graphics diff --git a/source/core/rendering/line_renderer.cpp b/source/core/rendering/line_renderer.cpp index 12e584c..d802d31 100644 --- a/source/core/rendering/line_renderer.cpp +++ b/source/core/rendering/line_renderer.cpp @@ -58,8 +58,7 @@ bool linea(SDL_Renderer* renderer, int x1, int y1, int x2, int y2, bool dibuixar SDL_SetRenderDrawColor(renderer, color_final.r, color_final.g, color_final.b, 255); // Renderitzar amb coordenades físiques - SDL_RenderLine(renderer, static_cast(px1), static_cast(py1), - static_cast(px2), static_cast(py2)); + SDL_RenderLine(renderer, static_cast(px1), static_cast(py1), static_cast(px2), static_cast(py2)); } // Algorisme de Bresenham original (conservat per a futura detecció de diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 858ab8a..6afbd53 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -7,13 +7,13 @@ #include #include +#include "core/audio/audio.hpp" +#include "core/defaults.hpp" +#include "core/rendering/sdl_manager.hpp" #include "game/escenes/escena_joc.hpp" #include "game/escenes/escena_logo.hpp" #include "game/escenes/escena_titol.hpp" #include "game/options.hpp" -#include "core/audio/audio.hpp" -#include "core/defaults.hpp" -#include "core/rendering/sdl_manager.hpp" #include "gestor_escenes.hpp" #include "project.h" diff --git a/source/core/system/global_events.cpp b/source/core/system/global_events.cpp index 2ca8b85..26a801c 100644 --- a/source/core/system/global_events.cpp +++ b/source/core/system/global_events.cpp @@ -3,9 +3,9 @@ #include "global_events.hpp" +#include "core/input/mouse.hpp" #include "core/rendering/sdl_manager.hpp" #include "gestor_escenes.hpp" -#include "core/input/mouse.hpp" namespace GlobalEvents { diff --git a/source/game/entities/enemic.cpp b/source/game/entities/enemic.cpp index 6db99c1..fe62ed8 100644 --- a/source/game/entities/enemic.cpp +++ b/source/game/entities/enemic.cpp @@ -150,7 +150,10 @@ void Enemic::comportament_pentagon(float delta_time) { // Obtenir límits segurs float min_x, max_x, min_y, max_y; Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS, - min_x, max_x, min_y, max_y); + min_x, + max_x, + min_y, + max_y); // Zigzag: canvi d'angle més freqüent en tocar límits if (new_y >= min_y && new_y <= max_y) { @@ -159,7 +162,7 @@ void Enemic::comportament_pentagon(float delta_time) { // Probabilitat més alta de canvi d'angle if (static_cast(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) { float rand_angle = (static_cast(std::rand()) / RAND_MAX) * - Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX; + Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX; angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle; } } @@ -169,7 +172,7 @@ void Enemic::comportament_pentagon(float delta_time) { } else { if (static_cast(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) { float rand_angle = (static_cast(std::rand()) / RAND_MAX) * - Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX; + Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX; angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle; } } @@ -214,7 +217,10 @@ void Enemic::comportament_quadrat(float delta_time) { // Obtenir límits segurs float min_x, max_x, min_y, max_y; Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS, - min_x, max_x, min_y, max_y); + min_x, + max_x, + min_y, + max_y); // Bounce on walls (simple reflection) if (new_y >= min_y && new_y <= max_y) { @@ -260,7 +266,10 @@ void Enemic::comportament_molinillo(float delta_time) { // Obtenir límits segurs float min_x, max_x, min_y, max_y; Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS, - min_x, max_x, min_y, max_y); + min_x, + max_x, + min_y, + max_y); // Rare angle changes on wall hits if (new_y >= min_y && new_y <= max_y) { @@ -268,7 +277,7 @@ void Enemic::comportament_molinillo(float delta_time) { } else { if (static_cast(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) { float rand_angle = (static_cast(std::rand()) / RAND_MAX) * - Defaults::Enemies::Molinillo::CANVI_ANGLE_MAX; + Defaults::Enemies::Molinillo::CANVI_ANGLE_MAX; angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle; } } @@ -278,7 +287,7 @@ void Enemic::comportament_molinillo(float delta_time) { } else { if (static_cast(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) { float rand_angle = (static_cast(std::rand()) / RAND_MAX) * - Defaults::Enemies::Molinillo::CANVI_ANGLE_MAX; + Defaults::Enemies::Molinillo::CANVI_ANGLE_MAX; angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle; } } @@ -313,17 +322,17 @@ void Enemic::actualitzar_palpitacio(float delta_time) { // Randomize parameters float freq_range = Defaults::Enemies::Animation::PALPITACIO_FREQ_MAX - - Defaults::Enemies::Animation::PALPITACIO_FREQ_MIN; + Defaults::Enemies::Animation::PALPITACIO_FREQ_MIN; animacio_.palpitacio_frequencia = Defaults::Enemies::Animation::PALPITACIO_FREQ_MIN + (static_cast(std::rand()) / RAND_MAX) * freq_range; float amp_range = Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MAX - - Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MIN; + Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MIN; animacio_.palpitacio_amplitud = Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MIN + (static_cast(std::rand()) / RAND_MAX) * amp_range; float dur_range = Defaults::Enemies::Animation::PALPITACIO_DURACIO_MAX - - Defaults::Enemies::Animation::PALPITACIO_DURACIO_MIN; + Defaults::Enemies::Animation::PALPITACIO_DURACIO_MIN; animacio_.palpitacio_temps_restant = Defaults::Enemies::Animation::PALPITACIO_DURACIO_MIN + (static_cast(std::rand()) / RAND_MAX) * dur_range; } @@ -360,7 +369,7 @@ void Enemic::actualitzar_rotacio_accelerada(float delta_time) { // Randomize target speed (multiplier * base) float mult_range = Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MAX - - Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MIN; + Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MIN; float multiplier = Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MIN + (static_cast(std::rand()) / RAND_MAX) * mult_range; @@ -368,7 +377,7 @@ void Enemic::actualitzar_rotacio_accelerada(float delta_time) { // Randomize duration float dur_range = Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MAX - - Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MIN; + Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MIN; animacio_.drotacio_duracio = Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MIN + (static_cast(std::rand()) / RAND_MAX) * dur_range; } diff --git a/source/game/entities/enemic.hpp b/source/game/entities/enemic.hpp index af64d59..b9e3dcd 100644 --- a/source/game/entities/enemic.hpp +++ b/source/game/entities/enemic.hpp @@ -12,25 +12,25 @@ // Tipus d'enemic enum class TipusEnemic : uint8_t { - PENTAGON = 0, // Pentàgon esquivador (zigzag) - QUADRAT = 1, // Quadrat perseguidor (tracks ship) - MOLINILLO = 2 // Molinillo agressiu (fast, spinning) + PENTAGON = 0, // Pentàgon esquivador (zigzag) + QUADRAT = 1, // Quadrat perseguidor (tracks ship) + MOLINILLO = 2 // Molinillo agressiu (fast, spinning) }; // Estat d'animació (palpitació i rotació accelerada) struct AnimacioEnemic { - // Palpitation (breathing effect) - bool palpitacio_activa = false; - float palpitacio_fase = 0.0f; // Phase in cycle (0.0-2π) - float palpitacio_frequencia = 2.0f; // Hz (cycles per second) - float palpitacio_amplitud = 0.15f; // Scale variation (±15%) - float palpitacio_temps_restant = 0.0f; // Time remaining (seconds) + // Palpitation (breathing effect) + bool palpitacio_activa = false; + float palpitacio_fase = 0.0f; // Phase in cycle (0.0-2π) + float palpitacio_frequencia = 2.0f; // Hz (cycles per second) + float palpitacio_amplitud = 0.15f; // Scale variation (±15%) + float palpitacio_temps_restant = 0.0f; // Time remaining (seconds) - // Rotation acceleration (long-term spin modulation) - float drotacio_base = 0.0f; // Base rotation speed (rad/s) - float drotacio_objetivo = 0.0f; // Target rotation speed (rad/s) - float drotacio_t = 0.0f; // Interpolation progress (0.0-1.0) - float drotacio_duracio = 0.0f; // Duration of transition (seconds) + // Rotation acceleration (long-term spin modulation) + float drotacio_base = 0.0f; // Base rotation speed (rad/s) + float drotacio_objetivo = 0.0f; // Target rotation speed (rad/s) + float drotacio_t = 0.0f; // Interpolation progress (0.0-1.0) + float drotacio_duracio = 0.0f; // Duration of transition (seconds) }; class Enemic { @@ -60,10 +60,10 @@ class Enemic { // [NUEVO] Estat de la instància (separat de la geometria) Punt centre_; - float angle_; // Angle de moviment + float angle_; // Angle de moviment float velocitat_; - float drotacio_; // Delta rotació visual (rad/s) - float rotacio_; // Rotació visual acumulada + float drotacio_; // Delta rotació visual (rad/s) + float rotacio_; // Rotació visual acumulada bool esta_; float brightness_; // Factor de brillantor (0.0-1.0) @@ -74,8 +74,8 @@ class Enemic { AnimacioEnemic animacio_; // [NEW] Behavior state (type-specific) - float tracking_timer_; // For Quadrat: time since last angle update - const Punt* ship_position_; // Pointer to ship position (for tracking) + float tracking_timer_; // For Quadrat: time since last angle update + const Punt* ship_position_; // Pointer to ship position (for tracking) // [EXISTING] Private methods void mou(float delta_time); diff --git a/source/game/entities/nau.hpp b/source/game/entities/nau.hpp index 1bb2b9e..3231147 100644 --- a/source/game/entities/nau.hpp +++ b/source/game/entities/nau.hpp @@ -39,8 +39,8 @@ class Nau { // [NUEVO] Estat de la instància (separat de la geometria) Punt centre_; - float angle_; // Angle d'orientació - float velocitat_; // Velocitat (px/s) + float angle_; // Angle d'orientació + float velocitat_; // Velocitat (px/s) bool esta_tocada_; float brightness_; // Factor de brillantor (0.0-1.0) diff --git a/source/game/escenes/escena_joc.cpp b/source/game/escenes/escena_joc.cpp index 0fbcb85..cea0ca9 100644 --- a/source/game/escenes/escena_joc.cpp +++ b/source/game/escenes/escena_joc.cpp @@ -362,10 +362,10 @@ void EscenaJoc::tocado() { float ship_angle = nau_.get_angle(); debris_manager_.explotar( - nau_.get_forma(), // Ship shape (3 lines) - ship_pos, // Center position - ship_angle, // Ship orientation - 1.0f, // Normal scale + nau_.get_forma(), // Ship shape (3 lines) + ship_pos, // Center position + ship_angle, // Ship orientation + 1.0f, // Normal scale Defaults::Physics::Debris::VELOCITAT_BASE // 80 px/s ); diff --git a/source/game/escenes/escena_joc.hpp b/source/game/escenes/escena_joc.hpp index 3791d91..a87e0df 100644 --- a/source/game/escenes/escena_joc.hpp +++ b/source/game/escenes/escena_joc.hpp @@ -10,14 +10,14 @@ #include #include -#include "core/graphics/vector_text.hpp" -#include "core/rendering/sdl_manager.hpp" -#include "core/types.hpp" #include "../constants.hpp" #include "../effects/debris_manager.hpp" #include "../entities/bala.hpp" #include "../entities/enemic.hpp" #include "../entities/nau.hpp" +#include "core/graphics/vector_text.hpp" +#include "core/rendering/sdl_manager.hpp" +#include "core/types.hpp" // Classe principal del joc (escena) class EscenaJoc { @@ -45,10 +45,10 @@ class EscenaJoc { float itocado_; // Death timer (seconds) // Lives and game over system - int num_vides_; // Current lives count - bool game_over_; // Game over state flag - float game_over_timer_; // Countdown timer for auto-return (seconds) - Punt punt_spawn_; // Configurable spawn point + int num_vides_; // Current lives count + bool game_over_; // Game over state flag + float game_over_timer_; // Countdown timer for auto-return (seconds) + Punt punt_spawn_; // Configurable spawn point // Text vectorial Graphics::VectorText text_; diff --git a/source/game/escenes/escena_logo.hpp b/source/game/escenes/escena_logo.hpp index 24c0386..345c311 100644 --- a/source/game/escenes/escena_logo.hpp +++ b/source/game/escenes/escena_logo.hpp @@ -9,11 +9,11 @@ #include #include +#include "../effects/debris_manager.hpp" +#include "core/defaults.hpp" #include "core/graphics/shape.hpp" #include "core/rendering/sdl_manager.hpp" #include "core/types.hpp" -#include "../effects/debris_manager.hpp" -#include "core/defaults.hpp" class EscenaLogo { public: diff --git a/source/game/escenes/escena_titol.cpp b/source/game/escenes/escena_titol.cpp index 4b22c79..f6c884f 100644 --- a/source/game/escenes/escena_titol.cpp +++ b/source/game/escenes/escena_titol.cpp @@ -3,11 +3,14 @@ #include "escena_titol.hpp" +#include #include #include #include "core/audio/audio.hpp" +#include "core/graphics/shape_loader.hpp" #include "core/input/mouse.hpp" +#include "core/rendering/shape_renderer.hpp" #include "core/system/gestor_escenes.hpp" #include "core/system/global_events.hpp" #include "project.h" @@ -25,7 +28,8 @@ EscenaTitol::EscenaTitol(SDLManager& sdl) Defaults::Game::HEIGHT / 2.0f}; SDL_FRect area_completa{ - 0, 0, + 0, + 0, static_cast(Defaults::Game::WIDTH), static_cast(Defaults::Game::HEIGHT)}; @@ -35,6 +39,146 @@ EscenaTitol::EscenaTitol(SDLManager& sdl) area_completa, 150 // densitat: 150 estrelles (50 per capa) ); + + // Inicialitzar lletres del títol "ORNI ATTACK!" + inicialitzar_titol(); +} + +void EscenaTitol::inicialitzar_titol() { + using namespace Graphics; + + // === LÍNIA 1: "ORNI" === + std::vector fitxers_orni = { + "title/letra_o.shp", + "title/letra_r.shp", + "title/letra_n.shp", + "title/letra_i.shp"}; + + // Pas 1: Carregar formes i calcular amplades per "ORNI" + float ancho_total_orni = 0.0f; + + for (const auto& fitxer : fitxers_orni) { + auto forma = ShapeLoader::load(fitxer); + if (!forma || !forma->es_valida()) { + std::cerr << "[EscenaTitol] Error carregant " << fitxer << std::endl; + continue; + } + + // Calcular bounding box de la forma (trobar ancho i altura) + float min_x = FLT_MAX; + float max_x = -FLT_MAX; + float min_y = FLT_MAX; + float max_y = -FLT_MAX; + + for (const auto& prim : forma->get_primitives()) { + for (const auto& punt : prim.points) { + min_x = std::min(min_x, punt.x); + max_x = std::max(max_x, punt.x); + min_y = std::min(min_y, punt.y); + max_y = std::max(max_y, punt.y); + } + } + + float ancho_sin_escalar = max_x - min_x; + float altura_sin_escalar = max_y - min_y; + + // Escalar ancho, altura i offset amb ESCALA_TITULO + float ancho = ancho_sin_escalar * ESCALA_TITULO; + float altura = altura_sin_escalar * ESCALA_TITULO; + float offset_centre = (forma->get_centre().x - min_x) * ESCALA_TITULO; + + lletres_orni_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre}); + + ancho_total_orni += ancho; + } + + // Afegir espaiat entre lletres + ancho_total_orni += ESPAI_ENTRE_LLETRES * (lletres_orni_.size() - 1); + + // Calcular posició inicial (centrat horitzontal) per "ORNI" + float x_inicial_orni = (Defaults::Game::WIDTH - ancho_total_orni) / 2.0f; + float x_actual = x_inicial_orni; + + for (auto& lletra : lletres_orni_) { + lletra.posicio.x = x_actual + lletra.offset_centre; + lletra.posicio.y = Y_ORNI; + x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; + } + + std::cout << "[EscenaTitol] Línia 1 (ORNI): " << lletres_orni_.size() + << " lletres, ancho total: " << ancho_total_orni << " px\n"; + + // === Calcular posició Y dinàmica per "ATTACK!" === + // Totes les lletres ORNI tenen la mateixa altura, utilitzem la primera + float altura_orni = lletres_orni_.empty() ? 50.0f : lletres_orni_[0].altura; + y_attack_dinamica_ = Y_ORNI + altura_orni + SEPARACION_LINEAS; + + std::cout << "[EscenaTitol] Altura ORNI: " << altura_orni + << " px, Y_ATTACK dinàmica: " << y_attack_dinamica_ << " px\n"; + + // === LÍNIA 2: "ATTACK!" === + std::vector fitxers_attack = { + "title/letra_a.shp", + "title/letra_t.shp", + "title/letra_t.shp", // T repetida + "title/letra_a.shp", // A repetida + "title/letra_c.shp", + "title/letra_k.shp", + "title/letra_exclamacion.shp"}; + + // Pas 1: Carregar formes i calcular amplades per "ATTACK!" + float ancho_total_attack = 0.0f; + + for (const auto& fitxer : fitxers_attack) { + auto forma = ShapeLoader::load(fitxer); + if (!forma || !forma->es_valida()) { + std::cerr << "[EscenaTitol] Error carregant " << fitxer << std::endl; + continue; + } + + // Calcular bounding box de la forma (trobar ancho i altura) + float min_x = FLT_MAX; + float max_x = -FLT_MAX; + float min_y = FLT_MAX; + float max_y = -FLT_MAX; + + for (const auto& prim : forma->get_primitives()) { + for (const auto& punt : prim.points) { + min_x = std::min(min_x, punt.x); + max_x = std::max(max_x, punt.x); + min_y = std::min(min_y, punt.y); + max_y = std::max(max_y, punt.y); + } + } + + float ancho_sin_escalar = max_x - min_x; + float altura_sin_escalar = max_y - min_y; + + // Escalar ancho, altura i offset amb ESCALA_TITULO + float ancho = ancho_sin_escalar * ESCALA_TITULO; + float altura = altura_sin_escalar * ESCALA_TITULO; + float offset_centre = (forma->get_centre().x - min_x) * ESCALA_TITULO; + + lletres_attack_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre}); + + ancho_total_attack += ancho; + } + + // Afegir espaiat entre lletres + ancho_total_attack += ESPAI_ENTRE_LLETRES * (lletres_attack_.size() - 1); + + // Calcular posició inicial (centrat horitzontal) per "ATTACK!" + float x_inicial_attack = (Defaults::Game::WIDTH - ancho_total_attack) / 2.0f; + x_actual = x_inicial_attack; + + for (auto& lletra : lletres_attack_) { + lletra.posicio.x = x_actual + lletra.offset_centre; + lletra.posicio.y = y_attack_dinamica_; // Usar posició dinàmica + x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; + } + + std::cout << "[EscenaTitol] Línia 2 (ATTACK!): " << lletres_attack_.size() + << " lletres, ancho total: " << ancho_total_attack << " px\n"; } void EscenaTitol::executar() { @@ -129,22 +273,51 @@ void EscenaTitol::dibuixar() { return; } - // Estat MAIN: Dibuixar text de títol i copyright (sobre el starfield) + // Estat MAIN: Dibuixar títol i text (sobre el starfield) if (estat_actual_ == EstatTitol::MAIN) { - // Text principal centrat (vertical i horitzontalment) + // === Dibuixar lletres del títol "ORNI ATTACK!" === + + // Dibuixar "ORNI" (línia 1) + for (const auto& lletra : lletres_orni_) { + Rendering::render_shape( + sdl_.obte_renderer(), + lletra.forma, + lletra.posicio, + 0.0f, // sense rotació + ESCALA_TITULO, // escala 80% + true, // dibuixar + 1.0f // progrés complet (totalment visible) + ); + } + + // Dibuixar "ATTACK!" (línia 2) + for (const auto& lletra : lletres_attack_) { + Rendering::render_shape( + sdl_.obte_renderer(), + lletra.forma, + lletra.posicio, + 0.0f, // sense rotació + ESCALA_TITULO, // escala 80% + true, // dibuixar + 1.0f // progrés complet (totalment visible) + ); + } + + // === Text "PRESS BUTTON TO PLAY" (a sota del títol) === const std::string main_text = "PRESS BUTTON TO PLAY"; const float escala_main = 1.0f; const float spacing = 2.0f; float text_width = text_.get_text_width(main_text, escala_main, spacing); - float text_height = text_.get_text_height(escala_main); float x_center = (Defaults::Game::WIDTH - text_width) / 2.0f; - float y_center = (Defaults::Game::HEIGHT - text_height) / 2.0f; + // Usar posició dinàmica: ATTACK + altura lletres + separació + float altura_attack = lletres_attack_.empty() ? 50.0f : lletres_attack_[0].altura; + float y_center = y_attack_dinamica_ + altura_attack + 70.0f; // 70px sota "ATTACK!" text_.render(main_text, Punt{x_center, y_center}, escala_main, spacing); - // Copyright a la part inferior (centrat horitzontalment) + // === Copyright a la part inferior (centrat horitzontalment) === // Convert to uppercase since VectorText only supports A-Z std::string copyright = Project::COPYRIGHT; for (char& c : copyright) { @@ -168,7 +341,6 @@ void EscenaTitol::processar_events(const SDL_Event& event) { // Qualsevol tecla o clic de ratolí if (event.type == SDL_EVENT_KEY_DOWN || event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { - switch (estat_actual_) { case EstatTitol::INIT: // Saltar a MAIN diff --git a/source/game/escenes/escena_titol.hpp b/source/game/escenes/escena_titol.hpp index 8fd38ac..a1ad2fe 100644 --- a/source/game/escenes/escena_titol.hpp +++ b/source/game/escenes/escena_titol.hpp @@ -7,11 +7,14 @@ #include #include +#include +#include "core/defaults.hpp" +#include "core/graphics/shape.hpp" #include "core/graphics/starfield.hpp" #include "core/graphics/vector_text.hpp" #include "core/rendering/sdl_manager.hpp" -#include "core/defaults.hpp" +#include "core/types.hpp" class EscenaTitol { public: @@ -25,17 +28,36 @@ class EscenaTitol { MAIN // Pantalla de títol amb text }; + // Estructura per emmagatzemar informació de cada lletra del títol + struct LetraLogo { + std::shared_ptr forma; // Forma vectorial de la lletra + Punt posicio; // Posició en pantalla + float ancho; // Amplada escalada + float altura; // Altura escalada + float offset_centre; // Offset del centre per posicionament + }; + SDLManager& sdl_; - Graphics::VectorText text_; // Sistema de text vectorial + Graphics::VectorText text_; // Sistema de text vectorial std::unique_ptr starfield_; // Camp d'estrelles de fons - EstatTitol estat_actual_; // Estat actual de la màquina - float temps_acumulat_; // Temps acumulat per l'estat INIT + EstatTitol estat_actual_; // Estat actual de la màquina + float temps_acumulat_; // Temps acumulat per l'estat INIT + + // Lletres del títol "ORNI ATTACK!" + std::vector lletres_orni_; // Lletres de "ORNI" (línia 1) + std::vector lletres_attack_; // Lletres de "ATTACK!" (línia 2) + float y_attack_dinamica_; // Posició Y calculada dinàmicament per "ATTACK!" // Constants - static constexpr float DURACIO_INIT = 2.0f; // Duració de l'estat INIT (2 segons) + static constexpr float DURACIO_INIT = 2.0f; // Duració de l'estat INIT (2 segons) + static constexpr float ESCALA_TITULO = 0.6f; // Escala per les lletres del títol (50%) + static constexpr float ESPAI_ENTRE_LLETRES = 10.0f; // Espai entre lletres + static constexpr float Y_ORNI = 150.0f; // Posició Y de "ORNI" + static constexpr float SEPARACION_LINEAS = 10.0f; // Separació entre "ORNI" i "ATTACK!" (0.0f = pegades) // Mètodes privats void actualitzar(float delta_time); void dibuixar(); void processar_events(const SDL_Event& event); + void inicialitzar_titol(); // Carrega i posiciona les lletres del títol }; diff --git a/tools/svg_to_shp.py b/tools/svg_to_shp.py index 7404e7c..e02c108 100755 --- a/tools/svg_to_shp.py +++ b/tools/svg_to_shp.py @@ -44,8 +44,9 @@ def apply_transform(punto, matrix): def parse_svg_path(d_attr): """ - Convierte comandos SVG path (M y L) a lista de puntos [(x, y), ...] - Ejemplo: "M896,1693L896,1531.23L1219.53,1531.23..." → [(896, 1693), (896, 1531.23), ...] + Convierte comandos SVG path (M y L) a lista de polylines separadas. + Cada comando M inicia una nueva polyline. + Retorna: lista de listas de puntos [[(x,y), ...], [(x,y), ...], ...] """ # Reemplazar comas por espacios para facilitar parsing d_attr = d_attr.replace(',', ' ') @@ -55,7 +56,9 @@ def parse_svg_path(d_attr): d_attr = re.sub(r'([ML])', r'|\1', d_attr) commands = [c.strip() for c in d_attr.split('|') if c.strip()] - points = [] + polylines = [] # Lista de polylines + current_polyline = [] # Polyline actual + for cmd in commands: if not cmd: continue @@ -70,18 +73,29 @@ def parse_svg_path(d_attr): # Parsear pares de coordenadas coords = coords_str.split() + # Si es comando M (MoveTo), empezar nueva polyline + if cmd_letter == 'M': + # Guardar polyline anterior si tiene puntos + if current_polyline: + polylines.append(current_polyline) + current_polyline = [] + # Procesar en pares (x, y) i = 0 while i < len(coords) - 1: try: x = float(coords[i]) y = float(coords[i + 1]) - points.append((x, y)) + current_polyline.append((x, y)) i += 2 except (ValueError, IndexError): i += 1 - return points + # No olvidar la última polyline + if current_polyline: + polylines.append(current_polyline) + + return polylines def rect_to_points(rect_elem): @@ -103,11 +117,22 @@ def rect_to_points(rect_elem): ] -def calc_bounding_box(puntos): +def calc_bounding_box(polylines): """ - Calcula bounding box de una lista de puntos + Calcula bounding box de una o varias polylines + polylines puede ser: + - lista de puntos [(x,y), ...] + - lista de polylines [[(x,y), ...], [(x,y), ...]] Retorna: (min_x, max_x, min_y, max_y, ancho, alto) """ + # Aplanar si es lista de polylines + puntos = [] + if polylines and isinstance(polylines[0], list): + for polyline in polylines: + puntos.extend(polyline) + else: + puntos = polylines + if not puntos: return (0, 0, 0, 0, 0, 0) @@ -125,17 +150,18 @@ def calc_bounding_box(puntos): return (min_x, max_x, min_y, max_y, ancho, alto) -def normalizar_letra(nombre, puntos, altura_objetivo=100.0): +def normalizar_letra(nombre, polylines, altura_objetivo=100.0): """ Escala y traslada letra para que tenga altura_objetivo pixels y esté centrada en origen (0, 0) en esquina superior izquierda - Retorna: dict con puntos normalizados, centro, ancho, alto + polylines: lista de polylines [[(x,y), ...], [(x,y), ...]] + Retorna: dict con polylines normalizadas, centro, ancho, alto """ - if not puntos: + if not polylines: return None - min_x, max_x, min_y, max_y, ancho, alto = calc_bounding_box(puntos) + min_x, max_x, min_y, max_y, ancho, alto = calc_bounding_box(polylines) if alto == 0: print(f" [WARN] Letra {nombre}: altura cero") @@ -144,14 +170,26 @@ def normalizar_letra(nombre, puntos, altura_objetivo=100.0): # Factor de escala basado en altura escala = altura_objetivo / alto - # Normalizar puntos: + # Normalizar cada polyline: # 1. Trasladar a origen (restar min_x, min_y) # 2. Aplicar escala - puntos_norm = [] - for x, y in puntos: - x_norm = (x - min_x) * escala - y_norm = (y - min_y) * escala - puntos_norm.append((x_norm, y_norm)) + # 3. Cerrar polyline (último punto = primer punto) + polylines_norm = [] + total_puntos = 0 + + for polyline in polylines: + polyline_norm = [] + for x, y in polyline: + x_norm = (x - min_x) * escala + y_norm = (y - min_y) * escala + polyline_norm.append((x_norm, y_norm)) + + # Cerrar polyline si no está cerrada + if polyline_norm and polyline_norm[0] != polyline_norm[-1]: + polyline_norm.append(polyline_norm[0]) + + polylines_norm.append(polyline_norm) + total_puntos += len(polyline_norm) # Calcular dimensiones finales ancho_norm = ancho * escala @@ -162,10 +200,11 @@ def normalizar_letra(nombre, puntos, altura_objetivo=100.0): return { 'nombre': nombre, - 'puntos': puntos_norm, + 'polylines': polylines_norm, 'centro': centro, 'ancho': ancho_norm, - 'alto': alto_norm + 'alto': alto_norm, + 'total_puntos': total_puntos } @@ -178,6 +217,7 @@ def generar_shp(letra_norm, output_dir): scale: 1.0 center: cx, cy polyline: x1,y1 x2,y2 x3,y3 ... + polyline: x1,y1 x2,y2 x3,y3 ... (si hay múltiples formas) """ if not letra_norm: return @@ -198,13 +238,13 @@ def generar_shp(letra_norm, output_dir): f.write(f"center: {letra_norm['centro'][0]:.2f}, {letra_norm['centro'][1]:.2f}\n") f.write(f"\n") - # Polyline con todos los puntos - f.write("polyline: ") - puntos_str = " ".join([f"{x:.2f},{y:.2f}" for x, y in letra_norm['puntos']]) - f.write(puntos_str) - f.write("\n") + # Generar una línea polyline por cada forma + for polyline in letra_norm['polylines']: + puntos_str = " ".join([f"{x:.2f},{y:.2f}" for x, y in polyline]) + f.write(f"polyline: {puntos_str}\n") - print(f" ✓ {nombre_archivo:20} ({len(letra_norm['puntos']):3} puntos, " + print(f" ✓ {nombre_archivo:20} ({len(letra_norm['polylines'])} formas, " + f"{letra_norm['total_puntos']:3} puntos, " f"{letra_norm['ancho']:6.2f} x {letra_norm['alto']:6.2f} px)") @@ -232,14 +272,18 @@ def parse_svg(filepath): print(f"[INFO] Transform matrix: {transform_matrix}") - # Extraer paths y rects - paths = group.findall('svg:path', ns) - rects = group.findall('svg:rect', ns) + # Extraer paths y rects (buscar recursivamente en todos los descendientes) + paths = group.findall('.//svg:path', ns) + rects = group.findall('.//svg:rect', ns) print(f"[INFO] Encontrados {len(paths)} paths y {len(rects)} rects") - # Nombres de las letras para paths (sin I que es un rect) - nombres_paths = ['J', 'A', 'L', 'G', 'A', 'M', 'E', 'S'] + # Nombres de las letras para "ORNI ATTACK!" + # Grupo 1 (top): O, R, N, I (3 paths + 1 rect) + # Grupo 2 (bottom): A, T, T, A, C, K, ! (6 paths + 1 path para !) + # Total: 9 paths + 1 rect + # Asumiendo orden de aparición en SVG: + nombres_paths = ['O', 'R', 'N', 'A', 'T', 'T', 'A', 'C', 'K', 'EXCLAMACION'] letras = [] @@ -252,15 +296,18 @@ def parse_svg(filepath): if not d_attr: continue - # Parsear puntos del path - puntos = parse_svg_path(d_attr) + # Parsear polylines del path (ahora retorna lista de polylines) + polylines = parse_svg_path(d_attr) - # Aplicar transformación - puntos = [apply_transform(p, transform_matrix) for p in puntos] + # Aplicar transformación a cada polyline + polylines_transformed = [] + for polyline in polylines: + polyline_transformed = [apply_transform(p, transform_matrix) for p in polyline] + polylines_transformed.append(polyline_transformed) letras.append({ 'nombre': nombres_paths[i], - 'puntos': puntos + 'polylines': polylines_transformed }) # Procesar rects (la letra I es un rect) @@ -268,11 +315,12 @@ def parse_svg(filepath): puntos = rect_to_points(rect) # Aplicar transformación - puntos = [apply_transform(p, transform_matrix) for p in puntos] + puntos_transformed = [apply_transform(p, transform_matrix) for p in puntos] + # Rect es una sola polyline letras.append({ 'nombre': 'I', - 'puntos': puntos + 'polylines': [puntos_transformed] }) return letras @@ -326,7 +374,7 @@ def main(): for nombre in sorted(letras_unicas.keys()): letra = letras_unicas[nombre] - letra_norm = normalizar_letra(nombre, letra['puntos'], altura_objetivo=100.0) + letra_norm = normalizar_letra(nombre, letra['polylines'], altura_objetivo=100.0) if letra_norm: generar_shp(letra_norm, output_dir)