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éthodevaluesdes énumérations), - il est possible d'utiliser la classe
EnumMappour 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
JListest averti du changement de la sélection et appelle la méthodesetSelectedTransformationIndexde la classeFlameMakerGUIafin de mettre à jour l'index de la transformation sélectionnée. - Les observateurs de l'attribut
selectedTransformationIndexsont 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
setVariationWeightdu bâtisseur de fractale (ObservableFlameBuilder). - L'observateur du
ObservableFlameBuildercréé par le composant affichant la fractale est averti d'un changement et appelle la méthoderepaintde 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()));