Go to Hackademy website

Algorithme déterministe de génération de couleurs

Martin Catty

Posté par Martin Catty dans les catégories back

Le problème: générer une suite déterministe de nombres

Dans un programme il arrive parfois que vous ayez à générer une suite de nombres aléatoire. Pour cela rand fait très bien l’affaire.

Par contre vous pouvez avoir besoin de générer une suite de nombres déterministes, c’est à dire que la suite générée sera toujours la même.

Dans mon cas il s’agit de générer un tableau de couleurs pour un chart. Le nombre de couleurs n’est pas défini à l’avance mais je souhaite que les couleurs générées soit toujours les même pour éviter que le graphique change complètement à chaque rafraichissement.

Le fonctionnement de rand

Lorsqu’on utilise un générateur comme rand il commence par initialiser son seed, un nombre sur lequel sera basé la génération des nombres.

Ce nombre est normalement initialisé de façon complètement aléatoire, en se basant sur /dev/random ou /dev/urandom (sur les systèmes UNIX) pour maximiser l’entropie des nombres générés.

Le but étant que les nombres générés soient le moins «devinable» possible.

Dans notre cas nous voulons une suite déterministe, nous allons donc initialiser le seed nous même.

Pour cela en ruby 1.8 on peut utiliser srand:

srand 42
a = [rand, rand, rand]
srand 42
[rand, rand, rand] == a
=> true

En ruby 1.9 on a la classe Random directement:

generator = Random.new(42)
generator.rand

La génération de couleurs

def colors(size)
  1.upto(size).inject([]) { |colors, index|
    r, g, b = yield
    colors << "#%02x%02x%02x" % [r, g, b]
  }
end

def hsv_to_rgb(h, s, v)
  h_i = (h * 6).to_i
  f   = h * 6 - h_i
  p   = v * (1 - s)
  q   = v * (1 - f * s)
  t   = v * (1 - (1 - f) * s)
  case h_i
  when 0
    r, g, b = v, t, p
  when 1
    r, g, b = q, v, p
  when 2
    r, g, b = p, v, t
  when 3
    r, g, b = p, q, v
  when 4
    r, g, b = t, p, v
  when 5
    r, g, b = v, p, q
  end
  [(r * 256).to_i, (g * 256).to_i, (b * 256).to_i]
end

srand 42
c = colors(5) { hsv_to_rgb(rand, 0.5, 0.30) }

Plutôt que d’utiliser une génération pure de 3 couleurs avec rand(256) on utilise la représentation hsv (tsv en français) des couleurs: teinte, saturation, valeur.

Le premier paramètre code la couleur tandis que les suivant codent l’intensité et la brillance. L’intérêt est de générer des couleurs dans les même tons, pour éviter d’avoir des couleurs pastel et d’autres très vives dans le chart.

Vous pouvez donc ajuster manuellement votre teinte et votre saturation. Dans l’exemple on a 0.5 et 0.3.

Je ne vais pas vous expliquer l’algorithme de conversion hsv_to_rgb, je me suis servi de l’article de wikipedia: Teinte Saturation Valeur - Wikipédia

Pour le reste j’appelle colors en lui passant hsv_to_rgb en block. Une fois mes 3 couleurs obtenues, je transforme rgb en notation hexadécimale. Si la notation vous surprend jetez un œil du côté de sprintf.

Précision importante si vous ne lisez pas l’article de wikipedia, l’algorithme de conversion RGB vers TSV n’est pas réversible, vous pouvez donc représenter toutes les couleurs RGB en notation TSV mais pas l’inverse (car l’espace TSV contient plus de couleurs que celui RGB).

L’équipe Synbioz.

Libres d’être ensemble.

Articles connexes

Une brève histoire d'Elixir et Erlang/OTP

31/01/2019

Je développe depuis plusieurs années en Ruby. Depuis mon arrivée chez Synbioz, j’expérimente en plus avec Elixir de façon assez naturelle. En quoi Elixir est-il différent, me demanderez-vous ? Pour...

Écrire une thread pool en Ruby

10/01/2019

Pouvoir exécuter plusieurs tâches en parallèle, que ce soit dans un script ou une application, peut être vraiment très utile, surtout dans le cas où le traitement de ces tâches peut être très long....

Translation temporelle

31/05/2018

Cette semaine, je me suis essayé à un nouveau format d’article qui se présente sous la forme d’une nouvelle de Science-Fiction. Je tiens en passant à remercier Valentin pour ses illustrations....

Authentifier l'accès à vos ressources avec Dragonfly

11/05/2017

Pour ceux qui ne connaissent pas Dragonfly, c’est une application Rack qui peut être utilisée seule ou via un middleware. Le fait que ce soit une application Rack la rend compatible avec toutes les...