#define _USE_MATH_DEFINES #include "utils/color.hpp" #include // Para ranges::any_of #include // Para isxdigit #include // Para sinf, fmaxf, fminf, M_PI, fmodf, roundf, fmod #include // Para uint8_t #include // Para invalid_argument #include // Para basic_string, stoi, string // Método estático para crear Color desde string hexadecimal auto Color::fromHex(const std::string& hex_str) -> Color { std::string hex = hex_str; // Quitar '#' si existe if (!hex.empty() && hex[0] == '#') { hex = hex.substr(1); } // Verificar longitud válida (6 para RGB o 8 para RGBA) if (hex.length() != HEX_RGB_LENGTH && hex.length() != HEX_RGBA_LENGTH) { throw std::invalid_argument("String hexadecimal debe tener 6 o 8 caracteres"); } // Verificar que todos los caracteres sean hexadecimales válidos if (std::ranges::any_of(hex, [](char c) { return std::isxdigit(c) == 0; })) { throw std::invalid_argument("String contiene caracteres no hexadecimales"); } // Convertir cada par de caracteres a valores RGB(A) Uint8 r = static_cast(std::stoi(hex.substr(0, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE)); Uint8 g = static_cast(std::stoi(hex.substr(HEX_COMPONENT_LENGTH, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE)); Uint8 b = static_cast(std::stoi(hex.substr(HEX_COMPONENT_LENGTH * 2, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE)); Uint8 a = DEFAULT_ALPHA; // Alpha por defecto // Si tiene 8 caracteres, extraer el alpha if (hex.length() == HEX_RGBA_LENGTH) { a = static_cast(std::stoi(hex.substr(HEX_COMPONENT_LENGTH * 3, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE)); } return Color(r, g, b, a); } // Implementaciones de métodos estáticos de Color constexpr auto Color::rgbToHsv(Color color) -> HSV { float r = color.r / 255.0F; float g = color.g / 255.0F; float b = color.b / 255.0F; float max = fmaxf(fmaxf(r, g), b); float min = fminf(fminf(r, g), b); float delta = max - min; float h = 0.0F; if (delta > 0.00001F) { if (max == r) { h = fmodf((g - b) / delta, 6.0F); } else if (max == g) { h = ((b - r) / delta) + 2.0F; } else { h = ((r - g) / delta) + 4.0F; } h *= 60.0F; if (h < 0.0F) { h += 360.0F; } } float s = (max <= 0.0F) ? 0.0F : delta / max; float v = max; return {.h = h, .s = s, .v = v}; } constexpr auto Color::hsvToRgb(HSV hsv) -> Color { float c = hsv.v * hsv.s; float x = c * (1 - std::abs(std::fmod(hsv.h / 60.0F, 2) - 1)); float m = hsv.v - c; float r = 0; float g = 0; float b = 0; if (hsv.h < 60) { r = c; g = x; b = 0; } else if (hsv.h < 120) { r = x; g = c; b = 0; } else if (hsv.h < 180) { r = 0; g = c; b = x; } else if (hsv.h < 240) { r = 0; g = x; b = c; } else if (hsv.h < 300) { r = x; g = 0; b = c; } else { r = c; g = 0; b = x; } return Color( static_cast(roundf((r + m) * 255)), static_cast(roundf((g + m) * 255)), static_cast(roundf((b + m) * 255))); } // Implementaciones del namespace Colors namespace Colors { // Obtiene un color del vector de colores imitando al Coche Fantástico auto getColorLikeKnightRider(const std::vector& colors, int counter) -> Color { int cycle_length = (colors.size() * 2) - 2; size_t n = counter % cycle_length; size_t index; if (n < colors.size()) { index = n; // Avanza: 0,1,2,3 } else { index = (2 * (colors.size() - 1)) - n; // Retrocede: 2,1 } return colors[index]; } auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle { Cycle result{}; HSV base_hsv = Color::rgbToHsv(base); for (size_t i = 0; i < CYCLE_SIZE; ++i) { float t = static_cast(i) / (CYCLE_SIZE - 1); // 0 → 1 float hue_shift = 0.0F; float sat_shift = 0.0F; float val_shift = 0.0F; switch (style) { case ColorCycleStyle::SUBTLE_PULSE: // Solo brillo suave val_shift = 0.07F * sinf(t * M_PI); break; case ColorCycleStyle::HUE_WAVE: // Oscilación leve de tono hue_shift = 15.0F * (t - 0.5F) * 2.0F; val_shift = 0.05F * sinf(t * M_PI); break; case ColorCycleStyle::VIBRANT: // Cambios fuertes en tono y brillo hue_shift = 35.0F * sinf(t * M_PI); val_shift = 0.2F * sinf(t * M_PI); sat_shift = -0.2F * sinf(t * M_PI); break; case ColorCycleStyle::DARKEN_GLOW: // Se oscurece al centro val_shift = -0.15F * sinf(t * M_PI); break; case ColorCycleStyle::LIGHT_FLASH: // Se ilumina al centro val_shift = 0.25F * sinf(t * M_PI); break; } HSV adjusted = { .h = fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F), .s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)), .v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))}; Color c = Color::hsvToRgb(adjusted); result[i] = c; result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo } return result; } } // namespace Colors