tg4/Chapitre 2 - Récursivité/C06_tp_fractales_arbres.py
2023-10-19 15:29:33 +02:00

118 lines
5.6 KiB
Python

# coding: utf-8
#
# Chapitre 6 - TP 2 - Dessiner des fractales (page 129)
#
from math import cos, sin, radians
from matplotlib.colors import rgb2hex
from dessin import Dessin, Trait, Arc, main_loop
# constantes
TAILLE = 800 # taille de la surface de dessin
# la surface de dessin, avec un fond blanc
dessin_arbre = Dessin("white", TAILLE, TAILLE)
def ytree(
n,
x,
y,
orientation_arbre,
longueur,
ecart_angulaire_fils=radians(22.5),
ratio_longueur=0.8,
n_max=None,
):
""" Dessine un arbre binaire ytree de hauteur n+1.
Entrée:
- n : hauteur de l'arbre courant -1
- x,y : coordonnées de la racine de l'arbre
- orientation_arbre : orientation de l'arête partant de la racine du y-tree courant, en radians: radians(0) si horizontale, radians(90) = pi/2 pour orienter verticalement vers le haut
- longueur : longueur de l'arête partant de la racine du y-tree courant
- ratio_longueur : le ratio servant à diminuer la longueur d'un niveau à l'autre.
- n_max : le nombre de niveaux de l'arbre global. Utilisé uniquement pour attribuer la bonne couleur en fonction du niveau du y-tree courant dans l'arbre global.
-------
Un y-tree de niveau 1 (n=1) est un simple trait.
Un y-tree de niveau n>=2 est formé d'un trait suivi de 2 y-trees de niveau n-1.
Dans un y-tree:
=> c'est l'arête rejoignant un noeud à son père qui est l'axe de symétrie entre ses 2 sous-arbres.
=> l'angle entre les fils est constant: 2*ecart_angulaire_fils, donc chaque arête s'écarte de l'arête parente d'un angle +/-'ecart_angulaire_fils'.
On a choisi de diminuer la longueur d'un facteur constant à chaque niveau.
Les branches sont colorées proportionnellement à leur niveau dans l'arbre :
=> le lien partant de la racine globale est noir
=> pour n >= 2, les liens aboutissant aux feuilles sont verts
-------
Complexité: linéaire dans la taille de l'arbre.
"""
if n >= 0:
if n_max == None:
n_max = n
x1 = x + cos(orientation_arbre) * longueur
y1 = y + sin(orientation_arbre) * longueur
couleur = rgb2hex((0.0, 1.0, 0.0))
if n_max >= 1:
couleur = rgb2hex((0.0, float(n_max - n) / (n_max), 0.0))
Trait(dessin_arbre, x, y, x1, y1, couleur)
ytree(n-1, x1, y1, orientation_arbre + ecart_angulaire_fils, longueur*ratio_longueur, ecart_angulaire_fils, ratio_longueur, n_max)
ytree(n-1, x1, y1, orientation_arbre - ecart_angulaire_fils, longueur*ratio_longueur, ecart_angulaire_fils, ratio_longueur, n_max)
# A compléter
# Pour effectuer les appels récursifs :
# - ecart_angulaire_fils, ratio_longueur, n_max ne changent pas
# - la nouvelle origine est x1, y1
# - la longueur diminue et devient longueur*ratio_longueur
# - l'orientation des sous-arbre est obtenue en fonction de orientation_arbre et ecart_angulaire_fils
def dessiner_arbre_binaire_arc(n, x, y, w):
""" Dessine un arbre binaire de hauteur n+1, en représentant les arêtes par des arcs de cercle.
Entrée:
- x,y : coordonnées de la racine de l'arbre
- w : écart horizontal entre les 2 fils de la racine.
- h : écart vertical entre 2 niveaux
- n : hauteur de l'arbre courant -1
-------
L'arbre est représenté verticalement de haut en bas.
Les 2 noeuds sont reliés par des arcs de cercles à leurs parents (1/4 de cercle pour chaque fils, de manière à former un demi-cercle d'un fils à l'autre).
=> C'est l'axe vertical qui est l'axe de symétrie entre les 2 sous-arbres.
=> En divisant w par 2 à chaque niveau, on a la garantie que les branches ne se croisent pas (et comme les noeuds sont de simples points qui n'ont pas de largeur, il n'y a pas de risque que les feuilles se recouvrent).
-------
Complexité: linéaire dans la taille de l'arbre.
"""
if n > 0:
Arc(dessin_arbre, x - w / 2, y, x + w / 2, y + w, 0, 180)
dessiner_arbre_binaire_arc(n-1, x-w/2, y+w/2, w/2)
dessiner_arbre_binaire_arc(n-1, x+w/2, y+w/2, w/2)
def dessiner_arbre_binaire_trait(n, x, y, w, h):
""" Dessine un arbre binaire classique de hauteur n+1.
Entrée:
- x,y : coordonnées de la racine de l'arbre
- w : écart horizontal entre les 2 fils de la racine.
- h : écart vertical entre 2 niveaux
- n : hauteur de l'arbre courant -1
-------
L'arbre est représenté verticalement de haut en bas.
=> C'est l'axe vertical qui est la bisectrice entre les 2 arêtes rejoignant un noeud à ses fils.
=> En divisant w par 2 à chaque niveau, on a la garantie que les branches ne se croisent pas (et comme les noeuds sont de simples points qui n'ont pas de largeur, il n'y a pas de risque que les feuilles se recouvrent).
A vous de modifier pour que cet arbre soit fractal en modifiant l'écart vertical.
Tel qu'il est écrit en l'absence de modification, cet arbre n'est pas réellement fractal car l'angle entre les fils varie à chaque niveau (donc en "zoomant" on n'a pas exactement la même figure que la figure globale).
-------
Complexité: linéaire dans la taille de l'arbre.
"""
# A modifier :
if n > 0:
Trait(dessin_arbre, x, y, x - w / 2, y + h)
Trait(dessin_arbre, x, y, x + w / 2, y + h)
dessiner_arbre_binaire_trait(n - 1, x - w / 2, y + h, w / 2, h)
dessiner_arbre_binaire_trait(n - 1, x + w / 2, y + h, w / 2, h)
# Test :
ytree(10, 100, 200, 0, 50, radians(22.5), .8)
dessiner_arbre_binaire_arc(5, 600, 20, 160)
dessiner_arbre_binaire_trait(5, 600, 400, 160, 40)
main_loop()