/fract-ol

[42 SCHOOL - LEVEL 2] My first computer graphics project: a fractal generator built with the minilibx library.

Primary LanguageC

Fract-ol

This project was developed for 42 school. For comprehensive information regarding the requirements, please consult the PDF file in the subject folder of the repository. Furthermore, I have provided my notes and a concise summary below.

+ keywords: similar repeated patterns
+ similarity occurs at different scales
+ leaf, trees, cauliflower, bronchial system, coast line
+ set of complex numbers (real + imaginary)
+ estable iteration

Mindmaps (shinckel, 2023) mind-map_fract-ol1

mind-map_fract-ol2

mind-map_fract-ol3

Graph from Geogebra, author Ben Sparks. "Simple exploration of the Mandelbrot Set (and the orbits of the iteration with different 'c' values). Stable orbits are coloured black. The colours 'outside' the set are determined by how quickly the iteration diverges..." Screenshot 2023-08-01 at 8 56 42 PM

Visualization of the Mandelbrot and Julia iteration, made by Stefan Bion Click here mind-map_fract-ol4

High-level Overview

The Mandelbrot set, denoted M, is the set of complex numbers c such that the critical point z = 0 of the polynomial $P(z) = z^2 + c$ has an orbit that is not attracted to infinity. By iterating the formula $f_{c}(z) = z^2 + c$ and evaluating the behavior of the resulting complex numbers, it is possible to classify each pixel on the canvas as either part of the Mandelbrot set (bounded) or not (escaped). Theorem: The orbit of 0 tends to infinity if and only if at some point it has modulus >2.

  1. Choose a maximal iteration number N;
  2. For each pixel p of the image:
  • Let c be the complex number represented by p
  • Let z be a complex variable
  • Set z to 0
  1. Do the following N times:
  • If |z|>2 then color the pixel white, end this loop prematurely, go to the next pixel
  • Otherwise replace z by z*z+c, a.k.a. iterate through the Mandelbrot formula
  1. If the loop above reached its natural end: color the pixel p in black
  2. Go to the next pixel

References:
Math University Toulouse
WikiBooks
The Nature of Code, chapter 8
Distance Estimator algorithm

Concepts

Task Prototype Description
git submodule git submodule add git@github.com:shinckel/libft.git To add an external library to your project, it will generate a .gitmodules file. P.S. You must add targets to the Makefile!
xquartz brew install --cask xquartz allows cross-platform applications using X11 for the GUI to run on macOS
X11 x enables users to run graphical applications on a remote server and interact with them using their local display and I/O devices
Create canvas mlx_init() mlx_new_window() mlx_loop() Initializes the MiniLibX library and assigns the mlx(connection with the graphical server); creates a new window using the specified width, height, and name, and assigns the window pointer to fractal.win; enters the event loop of MiniLibX, which continuously listens for events such as key presses and mouse movements. This function call will keep the program running until the window is closed
put some color for (int x = 0; x < fractal.width; x++) { for (int y = 0; y < fractal.height; y++) { mlx_pixel_put(fractal.mlx, fractal.win, x, y, bg_color); } } Manage the color pixel per pixel: graphical server - window pointer - x/y coordinates - color
handling events mlx_key_hook(fractal.win, deal_keys, (void *)0); Intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a hook. In this case, intercepting keyboard or mouse event
scanline methods x (a.k.a. raster scan) All pixels in the image will be scanned. For each pixel, the color has to be determined. The coordinates of the pixel are converted into mathematical parameters. Then an algorithm is run on that parameter
aspect ratio width / height 4:3 320x40 display an image of a specific size, inside a rectangle that is another size(difference between image and container)
double double zx double pi = 3.14159 to handle the floating-point calculations accurately
orbit trap $0 * 0 - 0 * 0 + (-0.5) = -0.5$ ... $(-0.5) * (-0.5) - 0 * 0 + (-0.5) = 0.25 - 0.5 = -0.25$ an orbit refers to a sequence of values generated by repeatedly applying a function or transformation to an initial value. Each value in the sequence becomes the input for the next iteration, and the process continues indefinitely or until a specific condition is met. The behavior of the orbit: does the point belong to the fractal set? does it tend to infinity, remain bounded, or exhibit certain patterns?
Theorem The orbit of 0 tends to infinity if and only if at some point it has modulus >2 The modulus of a complex number measures its distance from the origin (0, 0) in the complex plane. Mathematically, if $z = a + bi$, where $a$ is the real part and $b$ is the imaginary part, then the modulus(a.k.a. magnitude) $z$ is given by $sqrt(a^2 + b^2)$. If there exists a value in the orbit whose distance from the origin exceeds 2, then the orbit will eventually escape to infinity. modulus of $z$: $\sqrt{3^2 + 4^2} = \sqrt{9 + 16} = \sqrt{25} = 5$
$f_{c}(z) = z^2 + c$ $(x^2 - y^2) + (2xy)i + (c_re + c_imi)$ in the complex plane, x is the real axis, therefore the real component of z, and y is the imaginary axis, the imaginary component of z. To calculate zx, I will extract the first part of the formula: fractal->zx_new = zx * zx - zy * zy + fractal->cx;, and for zy: fractal->zy_new = 2 * zx * zy + fractal->cy;
modulus of $z = sqrt(a^2 + b^2) &gt; 2$ zx * zx + zy * zy > 4.0 I don't need to calculate the square root of $(a^2 + b^2)$ as any number bigger than $4$ will have the square root bigger than $2$, as $\sqrt{4} = 2$
xarrow yarrow c.x = (x + frac->xarrow) / frac->zoom * (0.47 + 2.0) / (WIDTH - 1) - 2.0; position horizontally xarrow and vertically yarrow relative to the pixel coordinates x y. These values are necessary for moving the drawing in the screen through the key hooks
Mandelbrot boundary $x = (-2.0, 0.47)$ $y = (-1.12, 1.12)$ These are the coordinates of the outer boundary of the mandelbrot set. I can use it as value to establish its size in relation to the screen Screenshot 2023-08-01 at 9 01 23 PM
smooth colors $iter + 1 - log(log(sqrt(zx * zx + zy * zy))) / log(2)$ escape time (how long does it take, a.k.a. number of iterations, before going to infinity?), color algorithms: the "bands" are generated because we can only set colors to pixels in discrete numbers of iterations, therefore the colors are divided as solid areas. The outermost solid color is the one out of the bailout radius. References: Plotting algorithms - Wikipedia, Renormalizing the Mandelbrot Escape, Mandelbrot Set Coloring Presentation, Draw a Mandelbrot set fractal with smoothly shaded colors in C#
42 docs BitShifting void my_mlx_pixel_put(t_data *data, int x, int y, int color) In my opinion, these are two concepts really important for this project. The first, how RGB colors can be encoded in int format, the second, pushing images to a window instead of drawing pixel per pixel
Julia set $f_{c}(z) = z^2 + c$ It is deeply connected to the Mandelbrot set, the same formula a.k.a patterns that emerge from the Mandelbrot map. Key concept: instead of the complex number $c$ being related to the pixel you are iterating in the complex plane, it will be just a constant. If there is no imaginary part, the set will be symmetrical. Summary, different types
hooks TAB = 65289 XK_Tab key codes. For an improved version, I could use Keysyms instead. They are defined in two standard include files: <X11/keysym.h> and <X11/keysymdef.h>
color pallette rgb1 = ((int)(mu * 25) % 256) << 16; I could have more control of my get_color() results throught the establishment of a prior pallette. In my current implementation, I am multiplying my smooth value by a factor that produces variations in RGB values, and then normalizing it to the needed range (256 possible values in R, G and B). I could be much more efficient, see the example below in C++:
private Color GetColor(double mu)
        {
            int clr1 = (int)mu;
            double t2 = mu - clr1;
            double t1 = 1 - t2;
            clr1 = clr1 % Colors.Count;
            int clr2 = (clr1 + 1) % Colors.Count;

            byte r = (byte)(Colors[clr1].R * t1 + Colors[clr2].R * t2);
            byte g = (byte)(Colors[clr1].G * t1 + Colors[clr2].G * t2);
            byte b = (byte)(Colors[clr1].B * t1 + Colors[clr2].B * t2);

            return Color.FromArgb(255, r, g, b);
        }

Some of my results

Screenshot 2023-08-01 at 9 02 19 PM Screenshot 2023-08-01 at 9 02 42 PM Screenshot 2023-08-01 at 9 03 16 PM Screenshot 2023-08-01 at 9 04 09 PM Screenshot 2023-08-01 at 9 04 27 PM Screenshot 2023-08-01 at 9 05 04 PM Screenshot 2023-08-01 at 9 05 36 PM Screenshot 2023-08-01 at 9 06 09 PM Screenshot 2023-08-01 at 9 06 33 PM Screenshot 2023-08-01 at 9 06 52 PM