You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
8.3 KiB
224 lines
8.3 KiB
/* ——————————————————————————————————————————————————————————————————————————
|
|
Proyecto WIAWAIW
|
|
—————————————————————————————————————————————————————————————————————————————
|
|
Fichero main.go
|
|
Package app
|
|
Autor Juan V. Navarro juanvnl@activething.com
|
|
Creado 15/08/2025
|
|
—————————————————————————————————————————————————————————————————————————————
|
|
|
|
LICENSES AND TERMS OF USE
|
|
-------------------------
|
|
|
|
This software is licensed under the Elastic License v2.0 (the "License").
|
|
For full terms and additional information regarding permitted and prohibited
|
|
uses, please visit:
|
|
https://activething.com/wiawiaw/licenses
|
|
|
|
You may use, copy, modify, and redistribute this software internally within
|
|
your organization for any purpose, including research, development, and
|
|
testing, subject to the terms of this License.
|
|
|
|
You may NOT, however, use, provide, distribute, or make this software
|
|
available to any third party as part of a hosted service, SaaS offering, or
|
|
commercial product without first obtaining a commercial license from
|
|
Active Thing.
|
|
|
|
You may combine this software with other code, provided that such
|
|
combination does not circumvent the restrictions of this License.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. In no event shall
|
|
the authors or copyright holders be liable for any claim, damages, or other
|
|
liability arising from the use of this software.
|
|
|
|
—————————————————————————————————————————————————————————————————————————————
|
|
|
|
Web : activething.com | wiawiaw.com
|
|
git : g.activething.com | github.com/activething
|
|
Correo : dev@activething.com
|
|
|
|
—————————————————————————————————————————————————————————————————————————————
|
|
No deseo caminar sobre el agua", dijo Siddhartha.
|
|
Que los antiguos chamanes se contenten con tales habilidades.
|
|
—— Hermann Hesse, Siddhartha
|
|
—————————————————————————————————————————————————————————————————————————————
|
|
Copyright (c) 2025 Active Thing
|
|
————————————————————————————————————————————————————————————————————————————— */
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
// ReduceColor1 toma un color RGB y lo reduce a la mejor representación disponible
|
|
// según el soporte del terminal.
|
|
func ReduceColor(r, g, b uint8, hasTrueColor, has256Colors bool) string {
|
|
// 1. Soporte TrueFgColor (24 bits)
|
|
if hasTrueColor {
|
|
return fmt.Sprintf("\x1b[38;2;%d;%d;%dm", r, g, b)
|
|
}
|
|
|
|
// 2. Soporte 256 colores
|
|
if has256Colors {
|
|
// Convertir a un color del cubo 6x6x6
|
|
dr := math.Round(float64(r) / 51.0)
|
|
dg := math.Round(float64(g) / 51.0)
|
|
db := math.Round(float64(b) / 51.0)
|
|
|
|
// Calcular el índice
|
|
index := 16 + (36 * int(dr)) + (6 * int(dg)) + int(db)
|
|
|
|
// Considerar la paleta de grises de 24 niveles para mayor precisión
|
|
if r == g && g == b {
|
|
grayIndex := 232 + int(math.Round(float64(r)/10.5))
|
|
return fmt.Sprintf("\x1b[38;5;%d;1m", grayIndex)
|
|
}
|
|
|
|
return fmt.Sprintf("\x1b[38;5;%d;1m", index)
|
|
}
|
|
|
|
// 3. Soporte ANSI de 8 colores
|
|
// Usar la distancia euclidiana para encontrar el color más cercano.
|
|
colorsAnsi := []struct {
|
|
r, g, b uint8
|
|
code int
|
|
}{
|
|
{0, 0, 0, 30}, // Negro
|
|
{255, 0, 0, 31}, // Rojo
|
|
{0, 255, 0, 32}, // Verde
|
|
{255, 255, 0, 33}, // Amarillo
|
|
{0, 0, 255, 34}, // Azul
|
|
{255, 0, 255, 35}, // Magenta
|
|
{0, 255, 255, 36}, // Cian
|
|
{255, 255, 255, 37}, // Blanco
|
|
}
|
|
|
|
minDist := float64(math.MaxFloat64)
|
|
bestCode := 37 // Blanco por defecto
|
|
|
|
for _, c := range colorsAnsi {
|
|
dist := math.Pow(float64(r-c.r), 2) + math.Pow(float64(g-c.g), 2) + math.Pow(float64(b-c.b), 2)
|
|
if dist < minDist {
|
|
minDist = dist
|
|
bestCode = c.code
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("\x1b[0;%dm", bestCode)
|
|
}
|
|
|
|
|
|
// ReduceColor toma un color RGB y lo reduce a la mejor representación disponible
|
|
// según el soporte del terminal.
|
|
func ReduceColor2(r, g, b uint8, hasTrueColor, has256Colors bool) string {
|
|
// 1. Soporte TrueFgColor (24 bits)
|
|
if hasTrueColor {
|
|
return fmt.Sprintf("\x1b[38;2;%d;%d;%dm", r, g, b)
|
|
}
|
|
|
|
// 2. Soporte 256 colores
|
|
if has256Colors {
|
|
// Convertir a cubo 6x6x6 (valores: 0, 51, 102, 153, 204, 255)
|
|
r6 := int(math.Round(float64(r) * 5.0 / 255.0)) // Escalar a 0-5
|
|
g6 := int(math.Round(float64(g) * 5.0 / 255.0))
|
|
b6 := int(math.Round(float64(b) * 5.0 / 255.0))
|
|
index := 16 + (36 * r6) + (6 * g6) + b6
|
|
|
|
// Calcular distancia al color del cubo 6x6x6
|
|
rCube := uint8(r6 * 51)
|
|
gCube := uint8(g6 * 51)
|
|
bCube := uint8(b6 * 51)
|
|
distCube := weightedDistance(r, g, b, rCube, gCube, bCube)
|
|
|
|
// Verificar si un tono de gris es mejor (232-255)
|
|
gray := uint8((int(r) + int(g) + int(b)) / 3)
|
|
grayIndex := 232 + int(math.Round(float64(gray)/10.5))
|
|
grayValue := uint8((grayIndex - 232) * 10)
|
|
distGray := weightedDistance(r, g, b, grayValue, grayValue, grayValue)
|
|
|
|
if distGray < distCube {
|
|
return fmt.Sprintf("\x1b[38;5;%dm", grayIndex)
|
|
}
|
|
return fmt.Sprintf("\x1b[38;5;%dm", index)
|
|
}
|
|
|
|
// 3. Soporte ANSI de 8 colores (incluye brillantes)
|
|
colorsAnsi := []struct {
|
|
r, g, b uint8
|
|
code int
|
|
}{
|
|
{0, 0, 0, 30}, // Negro
|
|
{128, 0, 0, 31}, // Rojo
|
|
{0, 128, 0, 32}, // Verde
|
|
{128, 128, 0, 33}, // Amarillo
|
|
{0, 0, 128, 34}, // Azul
|
|
{128, 0, 128, 35}, // Magenta
|
|
{0, 128, 128, 36}, // Cian
|
|
{192, 192, 192, 37}, // Blanco
|
|
{128, 128, 128, 90}, // Negro brillante
|
|
{255, 0, 0, 91}, // Rojo brillante
|
|
{0, 255, 0, 92}, // Verde brillante
|
|
{255, 255, 0, 93}, // Amarillo brillante
|
|
{0, 0, 255, 94}, // Azul brillante
|
|
{255, 0, 255, 95}, // Magenta brillante
|
|
{0, 255, 255, 96}, // Cian brillante
|
|
{255, 255, 255, 97}, // Blanco brillante
|
|
}
|
|
|
|
minDist := float64(math.MaxFloat64)
|
|
bestCode := 37 // Blanco por defecto
|
|
|
|
for _, c := range colorsAnsi {
|
|
dist := weightedDistance(r, g, b, c.r, c.g, c.b)
|
|
if dist < minDist {
|
|
minDist = dist
|
|
bestCode = c.code
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("\x1b[0;%dm", bestCode)
|
|
}
|
|
|
|
// weightedDistance calcula la distancia euclidiana ponderada para mejor percepción visual
|
|
func weightedDistance(r1, g1, b1, r2, g2, b2 uint8) float64 {
|
|
// Ponderaciones basadas en la sensibilidad del ojo humano
|
|
dr := float64(r1 - r2) * 0.30
|
|
dg := float64(g1 - g2) * 0.59
|
|
db := float64(b1 - b2) * 0.11
|
|
return math.Sqrt(dr*dr + dg*dg + db*db)
|
|
}
|
|
|
|
|
|
func main() {
|
|
r, g, b := uint8(25), uint8(128), uint8(200)
|
|
|
|
// Caso 1: Soporte TrueFgColor
|
|
fmt.Printf("1- Soporte TrueFgColor: %sTexto de ejemplo\x1b[0m\n", ReduceColor(r, g, b, true, true))
|
|
fmt.Printf("2- Soporte TrueFgColor: %sTexto de ejemplo\x1b[0m\n", ReduceColor2(r, g, b, true, true))
|
|
|
|
// Caso 2: Soporte 256 colores
|
|
fmt.Printf("1- Soporte 256 colores: %sTexto de ejemplo\x1b[0m\n", ReduceColor(r, g, b, false, true))
|
|
fmt.Printf("2- Soporte 256 colores: %sTexto de ejemplo\x1b[0m\n", ReduceColor2(r, g, b, false, true))
|
|
|
|
// Caso 3: Soporte ANSI 8 colores
|
|
fmt.Printf("1- Soporte 8 colores: %sTexto de ejemplo\x1b[0m\n", ReduceColor(r, g, b, false, false))
|
|
fmt.Printf("2- Soporte 8 colores: %sTexto de ejemplo\x1b[0m\n", ReduceColor2(r, g, b, false, false))
|
|
|
|
// Caso 4: Sin color (manejarlo fuera de la función, ya que no se necesita código de escape)
|
|
fmt.Printf("Sin soporte: Texto de ejemplo\n")
|
|
}
|
|
|
|
/*
|
|
func main() {
|
|
|
|
|
|
|
|
|
|
ansiStylesMain()
|
|
}
|
|
|
|
*/ |