public class MandelBrotFractal : Microsoft.Xna.Framework.GameComponent, FractalI
{
protected double infx = -2, supx = 2, infy = -2, supy = 2; //limiti del piano complesso su cui calcoare la successione
protected double latox, latoy, incx, incy; //lunghezza dei lati e di quando devo incrementare il valore
//da un pixel e l'altro
protected int maxIteration = 250; //numero massimo di iterazione
protected Texture2D fractal; //texture
protected bool complete = true; //indica se il calcolo è completo o no
protected int width, height; //altezza e larghezza della texture
protected Color[] pixData; //vettore contenente il colore dei pixel della texture
protected int total; // numero totale dei pixel della texture
protected Queue<int> indici = new Queue<int>(); //indice del pixel che devo calcolare
protected int[,] iterazioni; //numero di iterazioni per ogni punto
protected Cmplx[,] valori; //valore di ogni punto
protected Cmplx zCm = new Cmplx(), cCm = new Cmplx(); //numeri complessi per il calcolo
public MandelBrotFractal(Game game, double infx, double supx, double infy, double supy, int maxInt)
: base(game)
{
//prendo i valori della larghezza e altezza della texture
width = this.Game.GraphicsDevice.PresentationParameters.BackBufferWidth;
height = this.Game.GraphicsDevice.PresentationParameters.BackBufferHeight;
//numero totale di pixel
total = width * height;
pixData = new Color[total]; //creo l'array per i colori dei pixel
//creo la texture
fractal = new Texture2D(this.Game.GraphicsDevice,
width,
height, 1,
TextureUsage.None, SurfaceFormat.Color);
//creo la matrice che conterrà il numero di iterazioni fatte per ogni punto
iterazioni = new int[height, width];
//creo la matrice di numeri complessi che conterrà il valore di ogni punto del piano che sto calcolando
//e di volta in volta il calcolo parziale
valori = new Cmplx[height, width];
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
valori[i, j] = new Cmplx();
}
public override void Initialize()
{
base.Initialize();
}
public override void Update(GameTime gameTime)
{
if (!complete)
{
//se il frattale è da completare eseguo il calcolo
calcolaFrattale();
}
base.Update(gameTime);
}
protected virtual Cmplx calcola(Cmplx z, Cmplx c)
{
//calcolo il valore
Cmplx ris = z.sqr().add(c);
return ris;
}
protected Cmplx ris;
private void calcolaFrattale()
{
//variabili usate per inizializzare c
double cre, cim;
int indice = 0;
//memorizzo quanti indici devo calcolare
int count = indici.Count;
for(int i = 0; i < count; i++) //faccio count estrazioni dalla coda
{
indice = indici.Dequeue();
//calcolo il corrispettivo indice di riga e di colonna
int r = indice / width;
int c = indice % width;
//calcolo il valore di c da usare (potrei memorizzarlo in una matrice?)
cre = infx + (c * incx);
cim = infy + (r * incy);
//recupero il valore di z
zCm = valori[r, c];
//controllo che il valore sia in modulo minore di 4 e che non abbia già fatto il max num di iterazioni
if (zCm.modsq() <= 4 && iterazioni[r, c] < maxIteration)
{
cCm.set(cre, cim);
ris = calcola(zCm, cCm); //eseguo il calcolo
valori[r, c] = ris; //aggiorno
iterazioni[r, c]++; //aggiorno
indici.Enqueue(indice); //reinserisco l'indice nella coda per il prossimo calcolo
}
else
{
if (iterazioni[r, c] == maxIteration)
{
//il punto fa parte dell'insieme: lo coloro di nero
pixData[indice] = Color.Black;
}
else
{
//il punto non fa parte dell'insieme
float val = (float)iterazioni[r, c] / maxIteration;
int h, s, v;
HsvToRgb(360.0f * val, 1.0f, 1.0f, out h, out s, out v);
//lo coloro in modo opportuno
pixData[indice] = new Color((Byte)h, (Byte)s, (Byte)v);
//Nota: cambiando come si calcola il colore posso ottenere scale di colore diverse
}
}
}
try
{
fractal.SetData(pixData);//aggiorno la texture
}
catch (Exception e)
{ }
if (indici.Count == 0)
{
//se la coda risulta vuota vuol dire che ho finito di calcolare
this.Game.Window.Title = "Render complete";
complete = true;
}
}
private Color makeColor(float r, float g, float b)
{
System.Drawing.Color color = System.Drawing.Color.FromArgb((int)(r), (int)(g), (int)(b));
return new Color(color.R, color.G, color.B);
}
//qui c'è la funzione per il passaggio dal HSV a RGB
//guardate i sorgenti
int Clamp(int i)
{
if (i < 0) return 0;
if (i > 255) return 255;
return i;
}
#region FracalI Membri di
public Texture2D Fractal
{
get { return fractal; }
}
/// <summary>
/// aggiorna il calcolo del frattale dando gli estremi del piano su cui calcolare e il massimo numero di iterazioni
/// </summary>
/// <param name="infx"></param>
/// <param name="supx"></param>
/// <param name="infy"></param>
/// <param name="supy"></param>
/// <param name="maxIt"></param>
public virtual void UpdateFractalDouble(double infx, double supx, double infy, double supy, int maxIt)
{
this.infx = infx;
this.supx = supx;
this.infy = infy;
this.supy = supy;
this.maxIteration = maxIt;
latox = Math.Abs(supx - infx);
latoy = Math.Abs(supy - infy);
incx = latox / width;
incy = latoy / height;
this.Game.Window.Title = "Render WIP";
complete = false;
indici.Clear();
for (int i = 0; i < total; i++)
{
int r = i / width;
int c = i % width;
indici.Enqueue(i);
iterazioni[r, c] = 0;
valori[r, c].set(0, 0);
pixData[i] = Color.Lerp(pixData[i], Color.Black, 0.7f);
}
fractal.SetData(pixData);
}
public bool Complete
{
get { return complete; }
}
public int Width
{
get { return width; }
}
public int Heigth
{
get { return height; }
}
/// <summary>
/// ridimenziona la texture su cui andiamo a calcolare il frattale
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public void resizeTexture(int width, int height)
{
//aggiorna i valori
this.width = width;
this.height = height;
total = width * height;
//ricrea il vettore dei colori
pixData = new Color[total];
//elimina la vecchia texture e la ricrea
fractal.Dispose();
fractal = new Texture2D(this.Game.GraphicsDevice,
width,
height, 1,
TextureUsage.None, SurfaceFormat.Color);
//ricrea la matrice con il numero di iterazioni fatte per ogni punto
iterazioni = new int[height, width];
//ricrea la matrice di numeri complessi per memorizzare il valore di ogni punto
valori = new Cmplx[width, height];
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
valori[i, j] = new Cmplx();
//forzo l'aggiornamento
UpdateFractalDouble(infx, supx, infy, supy, maxIteration);
}
#endregion
}