2009-08-06 11 views
6

Vorrei interpolare uniformemente il colore da Colore A (chiamiamolo rosso) a Colore C (chiamiamolo verde) passando per il colore B (chiamiamolo giallo), basato sul colore valore di una certa variabile.Interpolazione colore tra 3 colori in .NET

Se la variabile = 100, voglio verde puro. Se la variabile = 50, voglio il giallo puro. Se la variabile = 0, voglio rosso puro.

Capisco che puoi trattare ogni tripletta RGB come una coordinata nello spazio tridimensionale. Quello che sto cercando è un trucco di interpolazione lineare veloce e sporco che funzioni in modo pulito con il layout specifico del tipo di colore .NET (valori separati per ARGB ecc.).

+1

non può essere lineare con tre punti casuali – RBarryYoung

risposta

13

In primo luogo, si richiede l'interpolazione lineare ma non si specifica che il colore B si trova sulla linea tra il colore A e il colore C; questo è necessario. Secondo, non hai specificato, ma ho intenzione di semplificare l'ipotesi che il colore B sia il punto medio della linea tra il colore A e il colore C; il seguente codice viene facilmente modificato se questo non è vero. Infine, ho cambiato la tua ipotesi che il parametro sia un numero intero compreso tra zero e cento per essere un doppio tra zero e uno. Il codice è più facile da scrivere e più facile da capire in quest'ultimo caso, e può ancora essere usato con il primo (dividi i tuoi input di un centinaio).

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); 
    } 
} 

Come si modifica questo se il colore B non è il punto intermedio tra il colore A e il colore C? Il modo più semplice è il seguente. Se il parametro (che io chiamo "lambda") è inferiore a 0.5, moltiplicare lo lambda di due e restituire il colore interpolato tra il colore A e il colore B. Se il parametro è maggiore di 0.5, moltiplicare lo lambda per due e sottrarre uno (le mappe [0.5, 1] su [0, 1]) e restituire il colore interpolato tra il colore B e il colore C.

Se non ti piace il requisito che il colore B vivi sulla linea tra il colore A e il colore C, allora puoi usare esattamente la modifica che Ho appena descritto di fare un'interpolazione lineare-parziale tra i colori.

Infine, non è stato specificato se si desidera interpolare il cosiddetto valore alfa ("A" in "ARGB"). Il codice sopra è facilmente modificabile per gestire anche questa situazione. Aggiungere un altro ComponentSelector definito come color => color.A, utilizzare InterpolateComponent per interpolare questo valore e utilizzare il sovraccarico Color.FromArgb(int, int, int, int) di Color.FromArgb.

+0

Grazie per la risposta dettagliata. Esattamente quello che stavo cercando! – user144051

+0

Per una soluzione più generica, potrebbe essere utile [esaminare la mia risposta su una domanda doppia] (http://stackoverflow.com/a/13253304/590790). –

1

Un altro modo per fondere i colori utilizzando la distribuzione gaussiana simili (qualsiasi numero di colori tra 0,0 - 1,0 per aumentare la miscelazione valore 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); 
} 

Ulteriori informazioni http://en.wikipedia.org/wiki/Normal_distribution

Campione di miscelazione 3 colori:

enter image description here