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 :
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