L'ensemble de Julia

A propos du code

Le code dans cet article a été écrit avec Code::Blocks et la SDL 2.
Vous pouvez trouver ici un guide pour installer ces logiciels.
Bien qu'il soit basé sur la SDL, je n'utilise pas ses fonctions directement. J'ai écrit une petite bibliothèque
avec quelques fonctions basiques pour faciliter la compréhension et la portabilité dans un autre langage.
Vous pouvez en apprendre plus sur cette bibliothèque ici.

Le code de cet article utilise la classe de nombres complexes ajoutée dans l'ensemble de Mandelbrot.

Le cousin de Mandelbrot

L'ensemble de Julia est un cousin de l'ensemble de Mandelbrot.
Ils sont si proches qu'en général quand les mathématiciens veulent démontrer une propriété de l'ensemble de
Mandelbrot ils la prouvent d'abord dans l'ensemble de Julia.
Souvenez-vous de la formule qu'on a utilisée pour l'ensemble de Mandelbrot.
		z0 = 0
		zn+1 = zn2 + cpix
				
Où tous les nombres sont des complexes et cpix est le nombre complexe associé au pixel courant.
En fait, comme z1 est toujours égal à cpix, car 02 = 0, on pourrait aussi utiliser cette formule
qui nous donnerait le même ensemble.
		z0 = cpix
		zn+1 = zn2 + cpix
				
L'ensemble de Julia utilise presque la même formule, à part qu'à chaque itération au lieu d'ajouter cpix,
on ajoute un nombre complexe constant qu'on a choisi au début du dessin.
		z0 = cpix
		zn+1 = zn2 + c
				
Alors c'est facile de modifier un des programme qu'on a utilisé pour l'ensemble de Mandelbrot.
		#include <stdio.h>
		#include <stdlib.h>
		#include <math.h>
		#include "main.h"
		#include "Graphics.h"
		#include "System.h"
		#include "math/Complex.h"

		#define SCREEN_WIDTH    640
		#define SCREEN_HEIGHT   480
		#define MAX_ITERATIONS  256

		void drawJulia(int offsetx, int offsety, Color* palette)
		{
			for (int y = 0; y < SCREEN_HEIGHT; y += 8)
			{
				for (int x = 0; x < SCREEN_WIDTH; x += 8)
				{
					// convert the pixel pos to a complex number
					Complex z;
					z.re = (x + offsetx - SCREEN_WIDTH  / 2.0) * 4.0 / SCREEN_WIDTH;
					z.im = (y + offsety - SCREEN_HEIGHT / 2.0) * 4.0 / SCREEN_WIDTH;

					// julia parameter
					Complex c(-0.70176, -0.3842);

					int iteration = 0;

					while (z.squareMag() <= 16 &&
					       iteration < MAX_ITERATIONS)
					{
						// compute the new value
						z = z * z + c;
						iteration++;
					}

					// draw the pixel
					Color   col;

					if (iteration == MAX_ITERATIONS)
					{
						col = Color(0, 0, 0);
					}
					else
					{
						col = palette[iteration];
					}

					gfx.setPixel(x + offsetx, y + offsety, col);
				}
			}
		}

		int main(int argc, char* argv[])
		{
			// init the window
			gfx.init("Julia", SCREEN_WIDTH, SCREEN_HEIGHT);
			gfx.init2D();
			gfx.clearScreen(Color(0, 0, 0, SDL_ALPHA_OPAQUE));

			static Uint8 pattern[64] =
			{
				 0, 32,  8, 40,  2, 34, 10, 42,
				48, 16, 56, 24, 50, 18, 58, 26,
				12, 44,  4, 36, 14, 46,  6, 38,
				60, 28, 52, 20, 62, 30, 54, 22,
				 3, 35, 11, 43,  1, 33,  9, 41,
				51, 19, 59, 27, 49, 17, 57, 25,
				15, 47,  7, 39, 13, 45,  5, 37,
				63, 31, 55, 23, 61, 29, 53, 21
			};

			static Color    palette[256];

			// set up the palette
			for (int i = 0; i < 256; ++i)
			{
				float angle = 2.0 * M_PI * (float)i / 256.0;
				float offset = 2.0 * M_PI / 3.0;
				angle += 40.0 *  M_PI / 128.0;
				angle *= 1.3;

				palette[i].r = 128 + 128 * sin(angle);
				palette[i].g = 128 + 128 * sin(angle * 1.1 - offset);
				palette[i].b = 128 + 128 * sin(angle * 1.5 + offset);
			}

			for (int i = 0; i < 64; i++)
			{
				int j = 0;
				while (pattern[j] != i)
					j++;

				drawJulia(j % 8, j / 8, palette);

				// display and process the events
				gfx.render();
				sys.wait(50);
				sys.processEvents();
				if (sys.isQuitRequested() == true)
					exit(EXIT_SUCCESS);
			}

			// wait until we quit
			while (sys.isQuitRequested() == false)
			{
				sys.processEvents();
				sys.wait(50);
			}

			gfx.quit();

			return EXIT_SUCCESS;
		}

		Download source code
		Download executable for Windows
				
Ce programme dessine l'ensemble de Julia correspondant au nombre -0.70176 - 0.3842 * i


La famille Julia

Comme l'ensemble de Julia correspond à un nombre complexe donné, il n'y a pas qu'un seul ensemble, mais toute une
famille d'ensembles.
Pour les explorer, vous pourriez changer la valeur de la constante dans le programme précédent et le recompiler.
Mais je voulais faire quelque chose de plus facile, alors on va utiliser les coordonnées de la souris pour changer
ce paramètre.

D'abord on va devoir écouter l'évènement SDL_MOUSEMOTION dans CSystem::processEvents()
		void CSystem::processEvents()
		{
			SDL_Event event;

			if (sdl.PollEvent(&event) != 0)
			{
				if (event.type == SDL_QUIT)
				{
					mQuitRequest = true;
				}
				else if (event.type == SDL_MOUSEMOTION)
				{
					mMouseX = event.motion.x;
					mMouseY = event.motion.y;
				}
			}
		}
				
Ensuite, on va modifier notre code pour mettre la constante complexe en fonction de la position courante de la
souris, et relancer le dessin dès que la souris bouge.
		int main(int argc, char* argv[])
		{
			// init the window
			[...]

			// set up the palette
			[...]

			int lastMouseX = 0;
			int lastMouseY = 0;

			while (sys.isQuitRequested() == false)
			{
				lastMouseX = sys.mMouseX;
				lastMouseY = sys.mMouseY;

				// julia parameter
				Complex c;
				c.re = (sys.mMouseX - SCREEN_WIDTH  / 2.0) * 4.0 / SCREEN_WIDTH;
				c.im = (sys.mMouseY - SCREEN_HEIGHT / 2.0) * 4.0 / SCREEN_WIDTH;

				for (int i = 0; i < 64; i++)
				{
					int j = 0;
					while (pattern[j] != i)
						j++;

					drawJulia(j % 8, j / 8, c, palette);

					gfx.render();
					sys.processEvents();

					if (lastMouseX != sys.mMouseX ||
						lastMouseY != sys.mMouseY ||
						sys.isQuitRequested() == true)
					{
						break;
					}
				}

				sys.processEvents();
			}

			gfx.quit();

			return EXIT_SUCCESS;
		}

		Download source code
		Download executable for Windows
				
Maintenant, si vous bougez la souris, vous devriez voir l'ensemble changer en temps réel et prendre différentes
formes.

Le lien avec Mandelbrot

Les ensembles de Julia ont un lien profond avec l'ensemble de Mandelbrot.
Les formes qu'ils prennent quand la constante complexe varie sont liées au point correspondant du Mandelbrot.
Si vous prenez un point loin du Mandelbrot, vous ne verrez que de petites îles de points.
En vous déplaçant vers la frontière de l'ensemble, les formes vont devenir plus complexes, les îles vont se
connecter ensemble et des spirales vont apparaître.
Et vous ne verrez des zones noires dans l'ensemble de Julia que si le point correspondant tombe à l'intérieur de
l'ensemble de Mandelbrot.

Pour explorer ça visuellement, dans ce 3ième programme, on va dessiner l'ensemble de Mandelbrot à gauche de la
fenêtre et l'ensemble de Julia à droite.


Si vous bougez votre souris sur le Mandelbrot, l'ensemble de Julia correspondant apparaîtra à droite.
		#include <stdio.h>
		#include <stdlib.h>
		#include <math.h>
		#include "main.h"
		#include "Graphics.h"
		#include "System.h"
		#include "math/Complex.h"

		#define SCREEN_WIDTH    1280
		#define SCREEN_HEIGHT   480
		#define MAX_ITERATIONS  256

		// pixel coordinates to complex number
		Complex pixToComp(int x, int y)
		{
			Complex c;
			double w = SCREEN_WIDTH / 2.0;
			double h = SCREEN_HEIGHT;
			c.re = (x - w / 2.0) * 4.0 / w;
			c.im = (y - h / 2.0) * 4.0 / w;

			return c;
		}

		void drawJulia(int offsetx, int offsety, Complex c, Color* palette)
		{
			for (int y = 0; y < SCREEN_HEIGHT; y += 8)
			{
				for (int x = 0; x < SCREEN_WIDTH / 2; x += 8)
				{
					// convert the pixel pos to a complex number
					Complex z = pixToComp(x + offsetx, y + offsety);

					int iteration = 0;

					while (z.squareMag() <= 16 &&
					       iteration < MAX_ITERATIONS)
					{
						// compute the new value
						z = z * z + c;
						iteration++;
					}

					// draw the pixel
					Color   col;

					if (iteration == MAX_ITERATIONS)
					{
						col = Color(0, 0, 0);
					}
					else
					{
						col = palette[iteration];
					}

					gfx.setPixel(SCREEN_WIDTH / 2 + x + offsetx, y + offsety, col);
				}
			}
		}

		void drawMandelbrot(Color* palette)
		{
			for (int y = 0; y < SCREEN_HEIGHT; y++)
			{
				for (int x = 0; x < SCREEN_WIDTH / 2; x++)
				{
					// convert the pixel pos to a complex number
					Complex c = pixToComp(x, y);
					c.re -= 0.7;

					Complex z;
					int iteration = 0;

					while (z.squareMag() <= 16 &&
					       iteration < MAX_ITERATIONS)
					{
						// compute the new value
						z = z * z + c;
						iteration++;
					}

					// draw the pixel
					Color   col;

					if (iteration == MAX_ITERATIONS)
						col = Color(0, 0, 0);
					else
						col = palette[iteration];

					gfx.setPixel(x, y, col);
				}
			}
		}

		int main(int argc, char* argv[])
		{
			// init the window
			gfx.init("Julia 3", SCREEN_WIDTH, SCREEN_HEIGHT);
			gfx.init2D();
			gfx.clearScreen(Color(0, 0, 0, SDL_ALPHA_OPAQUE));

			static Uint8 pattern[64] =
			{
				 0, 32,  8, 40,  2, 34, 10, 42,
				48, 16, 56, 24, 50, 18, 58, 26,
				12, 44,  4, 36, 14, 46,  6, 38,
				60, 28, 52, 20, 62, 30, 54, 22,
				 3, 35, 11, 43,  1, 33,  9, 41,
				51, 19, 59, 27, 49, 17, 57, 25,
				15, 47,  7, 39, 13, 45,  5, 37,
				63, 31, 55, 23, 61, 29, 53, 21
			};

			static Color    palette[256];

			// set up the palette
			for (int i = 0; i < 256; ++i)
			{
				float angle = 2.0 * M_PI * (float)i / 256.0;
				float offset = 2.0 * M_PI / 3.0;
				angle += 40.0 *  M_PI / 128.0;
				angle *= 1.3;

				palette[i].r = 128 + 128 * sin(angle);
				palette[i].g = 128 + 128 * sin(angle * 1.1 - offset);
				palette[i].b = 128 + 128 * sin(angle * 1.5 + offset);
			}

			int lastMouseX = 0;
			int lastMouseY = 0;

			// julia parameter
			Complex c;

			drawMandelbrot(palette);

			while (sys.isQuitRequested() == false)
			{
				lastMouseX = sys.mMouseX;
				lastMouseY = sys.mMouseY;

				if (sys.mMouseX < SCREEN_WIDTH / 2)
				{
					c = pixToComp(sys.mMouseX, sys.mMouseY);
					c.re -= 0.7;
				}

				for (int i = 0; i < 64; i++)
				{
					int j = 0;
					while (pattern[j] != i)
						j++;

					drawJulia(j % 8, j / 8, c, palette);

					gfx.render();
					sys.processEvents();

					if (lastMouseX != sys.mMouseX ||
						lastMouseY != sys.mMouseY ||
						sys.isQuitRequested() == true)
					{
						break;
					}
				}

				sys.processEvents();
			}

			gfx.quit();

			return EXIT_SUCCESS;
		}

		Download source code
		Download executable for Windows
				

Liens

Vidéo du deuxième programme

Science & Vie Micro n°27 page 83

Vidéo: Ensemble de Julia en processing