928335576c
creat Balloon::Config per a inicialitzar globos
186 lines
5.5 KiB
C++
186 lines
5.5 KiB
C++
#define _USE_MATH_DEFINES
|
|
#include "color.h"
|
|
|
|
#include <cctype> // Para isxdigit
|
|
#include <cmath> // Para sinf, fmaxf, fminf, M_PI, fmodf, roundf, fmod
|
|
#include <cstdint> // Para uint8_t
|
|
#include <stdexcept> // Para invalid_argument
|
|
#include <string> // 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
|
|
for (char c : hex) {
|
|
if (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<Uint8>(std::stoi(hex.substr(0, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE));
|
|
Uint8 g = static_cast<Uint8>(std::stoi(hex.substr(HEX_COMPONENT_LENGTH, HEX_COMPONENT_LENGTH), nullptr, HEX_BASE));
|
|
Uint8 b = static_cast<Uint8>(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<Uint8>(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<uint8_t>(roundf((r + m) * 255)),
|
|
static_cast<uint8_t>(roundf((g + m) * 255)),
|
|
static_cast<uint8_t>(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<Color> &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<float>(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
|