Examen final - solution

1 Première moitié

1.1 Accumulateur et systèmes de coordonnées

Parties 1 et 2

images/coordinate-systems-sol.png

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éthode values 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 :

\[ \left(t_1 \circ t_2\right)(p) = t_1\left(t_2(p)\right) \]

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 :

  1. L'observateur de la JList est averti du changement de la sélection et appelle la méthode setSelectedTransformationIndex de la classe FlameMakerGUI afin de mettre à jour l'index de la transformation sélectionnée.
  2. 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.
  3. Les observateurs des champs textuels sont avertis du changement de ceux-ci et appellent la méthode setVariationWeight du bâtisseur de fractale (ObservableFlameBuilder).
  4. L'observateur du ObservableFlameBuilder créé par le composant affichant la fractale est averti d'un changement et appelle la méthode repaint 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()));
Michel Schinz – 2013-05-31 14:54