# Un programme que nous étudierons plus tard def Mandelbrot(f, Palette) :
I = CréerDessin(LARGEUR, HAUTEUR) for px in range(LARGEUR) : for py in range(HAUTEUR) :
# Chaque pixel se voit attribuer un certain entier n entre 0 et N_MAX
(x, y) = PixelVersPoint(px, py)
(n, u_n) = ItérationDeJulia(lambda z : f(z, x + 1j * y), 0.0)
# La couleur du pixel est choisie en fonction de cet entier
Couleur = Palette[int(n / N_MAX * (len(Palette) - 1))]
I.putpixel( (px, py), Couleur )
Afficher(I)
# On part du noir (i = 0) vers la couleur cible (i = TAILLE_PALETTE - 1) def ConstruirePaletteDégradéSimple(TAILLE_PALETTE, CouleurCible) :
P = [None] * TAILLE_PALETTE
(r_Cible, g_Cible, b_Cible) = CouleurCible for i in range(TAILLE_PALETTE) :
t = i / (TAILLE_PALETTE - 1)
r = int(t * r_Cible)
g = int(t * g_Cible)
b = int(t * b_Cible)
P[i] = (r, g, b) return P
>>> Centrer(0.0, 0.0, 2.0) >>> MAGENTA = (255, 40, 245) >>> P = ConstruirePaletteDégradéSimple(N_MAX + 1, MAGENTA) >>> Mandelbrot(lambda z, c : z ** 2 + c, P)
# Variante du précédent def ConstruirePaletteDégradéContour(TAILLE_PALETTE, CouleurCible) :
P = [None] * TAILLE_PALETTE
(r_Cible, g_Cible, b_Cible) = CouleurCible # On fait comme si la palette comportait une case en moins... for i in range(TAILLE_PALETTE - 1) :
t = i / (TAILLE_PALETTE - 2)
r = int(t * r_Cible)
g = int(t * g_Cible)
b = int(t * b_Cible)
P[i] = (r, g, b) # ...puis on attribue la couleur noire à la dernière case (l'indice maximal)
P[-1] = (0, 0, 0) return P
>>> Centrer(0.0, 0.0, 2.0) >>> MAGENTA_CLAIR = (255, 150, 255) >>> P = ConstruirePaletteDégradéContour(N_MAX + 1, MAGENTA_CLAIR) >>> Mandelbrot(lambda z, c : z ** 2 + c, P)
2 – Coloriage lisse
?
>>> Centrer(-0.7, 0.7, 0.5) >>> JAUNE = (255, 255, 40) >>> P = ConstruirePaletteDégradéSimple(N_MAX + 1, JAUNE) >>> Mandelbrot(lambda z, c : z ** 2 + c, P)
# Extrapolation de la palette from numpy import log as ln def Lisser(n, u_n, d, Palette) : if n == N_MAX : return Palette[-1] else :
ν = ln( ln(abs(u_n))/ln(R_MAX) ) / ln(d)
ξ = (n + 1 - ν) / N_MAX * (len(Palette) - 2)
i = int(ξ) ; ξ -= i
(r1, g1, b1) = Palette[i]
(r2, g2, b2) = Palette[i + 1]
r = int(r1 + ξ * (r2 - r1))
g = int(g1 + ξ * (g2 - g1))
b = int(b1 + ξ * (b2 - b1)) return (r, g, b)
# Utilisation de la palette avec lissage par extrapolation def Mandelbrot_lisse(f, d, Palette) :
I = CréerDessin(LARGEUR, HAUTEUR) for px in range(LARGEUR) : for py in range(HAUTEUR) :
(x, y) = PixelVersPoint(px, py)
(n, u_n) = ItérationDeJulia(lambda z : f(z, x + 1j * y), 0.0)
Couleur = Lisser(n, u_n, d, Palette)
I.putpixel( (px, py), Couleur )
Afficher(I)
>>> Centrer(-0.7, 0.7, 0.5) >>> JAUNE = (255, 255, 40) >>> P = ConstruirePaletteDégradéSimple(N_MAX + 1, JAUNE) >>> Mandelbrot_lisse(lambda z, c : z ** 2 + c, 2, P)
3 – Palettes cycliques
?
>>> Centrer(0.44246363018149587, 0.20865628845064585, 0.0000001) >>> N_MAX = 800 >>> VERT_FLUO = (90, 255, 140) >>> P = ConstruirePaletteDégradéSimple(100, VERT_FLUO) >>> Mandelbrot_lisse(lambda z, c : z ** 2 + c, d = 2, Palette = P)
# Utilisation d'une palette cyclique def Mandelbrot_cyclique(f, d, s, Palette) :
I = CréerDessin(LARGEUR, HAUTEUR) for px in range(LARGEUR) : for py in range(HAUTEUR) :
(x, y) = PixelVersPoint(px, py)
(n, u_n) = ItérationDeJulia(lambda z : f(z, x + 1j * y), 0.0)
Couleur = Cyclique(n, u_n, d, s, Palette)
I.putpixel( (px, py), Couleur )
Afficher(I)
# Extrapolation sur une palette cyclique def Cyclique(n, u_n, d, s, Palette) : if n == N_MAX : return Palette[-1] else :
ν = ln( ln(abs(u_n))/ln(R_MAX) ) / ln(d)
i_moins_1 = int(((n - 1) / N_MAX * len(Palette)) ** s) % len(Palette)
i = int((n / N_MAX * len(Palette)) ** s) % len(Palette) # Attention i_moins_1 peut être très différent de i - 1
(r1, g1, b1) = Palette[i_moins_1]
(r2, g2, b2) = Palette[i]
r = int(r1 + (1 - ν) * (r2 - r1))
g = int(g1 + (1 - ν) * (g2 - g1))
b = int(b1 + (1 - ν) * (b2 - b1)) return (r, g, b)
>>> Centrer(0.44246363018149587, 0.20865628845064585, 0.0000001) >>> N_MAX = 800 >>> VERT_FLUO = (90, 255, 140) >>> P = ConstruirePaletteDégradéSimple(100, VERT_FLUO) >>> Mandelbrot_cyclique(lambda z, c : z ** 2 + c, d = 2, s = 1.6, Palette = P)
4 – Dégradés multiples
?
# Extrapolation de la palette from random import shuffle as Mélanger def ConstruirePaletteDégradéMultiple(TAILLE_PALETTE, Couleurs) :
P = [None] * TAILLE_PALETTE # Répartition des différents dégradés sur la palette
(q, Reste) = divmod(TAILLE_PALETTE, len(Couleurs))
Restes = [1] * Reste + [0] * (len(Couleurs) - Reste)
Mélanger(Restes)
Indices = [] ; i = 0 for k in range(len(Couleurs)) :
Indices.append(i)
i += Restes[k]
i += q # Maintenant i doit être égal à len(Palette)
Indices.append(i) # Calcul de chaque dégradé for k in range(len(Couleurs)) :
(r1, g1, b1) = Couleurs[k]
(r2, g2, b2) = Couleurs[(k + 1) % len(Couleurs)] for i in range(Indices[k], Indices[k + 1]) :
t = (i - Indices[k]) / (Indices[k + 1] - Indices[k])
r = int(r1 + t * (r2 - r1))
g = int(g1 + t * (g2 - g1))
b = int(b1 + t * (b2 - b1))
P[i] = (r, g, b) return P
>>> Centrer(-0.7797023086458332, 0.1503391603125, 0.0000005) >>> N_MAX = 700 >>> P = ConstruirePaletteDégradéMultiple(100, [ROUGE, NOIR, ORANGE, NOIR]) >>> Mandelbrot_cyclique(lambda z, c : z ** 2 + c, d = 2, s = 1.6, Palette = P)
Pour terminer cette page, amusons-nous un peu avec le cercle chromatique. Il est constitué des couleurs qu'on appelle saturées, c'est-à-dire ayant une de leurs trois composantes à \(0\) et une autre à \(255\) (la saturation d'une couleur étant l'écart entre la plus grande et la plus petite des trois composantes rouge/vert/bleu). On peut obtenir toutes les couleurs saturées en faisant des dégradés depuis les six couleurs ayant une (ou deux) composante(s) à \(0\) et deux (ou une) composante(s) à \(255\) : à savoir les trois couleurs primaires de la synthèse additive (rouge, vert et bleu) et les trois couleurs primaires de la synthèse soustractive (jaune, magenta, cyan). Attention, il faut les placer dans le « bon » ordre (on met toujours à la suite des couleurs ayant deux composantes en commun).