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()
dansCFRComplexe.h
. -
Déplacez la fonction
Carre()
dansCFRComplexe.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 texteCFRMandelbrotView
-
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