Examen final - solution
1 Première moitié
1.1 Accumulateur et systèmes de coordonnées
Parties 1 et 2
Partie 3
L'image comportera 8 pixels, car à chaque case de l'accumulateur correspond un pixel. Parmi ces pixels, 6 seront noirs car ils ne contiennent aucun point, 1 sera blanc car il contient le nombre maximum de points (2) et le seul pixel qui ne sera ni noir ni blanc aura une intensité supérieure à \(\tfrac{1}{2}\) étant donné la formule logarithmique utilisée pour le calcul de l'intensité. Pour être précis, ce pixel aura l'intensité \((\log 2)/(\log 3) \approx 0.63\).
A noter que même sans se souvenir de la formule exacte, il suffisait de se souvenir que cette formule non linéaire était utilisée pour éviter que l'image ne soit trop sombre pour en conclure que le pixel gris aurait une intensité supérieure à \(\tfrac{1}{2}\).
1.2 Enumérations
Il s'agit de la classe Variation
qui, transformée en énumération, ressemblerait à ceci :
public enum Variation implements Transformation { LINEAR("Linear") { @Override public Point transformPoint(Point p) { return p; } }, // ... BUBBLE("Bubble") { @Override public Point transformPoint(Point p) { double factor = 4.0 / (Math.pow(p.r(), 2) + 4); return new Point(p.x() * factor, p.y() * factor); } }; private final String uiName; private Variation(String uiName) { this.uiName = uiName; } public String uiName() { return uiName; } }
Les avantages principaux de cette version par rapport à celle du projet sont :
- il n'est pas nécessaire de numéroter les variations, cela est fait automatiquement,
- il n'est pas nécessaire de créer à la main le tableau
ALL_VARIATIONS
, cela est fait automatiquement (accessible via la méthodevalues
des énumérations), - il est possible d'utiliser la classe
EnumMap
pour stocker les poids des variations.
A noter que ni la classe Color
ni la classe AffineTransformation
ne sont représentables par une énumération, car le nombre d'instances différentes de chacune de ces classes est infini. Cela n'est pas compatible avec la notion d'énumération, qui suppose justement que toutes les instances de la classe sont énumérables.
1.3 Transformations
Partie 1
La méthode transformPoint
de la classe ComposedTransformation
enchaîne simplement les deux transformations. Il s'agit en fait d'une simple composition de fonctions mathématique :
La méthode transformPoint
ressemble donc à ceci :
public Point transformPoint(Point p) { return t1.transformPoint(t2.transformPoint(p)); }
Partie 2
Le programme résultant serait moins rapide que celui que nous avons écrit, car les transformations composées seraient gardées sous la forme d'instances de ComposedTransformation
chaînées. En particulier, si l'utilisateur clique 100 fois sur un des 14 boutons de transformation de la partie affine d'une transformation Flame, on se retrouve à l'exécution avec une chaîne de 100 instances de ComposedTransformation
. Tout appel à la méthode transformPoint
d'une telle chaîne implique 99 appels à la méthode transformPoint
de ComposedTransformation
, et comme cela est fait à (presque) chaque itération de l'algorithme du chaos, le coût est important.
Avec la version actuelle du programme, la composition est calculée une fois pour toutes au moyen de la multiplication matricielle.
2 Seconde moitié
2.1 Redessin inutile
Partie 1
La description complète du problème est la suivante :
- L'observateur de la
JList
est averti du changement de la sélection et appelle la méthodesetSelectedTransformationIndex
de la classeFlameMakerGUI
afin de mettre à jour l'index de la transformation sélectionnée. - Les observateurs de l'attribut
selectedTransformationIndex
sont informés, et parmi ceux-ci se trouve celui responsable du chargement des poids des six variations dans les champs textuels correspondants. Cet observateur charge donc les poids de la nouvelle transformation Flame sélectionnée dans les champs textuels. - Les observateurs des champs textuels sont avertis du changement de ceux-ci et appellent la méthode
setVariationWeight
du bâtisseur de fractale (ObservableFlameBuilder
). - L'observateur du
ObservableFlameBuilder
créé par le composant affichant la fractale est averti d'un changement et appelle la méthoderepaint
de ce composant, ce qui provoque son redessin, en l'occurrence inutile.
Partie 2
Il est possible d'intervenir à plusieurs endroits, nous avons choisi de le faire au point 4.
Notre solution consiste simplement à regarder, dans la méthode setVariationWeight
de la classe ObservableFlameBuilder
si le nouveau poids est égal à l'ancien et, si tel est le cas, ne pas avertir les observateurs.
Notre méthode setVariationWeight
ressemble donc à ceci :
void setVariationWeight(int index, Variation variation, double newWeight) { double currentWeight = flameBuilder.variationWeight(index, variation); if (newWeight == currentWeight) return; flameBuilder.setVariationWeight(index, variation, newWeight); fireFlameChanged(); }
2.2 Dessin de la grille
Partie 1
Les lignes dessinées sont verticales.
Partie 2
Les points yMin
et yMax
doivent être dans le système de coordonnées du composant, et doivent couvrir tout le cadre. Le code qui les calcule est donc :
Point yMin = planeToComponent.transformPoint( new Point(x, frame.bottom())); Point yMax = planeToComponent.transformPoint( new Point(x, frame.top()));