Générateur d'images fractales (3)

10022009 In: Pas à pas

Après la génération de notre premier image dans l'article précédent, nous allons remanier notre programme pour le rendre plus propre.

Remaniement

Le remaniement (en anglais: Refactoring) est une opération qu'il est bon de faire continuellement. Il s'agit :

  • de changer l'architecture de l'application
  • d'améliorer la documentation
  • ou simplement de renommer certaines classes, méthodes ou variables.

Le but est de faciliter les modifications à venir, en gardant le code le plus propre possible tout au long de la vie du logiciel. Notez bien que nous n'ajoutons pas de fonctionnalités.

Isoler les fonctions sur les nombres complexes

Les fonctions mathématiques peuvent être réutilisées à divers endroits de notre programme, et pas seulement pour la calcul de l'image fractale, nous allons donc les séparer:

  • Créez un nouveau source C, appelé CFRComplexe.c (menu File > New File > C and C++ > C File).
  • Déplacez la définition de la structure ainsi que le prototype de Carre() dans CFRComplexe.h.
  • Déplacez la fonction Carre() dans CFRComplexe.c.

Pendant que nous y sommes, nous créons la fonction ModuleAuCarre():

double ModuleAuCarre(Complexe_t z)
{
    return z.reel*z.reel + z.imag*z.imag;
}

N'oubliez pas de rajouter son prototype dans le .h.

Isoler le calcul de l'image fractale

Les calculs appartenant à la couche Modèle, nous allons créer un objet CFRMandelbrotRender qui ne fait que générer la bitmap:

@interface CFRMandelbrotRender : NSObject {

}

- (NSBitmapImageRep*) bitmapImageRep;
@end

Déplaçons dans CFRMandelbrot.m tout le code qui fait le calcul de la bitmap:

- (NSBitmapImageRep*) bitmapImageRep
{
    // Créer la bitmap
    NSBitmapImageRep* bitmapRep = 
        [[NSBitmapImageRep alloc]
        initWithBitmapDataPlanes:NULL
        pixelsWide:400
        pixelsHigh:300
        bitsPerSample:8
        samplesPerPixel:1
        hasAlpha:NO
        isPlanar:NO
        colorSpaceName:NSDeviceWhiteColorSpace
        bytesPerRow:0
        bitsPerPixel:8];

    // Calculer l'ensemble de Mandelbrot:
    // Parcourir tous les points de la bitmap
    double x, y;
    for(x = 0; x < 400; x++)
    {
        for(y = 0; y < 300; y++)
        {
            // Convertir les coordonnées
            Complexe_t c;
            c.reel =  x/100.0 - 2;
            c.imag   = -y/100.0 + 1.5;

            // Initialiser z[0]
            Complexe_t z = {0.0, 0.0};

            NSUInteger n;
            for(n=0; n < MAX_ITERATIONS; n++)
            {
                // z[n+1] = z[n+1]^2 + c
                Complexe_t zCarre = Carre(z);
                z.reel = zCarre.reel + c.reel;
                z.imag = zCarre.imag + c.imag;

                // La suite diverge si |z| > 2
                if(ModuleAuCarre(z) > 4.0)
                    break;
            }

            // Donner le niveau de gris au pixel
            NSUInteger nuance = n * 255 / MAX_ITERATIONS;
            [bitmapRep setPixel:&nuance atX:x y:y];
        }
    }

    [bitmapRep autorelease];
    return bitmapRep;
}

Les modifications sont les suivantes:

  • la nouvelle fonction ModuleAuCarre() est appelée au lieu de faire le calcul en ligne.
  • la méthode [bitmapRep autorelease] est appelée pour que la mémoire occupée par la bitmap soit libérée, mais seulement après que la vue ait pu l'afficher.

Remaniement de la vue

Renommer

La vue pourrait maintenant servir à afficher autre chose que l'ensemble de Mandelbrot, par exemple celui de Julia. Ceci nous pousse à la renommer. Nous allons utiliser l'outil de Refactoring de XCode pour cela:

  • Ouvrez CFRMandelbrotView.m
  • Sur la ligne @implementation CFRMandelbrotView, sélectionnez le texte CFRMandelbrotView
  • Choisissez l'article de menu Edit > Refactor…
  • Le menu pop-up étant sur Rename, tapez le nouveau nom: CFRFractalView
  • Cliquez sur Preview, puis Apply.

Voilà un outil bien pratique, d'autant plus qu'il renomme la classe y-compris dans le fichier XIB !

Fixer la classe de rendu

Mettre à part le code qui fait le rendu nous impose maintenant de créer un lien pour récupérer la bitmap. Ajoutons une outlet à CFRFractalView:

#import "CFRMandelbrotRender.h"

@interface CFRFractalView : NSView {
    IBOutlet CFRMandelbrotRender* render;
}

@end

Basculez sous Interface Builder. Instanciez un exemplaire de CFRMandelbrotRender, et reliez l'outlet render de la vue à cet objet.

Méthode drawRect:

Retournons sous XCode, dans CFRFractalView.m. Ajoutons à la méthode drawRect: le nécessaire pour obtenir la bitmap et l'afficher:

- (void)drawRect:(NSRect)rect
{
    if(render)  // L'outlet est fixée
    {
        // Afficher la bitmap
        NSBitmapImageRep* bitmapRep = [render bitmapImageRep];
        [bitmapRep drawAtPoint:NSMakePoint(0,0)];       
    }

    else    // L'outlet "render" n'est pas fixée
    {
        [[NSColor blueColor] set];
        NSRectFill(rect);
    }

}

J'ai ici gérer le cas où l'on aurait oublier de relier l'outlet render. La vue serait alors emplie de bleu.

Résultat

Nous pouvons à présent lancer le programme: le résultat est très exactement le même. Mission accomplie !

Nous disposons dorénavant d'une bonne base pour poursuivre le développement. À bientôt pour la suite.

Le projet XCode complet à télécharger.

Renaud Pradenc
Céroce.com

Articles similaires

Partager