diff --git a/Asteroids.ico b/Asteroids.ico
deleted file mode 100755
index ce40682..0000000
Binary files a/Asteroids.ico and /dev/null differ
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a8e9125..eef6346 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -35,6 +35,7 @@ configure_file(${CMAKE_SOURCE_DIR}/source/project.h.in ${CMAKE_BINARY_DIR}/proje
set(APP_SOURCES
source/main.cpp
source/core/system/director.cpp
+ source/core/system/global_events.cpp
source/core/rendering/sdl_manager.cpp
source/core/rendering/line_renderer.cpp
source/core/rendering/color_oscillator.cpp
diff --git a/Makefile b/Makefile
index bd4f719..5522d38 100644
--- a/Makefile
+++ b/Makefile
@@ -35,6 +35,7 @@ endif
APP_SOURCES := \
source/main.cpp \
source/core/system/director.cpp \
+ source/core/system/global_events.cpp \
source/core/rendering/sdl_manager.cpp \
source/core/rendering/line_renderer.cpp \
source/core/rendering/color_oscillator.cpp \
diff --git a/asteroids b/asteroids
deleted file mode 100755
index 9f0079b..0000000
Binary files a/asteroids and /dev/null differ
diff --git a/data/shapes/letra_a.shp b/data/shapes/letra_a.shp
new file mode 100644
index 0000000..2f6c379
--- /dev/null
+++ b/data/shapes/letra_a.shp
@@ -0,0 +1,9 @@
+# letra_a.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 71.43 x 100.00 px
+
+name: letra_a
+scale: 1.0
+center: 35.71, 50.00
+
+polyline: 28.57,71.43 28.57,100.00 0.00,100.00 0.00,14.29 14.29,14.29 14.29,0.00 57.14,0.00 57.14,14.29 71.43,14.29 71.43,100.00 42.86,100.00 42.86,71.43 28.57,14.29 28.57,57.14 42.86,57.14 42.86,14.29
diff --git a/data/shapes/letra_e.shp b/data/shapes/letra_e.shp
new file mode 100644
index 0000000..d0fd9f6
--- /dev/null
+++ b/data/shapes/letra_e.shp
@@ -0,0 +1,9 @@
+# letra_e.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 71.43 x 100.00 px
+
+name: letra_e
+scale: 1.0
+center: 35.71, 50.00
+
+polyline: 57.14,28.57 57.14,42.86 28.57,42.86 28.57,85.71 71.43,85.71 71.43,100.00 0.00,100.00 0.00,0.00 71.43,0.00 71.43,14.29 28.57,14.29 28.57,28.57
diff --git a/data/shapes/letra_g.shp b/data/shapes/letra_g.shp
new file mode 100644
index 0000000..c3940eb
--- /dev/null
+++ b/data/shapes/letra_g.shp
@@ -0,0 +1,9 @@
+# letra_g.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 71.43 x 100.00 px
+
+name: letra_g
+scale: 1.0
+center: 35.71, 50.00
+
+polyline: 14.29,0.00 57.14,0.00 57.14,14.29 28.57,14.29 28.57,85.71 42.86,85.71 42.86,42.86 71.43,42.86 71.43,100.00 14.29,100.00 14.29,85.71 0.00,85.71 0.00,14.29 14.29,14.29
diff --git a/data/shapes/letra_i.shp b/data/shapes/letra_i.shp
new file mode 100644
index 0000000..453939f
--- /dev/null
+++ b/data/shapes/letra_i.shp
@@ -0,0 +1,9 @@
+# letra_i.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 28.57 x 100.00 px
+
+name: letra_i
+scale: 1.0
+center: 14.29, 50.00
+
+polyline: 0.00,0.00 28.57,0.00 28.57,100.00 0.00,100.00 0.00,0.00
diff --git a/data/shapes/letra_j.shp b/data/shapes/letra_j.shp
new file mode 100644
index 0000000..1e81cf8
--- /dev/null
+++ b/data/shapes/letra_j.shp
@@ -0,0 +1,9 @@
+# letra_j.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 57.14 x 100.00 px
+
+name: letra_j
+scale: 1.0
+center: 28.57, 50.00
+
+polyline: 0.00,100.00 0.00,85.71 28.57,85.71 28.57,0.00 57.14,0.00 57.14,85.71 42.86,85.71 42.86,100.00
diff --git a/data/shapes/letra_l.shp b/data/shapes/letra_l.shp
new file mode 100644
index 0000000..ab0a35c
--- /dev/null
+++ b/data/shapes/letra_l.shp
@@ -0,0 +1,9 @@
+# letra_l.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 57.14 x 100.00 px
+
+name: letra_l
+scale: 1.0
+center: 28.57, 50.00
+
+polyline: 0.00,0.00 28.57,0.00 28.57,85.71 57.14,85.71 57.14,100.00 0.00,100.00
diff --git a/data/shapes/letra_m.shp b/data/shapes/letra_m.shp
new file mode 100644
index 0000000..93ff5c3
--- /dev/null
+++ b/data/shapes/letra_m.shp
@@ -0,0 +1,9 @@
+# letra_m.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 100.00 x 100.00 px
+
+name: letra_m
+scale: 1.0
+center: 50.00, 50.00
+
+polyline: 71.43,57.14 57.14,57.14 57.14,71.43 42.86,71.43 42.86,57.14 28.57,57.14 28.57,100.00 0.00,100.00 0.00,0.00 14.29,0.00 14.29,14.29 28.57,14.29 28.57,28.57 42.86,28.57 42.86,42.86 57.14,42.86 57.14,28.57 71.43,28.57 71.43,14.29 85.71,14.29 85.71,0.00 100.00,0.00 100.00,100.00 71.43,100.00
diff --git a/data/shapes/letra_s.shp b/data/shapes/letra_s.shp
new file mode 100644
index 0000000..e210945
--- /dev/null
+++ b/data/shapes/letra_s.shp
@@ -0,0 +1,9 @@
+# letra_s.shp
+# Generado automáticamente desde jailgames.svg
+# Dimensiones: 57.14 x 100.00 px
+
+name: letra_s
+scale: 1.0
+center: 28.57, 50.00
+
+polyline: 0.00,85.71 28.57,85.71 28.57,57.14 14.29,57.14 14.29,42.86 0.00,42.86 0.00,14.29 14.29,14.29 14.29,0.00 57.14,0.00 57.14,14.29 28.57,14.29 28.57,42.86 42.86,42.86 42.86,57.14 57.14,57.14 57.14,85.71 42.86,85.71 42.86,100.00 0.00,100.00
diff --git a/jailgames.svg b/jailgames.svg
new file mode 100644
index 0000000..8b69eb2
--- /dev/null
+++ b/jailgames.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/source/game/escenes/escena_joc.cpp b/source/game/escenes/escena_joc.cpp
index 41122ab..0344688 100644
--- a/source/game/escenes/escena_joc.cpp
+++ b/source/game/escenes/escena_joc.cpp
@@ -4,6 +4,7 @@
#include "escena_joc.hpp"
#include "../../core/system/gestor_escenes.hpp"
+#include "../../core/system/global_events.hpp"
#include
#include
#include
@@ -49,29 +50,13 @@ void EscenaJoc::executar() {
continue;
}
- // Tecles globals de finestra (F1/F2/F3)
- if (event.type == SDL_EVENT_KEY_DOWN) {
- switch (event.key.key) {
- case SDLK_F1:
- sdl_.decreaseWindowSize();
- continue;
- case SDLK_F2:
- sdl_.increaseWindowSize();
- continue;
- case SDLK_F3:
- sdl_.toggleFullscreen();
- continue;
- }
+ // Events globals (F1/F2/F3/ESC/QUIT)
+ if (GlobalEvents::handle(event, sdl_)) {
+ continue;
}
- // Processament normal del joc
+ // Processament específic del joc (SPACE per disparar)
processar_input(event);
-
- // Detectar tancament de finestra o ESC
- if (event.type == SDL_EVENT_QUIT ||
- (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE)) {
- GestorEscenes::actual = GestorEscenes::Escena::EIXIR;
- }
}
// Actualitzar física del joc amb delta_time real
diff --git a/source/game/escenes/escena_logo.cpp b/source/game/escenes/escena_logo.cpp
index 1757791..5c22b1c 100644
--- a/source/game/escenes/escena_logo.cpp
+++ b/source/game/escenes/escena_logo.cpp
@@ -3,11 +3,17 @@
#include "escena_logo.hpp"
#include "../../core/system/gestor_escenes.hpp"
+#include "../../core/system/global_events.hpp"
+#include "../../core/graphics/shape_loader.hpp"
+#include "../../core/rendering/shape_renderer.hpp"
#include
+#include
+#include
EscenaLogo::EscenaLogo(SDLManager& sdl)
: sdl_(sdl), temps_acumulat_(0.0f) {
std::cout << "Escena Logo: Inicialitzant...\n";
+ inicialitzar_lletres();
}
void EscenaLogo::executar() {
@@ -32,34 +38,21 @@ void EscenaLogo::executar() {
continue;
}
- // Tecles globals de finestra (F1/F2/F3)
- if (event.type == SDL_EVENT_KEY_DOWN) {
- switch (event.key.key) {
- case SDLK_F1:
- sdl_.decreaseWindowSize();
- continue;
- case SDLK_F2:
- sdl_.increaseWindowSize();
- continue;
- case SDLK_F3:
- sdl_.toggleFullscreen();
- continue;
- }
+ // Events globals (F1/F2/F3/ESC/QUIT)
+ if (GlobalEvents::handle(event, sdl_)) {
+ continue;
}
- // Processar events de l'escena
+ // Processar events de l'escena (qualsevol tecla/clic salta al joc)
processar_events(event);
-
- // ESC o tancar finestra = eixir
- if (event.type == SDL_EVENT_QUIT ||
- (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE)) {
- GestorEscenes::actual = GestorEscenes::Escena::EIXIR;
- }
}
// Actualitzar lògica
actualitzar(delta_time);
+ // Actualitzar colors oscil·lats (efecte verd global)
+ sdl_.updateColors(delta_time);
+
// Dibuixar
dibuixar();
}
@@ -67,18 +60,117 @@ void EscenaLogo::executar() {
std::cout << "Escena Logo: Finalitzant...\n";
}
+void EscenaLogo::inicialitzar_lletres() {
+ using namespace Graphics;
+
+ // Llista de fitxers .shp (A repetida per a les dues A's)
+ std::vector fitxers = {
+ "letra_j.shp", "letra_a.shp", "letra_i.shp", "letra_l.shp",
+ "letra_g.shp", "letra_a.shp", "letra_m.shp", "letra_e.shp", "letra_s.shp"
+ };
+
+ // Pas 1: Carregar totes les formes i calcular amplades
+ float ancho_total = 0.0f;
+
+ for (const auto& fitxer : fitxers) {
+ auto forma = ShapeLoader::load(fitxer);
+ if (!forma || !forma->es_valida()) {
+ std::cerr << "[EscenaLogo] Error carregant " << fitxer << std::endl;
+ continue;
+ }
+
+ // Calcular bounding box de la forma (trobar ancho)
+ float min_x = FLT_MAX;
+ float max_x = -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);
+ }
+ }
+
+ float ancho = max_x - min_x;
+
+ lletres_.push_back({
+ forma,
+ {0.0f, 0.0f}, // Posició es calcularà després
+ ancho
+ });
+
+ ancho_total += ancho;
+ }
+
+ // Pas 2: Afegir espaiat entre lletres
+ ancho_total += ESPAI_ENTRE_LLETRES * (lletres_.size() - 1);
+
+ // Pas 3: Calcular posició inicial (centrat horitzontal)
+ constexpr float PANTALLA_ANCHO = 640.0f;
+ constexpr float PANTALLA_ALTO = 480.0f;
+
+ float x_inicial = (PANTALLA_ANCHO - ancho_total) / 2.0f;
+ float y_centre = PANTALLA_ALTO / 2.0f;
+
+ // Pas 4: Assignar posicions a cada lletra
+ float x_actual = x_inicial;
+
+ for (auto& lletra : lletres_) {
+ // Centre de la lletra en pantalla
+ lletra.posicio.x = x_actual + lletra.ancho / 2.0f;
+ lletra.posicio.y = y_centre;
+
+ // Avançar per a següent lletra
+ x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
+ }
+
+ std::cout << "[EscenaLogo] " << lletres_.size()
+ << " lletres carregades, ancho total: " << ancho_total << " px\n";
+}
+
+float EscenaLogo::calcular_escala_zoom(float temps) const {
+ if (temps >= DURACIO_ZOOM) {
+ return ESCALA_FINAL; // Animació acabada
+ }
+
+ // Progrés normalitzat (0.0 a 1.0)
+ float t = temps / DURACIO_ZOOM;
+
+ // Ease-out quadràtic: y = 1 - (1-t)^2
+ // Comença ràpid, acaba suau
+ float factor = 1.0f - (1.0f - t) * (1.0f - t);
+
+ // Interpolar entre escala inicial i final
+ return ESCALA_INICIAL + (ESCALA_FINAL - ESCALA_INICIAL) * factor;
+}
+
void EscenaLogo::actualitzar(float delta_time) {
temps_acumulat_ += delta_time;
- // Després de 2 segons, saltar al joc
- if (temps_acumulat_ >= 2.0f) {
+ // Després de DURACIO_TOTAL segons, saltar al joc
+ if (temps_acumulat_ >= DURACIO_TOTAL) {
GestorEscenes::actual = GestorEscenes::Escena::JOC;
}
}
void EscenaLogo::dibuixar() {
- // Pantalla negra
+ // Fons negre
sdl_.neteja(0, 0, 0);
+
+ // Calcular escala actual del zoom
+ float escala = calcular_escala_zoom(temps_acumulat_);
+
+ // Dibuixar cada lletra amb l'escala animada
+ for (const auto& lletra : lletres_) {
+ Rendering::render_shape(
+ sdl_.obte_renderer(),
+ lletra.forma,
+ lletra.posicio, // Posició en pantalla
+ 0.0f, // Sense rotació
+ escala, // Escala animada
+ true // Dibuixar
+ );
+ }
+
sdl_.presenta();
}
diff --git a/source/game/escenes/escena_logo.hpp b/source/game/escenes/escena_logo.hpp
index 8f8a1c9..5a7edf9 100644
--- a/source/game/escenes/escena_logo.hpp
+++ b/source/game/escenes/escena_logo.hpp
@@ -1,11 +1,15 @@
// escena_logo.hpp - Pantalla d'inici del joc
-// Mostra pantalla negra durant 2 segons i salta al joc
+// Mostra logo JAILGAMES animat amb zoom i salta al joc
// © 2025 Port a C++20
#pragma once
#include "../../core/rendering/sdl_manager.hpp"
+#include "../../core/graphics/shape.hpp"
+#include "../../core/types.hpp"
#include
+#include
+#include
class EscenaLogo {
public:
@@ -16,6 +20,25 @@ private:
SDLManager& sdl_;
float temps_acumulat_;
+ // Estructura per a cada lletra del logo
+ struct LetraLogo {
+ std::shared_ptr forma;
+ Punt posicio; // Posició final en pantalla
+ float ancho; // Ancho per a càlcul de centrat
+ };
+
+ std::vector lletres_; // 9 lletres: J-A-I-L-G-A-M-E-S
+
+ // Constants d'animació
+ static constexpr float DURACIO_ZOOM = 1.5f; // Duració del zoom (segons)
+ static constexpr float DURACIO_TOTAL = 3.0f; // Duració total abans d'anar al joc
+ static constexpr float ESCALA_INICIAL = 0.1f; // Escala inicial (10%)
+ static constexpr float ESCALA_FINAL = 1.0f; // Escala final (100%)
+ static constexpr float ESPAI_ENTRE_LLETRES = 10.0f; // Espaiat entre lletres
+
+ // Mètodes privats
+ void inicialitzar_lletres();
+ float calcular_escala_zoom(float temps) const;
void actualitzar(float delta_time);
void dibuixar();
void processar_events(const SDL_Event& event);
diff --git a/tools/svg_to_shp.py b/tools/svg_to_shp.py
new file mode 100755
index 0000000..7404e7c
--- /dev/null
+++ b/tools/svg_to_shp.py
@@ -0,0 +1,340 @@
+#!/usr/bin/env python3
+"""
+svg_to_shp.py - Conversor de jailgames.svg a archivos .shp individuales
+Extrae letras del SVG y las convierte al formato .shp del juego Orni Attack
+
+Uso: python3 svg_to_shp.py
+"""
+
+import sys
+import os
+import re
+import xml.etree.ElementTree as ET
+
+
+def parse_transform_matrix(transform_str):
+ """
+ Parsea string "matrix(a,b,c,d,e,f)" → tupla (a,b,c,d,e,f)
+ """
+ match = re.search(r'matrix\(([\d\.\-,\s]+)\)', transform_str)
+ if not match:
+ return (1, 0, 0, 1, 0, 0) # Identidad por defecto
+
+ values = re.split(r'[,\s]+', match.group(1).strip())
+ values = [float(v) for v in values if v]
+
+ if len(values) == 6:
+ return tuple(values)
+ return (1, 0, 0, 1, 0, 0)
+
+
+def apply_transform(punto, matrix):
+ """
+ Aplica transformación matricial 2D a un punto (x, y)
+ matrix = (a, b, c, d, e, f)
+ x' = a*x + c*y + e
+ y' = b*x + d*y + f
+ """
+ x, y = punto
+ a, b, c, d, e, f = matrix
+ x_new = a * x + c * y + e
+ y_new = b * x + d * y + f
+ return (x_new, y_new)
+
+
+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), ...]
+ """
+ # Reemplazar comas por espacios para facilitar parsing
+ d_attr = d_attr.replace(',', ' ')
+
+ # Split por comandos M y L
+ # Añadir marcador antes de cada comando
+ d_attr = re.sub(r'([ML])', r'|\1', d_attr)
+ commands = [c.strip() for c in d_attr.split('|') if c.strip()]
+
+ points = []
+ for cmd in commands:
+ if not cmd:
+ continue
+
+ # Extraer letra de comando y coordenadas
+ cmd_letter = cmd[0]
+ coords_str = cmd[1:].strip()
+
+ if not coords_str:
+ continue
+
+ # Parsear pares de coordenadas
+ coords = coords_str.split()
+
+ # 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))
+ i += 2
+ except (ValueError, IndexError):
+ i += 1
+
+ return points
+
+
+def rect_to_points(rect_elem):
+ """
+ Convierte elemento a lista de puntos (rectángulo cerrado)
+ """
+ x = float(rect_elem.get('x', 0))
+ y = float(rect_elem.get('y', 0))
+ width = float(rect_elem.get('width', 0))
+ height = float(rect_elem.get('height', 0))
+
+ # Rectángulo cerrado: 5 puntos (último = primero)
+ return [
+ (x, y),
+ (x + width, y),
+ (x + width, y + height),
+ (x, y + height),
+ (x, y) # Cerrar
+ ]
+
+
+def calc_bounding_box(puntos):
+ """
+ Calcula bounding box de una lista de puntos
+ Retorna: (min_x, max_x, min_y, max_y, ancho, alto)
+ """
+ if not puntos:
+ return (0, 0, 0, 0, 0, 0)
+
+ xs = [p[0] for p in puntos]
+ ys = [p[1] for p in puntos]
+
+ min_x = min(xs)
+ max_x = max(xs)
+ min_y = min(ys)
+ max_y = max(ys)
+
+ ancho = max_x - min_x
+ alto = max_y - min_y
+
+ return (min_x, max_x, min_y, max_y, ancho, alto)
+
+
+def normalizar_letra(nombre, puntos, 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
+ """
+ if not puntos:
+ return None
+
+ min_x, max_x, min_y, max_y, ancho, alto = calc_bounding_box(puntos)
+
+ if alto == 0:
+ print(f" [WARN] Letra {nombre}: altura cero")
+ return None
+
+ # Factor de escala basado en altura
+ escala = altura_objetivo / alto
+
+ # Normalizar puntos:
+ # 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))
+
+ # Calcular dimensiones finales
+ ancho_norm = ancho * escala
+ alto_norm = alto * escala
+
+ # Centro de la letra
+ centro = (ancho_norm / 2.0, alto_norm / 2.0)
+
+ return {
+ 'nombre': nombre,
+ 'puntos': puntos_norm,
+ 'centro': centro,
+ 'ancho': ancho_norm,
+ 'alto': alto_norm
+ }
+
+
+def generar_shp(letra_norm, output_dir):
+ """
+ Genera archivo .shp con formato del juego Orni Attack
+
+ Formato:
+ name: letra_x
+ scale: 1.0
+ center: cx, cy
+ polyline: x1,y1 x2,y2 x3,y3 ...
+ """
+ if not letra_norm:
+ return
+
+ nombre_archivo = f"letra_{letra_norm['nombre'].lower()}.shp"
+ filepath = os.path.join(output_dir, nombre_archivo)
+
+ with open(filepath, 'w', encoding='utf-8') as f:
+ # Header con comentarios
+ f.write(f"# {nombre_archivo}\n")
+ f.write(f"# Generado automáticamente desde jailgames.svg\n")
+ f.write(f"# Dimensiones: {letra_norm['ancho']:.2f} x {letra_norm['alto']:.2f} px\n")
+ f.write(f"\n")
+
+ # Metadatos
+ f.write(f"name: letra_{letra_norm['nombre'].lower()}\n")
+ f.write(f"scale: 1.0\n")
+ 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")
+
+ print(f" ✓ {nombre_archivo:20} ({len(letra_norm['puntos']):3} puntos, "
+ f"{letra_norm['ancho']:6.2f} x {letra_norm['alto']:6.2f} px)")
+
+
+def parse_svg(filepath):
+ """
+ Parsea jailgames.svg y extrae las 9 letras con sus puntos transformados
+
+ Retorna: lista de dicts con {nombre, puntos}
+ """
+ tree = ET.parse(filepath)
+ root = tree.getroot()
+
+ # Namespace SVG
+ ns = {'svg': 'http://www.w3.org/2000/svg'}
+
+ # Buscar grupo con transform
+ groups = root.findall('.//svg:g[@transform]', ns)
+ if not groups:
+ print("[ERROR] No se encontró grupo con transform")
+ return []
+
+ group = groups[0]
+ transform_str = group.get('transform', '')
+ transform_matrix = parse_transform_matrix(transform_str)
+
+ print(f"[INFO] Transform matrix: {transform_matrix}")
+
+ # Extraer paths y rects
+ 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']
+
+ letras = []
+
+ # Procesar paths
+ for i, path in enumerate(paths):
+ if i >= len(nombres_paths):
+ break
+
+ d_attr = path.get('d')
+ if not d_attr:
+ continue
+
+ # Parsear puntos del path
+ puntos = parse_svg_path(d_attr)
+
+ # Aplicar transformación
+ puntos = [apply_transform(p, transform_matrix) for p in puntos]
+
+ letras.append({
+ 'nombre': nombres_paths[i],
+ 'puntos': puntos
+ })
+
+ # Procesar rects (la letra I es un rect)
+ for rect in rects:
+ puntos = rect_to_points(rect)
+
+ # Aplicar transformación
+ puntos = [apply_transform(p, transform_matrix) for p in puntos]
+
+ letras.append({
+ 'nombre': 'I',
+ 'puntos': puntos
+ })
+
+ return letras
+
+
+def main():
+ if len(sys.argv) != 3:
+ print("Uso: python3 svg_to_shp.py ")
+ print("Ejemplo: python3 svg_to_shp.py jailgames.svg data/shapes/")
+ sys.exit(1)
+
+ svg_path = sys.argv[1]
+ output_dir = sys.argv[2]
+
+ # Verificar que el SVG existe
+ if not os.path.exists(svg_path):
+ print(f"[ERROR] No se encuentra el archivo: {svg_path}")
+ sys.exit(1)
+
+ # Crear directorio de salida si no existe
+ os.makedirs(output_dir, exist_ok=True)
+
+ print(f"\n{'='*70}")
+ print(f" SVG → .shp Converter para Orni Attack")
+ print(f"{'='*70}\n")
+ print(f"Input: {svg_path}")
+ print(f"Output: {output_dir}/\n")
+
+ # Parsear SVG
+ print("[1/3] Parseando SVG...")
+ letras = parse_svg(svg_path)
+
+ if not letras:
+ print("[ERROR] No se pudieron extraer letras del SVG")
+ sys.exit(1)
+
+ print(f" ✓ Extraídas {len(letras)} letras\n")
+
+ # Filtrar duplicados (solo una 'A')
+ print("[2/3] Filtrando duplicados...")
+ letras_unicas = {}
+ for letra in letras:
+ nombre = letra['nombre']
+ if nombre not in letras_unicas:
+ letras_unicas[nombre] = letra
+
+ print(f" ✓ {len(letras_unicas)} letras únicas: {', '.join(sorted(letras_unicas.keys()))}\n")
+
+ # Normalizar y generar .shp
+ print("[3/3] Generando archivos .shp (altura objetivo: 100px)...\n")
+
+ for nombre in sorted(letras_unicas.keys()):
+ letra = letras_unicas[nombre]
+ letra_norm = normalizar_letra(nombre, letra['puntos'], altura_objetivo=100.0)
+
+ if letra_norm:
+ generar_shp(letra_norm, output_dir)
+
+ print(f"\n{'='*70}")
+ print(f" ✓ Conversión completada: {len(letras_unicas)} archivos .shp generados")
+ print(f"{'='*70}\n")
+
+
+if __name__ == '__main__':
+ main()