Is my fractal optimized? Could it animate?

Hello everyone, hope you are doing fine.
I am trying to implement a recursive system. The recursion is working properly, my questions are: is this optimal? Am i doing this in a efficient way? Can i implement some kind of delay between the instances of the fractal?

PGraphics g_1, g_2, g_3, g_4, g_5;
float t = 0;

void setup() {
	size(1000, 1000, P3D);
	background(0);

	fill(250);
	noStroke();
	noSmooth();


  g_1 = createGraphics(50, 50);
  g_2 = createGraphics(100, 100);
  g_3 = createGraphics(500, 500);
  g_4 = createGraphics(1000, 1000);

	
  	

	
}

void draw() {

	t += 0.07;
	  g_1 = createGraphics(50, 50);
  g_2 = createGraphics(100, 100);
  g_3 = createGraphics(500, 500);
  g_4 = createGraphics(500, 500);
  g_5 = createGraphics(1000, 1000);


	g_1.beginDraw();
  	g_1.fill(255);
  	g_1.stroke(255);
	g_1.point(2, 2);
	g_1.endDraw();


	g_2.beginDraw();
	for (int i = 1; i < 7; ++i) {
		g_2.tint(i*30, 50, 50);
		g_2.image(g_1, 20+(i*(1+sin(t/2.0))), 20+(i*((1+sin(t/2.0))*4.0)));
	}
	g_2.endDraw();


	g_3.beginDraw();
	for (int i = 1; i < 9; ++i) {
		//g_3.scale(1+(1.1+sin(t/2.0))*4);
		//rotate(i);
		g_3.image(g_2, i*5, 0);
	}
	g_3.endDraw();


	g_4.beginDraw();
	g_4.translate(250, 250);
	for (int i = 1; i < 20; ++i) {
		g_4.scale(1+(i/903.0));
		g_4.rotate(i/47.0);
		g_4.image(g_3, 00, 0);
	}
	g_4.endDraw();




	g_5.beginDraw();
	g_5.translate(250, 250);
	g_5.scale(0.2);
	for (int i = 1; i < 40; ++i) {
		g_5.scale(1+(i/43.0));
		g_5.tint(200, i*10, i*10);
		g_5.image(g_4, i*5, 0);
	}
	g_5.endDraw();


	background(0);
	image(g_5, 0, 0);

}

Hi,

Welcome to the forum! :wink:

If you check the definition of recursion on Wikipedia :

In computer science, recursion is a method of solving a problem where the solution depends on solutions to smaller instances of the same problem.

Recursion solves such recursive problems by using functions that call themselves from within their own code.

So your code doesn’t exactly use what we usually describe as recursion. You are manually running a for loop multiple times with different depth but that’s very tedious! :grinning:

Remember that programming is always trying to avoid repetition.

Let’s suppose I want to draw this shape :

fractal

As you can see, there’s a main circle then on each sides there’s 4 smaller circles that themselves have other smaller circles as well.

A naĂŻve solution might be to implement it like this :

// Go to the center of the screen
translate(width / 2, height / 2);

noFill();

// The first radius
float r1 = 100;

// First circle
circle(0, 0, r1 * 2);

// Second radius
float r2 = r1 / 2;

// Second circles
circle(r1, 0, r2 * 2);
circle(-r1, 0, r2 * 2);
circle(0, r1, r2 * 2);
circle(0, -r1, r2 * 2);

// Third radius
float r3 = r2 / 2;

// Second circles
circle(r1 + r2, 0, r3 * 2);
circle(r1 - r2, 0, r3 * 2);
circle(r1, r2, r3 * 2);
circle(r1, -r2, r3 * 2);

circle(-r1 + r2, 0, r3 * 2);
circle(-r1 - r2, 0, r3 * 2);
circle(-r1, r2, r3 * 2);
circle(-r1, -r2, r3 * 2);

circle(0, r1 + r2, r3 * 2);
circle(0, r1 - r2, r3 * 2);
circle(r2, r1, r3 * 2);
circle(-r2, r1, r3 * 2);

circle(0, -r1 + r2, r3 * 2);
circle(0, -r1 - r2, r3 * 2);
circle(r2, -r1, r3 * 2);
circle(-r2, -r1, r3 * 2);

It gives exactly the same result but this is very tedious (and you’ll never want to do this by hand :yum: )

What we can extract from that is that each time we take position and a radius then we draw a circle. From that circle we do the same at each corners with circles that are half the size.

This is exactly what recursion is for : a function that calls itself :

void circles(float x, float y, float radius, int depth) {
  if (depth == 0) return;

  noFill();
  circle(x, y, radius * 2);

  float half = radius / 2;
  circles(x + radius, y, half, depth - 1);
  circles(x - radius, y, half, depth - 1);
  circles(x, y + radius, half, depth - 1);
  circles(x, y - radius, half, depth - 1);
}

If you wonder what the depth argument is for, ask you the question :

If a function calls itself over and over, it will be infinite (a calls b calls c calls d…). Therefore how can I stop it ?

We need what we call a base case, this is a case where we don’t need to do recursion : in this case when we reached the desired depth in the function call history (bc we are decreasing the depth parameter each time).

That’s why the statement :

if (depth == 0) return;

is really important because as soon as we reach the higher depth, we exit the function and don’t infinitely call the functions.

Now the power of this is that it’s less lines of code and easily procedural :

circles(width / 2, height / 2, 100, 4);
// or
circles(width / 2, height / 2, 100, 6);

fractal fractal8

This example was to show you how you can use recursion to easily implement algorithms that would be impossible to code by hand!

Also for performance, I think that you shouldn’t use PGraphics but directly draw the points using a recursive function!

Have fun :yum:

4 Likes

Hello, thank you!
I am familiar with the type of recursion you are mentioning, but i want certain parts of the fractal to be done using different transform operations. With my approach, i would only need to have one image ready and draw it with simple rotations, scales, translates and it doesn’t need to calculate very single smaller elements again, because they are stored as pixel information.

Im not really sure how i could do the process i am imagining using the recursive function approach… I am envisioning something like:
i draw a dot;
i duplicate the dot 5 times changing the x position each time;
i take these 5 dots and duplicate them, rotating and scaling each time;
i take this spiral made of dots and rotate it;

How would it be best to implement something like this?

1 Like

If you would like to implement a recursion and animate it, one possible strategy would be as follows:

  • Within the recursive function, save the specifications of each part of the fractal as data rather than actually draw it.
  • From within setup(), call the recursive function. The data will then get saved.
  • Call frameRate() to set the rate of the animation.
  • From draw(), fetch the specifications for one part of the fractal during each frame, and render that part.
  • Call noLoop() after all parts of the fractal have been rendered.

I like Python, and so used Python Mode to implement @josephh’s circles fractal based on the above strategy. Here’s the code:

params = [] # each item will be a list of specs for a circle
def setup():
    size(400, 400)
    background(255)
    noFill()
    frameRate(10)
    recursive_circle(width / 2, height / 2, 200, 4)

def draw():
    if frameCount <= len(params):
        item = params[frameCount - 1]
        circle(item[0], item[1], item[2])
    else:
        noLoop()

def recursive_circle(x, y, d, level):
    # params.append([x, y, d])
    if level == 0:
        # base case; do nothing
        pass
    else:
        # recursive case
        recursive_circle(x - d / 2, y, d / 2, level - 1)
        recursive_circle(x, y - d / 2, d / 2, level - 1)
        recursive_circle(x + d / 2, y, d / 2, level - 1)
        recursive_circle(x, y + d / 2, d / 2, level - 1)
    params.append([x, y, d])

Here’s a screen capture of the process partway through its execution:

1 Like