Skip to content

Fractales

Énoncé

En se rappelant qu’un arbre est juste une branche sur laquelle poussent d’autres branches, créez un générateur d’arbres en SVG. Voici un exemple de résultat :

fractale.svg

Quelques indications

On vous recommande de bien découper votre code en fonctions.

Vous pouvez par exemple écrire des fonctions de manipulations des points : addition, soustraction, multiplication par un scalaire, calcul d'une distance entre deux points, rotation autour d'un angle, etc.

On rappelle à ce propos la formule permettant de calculer la rotation d'un point (x, y) par un angle α :

x' = x × cos(α) - y × sin(α)

y' = x × sin(α) + y × cos(α)

Pour la fonction récursive permettant de dessiner les branches :

  • on dessine des branches de moins en moins longues jusqu'à atteindre une taille minimale limite : ça sera le cas de base de la récursivité ;
  • on tirera aléatoirement tous les paramètres des sous-branches partant de la branche courante : nombre de sous-branches, côtés d'où elles partent, angles par rapport à la branche initiale, etc.

Il n'y a pas de solution fixe attendue : laissez libre cours à votre créativité !

Correction

Cliquez ici pour révéler la correction.

Voici le programme utilisé pour générer l'image ci-dessus. Il faut bien penser à découper notre code en fonctions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/env python3

"""Fractales : arbres, un exo sympa pour la recursion."""

from random import random, randint, choice
from math import pi, cos, sin, sqrt

import svg


def sub(point1, point2):
    """Renvoie un nouveau point égale à point1 - point2."""
    return (point1[0] - point2[0], point1[1] - point2[1])


def add(point1, point2):
    """Renvoie un nouveau point égale à point1 + point2."""
    return (point1[0] + point2[0], point1[1] + point2[1])


def mul(point, scalaire):
    """Renvoie un nouveau point égale à point * scalaire."""
    return (point[0] * scalaire, point[1] * scalaire)


def distance_a(point1, point2):
    """Renvoie la distance entre point1 et point2"""
    x_diff = point1[0] - point2[0]
    y_diff = point1[1] - point2[1]
    return sqrt(x_diff * x_diff + y_diff * y_diff)


def rotation(point, angle):
    """Tourne point autour de l'origine de l'angle donné en radians."""
    cosinus, sinus = cos(angle), sin(angle)
    return (
        cosinus * point[0] - sinus * point[1],
        sinus * point[0] + cosinus * point[1],
    )


def dessine_branche(depart, arrivee, limite):
    """Dessine la branche entre les deux points donnés.

    Cette fonction S'appelle récursivement jusqu'à ce que
    la taille de la branche soit inférieure à la limite.
    """

    # On s'arrête quand la branche est en dessous de la
    # taille limite
    taille_branche = distance_a(depart, arrivee)
    if taille_branche < limite:
        return

    # On dessine la branche
    print(svg.genere_segment(depart, arrivee))

    # On tire entre 2 et 4 sous branches au hasard
    for _ in range(randint(2, 4)):
        cote = choice((1, -1))  # à gauche ou à droite ?
        alpha = random() / 3  # dans le tiers du haut
        nouveau_depart = add(mul(depart, alpha), mul(arrivee, (1 - alpha)))
        taille = random()  # taille quelconque mais <= 1 * taille courante
        nouvelle_arrivee_relative_to_orig = mul(sub(arrivee, depart), taille)
        nouvelle_arrivee_relative_to_orig_rot = rotation(
            nouvelle_arrivee_relative_to_orig, cote * pi / 4 * random()
        )
        nouvelle_arrivee = add(nouveau_depart, nouvelle_arrivee_relative_to_orig_rot)
        dessine_branche(nouveau_depart, nouvelle_arrivee, limite)


def dessine_arbre():
    """Génère un arbre au format SVG sur la sortie standard."""
    print(svg.genere_balise_debut_image(800, 600))
    print(svg.genere_balise_debut_groupe("black", "black", 1))
    print(svg.genere_rectangle((0, 0), 800, 600))
    print(svg.genere_balise_fin_groupe())
    print(svg.genere_balise_debut_groupe("white", "none", 1))
    dessine_branche((400, 550), (400, 350), 5)
    print(svg.genere_balise_fin_groupe())
    print(svg.genere_balise_fin_image())


dessine_arbre()

Exercices