Les programmes qui suivent sont dans le répertoire examples/recur.
Ces programmes se trouvent dans examples/recur/carre.cxx.
On commence par des exemples simples.
On trace un carré puis le carré qui joint les milieux des cotés etc...
on s’arrête quand les segments à dessiner deviennent trop petits ou quand
on a un dessin de profondeur n (n est le nombre d’étapes necessaires
pour réaliser le dessin).
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p),
puis on valide avec OK :
carres(A,B):={ DispG(); carre(A,B); if (longueur2(A,B)>0.01) { carres(A+(B-A)/2,B+(B-A)*i/2); } };
On tape :
carres(-1.0,1.0)
On obtient le dessin des carrés du plus grand au plus petit : le dessin
du carré (-1,1,1+2*i,-1+2*i) puis du carré (0,1+i,i,-1+i)....
bf Remarque
si on tape
carres2(A,B):={ DispG(); if (longueur2(A,B)>0.01) { carres2(A+(B-A)/2,B+(B-A)*i/2); } carre(A,B); };
puis :
carres2(-1.0,1.0)
le dessin des carrés ne se fera pas dans le même ordre et se fera
du plus petit au plus grand.
Autre test d’arrêt
On peut avoir besion de connaitre le nombre de n de fois que l’on fait
le ou les appels récursifs pour avoir un dessin de "profondeur" n.
On rajoute pour cela un paramètre qui sera la profondeur.
Dans l’exemple ci-dessus, on tape dans un éditeur de programme (que l’on
ouvre avec Alt+p), puis on valide avec
OK le programme :
carrep(A,B,n):={ DispG(); carre(A,B); if (n==0) return 0; carrep(A+(B-A)/2,B+(B-A)*i/2,n-1); };
On tape :
carres(-1.0,1.0,5)
On obtient le dessin des carrés du plus grand au plus petit :le dessin
du carré (-1,1,1+2*i,-1+2*i) puis du carré (0,1+i,i,-1+i)...
Généralisation
On trace un carré ABCD, puis le carré MNPQ avec :
AM=a*AB,
BN=a*BC,
CO=a*CD,
DP=a*DA,
où a est un nombre réel entre 0 et 1.
carresp(A,B,a):={ DispG(); carre(A,B); if (longueur2(A,B)>0.01) { carresp(A+(B-A)*a,B+(B-A)*i*a,a); } };
On tape par exemple :
carresp(-1.0,1.0,0.2)
Ces programmes se trouvent dans examples/recur/triangle.cxx.
On trace un triangle puis le triangle qui joint les milieux des cotés etc...on s’arrête quand les segments à dessiner deviennent trop petits.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p),
puis on valide avec OK :
triangles(A,B,C):={ DispG(); triangle(A,B,C); if (longueur2(A,B)>0.01) { triangles(A+(B-A)/2,B+(C-B)/2,C+(A-C)/2); } };
On tape :
triangles(-2.0,1,2*i)
On obtient le dessin des triangles du plus grand au plus petit :le dessin
du triangle (-2,1,2*i) puis du triangle (-0.5,0.5+i,-1+i,)....
Remarque si on tape
trianglesp(A,B,C):={ DispG(); if (longueur2(A,B)>0.01) { trianglesp(A+(B-A)/2,B+(C-B)/2,C+(A-C)/2); } triangle(A,B,C); };
On tape :
trianglesp(-2.0,1,2*i)
le dessin des triangles ne se fera pas dans le même ordre et se fera
du plus petit au plus grand.
Généralisation
On trace un triangle ABC, puis le triangle MNP avec :
AM=a*AB,
BN=a*BC,
CO=a*CD,
où a est un nombre réel entre 0 et 1.
trianglep(A,B,C,a):={ DispG(); triangle(A,B,C); if (longueur2(A,B)>0.01) { trianglep(A+(B-A)*a,B+(C-B)*a,C+(A-C)*a,a); } };
On tape par exemple :
trianglep(-2.0,1.0,2*i,0.2)
Voici des exemples encore assez simples.
Ces programmes se trouvent dans examples/recur/triangle.cxx.
On trace un triangle puis on joint les milieux des cotés.
On obtient ainsi 4 petits triangles semblables au précédent.
On recommence le même processus avec les trois triangles qui ont un angle
commun avec le grand triangle et ainsi de suite.....on s’arrête quand les
segments à dessiner deviennent trop petits.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p),
puis on valide avec OK :
triangle3(A,B,C):={ DispG(); triangle(A,B,C); if (longueur2(A,B)<0.01) return 0; triangle3(A,A+(B-A)/2,C+(A-C)/2); triangle3(A+(B-A)/2,B,B+(C-B)/2); triangle3(C+(A-C)/2,B+(C-B)/2,C); };
On tape par exemple :
triangle3(-2.0,1.0,2*i)
Remarque
Le tracé du triangle ne peut se faire qu’a la fin car il suffit de tracer
les derniers petits triangles on écrit donc dans un éditeur de programme
(que l’on ouvre avec Alt+p), puis on valide avec OK :
trianglep(A,B,C):={ DispG(); if (longueur2(A,B)<0.01) {triangle(A,B,C);return 0;} trianglep(A,A+(B-A)/2,C+(A-C)/2); trianglep(A+(B-A)/2,B,B+(C-B)/2); trianglep(C+(A-C)/2,B+(C-B)/2,C); };
On tape par exemple :
trianglep(-2.0,1.0,2*i)
Généralisation
On trace un triangle ABC, puis le triangle MNP avec :
AM=a*AB,
BN=a*BC,
CO=a*CD,
où a est un nombre réel entre 0 et 1.
triangle3p(A,B,C,a):={ DispG(); if (longueur2(A,B)<0.02) {triangle(A,B,C);return 0;} triangle3p(A,A+(B-A)*a,C+(A-C)*a,a); triangle3p(A+(B-A)*a,B,B+(C-B)*a,a); triangle3p(C+(A-C)*a,B+(C-B)*a,C,a); };
On tape par exemple :
triangle3p(-2.0,1.0,2*i,0.6)
Et avec un autre test d’arrêt en utilisant la profondeur n du dessin :
triangle3an(A,B,C,a,n):={ DispG(); if (n==0) {triangle(A,B,C);return 0;} triangle3an(A,A+(B-A)*a,C+(A-C)*a,a,n-1); triangle3an(A+(B-A)*a,B,B+(C-B)*a,a,n-1); triangle3an(C+(A-C)*a,B+(C-B)*a,C,a,n-1); };
On tape par exemple :
triangle3an(-2.0,1.0,2*i,0.6,3)
Ces programmes se trouvent dans examples/recur/polygone.cxx.
On considère un hexagone de coés de longueur l, on remplace cet hexagone
par 7 hexagones de cotés
de longueur l/3 qui sont :les 6
hexagones ayant un angle commun avec l’hexagone de départ et un septième hexagone se trouvant au centre.
On obtient ainsi 7 petits hexagones semblables au précédent et on recommence le même processus.
On tape la fonction hexagone :
// dessin d'un hexagone hexagone(x,y):={ local a,b,c; DispG(); a:=x; b:=y; for (j:=1;j<=6;j++) { c:=a+(b-a)*exp(evalf(i*pi*2/3)); segment(a,c); b:=a; a:=c; } };
puis on fait un premier appel récursif correspondant à l’hexagone du centre et avec la même itération que dans la fonction hexagone, on fait un appel récursif au lieu de tracer un segment pour les hexagones des angles, on tape :
hexagones(x,y,n):={ local a,b,c; a:=x; b:=y; DispG(); if (n==0) {hexagone(a,b);return 0;} c:=a+(b-a)*2/3*exp(evalf(i*pi/3)) // dessin de l'hexagone central hexagones(c,c+(b-a)/3, n-1); //dessin des 6 hexagones dans les angles for (j:=1;j<=6;j++) { c:=a+(b-a)*exp(evalf(i*pi*2/3)); hexagones(c,c+(a-c)/3,n-1); b:=a; a:=c; } };
Ces programmes se trouvent dans examples/recur/polygone.cxx.
On part d’un polygone régulier P à k sommets et on le remplace par k
polygones réguliers à k sommets de façon à ce que ces k
polygones aient chacun un angle commun avec P et de façon à ce qu’ils
ne se chevauchent pas. Puis on contine le processus et on ne dessine que les
derniers petits hexagones.
Par exemple un hexagone H de coté a est remplacé par 6 hexagones de
côtés a/3 obtenus par homothétie de rapport 1/3 et de centre les
sommets de l’hexagone H.
Pour écrire une procédure générale il faut faire un peu de
trigonométrie.
Le calcul du coté h du petit polygone k-régulier doit vérifier :
- si k=3, 4 on a h=(b−a)/2
- si k=5, 6, 7, 8 on a h+hcos(2π/k)=(b−a)/2
- si k=9, 10, 11, 12, 13, 14, 15, 16 on a :
h+hcos(2π/k)+hcos(4π/k)=(b−a)/2
- si p=iquo(k-1,4), on a :
h∑l=0p cos(2pπ/k)=(b−a)/2
On a donc :
s=∑l=0p cos(2pπ/k)=(sin((2p+1)π/k)+sin(π/k))/(2sin(π/k))
donc h=(b−a)/2/s
et on tape :
//napperon de Cantor ou de Sierpinski k=3,4... //utilise isopolygone(a,b,k) k>0 //ex polyserp(-1-2*i,1-2*i,5,3); polyserp(-2*i,1-2*i,9,2) polyserp(x,y,k,n):={ local a,b,c,h,j,q,p,s; DispG(); if (n==0) {isopolygone(x,y,k);return 0;} a:=x; b:=y; //pour k=3 ou 4 h:=(b-a)/3; //pour k=5,6,7,8 h:=(b-a)/2/(cos(evalf(2*pi/k))+1); //pour autre k il faut calculer s avec la trigo ou avec //s:=1;for (l:=1;l<=iquo(k-1,4);l++){s:=s+cos(2*l*evalf(pi)/k);} p:=iquo(k-1,4); s:=(sin(evalf(pi)/k)+sin((2*p+1)*evalf(pi)/k))/ 2/sin(evalf(pi)/k); for (j:=1;j<=k;j++) { h:=(b-a)/2/s; polyserp(a,a+h,k,n-1); c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k)); b:=a; a:=c; } };
Le programme du dessin d’un polygone régulier à k cotés peut vous aider á comprendre le programme précédent.
// dessin d'un polygone regulier de k cotes polyreg(x,y,k):={ local a,b,c; a:=x; b:=y; DispG(); for (j:=1;j<=k;j++) { c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k)); segment(a,c); b:=a; a:=c; } };
Remarque
Une faute de signe peut vous faire voir de jolis dessins pour k=7,8...et n=2
//utilise polyreg(a,b,k) k>0 k=nb de cotes //ex polyserr(-2*i,1-2*i,8,2); polyserr(-2*i,1-2*i,9,2) polyserr(x,y,k,n):={ local a,b,c,h,j,q,p,s; //if (n==0) return isopolygone(x,y,k); DispG(); if (n==0) {polyreg(x,y,k);return 0;} a:=x; b:=y; p:=iquo(k-1,4); s:=(sin(evalf(pi)/k)-sin((2*p+1)*evalf(pi)/k))/ 2/sin(evalf(pi)/k); for (j:=1;j<=k;j++) { if ( s!=0) h:=(b-a)/2/s; else h:=(b-a)/3; polyserr(a,a+h,k,n-1); c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k)); b:=a; a:=c; } };
Un autre dessin avec des octogones en tracant un octogone au centre et 8 octogones dans les angles :
polyserp8(x,y,n):={ local a,b,c,h,j,q,p,s,k; //if (n==0) return isopolygone(x,y,8); k:=8; DispG(); if (n==0) {polyreg(x,y,k);return 0;} a:=x; b:=y; p:=iquo(k-1,4); s:=(sin(evalf(pi)/k)+sin((2*p+1)*evalf(pi)/k))/ 2/sin(evalf(pi)/k); h:=(b-a)/2/s; polyserp8(a+h+i*h*(sqrt(2)+1),a+h*(sqrt(2)+1)+i*h*(sqrt(2)+1),n-1); for (j:=1;j<=k;j++) { polyserp8(a,a+h,n-1); c:=a+(b-a)*exp(evalf(i*pi*(k-2)/k)); b:=a; a:=c; h:=(b-a)/2/s;} };
Ces programmes se trouvent dans examples/recur/flocon.cxx.
Tous les programmes qui sont dans cette section se trouve dans le fichier :
flocon.cas
On considère un segment AB et on place las points PQR tels que :
3*AP=AB
3*BQ=BA
et le triangle PQR est équilatèral direct.
On remplace alors le tracé du segment AB par le tracé APRQB.
On continue en faisant subir le même traitement aux 4 segments AP,PR,RQ,QB...on s’arrête quand la longueur des segments devient trop petite ou
quand la profondeur est nulle.
Il y a donc 4 appels récursifs.
flocon(A,B):={ DispG(); if (longueur2(A,B)<0.01) {segment(A,B);return 0;} flocon(A,A+(B-A)/3); flocon(A+(B-A)/3,A+(B-A)/3*(1+exp(i*pi/3))); flocon(A+(B-A)/3*(1+exp(i*pi/3)),A+2*(B-A)/3); flocon(A+2*(B-A)/3,B); };
On tape par exemple :
flocon(-1.0,1.0)
ou avec la profondeur :
floconp(A,B,n):={ local h; DispG(); if (n==0) {segment(A,B);return 0;} h:=(B-A)/3; floconp(A,A+h,n-1); floconp(A+h,A+h*(1+exp(i*pi/3)),n-1); floconp(A+h*(1+exp(i*pi/3)),A+2*h,n-1); floconp(A+2*h,B,n-1); };
On tape par exemple :
floconp(-2.0,2.0,4)
Généralisation
On coupe le segment initial AB en trois en placant P et Q de façon
à avoir :
AP=a*AB
BQ=a*BA
avec 0.25< a <0.5. puis on construit un triangle isocéle PQR tel
que PR=QR=AP . On remplace le segment AB par les segments APRQB et on recommence le processus. Il y a donc 4 appels récursifs.
flocong(A,B,a,n):={ local h,t; DispG(); if (n==0) {segment(A,B);return 0;} t:=acos((0.5-a)/a); h:=(B-A)*a; flocong(A,A+h,a,n-1); flocong(A+h,A+h*(1+exp(i*t)),a,n-1); flocong(A+h*(1+exp(i*t)),B-h,a,n-1); flocong(B-h,B,a,n-1); };
On tape par exemple :
flocong(-2.0,2.0,0.4,4)
Ces programmes se trouvent dans examples/recur/carre.cxx.
On trace un carré puis puis on partage les cotés de ce carré en trois
partie égales. On obtient ainsi 9 carrés.
On recommence le même processus avec les 8 carrés qui ont un coté
commun avec le grand carré et ainsi de suite.....on s’arrête quand les segments à dessiner deviennent trop petits. Il y a donc 8 appels récursifs.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p),
puis on valide avec OK :
carre8(A,B):={ local h; DispG(); carre(A,B); if (longueur2(A,B)<0.01) return 0; h:=(B-A)/3; carre8(A,A+h); carre8(A+h,A+2*h); carre8(A+2*h,B); carre8(A+i*h,A+i*h+h); carre8(A+i*h+2*h,B+i*h); carre8(A+2*i*h,A+2*i*h+h); carre8(A+2*i*h+h,A+2*i*h+2*h); carre8(A+2*i*h+2*h,B+2*i*h); };
On tape par exemple :
carre8(-1.0,1.0)
Autre test d’arrêt
On peut avoir besion de connaitre le nombre de n de fois que l’on fait
le ou les appels récursifs pour avoir un dessin de "profondeur" n.
On rajoute pour cela un paramètre qui sera la profondeur.
Dans l’exemple ci-dessus, on tape dans un éditeur de programme (que l’on
ouvre avec Alt+p), puis on valide avec OK le programme :
carre8p(A,B,n):={ local h; h:=(B-A)/3; DispG(); carre(A,B); if (n==0) return 0; h:=(B-A)/3; carre8p(A,A+h,n-1); carre8p(A+h,A+2*h,n-1); carre8p(A+2*h,B,n-1); carre8p(A+i*h,A+i*h+h,n-1); carre8p(A+i*h+2*h,B+i*h,n-1); carre8p(A+2*i*h,A+2*i*h+h,n-1); carre8p(A+2*i*h+h,A+2*i*h+2*h,n-1); carre8p(A+2*i*h+2*h,B+2*i*h,n-1); };
On tape par exemple :
carre8p(-1.0,1.0,3)
Ces programmes se trouvent dans examples/recur/peano.cxx.
On trace la diagonale AC d’un carré ABCD.
Puis puis on partage les cotés de ce carrés en trois
partie égales. On obtient ainsi 9 carrés.
On remplace alors la diagonale du carré précédent par les
diagonales des 9 carrés de façon à avoir une ligne continue allant de A à C. On recommence le même processus avec les 9 carrés de façon à avoir une ligne continue allant de A à C, et ainsi de suite.....on s’arrête quand les segments à dessiner deviennent trop petits.
On a choisit comme paramètre les affixes des points A et B et d’utiliser la profondeur comme test d’arrêt.
On tape dans un éditeur de programme (que l’on ouvre avec Alt+p),
puis on valide avec OK :
ou on utilise la commande :
read("peano.cas") car ce progrmme se trouve dans le fichier
peano.cas.
//arc qui remplit le carre de cote x,y peano(x,y,n):={ local u,v; DispG(); if (n==0) {segment(x,y+3*v);return 0;} u:=(y-x)/3; v:=i*u; peano(x,x+u,n-1); peano(x+u+v,x+u,n-1); peano(x+2*u,y,n-1); peano(y+v,y+2*v,n-1); peano(x+2*(u+v),x+u+2*v,n-1); peano(x+(u+v),x+u+2*v,n-1); peano(x+2*v,x+2*v+u,n-1); peano(x+3*v+u,x+u+2*v,n-1); peano(x+2*(u+v),y+2*v,n-1); };
On tape par exemple :
peano(-i,3-i,3)
Vous pouvez voir les différentes étapes de la construction en faisant
successivement n=1,2,3,4 en utilisant le bouton stop si le tracé est
trop long.
Dans ce chapitre on va faire des dessins qui obligent à écrire plusieurs procédures récursives qui s’appellent l’une l’autre.
Ces programmes se trouvent dans examples/recur/sphinx.cxx.
Voici un sphinx droit :
sphinxd(x,y):={ local z,u,t; DispG(); z:=x+2*(y-x)/3*exp(evalf(pi)*i/3); t:= y+(x-y)/3*exp(-evalf(pi)*i/3); u:=t+(x-y)/3; segment(x,z); segment(z,u); segment(u,t); segment(t,y); segment(y,x); };
Voici un sphinx gauche :
sphinxg(x,y):={ local z,u,t; DispG(); z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3); t:= x+(y-x)/3*exp(evalf(pi)*i/3); u:=t+(y-x)/3; segment(y,z); segment(z,u); segment(u,t); segment(t,x); segment(x,y); };
Voici un sphinx droit et ses 4 petits composés de trois sphinx gauches et d’un sphinx droit.
sphinxd4(x,y):={ local z,u,t; DispG(); z:=x+2*(y-x)/3*exp(3.14*i/3); t:= y+(x-y)/3*exp(-3.14*i/3); u:=t+(x-y)/3; segment(x,z); segment(z,u); segment(u,t); segment(t,y); segment(y,x); sphinxg(x,(x+y)/2); sphinxg((x+y)/2,y); sphinxg(t,t+(x-y)/2); sphinxd(z,(3*x+z)/4); };
Voici un sphinx gauche et ses 4 petits
sphinxg4(x,y):={ local z,u,t; DispG(); z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3); t:= x+(y-x)/3*exp(evalf(pi)*i/3); u:=t+(y-x)/3; segment(y,z); segment(z,u); segment(u,t); segment(t,x); segment(x,y); sphinxd(x,(x+y)/2); sphinxd((x+y)/2,y); sphinxd(t+(y-x)/2,t); sphinxg((3*y+z)/4,z); };
et voici toute la famille des sphinx droits (sphinxds(x,y,n) est une fonction récursive qui utilise la fonction récursive sphinxgs(x,y,n)).
sphinxds(x,y,n):={ local z,u,t; DispG(); if (n==0) return 0; z:=x+2*(y-x)/3*exp(evalf(pi)*i/3); t:= y+(x-y)/3*exp(-evalf(pi)*i/3); u:=t+(x-y)/3; segment(x,z); segment(z,u); segment(u,t); segment(t,y); segment(y,x); sphinxgs(x,(x+y)/2,n-1); sphinxgs((x+y)/2,y,n-1); sphinxgs(t,t+(x-y)/2,n-1); sphinxds(z,(3*x+z)/4,n-1); };
et toute la famille des sphinx gauches
sphinxgs(x,y,n):={ local z,u,t,p; DispG(); if (n==0) return 0; z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3); t:= x+(y-x)/3*exp(evalf(pi)*i/3); u:=t+(y-x)/3; segment(y,z); segment(z,u); segment(u,t); segment(t,x); segment(x,y); sphinxds(x,(x+y)/2,n-1); sphinxds((x+y)/2,y,n-1); sphinxds(t+(y-x)/2,t,n-1); sphinxgs((3*y+z)/4,z,n-1); };
ou encore en ne dessinant que la dernière génération du sphinx droit :
sphindps(-2,2,4) met 0.52s alors que sphinds(-2,2,4) met 0.83s
sphinxdps(x,y,n):={ local z,u,t; DispG(); if (n==1) {sphinxd(x,y);return 0;} z:=x+2*(y-x)/3*exp(evalf(pi)*i/3); t:= y+(x-y)/3*exp(-evalf(pi)*i/3); u:=t+(x-y)/3; sphinxgps(x,(x+y)/2,n-1); sphinxgps((x+y)/2,y,n-1); sphinxgps(t,t+(x-y)/2,n-1); sphinxdps(z,(3*x+z)/4,n-1); };
et en ne dessinant que la dernière génération du sphinx gauche
sphinxgps(x,y,n):={ local z,u,t,p; DispG(); if (n==1) {sphinxg(x,y);return 0} z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3); t:= x+(y-x)/3*exp(evalf(pi)*i/3); u:=t+(y-x)/3; sphinxdps(x,(x+y)/2,n-1); sphinxdps((x+y)/2,y,n-1); sphinxdps(t+(y-x)/2,t,n-1); sphinxgps((3*y+z)/4,z,n-1); };
et en ne dessinant pas un sphinx gauche mais un segment
sphinxdpst(x,y,n):={ local z,u,t; DispG(); if (n==1) return sphinxd(x,y); z:=x+2*(y-x)/3*exp(evalf(pi)*i/3); t:= y+(x-y)/3*exp(-evalf(pi)*i/3); u:=t+(x-y)/3; sphinxgpst(x,(x+y)/2,n-1); sphinxgpst((x+y)/2,y,n-1); segment(t,t+(x-y)/3); sphinxdpst(z,(3*x+z)/4,n-1); }; et en ne dessinant pas un sphinx droit mais un segment \begin{verbatim} sphinxgpst(x,y,n):={ local z,u,t,p; DispG(); if (n==1) return sphinxg(x,y); z:=y+2*(x-y)/3*exp(-evalf(pi)*i/3); t:= x+(y-x)/3*exp(evalf(pi)*i/3); u:=t+(y-x)/3; sphinxdpst(x,(x+y)/2,n-1); sphinxdpst((x+y)/2,y,n-1); segment(t+(y-x)/3,t); sphinxgpst((3*y+z)/4,z,n-1); };
Ces programmes se trouvent dans examples/recur/dragon.cxx.
On se donne deux points A et B (ou deux nombres complexes a et b qui
sont l’affixe de ces points) et on considère le carré ACBD direct ayant
pour diagonale AB.
Le segment AB peut donner naissance à un dragon gauche, pour cela, on
remplace le segment AB par les deux côtés
AD et DB du carré ACBD situé à gauche du vecteur AB ou,
le segment AB peut donner naissance à un dragon droit, pour cela, on
remplace le segment AB par les deux côtés
AC et CB du carré ACBD situé à droite du vecteur AB.
Pour la fabrication du dragon gauche, ces deux segments sont considérés
comme allant donner naissance à un dragon gauche (AD) et à un dragon
droit (DB) et
pour la fabrication du dragon droit, ces deux segments sont considérés
comme allant donner naissance à un dragon gauche (AC) et à un dragon
droit (CB).
On a :
b−c=i*(a−c) et b−d=−i*(a−d) donc :
c=(b−i*a)*(1+i)/2 et d=(b+i*a)*(1−i)/2.
On écrit donc en prenant comme test d’arrêt la profondeur n c’est à
dire le nombre de générations.
// dessine un dragon dragong(-i,2+i,10) //x=a,y=b et d=u dragong(x,y,n):={ local u; DispG(); if (n==0){segment(x,y);return 0;} u:=(y+i*x)*(1-i)/2; dragong(x,u,n-1); dragond(u,y,n-1); }; // dessine un dragon dragond(-i,2+i,10) //x=a,y=b et c=u dragond(x,y,n):={ local u; DispG(); if (n==0){segment(x,y);return 0;} u:=(y-i*x)*(1+i)/2; dragong(x,u,n-1); dragond(u,y,n-1); };
Remarque
Il est facile d’obtenir la courbe du dragon en prenant une longue bande de
papier que l’on plie n fois sur elle même, toujours dans le même sens.
Lorsqu’on a pris soin de bien marquer les plis, on obtient un dragon
lorsqu’on déplie la bande en disposant les plis à angle droit.
Ce n’est pas tout à fait ce que l’on a programmer car dans le programme à
chaque étape on multiplie la longueur du dragon par √2.
Sauriez vous programmer le dragon de la bande de papier ?
Voici la solution : on remarquera que le dragon droit est réalisé par la
deuxième moitié de la bande de papier et donc la fonction
dragonpapierd est la fonction
dragonpapierg en changeant gauche en droite, et en
commençant par la dernière instruction.
dragonpapierg(x,y,n):={ local u,v,a,b; DispG(); if (n==0){segment(x,y); return y;} u:=x+(y-x)/2; a:=dragonpapierg(x,u,n-1); v:=a+(y-x)*i/2; b:=dragonpapierd(a,v,n-1); return b }; dragonpapierd(x,y,n):={ local u,v,a,b; DispG(); if (n==0){segment(x,y); return y;} v:=x+(y-x)*i/2; b:=dragonpapierg(x,v,n-1); u:=a+(y-x)/2; a:=dragonpapierd(b,u,n-1); return a };
On tape :
dragonpapierg(-3.0,13,8)
Voici une autre solution où on repère l’arrivée et la direction du
dernier trait. Dans ce cas on connait le départ et la direction de départ
du dragon droit....mais c’est nettement plus compliqué.
dragonpaperg(x,y,n):={ local u,v,a,b; DispG(); if (n==0){segment(x,y); return (x,y);} u:=x+(y-x)/2; a:=dragonpaperg(x,u,n-1); v:=a[1]+abs((y-x)/(a[1]-a[0]))*(a[1]-a[0])*i/2; b:=dragonpaperd(a[1],v,n-1); return b }; dragonpaperd(x,y,n):={ local u,v,a,b; DispG(); if (n==0){segment(x,y); return (x,y);} u:=x+(y-x)/2; a:=dragonpaperg(x,u,n-1); v:=a[1]-abs((y-x)/(a[1]-a[0]))*(a[1]-a[0])*i/2; b:=dragonpaperd(a[1],v,n-1); return b };
On tape :
dragonpaperg(-3,13,8)
Parmi les nombreuses courbes inventées par Péano on va en décrire deux
qui sont des courbes rècursives formées par des arcs de cercle.
Ces programmes se trouvent dans examples/recur/peano.cxx
On écrit la fonction arcg (resp arcd) qui dessine des arcs définit par le début de l’arc, la fin de l’arc, et de mesure π/3 (resp −π/3).
//arc x y de mesure +pi/3 arcg(x,y):={ return arc(x,y,pi/3); }; //arc x y de de mesure -pi/3 arcd(x,y):={ return arc(x,y,-pi/3); };
Puis on écrit la fonction peanog (resp peanod) :
Soient deux points A d’affixe a et B d’affixe b.
Pour la fonction peanog, on débute par l’arc
AB de mesure π/3, situé sur le cercle de centre Cg d’affixe
cg=(b−a*exp(i*π/3))*(1−exp(−i*π/3)) que l’on appellera arcg.
Pour la fonction peanod, on débute par l’arc
AB de mesure −π/3, situé sur le cercle de centre Cd d’affixe
cd=(b−a*exp(−i*π/3))*(1−exp(i*π/3)) que l’on appellera arcd.
On remplace ensuite arcg (resp arcd) par 13 arcs de mesure
π/3 ou de mesure −π/3 selon le dessin que l’on obtient en tapant :
peanog(-2-2*i,2-2*i,1) (resp peanod(-2-2*i,2-2*i,1)).
Ces deux figures sont symétriques.
Et on continue en appliquant le même traitement à chacun de ses 13 arcs
en remplacant les arcg (resp arcd) par 13 arcs.
// courbe de peano avec 13 arcs //par ex peanod(-2-2*i,2-2*i,3) peanod(x,y,n):={ local c1,b,c,d,e1,f,g,h,i1,j,k,l,m; DispG(); if (n==0) {arc(x,y,-pi/3);return 0;} c1:=x+(y-x)*exp(evalf(pi)*2*i/3)/3; b:=x+(y-x)/3*exp(evalf(pi)*i/3); c:=x+(y-x)/3*2*exp(evalf(pi)*i/3); d:=c+(y-x)/3; e1:=b+2*(y-x)/3; f:=c1+(y-x)*(15+i*sqrt(3))/18; g:=c1+(y-x)*(6+i*sqrt(3))/9; h:=f-(y-x)/3; i1:=h-i*(y-x)/9*sqrt(3); j:=i1+(y-x)/3; k:=g-i*2*(y-x)/9*sqrt(3); l:=x+(y-x)/3; m:=x+2*(y-x)/3; peanog(x,b,n-1); peanod(b,c,n-1); peanod(c,d,n-1); peanod(d,e1,n-1); peanod(e1,f,n-1); peanog(f,g,n-1); peanog(g,h,n-1); peanog(h,i1,n-1); peanod(i1,j,n-1); peanod(j,k,n-1); peanog(k,l,n-1); peanog(l,m,n-1); peanod(m,y,n-1); }; peanog(x,y,n):={ local c1,b,c,d,e1,f,g,h,i1,j,k,l,m; DispG(); if (n==0) { arc(x,y,pi/3);return 0;} c1:=x+(y-x)*exp(evalf(-2*pi)*i/3)/3; b:=x+(y-x)/3*exp(evalf(-pi)*i/3); c:=x+(y-x)/3*2*exp(evalf(-pi)*i/3); d:=c+(y-x)/3; e1:=b+2*(y-x)/3; f:=c1+(y-x)*(15-i*sqrt(3))/18; g:=c1+(y-x)*(6-i*sqrt(3))/9; h:=f-(y-x)/3; i1:=h+i*(y-x)/9*sqrt(3); j:=i1+(y-x)/3; k:=g+i*2*(y-x)/9*sqrt(3); l:=x+(y-x)/3; m:=x+2*(y-x)/3; peanod(x,b,n-1); peanog(b,c,n-1); peanog(c,d,n-1); peanog(d,e1,n-1); peanog(e1,f,n-1); peanod(f,g,n-1); peanod(g,h,n-1); peanod(h,i1,n-1); peanog(i1,j,n-1); peanog(j,k,n-1); peanod(k,l,n-1); peanod(l,m,n-1); peanog(m,y,n-1); };
On va maintenant décrire la courbe de Péano ternaire qui remplit un carré
direct de coté XY, en étant C1 .
Soient x est l’affixe de X et y est l’affixe de Y.
Le dessin de base est obtenu en tapant:
bases(x,y)
il est composé de 8 arcs de cercle de mesure π/2 ou de mesure −π/2
et débute au point A d’affixe a:=x+(y−x)/6*1+i) et se termine au point
K d’affixe k, symétrique de A par rapport au centre du carré.
Á partir de ce dessin de base on fait la figure un :
un(x,y)
qui est composée d’un arc de mesure π/2 commençant au point
d’affixe :
a−(y−x)/3 et se terminant au point A suivi du dessin de base.
Á partir de ce dessin de base on fait la figure deux :
deux(x,y)
qui est composée d’un arc de mesure π/2 commençant au point
d’affixe :
a−i*(y−x)/3 et se terminant au point A suivi du dessin de base.
On partage le carré en 9 petits carrés et dans chacun des petits carrés
on trace la figure de base ou la figure de baseun ou la figure de basedeux de façon à obtenir une ligne continue etc....
On écrit ensuite les procédures :
peano0 qui débute par la figure bases,
peano1 qui débute par la figure un,
peano2 qui débute par la figure de deux,
Taper par exemple peano0(-1,2,1) pour voir l’étape 1.
//motif de base bases(x,y):={ local a,b,c,d,e1,f,g,h,i1,k; DispG(); h:=(y-x)/3; a:=x+h/2+i*h/2; b:=a+i*h; c:=b+i*h; d:=c+h e1:=b+h; f:=a+h; g:=f+h; k:=e1+h; i1:=d+h; arc(a,b,pi/2); arc(c,b,pi/2); arc(d,c,pi/2); arc(e1,d,pi/2); arc(e1,f,pi/2); arc(f,g,pi/2); arc(g,k,pi/2); arc(i1,k,pi/2); }; //un arc et le motif de base un(x,y):={ local h,a; DispG(); h:=(y-x)/3; a:=x+h/2+i*h/2; arc(a-h,a,pi/2); bases(x,y); }; //un autre arc et le motif de base deux(x,y):={ local h,a; DispG(); h:=(y-x)/3; a:=x+h/2+i*h/2; arc(a,a-h*i,pi/2); bases(x,y); }; //courbe qui remplit un carre debute par le motif bases // ex peano0(-1,2,1) ou peano0(-1,2,3) // utilise bases un deux peano1 peano2 peano0(x,y,n):={ local a,h; DispG(); if (n==0) {bases(x,y);return 0;} h:=(y-x)/3; a:=x+h; peano0(x,a,n-1); peano2(a+i*h,a,n-1); peano1(a+h,y,n-1); peano1(y+i*h,y+2*i*h,n-1); peano1(a+h+2*i*h,a+2*i*h,n-1); peano2(a+i*h,a+2*i*h,n-1); peano2(x+2*i*h,a+2*i*h,n-1); peano2(a+3*i*h,a+2*i*h,n-1); peano1(a+h+2*i*h,y+2*i*h,n-1); }; //courbe qui remplit un carre debute par le motif un // ex peano1(-1,2,1) // utilise bases un deux peano2 peano1(x,y,n):={ local a,h; DispG(); if (n==0) {un(x,y);return 0;} h:=(y-x)/3; a:=x+h; peano1(x,a,n-1); peano2(a+i*h,a,n-1); peano1(a+h,y,n-1); peano1(y+i*h,y+2*i*h,n-1); peano1(a+h+2*i*h,a+2*i*h,n-1); peano2(a+i*h,a+2*i*h,n-1); peano2(x+2*i*h,a+2*i*h,n-1); peano2(a+3*i*h,a+2*i*h,n-1); peano1(a+h+2*i*h,y+2*i*h,n-1); }; //courbe qui remplit un carre debute par le motif deux // ex peano2(-1,2,1) // utilise bases un deux peano1 peano2(x,y,n):={ local a,h; DispG(); if (n==0) {deux(x,y);return 0;} h:=(y-x)/3; a:=x+h; peano2(x,a,n-1); peano2(a+i*h,a,n-1); peano1(a+h,y,n-1); peano1(y+i*h,y+2*i*h,n-1); peano1(a+h+2*i*h,a+2*i*h,n-1); peano2(a+i*h,a+2*i*h,n-1); peano2(x+2*i*h,a+2*i*h,n-1); peano2(a+3*i*h,a+2*i*h,n-1); peano1(a+h+2*i*h,y+2*i*h,n-1); };
Ces programmes se trouvent dans examples/recur/autres.cxx
//courbe de hilbert par exemple hilg(-2,0,4) est //compos\'e par 4 morceaux hilg hild berg et berd //ou hild(0,2,4) ou berg(2,2-2*i,4) ou berd(2,2-2*i,4) //le morceau 1 hilg(x,y,n):={ local u,v,a,b; DispG(); if (n==0) {segment(x,y);return 0;} u:=(y-x)/2; v:=u*i; hild(x,x+v,n-1); hilg(x+v,x+v+u,n-1); a:=berg(x+v+u,x+u,n-1); b:=berd(a,a+u,n-1); }; //le morceau 2 hild(x,y,n):={ local u,v,a,b; DispG(); if (n==0) {segment(x,y);return 0;} u:=(y-x)/2; v:=u*i; hilg(x,x-v,n-1); hild(x-v,x-v+u,n-1); b:=berd(x-v+u,x+u,n-1); a:=berg(b,b+u,n-1); }; //le morceau 3 berg(x,y,n):={ local u,v,b; DispG(); if (n==0) {segment(x,y);return y;} v:=(x-y)/2; u:=-v*i;hild(x,x+v,n-1); hilg(x+v,x+v+u,n-1); b:=berg(x+v+u,x+u,n-1); hild(b,b-v,n-1); return(b-v); }; //le morceau 4 berd(x,y,n):={ local u,v,a; DispG(); if (n==0) {segment(x,y);return y;} v:=(x-y)/2; u:=-v*i; hilg(x,x+v,n-1); hild(x+v,x+v-u,n-1); a:=berd(x+v-u,x-u,n-1); hilg(a,a-v,n-1); return a-v; };
//gosper(-2-2*i,2-2*i,2)ou gosper(-2-2*i,2-2*i,3) gosper(x,y,n):={ local a,b,c,d,f,g; DispG(); if (n==0) return segment(x,y); a:=x+(y-x)/sqrt(7)*exp(evalf(-i*acos(5*sqrt(7)/14))); c:=x+(a-x)*exp(evalf(i*pi/3)); b:=c+a-x; d:=c+(a-x)*exp(evalf(2*i*pi/3)); f:=d+2*(a-x); g:=(d+f)/2; gosper(x,a,n-1); gosper(b,a,n-1); gosper(c,b,n-1); gosper(c,d,n-1); gosper(d,g,n-1); gosper(g,f,n-1); gosper(y,f,n-1); };
Ces programmes se trouvent dans examples/recur/plantes.cxx
//Voici des sapins....sapin(0,2*i) sapin(x,y):={ DispG(); if (abs(x-y)<0.5) {segment(x,y); return 0;} sapin(x,x+(y-x)*0.5*exp(i)); sapin(x,x+(y-x)*0.5*exp(-i)); segment(x,(3*x+y)/4); sapin((3*x+y)/4,y); }; //Voici des fleurs....fleur(0,2*i) fleur(x,y):={ DispG(); if (abs(x-y)<0.5) {segment(x,y);cercle(y,(y-x)*0.3); return 0;} segment(x,y);cercle(y,(y-x)*0.3);cercle(y,(y-x)*0.2); fleur(x,x+(y-x)*0.5*exp(i*0.5)); fleur(x,x+(y-x)*0.5*exp(-i*0.5)); }; //Voici des arbres....arbre(0,2*i) arbre(x,y):={ DispG(); if (abs(x-y)<0.2) {segment(x,y); return 0;} segment(x,(x+y)/2); arbre((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i*0.5)); arbre((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i*0.5)); }; //et des arbres moins deplumes arbre2(0,2*i) arbre2(x,y):={ DispG(); if (abs(x-y)<0.2) {segment(x,y); return 0;} segment(x,(x+y)/2); arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i*0.5)); arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i*0.5)); arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i)); arbre2((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i)); }; //un epineux arbre3(0,2*i) arbre3(x,y):={ DispG(); if (abs(x-y)<0.2) {segment(x,y); return 0;} segment(x,(x+y)*0.5); arbre3((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(i*0.5)); arbre3((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(-i*0.5)); arbre3((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i)); arbre3((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i)); }; //une fougere par ex fougeres(-2*i,2*i) fougere(x,y):={ local a; DispG(); if (abs(x-y)<0.2) {segment(x,y); return 0;} a:=x+(y-x)*0.15*exp(-i*0.2); segment(x,a); fougere(a,a+(y-x)*0.33*exp(i*1.2)); fougere(a,a+(y-x)*0.33*exp(-i*1.2)); fougere(a,a+(y-x)*0.85*exp(-i*0.2)); }; //par ex fougeres(-2*i,2*i,0.05,6) fougeres(x,y,t,n):={ local a; DispG(); if (n==0) {segment(x,(x+y)/2); return 0;} //a:=x+(y-x)*0.15*exp(-i*t); a:=x+(y-x)*0.15; segment(x,a); fougeres(a,a+(y-x)*0.33*exp(i*1.2),t,n-1); fougeres(a,a+(y-x)*0.33*exp(-i*1.2),t,n-1); fougeres(a,a+(y-x)*0.85*exp(-i*t),t,n-1); }; //et le bouquet final bouquet(0,2*i) bouquet(x,y):={ DispG(); if (abs(x-y)<0.2) {segment(x,y); return 0;} segment(x,(x+y)*0.5); bouquet((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(i*0.5)); bouquet((3*x+y)/4,(3*x+y)/4+(y-x)*0.25*exp(-i*0.5)); bouquet((x+y)/2,(x+y)/2+(y-x)*0.5*exp(i)); bouquet((x+y)/2,(x+y)/2+(y-x)*0.5*exp(-i)); bouquet((x+y)/2,(x+y)/2+(y-x)*0.5); };Retour à la page personnelle de Bernard Parisse.