Skip to main content

The Escape-Time Algorithm

Fract’ol implements the classic escape-time algorithm to render both Mandelbrot and Julia sets. This algorithm determines whether a complex number belongs to the fractal set by iteratively applying a mathematical function and checking if the result “escapes” to infinity.

Core Mathematical Formula

The fundamental iteration formula is:
z = z² + c
Where:
  • z is a complex number that gets updated each iteration
  • c is a complex constant
  • For Mandelbrot: z starts at 0, c is the pixel coordinate
  • For Julia: z starts at the pixel coordinate, c is a constant parameter
Complex number squaring follows the algebraic expansion: (a + bi)² = (a² - b²) + 2abi

Implementation Details

The Main Pixel Handler

Every pixel on screen is processed through the handle_pixel() function in fractol_render.c:37-61:
void	handle_pixel(int x, int y, t_fractol *fractol)
{
	t_complex	z;
	t_complex	c;
	int			i;
	int			color;

	i = 0;
	z.x = (map(x, -2, +2, WIDTH) * fractol->zoom) + fractol->shift_x;
	z.y = (map(y, +2, -2, HEIGHT) * fractol->zoom) + fractol->shift_y;
	mandel_vs_julia(&z, &c, fractol);
	while (i < fractol->iterations_definition)
	{
		z = sum_complex(sqare_complex(z), c);
		if ((z.x * z.x) + (z.y * z.y) > fractol->escape_value)
		{
			color = map(i, PSYCHEDELIC_LIME, PSYCHEDELIC_MINT,
					fractol->iterations_definition);
			my_pixel_put(x, y, &fractol->img, color);
			return ;
		}
		i++;
	}
	my_pixel_put(x, y, &fractol->img, WHITE);
}
1
Step 1: Coordinate Transformation
2
Screen pixel coordinates (x, y) are mapped to the complex plane:
3
z.x = (map(x, -2, +2, WIDTH) * fractol->zoom) + fractol->shift_x;
z.y = (map(y, +2, -2, HEIGHT) * fractol->zoom) + fractol->shift_y;
4
Note that the y-axis is inverted (+2, -2) to match standard mathematical orientation.
5
Step 2: Set Type Selection
6
The mandel_vs_julia() function determines initial values:
7
if (!ft_strncmp(fractol->name, "Julia"))
{
	c.x = fractol->julia_x;
	c.y = fractol->julia_y;
}
else
{
	c.x = z.x;
	c.y = z.y;
}
8
Step 3: Iteration Loop
9
The core iteration applies z = z² + c until escape or maximum iterations:
10
while (i < fractol->iterations_definition)
{
	z = sum_complex(sqare_complex(z), c);
	if ((z.x * z.x) + (z.y * z.y) > fractol->escape_value)
	{
		// Point escapes - color based on iteration count
		color = map(i, PSYCHEDELIC_LIME, PSYCHEDELIC_MINT,
				fractol->iterations_definition);
		my_pixel_put(x, y, &fractol->img, color);
		return ;
	}
	i++;
}
11
Step 4: Escape Detection
12
A point has “escaped” when its magnitude exceeds the escape threshold:
13
(z.x * z.x) + (z.y * z.y) > fractol->escape_value
14
This computes |z|² > 4 (default escape_value = 4 in fractol_init.c:23).
15
Using squared magnitude |z|² instead of |z| avoids expensive sqrt() calls, providing significant performance improvement.

Mathematical Operations

Complex Number Squaring

From math_utils.c:33-40, the squaring operation implements the algebraic formula:
t_complex	sqare_complex(t_complex z)
{
	t_complex	result;

	result.x = (z.x * z.x) - (z.y * z.y);  // Real: a² - b²
	result.y = 2 * z.x * z.y;               // Imaginary: 2ab
	return (result);
}
Derivation:
(a + bi)² = a² + 2abi + (bi)²
          = a² + 2abi + b²i²
          = a² + 2abi - b²    (since i² = -1)
          = (a² - b²) + 2abi

Complex Number Addition

From math_utils.c:24-31:
t_complex	sum_complex(t_complex z1, t_complex z2)
{
	t_complex	result;

	result.x = z1.x + z2.x;
	result.y = z1.y + z2.y;
	return (result);
}
Complex addition is straightforward: (a + bi) + (c + di) = (a + c) + (b + d)i

The Map Function

The map() function in math_utils.c:15-22 performs linear interpolation to transform coordinates:
double	map(double unscaled_num, double new_min, double new_max, double old_max)
{
	double	old_min;

	old_min = 0;
	return ((new_max - new_min) * (unscaled_num - old_min)
		/ (old_max - old_min) + new_min);
}
Formula:
mapped_value = (new_max - new_min) × (value - old_min) / (old_max - old_min) + new_min
This function serves two critical purposes:
  1. Coordinate Mapping: Transforms pixel coordinates (0 to WIDTH/HEIGHT) to complex plane (-2 to +2)
  2. Color Mapping: Interpolates iteration count to RGB color values
The map function assumes old_min = 0, which works perfectly for screen coordinates and iteration counts.

Color Calculation

Color is determined by the iteration count at which a point escapes:
color = map(i, PSYCHEDELIC_LIME, PSYCHEDELIC_MINT, fractol->iterations_definition);
This creates a smooth gradient:
  • Points that escape quickly (low i) → PSYCHEDELIC_LIME (0x00FF00)
  • Points that escape slowly (high i) → PSYCHEDELIC_MINT (0x98FF98)
  • Points that never escape → WHITE (0xFFFFFF)

Color Interpolation Example

If iterations_definition = 30:
  • i = 0: Pure lime green (escaped immediately)
  • i = 15: Mix of lime and mint
  • i = 29: Nearly mint green (almost didn’t escape)
  • i = 30: White (in the set)
Linear interpolation provides smooth color gradients without discontinuous jumps. While more sophisticated coloring schemes exist (logarithmic, histogram-based), linear interpolation is computationally efficient and produces aesthetically pleasing results for real-time rendering.
Mathematically, the escape radius of 2 (or squared: 4) is sufficient because if |z| > 2 for quadratic polynomials, the sequence will diverge to infinity. Using exactly 4 as the threshold optimizes both mathematical correctness and computational efficiency.

Mandelbrot vs Julia Selection

The key difference between the two fractals is handled in fractol_render.c:23-35:
static void	mandel_vs_julia(t_complex *z, t_complex *c, t_fractol *fractol)
{
	if (!ft_strncmp(fractol->name, "Julia"))
	{
		c->x = fractol->julia_x;
		c->y = fractol->julia_y;
	}
	else
	{
		c->x = z->x;
		c->y = z->y;
	}
}
Mandelbrot Set:
  • z₀ = 0
  • c = pixel coordinate
  • Each pixel tests if that coordinate is in the set
Julia Set:
  • z₀ = pixel coordinate
  • c = constant (julia_x, julia_y)
  • Each pixel tests different starting points with the same c
A Julia set is essentially a “slice” through the Mandelbrot set at a specific c value. Every point in the Mandelbrot set has a corresponding Julia set.

Algorithm Complexity

For each pixel:
  • Time Complexity: O(n) where n = iterations_definition
  • Space Complexity: O(1) - only stores current z, c values
For the entire image:
  • Total Operations: WIDTH × HEIGHT × iterations_definition
  • At default settings: 2000 × 1500 × 30 = 90,000,000 operations per frame
Increasing resolution or iteration count has a multiplicative effect on rendering time. Doubling both parameters quadruples the computation required.

Build docs developers (and LLMs) love