Développement Mac et iPhone
Cet épisode sera consacré au redimensionnement de la vue qui affiche l'image fractale.
Ajoutons naïvement la possibilité de redimensionner la vue, sans trop penser aux conséquences.
Ajoutons deux variables d'instance pour savoir à quelles dimensions faire le rendu:
@interface CFRMandelbrotRender : NSObject {
// Dimensions de la bitmap
NSUInteger largeurBitmap;
NSUInteger hauteurBitmap;
}
- (void) setLargeurBitmap:(NSUInteger)largeurPixels;
- (void) setHauteurBitmap:(NSUInteger)hauteurPixels;
- (NSBitmapImageRep*) bitmapImageRep;
@end
N'oublions pas de les initialiser:
- (id) init
{
if(self = [super init])
{
largeurBitmap = 400;
hauteurBitmap = 300;
}
return self;
}
Il faut également ajouter les setters de ces deux variables d'instance:
- (void) setLargeurBitmap:(NSUInteger)largeurPixels
{
largeurBitmap = largeurPixels;
}
- (void) setHauteurBitmap:(NSUInteger)hauteurPixels
{
hauteurBitmap = hauteurPixels;
}
À vous maintenant de modifier le code de -bitmapImageRep
où sont utilisées ces dimensions.
C'est la vue qui va dire au CFRMandelbrotRender à quelle taille il doit générer la bitmap:
- (void)drawRect:(NSRect)rect
{
…
// Afficher la bitmap
[render setLargeurBitmap:[self bounds].size.width];
[render setHauteurBitmap:[self bounds].size.height];
NSBitmapImageRep* bitmapRep = [render bitmapImageRep];
[bitmapRep drawAtPoint:NSMakePoint(0,0)];
}
[self bounds] renvoie un NSRect définissant les limites de la vue. Nous fixons les dimensions de la bitmap pour qu'elles collent à celles de la vue.
Filez éditer MyDocument.xib sous Interface Builder:
Enregistrez, puis lancez le programme.
La bitmap s'est agrandie mais l'ensemble reste collé dans le coin supérieur gauche! En fait, notre programme calcule la partie droite de l'ensemble, alors que ce que nous voulons est que l'ensemble remplisse tout la vue.
Nous voilà de retour à la planche à dessin. Posons déjà le comportement attendu lors d'un redimensionnement:
Pour la génération de la bitmap, il faut utiliser les mêmes proportions que la vue: je fais le choix de privilégier la largeur, qui sera complètement affiché. La hauteur sera, elle, déterminée en fonction de la largeur et des proportions de la vue.
Deux variables d'instance deviennent nécessaires:
@interface CFRMandelbrotRender : NSObject {
// Dimensions de la bitmap
NSUInteger largeurBitmap;
NSUInteger hauteurBitmap;
// Intervalle de calcul
double largeur; // Largeur de l'intervalle du repère calculé
Complexe_t centre; // Centre du repère
}
Il faut penser à initialiser ces variables:
- (id) init
{
if(self = [super init])
{
largeurBitmap = 400;
hauteurBitmap = 300;
largeur = 4.0;
centre.reel = 0.7;
centre.imag = 0.0;
}
return self;
}
Nous allons devoir reprendre la formule de conversion des coordonnées entre la bitmap et le repère mathématique. Voici la représentation du problème:
Nous voulons déterminer la position de point.
On a les relations:
échelle = largeur / largeurBitmap = hauteur / hauteurBitmap
=> hauteur = (largeur / largeurBitmap) * hauteurBitmap
Le plus simple est de réfléchir en localisant "point" en fonction du point inférieur gauche du repère, puis de translater (dans ce qui suit, pointLocal est le point intermédiaire).
Pour l'abscisse:
pointBitmap.x / largeurBitmap = pointLocal.x / largeur
=> pointLocal.x = largeur * (pointBitmap.x / largeurBitmap)
Pour l'ordonnée, c'est à peine plus difficile:
(hauteurBitmap - pointBitmap.y) / hauteurBitmap = pointLocal.y / hauteur
=> pointLocal.y = hauteur * (1 - pointBitmap.y/hauteurBitmap)
Maintenant, il faut translater par rapport au coin inférieur gauche:
coinInférieurGauche.x = centre.x - largeur/2
coinInférieurGauche.y = centre.y - hauteur/2
Au final:
point = pointLocal + coinInférieurGauche
point.x = largeur * (pointBitmap.x/largeurBitmap.x - 1/2) + centre.x point.y = hauteur * (1/2 - pointBitmap.y/hauteurBitmap) + centre.y
Une fois les formules converties en code:
- (Complexe_t) complexeAvecCoordBitmapX:(NSUInteger)bitmapX y:(NSUInteger)bitmapY
{
Complexe_t point;
double hauteur = (largeur/(double)largeurBitmap) * (double)hauteurBitmap;
point.reel = largeur * ((double)bitmapX/(double)largeurBitmap - 0.5) + centre.reel;
point.imag = hauteur * (0.5 - (double)bitmapY/(double)hauteurBitmap) + centre.imag;
return point;
}
Nous utilisons dorénavant cette méthode pour fournir les coordonnées de c:
c = [self complexeAvecCoordBitmapX:x y:y];
Essayons maintenant de lancer notre application. Ça marche !
Vous pouvez changer l'intervalle de calcul. Mettez, dans la méthode -init, largeur
à 2: ça zoome.
Essayez de modifier les coordonnées du centre: on peut se déplacer.
Nous sommes presque prêts à permettre la navigation dans l'ensemble de Mandelbrot. Presque. En effet, je ne sais pas chez vous, mais mon iMac G5 se fait un peu vieux et le redimensionnement de la fenêtre est loin d'être fluide. Nous nous attaquerons donc à l'optimisation dans le prochain épisode.
Le projet XCode complet à télécharger.
Renaud Pradenc
Céroce.com