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

10022009 Dans: 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

4 Réponses à “Générateur d'images fractales (3)”

  1. Renaud Pradenc
    11 février 2009 | 11:17

    @Franck: je me suis lancé dans ce projet parce que beaucoup de gens ont l'air un peu perdus même après avoir lu le livre d'Hillegaas. Mon but est de connecter l'apprentissage à la réalité du terrain. Merci pour vos commentaires qui m'encouragent.

  2. CARTON Franck
    11 février 2009 | 13:41

    Merci pour ce tres bon tuto concernant le developpement Mac OS X. Plein de chose interessante qui me semble un bon complement à la literatture sur le sujet.

  3. CARTON Franck
    16 février 2009 | 06:48

    A cette effet ne serait il pas possible de rajouter a la fin une archive regroupant les differents chapitres de l'article ainsi que les differentes étapes du projet. Ceci permettrais je pense aux personnes interessées de pouvoir travailler plus aisement sans avoir a naviguer entre les differentes pages de l'article (même si la section pas a pas facilite la navigation actuellement, j'ai peur que dans quelques temps...)

    P.S. : cocoa.fr... le site qui anticipe les reactions de ses lecteurs ? (cf mon message posté le 11 fevrier a 13:41 et votre reponse le même jour à... 11:17 ;) )

  4. Renaud Pradenc
    16 février 2009 | 08:44

    @Franck: Pour l'instant, l'organisation convient. De plus, cette série n'est pour l'instant qu'un essai, il n'est pas certain qu'il y a aura d'autres pas-à-pas par la suite. Je suis souvent partisan de l'effort minimum, donc pour l'instant, on ne fait rien.

Laisser un commentaire