gitignore no ha deixat versionar cap fitxer de core

afegida gestió de ratolí
This commit is contained in:
2025-12-03 09:42:45 +01:00
parent aa66dd41c1
commit 9f0dfc4e24
39 changed files with 2983 additions and 3 deletions

View File

@@ -0,0 +1,155 @@
// shape.cpp - Implementació del sistema de formes vectorials
// © 2025 Port a C++20 amb SDL3
#include "core/graphics/shape.hpp"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
namespace Graphics {
Shape::Shape(const std::string& filepath)
: centre_({0.0f, 0.0f}),
escala_defecte_(1.0f),
nom_("unnamed") {
carregar(filepath);
}
bool Shape::carregar(const std::string& filepath) {
// Llegir fitxer
std::ifstream file(filepath);
if (!file.is_open()) {
std::cerr << "[Shape] Error: no es pot obrir " << filepath << std::endl;
return false;
}
// Llegir tot el contingut
std::stringstream buffer;
buffer << file.rdbuf();
std::string contingut = buffer.str();
file.close();
// Parsejar
return parsejar_fitxer(contingut);
}
bool Shape::parsejar_fitxer(const std::string& contingut) {
std::istringstream iss(contingut);
std::string line;
while (std::getline(iss, line)) {
// Trim whitespace
line = trim(line);
// Skip comments and blanks
if (line.empty() || line[0] == '#')
continue;
// Parse command
if (starts_with(line, "name:")) {
nom_ = trim(extract_value(line));
} else if (starts_with(line, "scale:")) {
try {
escala_defecte_ = std::stof(extract_value(line));
} catch (...) {
std::cerr << "[Shape] Warning: escala invàlida, usant 1.0" << std::endl;
escala_defecte_ = 1.0f;
}
} else if (starts_with(line, "center:")) {
parse_center(extract_value(line));
} else if (starts_with(line, "polyline:")) {
auto points = parse_points(extract_value(line));
if (points.size() >= 2) {
primitives_.push_back({PrimitiveType::POLYLINE, points});
} else {
std::cerr << "[Shape] Warning: polyline amb menys de 2 punts ignorada"
<< std::endl;
}
} else if (starts_with(line, "line:")) {
auto points = parse_points(extract_value(line));
if (points.size() == 2) {
primitives_.push_back({PrimitiveType::LINE, points});
} else {
std::cerr << "[Shape] Warning: line ha de tenir exactament 2 punts"
<< std::endl;
}
}
// Comandes desconegudes ignorades silenciosament
}
if (primitives_.empty()) {
std::cerr << "[Shape] Error: cap primitiva carregada" << std::endl;
return false;
}
return true;
}
// Helper: trim whitespace
std::string Shape::trim(const std::string& str) const {
const char* whitespace = " \t\n\r";
size_t start = str.find_first_not_of(whitespace);
if (start == std::string::npos)
return "";
size_t end = str.find_last_not_of(whitespace);
return str.substr(start, end - start + 1);
}
// Helper: starts_with
bool Shape::starts_with(const std::string& str,
const std::string& prefix) const {
if (str.length() < prefix.length())
return false;
return str.compare(0, prefix.length(), prefix) == 0;
}
// Helper: extract value after ':'
std::string Shape::extract_value(const std::string& line) const {
size_t colon = line.find(':');
if (colon == std::string::npos)
return "";
return line.substr(colon + 1);
}
// Helper: parse center "x, y"
void Shape::parse_center(const std::string& value) {
std::string val = trim(value);
size_t comma = val.find(',');
if (comma != std::string::npos) {
try {
centre_.x = std::stof(trim(val.substr(0, comma)));
centre_.y = std::stof(trim(val.substr(comma + 1)));
} catch (...) {
std::cerr << "[Shape] Warning: centre invàlid, usant (0,0)" << std::endl;
centre_ = {0.0f, 0.0f};
}
}
}
// Helper: parse points "x1,y1 x2,y2 x3,y3"
std::vector<Punt> Shape::parse_points(const std::string& str) const {
std::vector<Punt> points;
std::istringstream iss(trim(str));
std::string pair;
while (iss >> pair) { // Whitespace-separated
size_t comma = pair.find(',');
if (comma != std::string::npos) {
try {
float x = std::stof(pair.substr(0, comma));
float y = std::stof(pair.substr(comma + 1));
points.push_back({x, y});
} catch (...) {
std::cerr << "[Shape] Warning: punt invàlid ignorat: " << pair
<< std::endl;
}
}
}
return points;
}
} // namespace Graphics

View File

@@ -0,0 +1,64 @@
// shape.hpp - Sistema de formes vectorials
// © 2025 Port a C++20 amb SDL3
#pragma once
#include <string>
#include <vector>
#include "core/types.hpp"
namespace Graphics {
// Tipus de primitiva dins d'una forma
enum class PrimitiveType {
POLYLINE, // Seqüència de punts connectats
LINE // Línia individual (2 punts)
};
// Primitiva individual (polyline o line)
struct ShapePrimitive {
PrimitiveType type;
std::vector<Punt> points; // 2+ punts per polyline, exactament 2 per line
};
// Classe Shape - representa una forma vectorial carregada des de .shp
class Shape {
public:
// Constructors
Shape() = default;
explicit Shape(const std::string& filepath);
// Carregar forma des de fitxer .shp
bool carregar(const std::string& filepath);
// Getters
const std::vector<ShapePrimitive>& get_primitives() const {
return primitives_;
}
const Punt& get_centre() const { return centre_; }
float get_escala_defecte() const { return escala_defecte_; }
bool es_valida() const { return !primitives_.empty(); }
// Info de depuració
std::string get_nom() const { return nom_; }
size_t get_num_primitives() const { return primitives_.size(); }
private:
std::vector<ShapePrimitive> primitives_;
Punt centre_; // Centre/origen de la forma
float escala_defecte_; // Escala per defecte (normalment 1.0)
std::string nom_; // Nom de la forma (per depuració)
// Parsejador del fitxer
bool parsejar_fitxer(const std::string& contingut);
// Helpers privats per parsejar
std::string trim(const std::string& str) const;
bool starts_with(const std::string& str, const std::string& prefix) const;
std::string extract_value(const std::string& line) const;
void parse_center(const std::string& value);
std::vector<Punt> parse_points(const std::string& str) const;
};
} // namespace Graphics

View File

@@ -0,0 +1,72 @@
// shape_loader.cpp - Implementació del carregador amb caché
// © 2025 Port a C++20 amb SDL3
#include "core/graphics/shape_loader.hpp"
#include <iostream>
namespace Graphics {
// Inicialització de variables estàtiques
std::unordered_map<std::string, std::shared_ptr<Shape>> ShapeLoader::cache_;
std::string ShapeLoader::base_path_ = "data/shapes/";
std::shared_ptr<Shape> ShapeLoader::load(const std::string& filename) {
// Check cache first
auto it = cache_.find(filename);
if (it != cache_.end()) {
std::cout << "[ShapeLoader] Cache hit: " << filename << std::endl;
return it->second; // Cache hit
}
// Resolve full path
std::string fullpath = resolve_path(filename);
// Create and load shape
auto shape = std::make_shared<Shape>();
if (!shape->carregar(fullpath)) {
std::cerr << "[ShapeLoader] Error: no s'ha pogut carregar " << filename
<< std::endl;
return nullptr;
}
// Verify shape is valid
if (!shape->es_valida()) {
std::cerr << "[ShapeLoader] Error: forma invàlida " << filename
<< std::endl;
return nullptr;
}
// Cache and return
std::cout << "[ShapeLoader] Carregat: " << filename << " ("
<< shape->get_nom() << ", " << shape->get_num_primitives()
<< " primitives)" << std::endl;
cache_[filename] = shape;
return shape;
}
void ShapeLoader::clear_cache() {
std::cout << "[ShapeLoader] Netejant caché (" << cache_.size() << " formes)"
<< std::endl;
cache_.clear();
}
size_t ShapeLoader::get_cache_size() { return cache_.size(); }
std::string ShapeLoader::resolve_path(const std::string& filename) {
// Si és un path absolut (comença amb '/'), usar-lo directament
if (!filename.empty() && filename[0] == '/') {
return filename;
}
// Si ja conté el prefix base_path, usar-lo directament
if (filename.find(base_path_) == 0) {
return filename;
}
// Altrament, afegir base_path (ara suporta subdirectoris)
return base_path_ + filename;
}
} // namespace Graphics

View File

@@ -0,0 +1,39 @@
// shape_loader.hpp - Carregador estàtic de formes amb caché
// © 2025 Port a C++20 amb SDL3
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include "core/graphics/shape.hpp"
namespace Graphics {
// Carregador estàtic de formes amb caché
class ShapeLoader {
public:
// No instanciable (tot estàtic)
ShapeLoader() = delete;
// Carregar forma des de fitxer (amb caché)
// Retorna punter compartit (nullptr si error)
// Exemple: load("ship.shp") → busca a "data/shapes/ship.shp"
static std::shared_ptr<Shape> load(const std::string& filename);
// Netejar caché (útil per debug/recàrrega)
static void clear_cache();
// Estadístiques (debug)
static size_t get_cache_size();
private:
static std::unordered_map<std::string, std::shared_ptr<Shape>> cache_;
static std::string base_path_; // "data/shapes/"
// Helpers privats
static std::string resolve_path(const std::string& filename);
};
} // namespace Graphics

View File

@@ -0,0 +1,163 @@
// starfield.cpp - Implementació del sistema d'estrelles de fons
// © 2025 Orni Attack
#include "core/graphics/starfield.hpp"
#include <cmath>
#include <cstdlib>
#include <iostream>
#include "core/defaults.hpp"
#include "core/rendering/shape_renderer.hpp"
namespace Graphics {
// Constructor
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) {
// Carregar forma d'estrella
shape_estrella_ = std::make_shared<Shape>("data/shapes/star.shp");
if (!shape_estrella_->es_valida()) {
std::cerr << "ERROR: No s'ha pogut carregar data/shapes/star.shp" << std::endl;
return;
}
// Configurar 3 capes amb diferents velocitats i escales
// Capa 0: Fons llunyà (lenta, petita)
capes_.push_back({20.0f, 0.3f, 0.8f, densitat / 3});
// Capa 1: Profunditat mitjana
capes_.push_back({40.0f, 0.5f, 1.2f, densitat / 3});
// Capa 2: Primer pla (ràpida, gran)
capes_.push_back({80.0f, 0.8f, 2.0f, densitat / 3});
// Calcular radi màxim (distància del centre al racó més llunyà)
float dx = std::max(punt_fuga_.x, area_.w - punt_fuga_.x);
float dy = std::max(punt_fuga_.y, area_.h - punt_fuga_.y);
radi_max_ = std::sqrt(dx * dx + dy * dy);
// Inicialitzar estrelles amb posicions distribuïdes (pre-omplir pantalla)
for (int capa_idx = 0; capa_idx < 3; capa_idx++) {
int num = capes_[capa_idx].num_estrelles;
for (int i = 0; i < num; i++) {
Estrella estrella;
estrella.capa = capa_idx;
// Angle aleatori
estrella.angle = (static_cast<float>(rand()) / RAND_MAX) * 2.0f * Defaults::Math::PI;
// Distància aleatòria (0.0 a 1.0) per omplir tota la pantalla
estrella.distancia_centre = static_cast<float>(rand()) / RAND_MAX;
// Calcular posició des de la distància
float radi = estrella.distancia_centre * radi_max_;
estrella.posicio.x = punt_fuga_.x + radi * std::cos(estrella.angle);
estrella.posicio.y = punt_fuga_.y + radi * std::sin(estrella.angle);
estrelles_.push_back(estrella);
}
}
}
// Inicialitzar una estrella (nova o regenerada)
void Starfield::inicialitzar_estrella(Estrella& estrella) {
// Angle aleatori des del punt de fuga cap a fora
estrella.angle = (static_cast<float>(rand()) / RAND_MAX) * 2.0f * Defaults::Math::PI;
// Distància inicial petita (5% del radi màxim) - neix prop del centre
estrella.distancia_centre = 0.05f;
// Posició inicial: molt prop del punt de fuga
float radi = estrella.distancia_centre * radi_max_;
estrella.posicio.x = punt_fuga_.x + radi * std::cos(estrella.angle);
estrella.posicio.y = punt_fuga_.y + radi * std::sin(estrella.angle);
}
// 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);
}
// Calcular escala dinàmica segons distància del centre
float Starfield::calcular_escala(const Estrella& estrella) const {
const CapaConfig& capa = capes_[estrella.capa];
// 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;
}
// Calcular brightness dinàmica segons distància del centre
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;
}
// Actualitzar posicions de les estrelles
void Starfield::actualitzar(float delta_time) {
for (auto& estrella : estrelles_) {
// Obtenir configuració de la capa
const CapaConfig& capa = capes_[estrella.capa];
// Moure cap a fora des del centre
float velocitat = capa.velocitat_base;
float dx = velocitat * std::cos(estrella.angle) * delta_time;
float dy = velocitat * std::sin(estrella.angle) * delta_time;
estrella.posicio.x += dx;
estrella.posicio.y += dy;
// Actualitzar distància del centre
float dx_centre = estrella.posicio.x - punt_fuga_.x;
float dy_centre = estrella.posicio.y - punt_fuga_.y;
float dist_px = std::sqrt(dx_centre * dx_centre + dy_centre * dy_centre);
estrella.distancia_centre = dist_px / radi_max_;
// Si ha sortit de l'àrea, regenerar-la
if (fora_area(estrella)) {
inicialitzar_estrella(estrella);
}
}
}
// Dibuixar totes les estrelles
void Starfield::dibuixar() {
if (!shape_estrella_->es_valida()) {
return;
}
for (const auto& estrella : estrelles_) {
// Calcular escala i brightness dinàmicament
float escala = calcular_escala(estrella);
float brightness = calcular_brightness(estrella);
// Renderitzar estrella sense rotació
Rendering::render_shape(
renderer_,
shape_estrella_,
estrella.posicio,
0.0f, // angle (les estrelles no giren)
escala, // escala dinàmica
true, // dibuixar
1.0f, // progress (sempre visible)
brightness // brightness dinàmica
);
}
}
} // namespace Graphics

View File

@@ -0,0 +1,80 @@
// starfield.hpp - Sistema d'estrelles de fons amb efecte de profunditat
// © 2025 Orni Attack
#pragma once
#include <SDL3/SDL.h>
#include <memory>
#include <vector>
#include "core/graphics/shape.hpp"
#include "core/types.hpp"
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
};
// 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);
// Actualitzar posicions de les estrelles
void actualitzar(float delta_time);
// Dibuixar totes les estrelles
void dibuixar();
// 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)
};
// 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;
// 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;
// Dades
std::vector<Estrella> estrelles_;
std::vector<CapaConfig> capes_; // Configuració de les 3 capes
std::shared_ptr<Shape> 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
};
} // namespace Graphics

View File

@@ -0,0 +1,263 @@
// vector_text.cpp - Implementació del sistema de text vectorial
// © 2025 Port a C++20 amb SDL3
#include "core/graphics/vector_text.hpp"
#include <iostream>
#include "core/graphics/shape_loader.hpp"
#include "core/rendering/shape_renderer.hpp"
namespace Graphics {
// Constants per a mides base dels caràcters
constexpr float char_width = 20.0f; // Amplada base del caràcter
constexpr float char_height = 40.0f; // Altura base del caràcter
VectorText::VectorText(SDL_Renderer* renderer)
: renderer_(renderer) {
load_charset();
}
void VectorText::load_charset() {
// Cargar dígitos 0-9
for (char c = '0'; c <= '9'; c++) {
std::string filename = get_shape_filename(c);
auto shape = ShapeLoader::load(filename);
if (shape && shape->es_valida()) {
chars_[c] = shape;
} else {
std::cerr << "[VectorText] Warning: no s'ha pogut carregar " << filename
<< std::endl;
}
}
// Cargar lletres A-Z (majúscules)
for (char c = 'A'; c <= 'Z'; c++) {
std::string filename = get_shape_filename(c);
auto shape = ShapeLoader::load(filename);
if (shape && shape->es_valida()) {
chars_[c] = shape;
} else {
std::cerr << "[VectorText] Warning: no s'ha pogut carregar " << filename
<< std::endl;
}
}
// Cargar símbolos
const std::string symbols[] = {".", ",", "-", ":", "!", "?"};
for (const auto& sym : symbols) {
char c = sym[0];
std::string filename = get_shape_filename(c);
auto shape = ShapeLoader::load(filename);
if (shape && shape->es_valida()) {
chars_[c] = shape;
} else {
std::cerr << "[VectorText] Warning: no s'ha pogut carregar " << filename
<< std::endl;
}
}
// Cargar símbolo de copyright (©) - UTF-8 U+00A9
// Usem el segon byte (0xA9) com a key interna
{
char c = '\xA9'; // 169 decimal
std::string filename = "font/char_copyright.shp";
auto shape = ShapeLoader::load(filename);
if (shape && shape->es_valida()) {
chars_[c] = shape;
} else {
std::cerr << "[VectorText] Warning: no s'ha pogut carregar " << filename
<< std::endl;
}
}
std::cout << "[VectorText] Carregats " << chars_.size() << " caràcters"
<< std::endl;
}
std::string VectorText::get_shape_filename(char c) const {
// Mapeo carácter → nombre de archivo (amb prefix "font/")
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return std::string("font/char_") + c + ".shp";
// Lletres majúscules A-Z
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
return std::string("font/char_") + c + ".shp";
// Lletres minúscules a-z (convertir a majúscules)
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
return std::string("font/char_") + char(c - 32) + ".shp";
// Símbols
case '.':
return "font/char_dot.shp";
case ',':
return "font/char_comma.shp";
case '-':
return "font/char_minus.shp";
case ':':
return "font/char_colon.shp";
case '!':
return "font/char_exclamation.shp";
case '?':
return "font/char_question.shp";
case ' ':
return ""; // Espai es maneja sense carregar shape
case '\xA9': // Copyright symbol (©) - UTF-8 U+00A9
return "font/char_copyright.shp";
default:
return ""; // Caràcter no suportat
}
}
bool VectorText::is_supported(char c) const {
return chars_.find(c) != chars_.end();
}
void VectorText::render(const std::string& text, const Punt& posicio, float escala, float spacing) {
if (!renderer_) {
return;
}
// Ancho de un carácter base (20 px a escala 1.0)
const float char_width_scaled = char_width * escala;
// Spacing escalado
const float spacing_scaled = spacing * escala;
// Altura de un carácter escalado (necesario para ajustar Y)
const float char_height_scaled = char_height * escala;
// Posición actual del centro del carácter (ajustada desde esquina superior
// izquierda)
float current_x = posicio.x;
// Iterar sobre cada byte del string (con detecció UTF-8)
for (size_t i = 0; i < text.length(); i++) {
unsigned char c = static_cast<unsigned char>(text[i]);
// Detectar copyright UTF-8 (0xC2 0xA9)
if (c == 0xC2 && i + 1 < text.length() &&
static_cast<unsigned char>(text[i + 1]) == 0xA9) {
c = 0xA9; // Usar segon byte com a key
i++; // Saltar el següent byte
}
// Manejar espacios (avanzar sin dibujar)
if (c == ' ') {
current_x += char_width_scaled + spacing_scaled;
continue;
}
// Verificar si el carácter está soportado
auto it = chars_.find(c);
if (it != chars_.end()) {
// Renderizar carácter
// Ajustar Y para que posicio represente esquina superior izquierda
// (render_shape espera el centro, así que sumamos la mitad de la altura)
Punt char_pos = {current_x, posicio.y + char_height_scaled / 2.0f};
Rendering::render_shape(renderer_, it->second, char_pos, 0.0f, escala, true);
// Avanzar posición
current_x += char_width_scaled + spacing_scaled;
} else {
// Carácter no soportado: saltar (o renderizar '?' en el futuro)
std::cerr << "[VectorText] Warning: caràcter no suportat '" << c << "'"
<< std::endl;
current_x += char_width_scaled + spacing_scaled;
}
}
}
float VectorText::get_text_width(const std::string& text, float escala, float spacing) const {
if (text.empty()) {
return 0.0f;
}
const float char_width_scaled = char_width * escala;
const float spacing_scaled = spacing * escala;
// Ancho total = (número de caracteres × char_width) + (espacios entre
// caracteres)
float width = text.length() * char_width_scaled;
// Añadir spacing entre caracteres (n-1 espacios para n caracteres)
if (text.length() > 1) {
width += (text.length() - 1) * spacing_scaled;
}
return width;
}
float VectorText::get_text_height(float escala) const {
return char_height * escala;
}
} // namespace Graphics

View File

@@ -0,0 +1,46 @@
// vector_text.hpp - Sistema de texto vectorial con display de 7-segmentos
// © 2025 Port a C++20 amb SDL3
#pragma once
#include <SDL3/SDL.h>
#include <memory>
#include <string>
#include <unordered_map>
#include "core/graphics/shape.hpp"
#include "core/types.hpp"
namespace Graphics {
class VectorText {
public:
VectorText(SDL_Renderer* renderer);
// Renderizar string completo
// - text: cadena a renderizar (soporta: A-Z, a-z, 0-9, '.', ',', '-', ':',
// '!', '?', ' ')
// - posicio: posición inicial (esquina superior izquierda)
// - escala: factor de escala (1.0 = 20×40 px por carácter)
// - spacing: espacio entre caracteres en píxeles (a escala 1.0)
void render(const std::string& text, const Punt& posicio, float escala = 1.0f, float spacing = 2.0f);
// Calcular ancho total de un string (útil para centrado)
float get_text_width(const std::string& text, float escala = 1.0f, float spacing = 2.0f) const;
// Calcular altura del texto (útil para centrado vertical)
float get_text_height(float escala = 1.0f) const;
// Verificar si un carácter está soportado
bool is_supported(char c) const;
private:
SDL_Renderer* renderer_;
std::unordered_map<char, std::shared_ptr<Shape>> chars_;
void load_charset();
std::string get_shape_filename(char c) const;
};
} // namespace Graphics