Files
coffee-crisis-ae/source/color.cpp
T
2025-08-06 13:05:04 +02:00

182 lines
5.3 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);
}
// 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];
}
constexpr auto 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, s, v};
}
constexpr auto 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)));
}
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> ColorCycle {
ColorCycle result{};
HSV base_hsv = rgbToHsv(base);
for (size_t i = 0; i < COLOR_CYCLE_SIZE; ++i) {
float t = static_cast<float>(i) / (COLOR_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 = {
fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
Color c = hsvToRgb(adjusted);
result[i] = c;
result[2 * COLOR_CYCLE_SIZE - 1 - i] = c; // espejo
}
return result;
}