Archive for 3 novembre 2011

Générer un dégradé en arc-en-ciel

3 novembre 2011

Pour un projet personnel (dont je reparlerais certainement plus tard sur ce blog), j’ai eu besoin de générer des points de couleur en fonction de la position de la valeur du point au sein d’un ensemble. Je souhaitais avoir quelque chose de continu, c’est à dire que 2 points ayant une valeur proche aient une couleur proche, et couvrir la plus large gamme possible de couleur, l’idéal étant d’avoir quelque chose qui se rapproche d’un arc en ciel.

Pour résumer, voici mes contraintes :

  • une variation continue des couleurs (autrement dit, hors de question de faire un saut du jaune au rouge par exemple si 2 points ont des valeurs voisines, chose que l’on obtient en général avec des algorithmes plus simplistes)
  • couvrir la plus large gamme possible de couleurs (pour résumer, je veux du rouge, du orange, du jaune, du vert, du bleu, du violet…)

C’est un problème d’apparence simple, mais qui nécessite d’être correctement modélisé pour parvenir à ses fins. Une simple étude du système RGB permet d’en trouver une solution. Traçons le graph des composantes rouges (R), vertes (V) et bleues (B) des couleurs ordonnées de l’arc en ciel. On obtient quelque chose comme ceci (on a en x les « numéro » des couleurs et en y la valeur entre 0 et 255 de chaque composante RGB. Les pastilles aux bases et aux sommets permettent de représenter la couleur résultante) :

RGB componant graphic

Graphique des composantes RVB

On voit ici que l’idée de l’algorithme est de ne faire varier qu’une seule composante à la fois.

Avec ce graphique, il est aisé de donner les fonctions résultantes des composantes R, V et B, qui sont de simple fonctions du premier degré, à définir par parties (l’objectif étant de d’avoir les valeurs de R, V et B sur l’intervalle [0;255], en ne faisant varier qu’une seule composante à la fois) :

  • Pour la composante rouge :
  (r) \left\{  \begin{array}{lrl}  \textrm{si } 0 \le x < 255 & :r= & 255 \\  \textrm{si } 255 \le x < 510 & :r= & 510-x \\  \textrm{si } 510 \le x < 1020 & :r= & 0 \\  \textrm{si } 1020 \le x \le 1275 & :r= & x-1020 \\  \textrm{si } 1275 \le x \le 1530 & :r= & 255 \\  \end{array}  \right.
  • Pour la composante verte :
  (v) \left\{  \begin{array}{lrl}  \textrm{si } 0 \le x < 255 & :v= & x \\  \textrm{si } 255 \le x < 765 & :v= & 255 \\  \textrm{si } 765 \le x < 1020 & :v= & 1020-x \\  \textrm{si } 1020 \le x \le 1530 & :v= & 0 \\  \end{array}  \right.
  • Pour la composante bleue :
  (b) \left\{  \begin{array}{lrl}  \textrm{si } 0 \le x < 510 & :b= & 0 \\  \textrm{si } 510 \le x < 765 & :b= & x-510 \\  \textrm{si } 765 \le x < 1275 & :b= & 255 \\  \textrm{si } 1275 \le x \le 1530 & :b= & 1530-x \\  \end{array}  \right.

Dans toutes ces formules, x représentant le « numéro » (ou l’indice) de la couleur que l’on souhaite obtenir. Si on a une valeur y comprise entre dans l’intervalle [y_{min};y_{max}], on peut obtenir x par la formule x = \dfrac{1530}{y_{max}-y_{min}} \times y.

A noter qu’ici, j’ai opté pour un intervalle de [0;1530] car il représente 6 times 255 soit le plus grand nombre de couleurs que l’on puisse obtenir avec cet algorithme (au delà, une même couleur pourrait avoir 2 numéros, ce qui serait bien sur inutile). Il est bien sur possible d’adapter cet algorithme de façon à avoir un nombre de nuances plus réduits (100 par exemple).

Cette algorithme produit ces variations (je vous présente ici la gamme complète des couleurs que peut générer l’algo) :

Dégradé généré par l'algorithme présenté

Dégradé généré par l’algorithme présenté

Passons au code, voici le code qui permet de générer les codes RGB correspondants aux valeurs. La fonction prend en argument l’intervalle et la valeur à partir de laquelle il faut  déterminer la couleur. Comme c’est du C++, la fonction retourne simplement un objet QColor (objet Qt).

QColor calcColor(double interval, double value){
QColor color;
int x = (1530/interval)*value;
int r;
int g;
int b;
if(x>=0 && x<255){
r = 255;
g = x;
b = 0;
}
if(x>=255 && x<510){
r = 510-x;
g = 255;
b = 0;
}
if(x>=510 && x<765){
r = 0;
g = 255;
b = x-510;
}
if(x>=765 && x<1020){
r = 0;
g = 1020-x;
b = 255;
}
if(x>=1020 && x<1275){
r = x-1020;
g = 0;
b = 255;
}
if(x>=1275 && x<=1530){
r = 255;
g = 0;
b = 1530-x;
}
color.setRgb(r, g, b);
return color;
}

A noter que cet algorithme devrait également fonctionner dans d’autres espaces de couleur que RGB (en CMY, cyan/magenta/jaune, notamment).