Série 6 – Images continues génériques : corrigé

Introduction

Le code du corrigé est disponible sous la forme d'une archive Zip, qui contient également le code de l'énoncé. Les solutions aux différents exercices sont brièvement discutées ci-dessous.

Exercice 1

Cet exercice ne présente pas de difficulté particulière.

Exercice 2

La méthode linearHorizontalGradientMask est une simplification de la méthode linearHorizontalGradient. Le passage de la seconde à la première est donc très simple, il suffit d'enlever les deux couleurs et de retourner la valeur de la proportion. On obtient alors :

static Image<Double> linearHorizontalGradientMask(double xL,
                                                  double xR){
    if (! (xR > xL))
        throw new IllegalArgumentException();

    return (x, y) -> {
        if (x <= xL)
            return 0d;
        else if (x >= xR)
            return 1d;
        else
            return (x - xL) / (xR - xL);
    };
}

Exercice 3

La méthode de composition se définit très facilement et n'est qu'une traduction de la version mathématique donnée dans l'énoncé :

public static Image<ColorRGB> compose(Image<ColorRGB> bg,
                                      Image<ColorRGB> fg,
                                      Image<Double> m) {
    return (x, y) ->
        bg.valueAt(x, y).mixWith(fg.valueAt(x, y),
                                 m.valueAt(x, y));
}

Exercice 4

Une fois la classe (bien entendu immuable) Complex des nombres complexes définie, l'écriture de la méthode mandelbrotSet ne pose plus d'énormes difficultés.

La première version proposée ici est une version itérative classique consistant à calculer, dans une boucle, les termes successifs de la suite et de s'arrêter dès que le nombre maximum de termes a été atteint ou que le module du terme actuel est supérieur ou égal à 2.

public static Image<Boolean> mandelbrotSetIt(int maxIt) {
    return (x, y) -> {
        Complex c = new Complex(x, y);
        Complex z = c;
        int i = 1;
        while (i < maxIt && z.modulusSquared() < 4d) {
            z = z.squared().add(c);
            i += 1;
        }
        return i == maxIt;
    };
}

Notez que pour des questions de performance, le calcul des termes de la suite commence avec \(z_1\) et pas \(z_0\), et que seul le carré du module est calcué et comparé avec \(2^2 = 4\).

La seconde version de cette méthode utilise la programmation par flots et consiste à calculer le flot des termes de la suite (au moyen de la méthode iterate), à n'en garder que le nombre maximum donné (au moyen de la méthode limit), et à vérifier si tous ont un module inférieur à 2 (au moyen de la méthode allMatch). Cette formulation est à la fois concise et élégante :

public static Image<Boolean> mandelbrotSet(int maxIt) {
    return (x, y) -> {
        Complex c = new Complex(x, y);
        return Stream.iterate(c, z -> z.squared().add(c))
            .limit(maxIt - 1)
            .allMatch(z -> z.modulusSquared() < 4d);
    };
}