2009-08-06 29 views
6

Me gustaría interpolar sin problemas el color del Color A (llamémoslo rojo) al Color C (llamémoslo verde) pasando por el color B (llamémoslo amarillo), basado en el valor de una cierta variable.Interpolación de colores entre 3 colores en .NET

Si la variable = 100, quiero verde puro. Si la variable = 50, quiero amarillo puro. Si la variable = 0, quiero rojo puro.

Entiendo que puede tratar cada triplete RGB como una coordenada en el espacio tridimensional. Lo que estoy buscando es un truco de interpolación lineal rápido y sucio que funcione limpiamente con el diseño específico del tipo de color .NET (valores separados para ARGB, etc.).

+1

no puede ser lineal con tres puntos al azar – RBarryYoung

Respuesta

13

En primer lugar, solicita la interpolación lineal, pero no especifica que el color B viva en la línea entre el color A y el color C; esto es necesario. En segundo lugar, no especificó, pero voy a hacer una suposición simplificadora de que el color B es el punto medio de la línea entre el color A y el color C; el siguiente código se modifica fácilmente si esto no es cierto. Por último, cambié tu suposición de que el parámetro es un número entero entre cero y cien para ser un doble entre cero y uno. El código es más fácil de escribir y más fácil de entender en el último caso, y aún puede usarse con el primero (divida sus entradas entre cien).

class ColorInterpolator { 
    delegate byte ComponentSelector(Color color); 
    static ComponentSelector _redSelector = color => color.R; 
    static ComponentSelector _greenSelector = color => color.G; 
    static ComponentSelector _blueSelector = color => color.B; 

    public static Color InterpolateBetween(
     Color endPoint1, 
     Color endPoint2, 
     double lambda) { 
     if (lambda < 0 || lambda > 1) { 
      throw new ArgumentOutOfRangeException("lambda"); 
     } 
     Color color = Color.FromRgb(
      InterpolateComponent(endPoint1, endPoint2, lambda, _redSelector), 
      InterpolateComponent(endPoint1, endPoint2, lambda, _greenSelector), 
      InterpolateComponent(endPoint1, endPoint2, lambda, _blueSelector) 
     ); 

     return color; 
    } 

    static byte InterpolateComponent(
     Color endPoint1, 
     Color endPoint2, 
     double lambda, 
     ComponentSelector selector) { 
     return (byte)(selector(endPoint1) 
      + (selector(endPoint2) - selector(endPoint1)) * lambda); 
    } 
} 

¿Cómo se modifica esto si el color B no es el punto medio entre el color A y el color C? La forma más fácil es la siguiente. Si el parámetro (lo que llamo "lambda") es menor que 0.5, multiplique lambda por dos y devuelva el color interpolado entre el color A y el color B. Si el parámetro es mayor que 0.5, multiplique lambda por dos y reste uno (este mapa [0.5, 1] en [0, 1]) y devuelve el color interpolado entre el color B y el color C.

Si no le gusta el requisito de que el color B viva en la línea entre el color A y el color C, puede usar exactamente la modificación que Acabo de describir una interpolación lineal por partes entre los colores.

Finalmente, no especificó si quiere interpolar el llamado valor alfa (la 'A' en "ARGB"). El código anterior se modifica fácilmente para manejar esta situación también. Agregue uno más ComponentSelector definido como color => color.A, use InterpolateComponent para interpolar este valor y use la sobrecarga Color.FromArgb(int, int, int, int) de Color.FromArgb.

+0

Gracias por la respuesta detallada. ¡Exactamente lo que estaba buscando! – user144051

+0

Para una solución más genérica, podría valer la pena [investigar mi respuesta en una pregunta duplicada] (http://stackoverflow.com/a/13253304/590790). –

1

Otra manera de mezclar los colores usando distribución gaussiana como esto (cualquier número de colores para una gama de 0,0 - 1,0, para aumentar la mezcla de valor sigma_2 aumento)

public static Color InterpolateColor(Color[] colors, double x) 
{ 
    double r = 0.0, g = 0.0, b = 0.0; 
    double total = 0.0; 
    double step = 1.0/(double)(colors.Length - 1); 
    double mu = 0.0; 
    double sigma_2 = 0.035; 

    foreach (Color color in colors) 
    {     
     total += Math.Exp(-(x - mu) * (x - mu)/(2.0 * sigma_2))/Math.Sqrt(2.0 * Math.PI * sigma_2); 
     mu += step; 
    } 

    mu = 0.0; 
    foreach(Color color in colors) 
    {     
     double percent = Math.Exp(-(x - mu) * (x - mu)/(2.0 * sigma_2))/Math.Sqrt(2.0 * Math.PI * sigma_2); 
     mu += step; 

     r += color.R * percent/total; 
     g += color.G * percent/total; 
     b += color.B * percent/total; 
    } 

    return Color.FromArgb(255, (int)r, (int)g, (int)b); 
} 

Más información http://en.wikipedia.org/wiki/Normal_distribution

Muestra de mezcla 3 colores:

enter image description here