
On va s'intéresser à la construction de dessins par la juxtaposition de carreaux élémentaires, comme cela se pratique pour diverses techniques artisanales (frises, dessus de lit, ...). Ce sujet est inspiré du langage Little Quilt décrit dans le livre Programming Languages: Concepts and Constructs de Ravi Sethi.
Par exemple,
java Quilt < mon_dessin.txt
avec un fichier mon_dessin.txt qui contient :
def bb = b + rot b + rot rot b + rot rot rot b; def bbb = rot bb + rot rot rot bb; show (rot bbb[3])[2];doit produire le dessin :
![]() |
Quelques explications
Par convention, le terme b désigne le carreau de base
.
L'opérateur rot permet de le tourner d'un quart de tour dans
le sens des aiguilles d'une montre.
Et l'opérateur + sert à juxtaposer de la gauche vers
la droite.
L'expression b + rot b + rot rot b + rot rot rot b
correspond donc au dessin
.
L'instruction
def bb = b + rot b + rot rot b + rot rot rot b;
permet de nommer ce dessin bb.
On utilise bb pour construire un autre dessin nommé
bbb :
![]() |
Ce qui est demandé
Le travail minimal demandé consiste à programmer la lecture
et l'interprétation de telles descriptions de dessins dont la
sémantique est précisée ci dessous, en partie 2.
La syntaxe du langage et les règles d'interprétation sont
données ensuite en parties 3 et 4.
Un certain nombre d'extensions indépendantes et facultatives sont proposées en partie 5. Elles permettent respectivement de colorier un dessin, d'utiliser des expressions arithmétiques dans les descriptions et de définir de nouvelles fonctions.
La remise des travaux se fera par la commande
/users/profs/info/Depot/INF_431/deposer documents
DM_1 groupe
avant la date limite du 23 avril 2003.
documents désigne un ou plusieurs fichiers sources
(terminaison en .java) et des fichiers d'exemple.
Un fichier, avec un nom comme lisez_moi.txt, contenant des
explications sur votre travail, comment utiliser le programme, ..., est
également souhaité.
groupe doit être remplacé par le numéro de
groupe de TD, de 1 à 12.
Pour faciliter la correction, il est demandé que la classe
principale s'appelle Quilt et que l'exécution se fasse
par :
java Quilt < mon_dessin.txt
Un mode spécial permettant de voir l'ordre de dessin est décrit
en 4.2, il sera activé par :
java Quilt debug < mon_dessin.txt
En parallèle, est lancé le concours du plus beau dessin qui servira à décorer le poly de l'an prochain. Le réglement du concours est donné à la fin de l'énoncé.
a =
b =
c =
d =
+
=

rot
=
[n]
=

...
  (n fois)
Pour tout dessin u, la largeur L(u) et la hauteur
H(u), exprimées en nombre de carreaux, sont définies
comme suit :
L(a) = L(b) = L(c) = L(d) = H(a) = H(b) = H(c) = H(d) = 1
L(u+v) = L(u)+L(v) et H(u+v) = H(u) = H(v)
L(rot(u)) = H(u) et H(rot(u)) = L(u)
L(u[n]) = n*L(u) et H(u[n]) = H(u)
pour n entier positif ou nul.
Lorsqu'un dessin est considéré comme une partie d'un dessin plus grand, son emplacement a aussi de l'importance. On utilise alors le coin supérieur gauche, ie. un point SG(u) dont les coordonnées sont exprimées en nombre de carreaux. Le coin supérieur gauche, la largeur et la hauteur déterminent le rectangle occupé par un dessin.
L'opération de rotation, notée rot consiste à
faire tourner un dessin de 90o dans le sens des aiguilles
d'une montre.
La rotation inverse n'existe pas en tant que transformation de base, on devra
l'exprimer par trois rotations successives.
Par exemple, le dessin
qui est
la rotation inverse de rot(b)+rot(rot(b)), peut s'écrire
rot(rot(rot( rot(b)+rot(rot(b)) ))).
La juxtaposition de deux dessins, u et v, n'est
définie que si les deux dessins ont la même hauteur,
ie. H(u)=H(v).
Cette opération, notée u+v revient à coller
v à droite de u, en les alignant horizontalement.
Il n'y a pas d'opérateur pour une juxtaposition verticale,
on peut l'exprimer en combinant une juxtaposition horizontale avec des
rotations.
Par exemple, le dessin
est aussi
la valeur de l'expression rot( b+rot(rot(rot(b))) ).
Voici d'autres exemples :
s'exprime par
rot( rot(rot(a)) + rot(a) ) + rot( rot(rot(rot(a))) + a)
s'exprime par
rot( rot(rot(a))+b )+rot( b+rot(rot(b)) ).
Les non-terminaux sont écrits en lettres capitales et | sert à séparer plusieurs choix possibles pour la dérivation d'un même non-terminal.
DESCRIPTION ::=
| DEFINITION DESCRIPTION
| INSTRUCTION DESCRIPTION
DEFINITION ::= def IDENTIFICATEUR = DESSIN ;
INSTRUCTION ::= show DESSIN ;
| size DESSIN ;
DESSIN ::= PRIMITIVE
| IDENTIFICATEUR
| ( DESSIN )
| DESSIN + DESSIN
| rot DESSIN
| DESSIN [ ENTIER ]
Dans un premier temps, on considère que ENTIER
désigne une constante numérique entière
(ie. une suite de chiffres), positive ou nulle.
Le passage à des expressions à valeurs entières
constitue une extension, cf. 5.2.
Règles de précédence
On fera en sorte que + ait une précédence
inférieure à rot et que rot ait une
précédence inférieure à [ ].
Si bien que l'expression
rot a[4]+bbb se comprend comme (rot(a[4]))+bbb et,
dans l'exemple d'introduction, rot bbb[3][2] aurait
donné un résultat différent de
(rot bbb[3])[2].
Il faudra faire un appel du type
PatchWork.init(java.awt.Color.lightGray); au début du
programme pour initialiser le graphique.
La couleur passée en paramètre est utilisée comme fond
d'écran.
Dans un premier temps, il est préférable d'utiliser une
couleur différente du blanc et du noir qui sont les deux couleurs de
dessin.
La réalisation de dessins en couleur constitue une extension,
cf. 5.1.
La méthode PatchWork.clear() efface tout avec la couleur passée à init.
Pour réaliser le dessin d'un carreau, on utilisera la fonction :
PatchWork.paint(int num, int x, int y, int r);
Le paramètre num indique le numéro de primitive,
avec les valeurs PatchWork.A, PatchWork.B,
PatchWork.C, PatchWork.D, ... respectivement pour les
dessins primitifs a, b, c, d, ...
La constante PatchWork.NB donne le nombre de primitives
définies dans PatchWork.
Les coordonnées x et y du coin supérieur
gauche doivent être exprimées en nombre de carreaux,
le passage à des coordonnées en pixels est fait
automatiquement par PatchWork.
Le paramètre r ne prend que l'une des
valeurs 0, 1, 2 ou 3 qui sert à
coder le nombre de quarts de tour (dans le sens des aiguilles d'une montre)
pour l'orientation du carreau.
Voici le dessin des différents carreaux réalisables par
PatchWork.paint :
|
|
|
| a b c d | 4 orientations de a | 4 orientations de b |
show a+b+c+d; show a + rot a + rot rot a + rot rot rot a; show rot(rot rot b + rot b) + rot(rot rot rot b + b);Le premier dessin est obtenu en enchaînant les appels suivants :
PatchWork.paint(PatchWork.A, 0, 0, 0); PatchWork.paint(PatchWork.B, 1, 0, 0); PatchWork.paint(PatchWork.C, 2, 0, 0); PatchWork.paint(PatchWork.D, 3, 0, 0);le deuxième dessin est obtenu par :
PatchWork.paint(PatchWork.A, 0, 0, 0); PatchWork.paint(PatchWork.A, 1, 0, 1); PatchWork.paint(PatchWork.A, 2, 0, 2); PatchWork.paint(PatchWork.A, 3, 0, 3);et le troisième dessin est obtenu par :
PatchWork.paint(PatchWork.B, 0, 0, 3); PatchWork.paint(PatchWork.B, 0, 1, 2); PatchWork.paint(PatchWork.B, 1, 0, 0); PatchWork.paint(PatchWork.B, 1, 1, 1);
Un contexte géométrique est formé de :
- les coordonnées du coin supérieur gauche du dessin,
en nombre de carreaux,
- une orientation parmi 4 (ie. rotations de 90o)
qui résulte du cumul des rotations utilisées pour
l'instanciation du dessin.
Le contexte géométrique initial correspond à un coin
supérieur gauche en (0,0) et à l'orientation
0o.
L'axe des y est orienté vers le bas, comme souvent
en graphique.
Le résultat retourné par l'interprétation d'un dessin u est le couple (L(u),H(u)). Cette information est généralement nécessaire pour placer le reste du dessin.
L'instruction size exécute le dessin dans le contexte géométrique initial sans tracé à l'écran et imprime les dimensions du dessin.
L'instruction show efface l'écran (cela consiste simplement à appeler PatchWork.clear()) puis exécute le dessin dans le contexte géométrique initial en traçant à l'écran. Pour fignoler, on peut passer les dimensions du dessin à PatchWork.resize qui ajuste les dimensions de la fenêtre graphique.
Juxtaposition
La manière de procéder à la réalisation du
dessin dépend du contexte d'orientation dans lequel on évalue
la somme.
Il faut commencer par placer le sous-dessin qui sera le plus en haut et
à gauche dans le résultat final puis placer l'autre, soit
juste à droite, soit juste en dessous, selon le contexte.
Par exemple, pour dessiner rot(rot(a+b+c)), on commencera par
placer c le plus à gauche, puis b
et a à droite pour finir, tous les trois dans un contexte
augmenté de deux rotations.
On remarquera que cela ne changerait pas le résultat si on avait
parenthésé a+(b+c) ou (a+b)+c.
Pour :
def disque = rot (rot rot a + rot a) + rot (rot rot rot a + a); show disque + rot disque;le dessin
se fait dans l'ordre
.
Autrement dit, on pourra écrire :
def x = a; def quatre_x = rot(rot rot x + rot x) + rot(rot rot rot x + x); show quatre_x; def x = rot(rot(rot rot a+b)+rot(b+rot rot b)); show quatre_x;et obtenir successivement deux dessins différents pour quatre_x, respectivement
| et |
|
Remarque
Pour ne pas utiliser trop de mémoire, on évite de construire
explicitement un arbre de syntaxe abstraite correspondant à la
totalité du dessin à réaliser.
Par exemple, pour dessiner aaa avec :
def aa = a + rot a + rot rot a + rot rot rot a; def aaa = rot aa + rot rot rot aa;on ne construit pas l'expression
DESSIN ::= ...
| color ( COLOR_SPEC , COLOR_SPEC ) DESSIN
COLOR_SPEC ::= COLOR_IDENT
| ( ENTIER , ENTIER , ENTIER )
La construction color ( ... ) est vue comme un opérateur
unaire, de même précédence que rot et permet de
spécifier les deux couleurs utilisées pour le dessin des
carreaux (par défaut black et white).
On obtient ensuite un objet java.awt.Color par le
constructeur Color(int r, int g, int b) et le changement effectif de
couleur se fait par un appel à :
PatchWork.setColor(Color c1, Color c2);
Cela affecte tous les appels à PatchWork.paint
qui suivent. En fin de portée d'un changement de couleur, il
faudra donc restaurer les couleurs précédentes.
Voici quelques exemples :
s'obtient par
show color(red,yellow)disque+color(yellow,red)disque;
s'obtient par
def x = rot(rot rot b+b)+rot(b+rot rot color(black,red)b); show color(black,yellow)(rot(rot rot rot x+rot rot x)+rot(x+rot x));
DESCRIPTION ::= ...
| AFFECTATION DESCRIPTION
AFFECTATION ::= let IDENTIFICATEUR = ENTIER ;
DESSIN ::= ...
| for IDENTIFICATEUR = ENTIER to ENTIER do DESSIN
Par exemple :
show for i = 0 to 4 do rot (c[i]+rot b+d[4-i]);
donne le dessin de taille 5x5 :
![]() |
DEFINITION ::= ...
| fun IDENTIFICATEUR ( x ) = DESSIN ;
DESSIN ::= ...
| IDENTIFICATEUR ( DESSIN )
Par exemple :
fun irot(x) = rot rot rot x; def bb = rot(aa) + irot(aa);Ensuite seulement, on pourra essayer de généraliser, par exemple :
Ce sujet a été réalisé avec la contribution de Jean-Jacques Lévy, Luc Maranget et Laurent Viennot.
Pour toutes suggestions, commentaires ou remarques, email : Philippe.Chassignet@polytechnique.fr