-
.
Il parametro che caratterizza un frattale è la sua dimensione.
In geometria euclidea i quattro elementi fondamentali, punto, retta, piano, volume, sono caratterizzati dall'avere le dimensioni rispettivamente di 0 (punto), 1 (retta), 2 (piano), 3 (spazio). Le dimensioni sono pertanto degli interi
Una figura frattale possiede invece, nella generalità dei casi, una dimensione frazionaria e proprio da questo fatto ne ha preso il nome.
Un frattale di dimensione 1.2, è più che una retta ma meno di un piano; questo significa che quel frattale riesce a completare una retta ma non riesce a coprire l'intero piano. Si dirà che è piu di una retta ma meno di un piano.
La dimensione di un frattale è definita (Hausdorff) come il rapporto tra i logaritmi di base arbitraria del numero di repliche ottenute ad ogni ciclo e il fattore di riduzione della dimensione lineare dell'elemento che si replica.
Nel caso del merletto di Sierpinsky, ad ogni passo, da un triangolo ne vengono generati tre (in realtà se ne generano 4, di cui uno viene tolto), e ognuno dei tre triangoli generati ha rapporto tra i lati, rispetto al lato di origine, pari ad 1/2. Indicando con k il denominatore di quest'ultima frazione, la dimensione frattale del merletto di Sierpinsky risulta:
Questa dimensione indica che il merletto è più di una retta ma meno di un piano e che quindi con tale struttura ricorsiva non è possibile ricoprire interamente un piano.
Esistono anche frattali aventi dimensioni inferiori all'unità, come per esempio il frattale di Cantor in cui ad ogni passo si elimina il terzo medio di un segmento di partenza.
Il numero degli elementi generati ad ogni ciclo è 2, mentre il rapporto tra le lunghezze dell'elemento generato e dell'elemento di origine è 1/3 -> k=3
la dimensione frattale dell'insieme di Cantor risulta:
L'insieme di Cantor, come è anche facile intuire, non riesce a ricoprire una retta essendo più di un punto ma meno di una retta.
Edited by afazio - 16/8/2019, 18:41. -
.
Consideriamo per esempio il frattale denominato "Setaccio di Apollonio";
Qui le cose iniziano a complicarsi già soltanto per comprendere cosa avviene ad ogni iterazione.
Di frattali ne esistono tanti quanti la nostra immaginazione possa immaginare, e siccome la mente umana è anche alquanto immaginifica, può senza particolare sforzo immaginarsi spazi e piani immaginari.
L'esempio del setaccio di Apollonio è uno di questi. Si parte da un punto P(x; iy) posto nel piano di Gauss-Argand e applicando delle cicliche trasformazioni si costruisce il setaccio.
Per chi interessato a meglio setacciare le ricorsioni previste nel setaccio, può dare una lettura al link Setaccio di Apollonio dello stesso Paul Bourke da cui spesso in passato ho attinto parecchie procedure geometriche.
Anche il più che famoso "insieme di Mandelbrot", bello, bellissimo specie se si associano i colori, è un insieme frattale definito sul piano complesso di Gauss:
Ma adesso spero di poter risolvere i due quesiti che ho proposto. Appena ci riesco, se l'afa non m'uccide prima, o se non lo fa prima qualcun'altro. riferisco.. -
.
Più facile di quanto immaginavo.
Partiamo dal considerare la formula che permette di determinare l'area di un triangolo equilatero di lato L0
Al passo successivo, quindi nel merletto di rango 1, si hanno N=3 triangoli di lato:
e pertanto l'area dei pieni del merletto di rango 1 risulta:
ossia:
In sostanza ad ogni ciclo l'area dei pieni risulta essere pari ai 3/4 dell'area dei pieni del ciclo precedente.
E'quindi possibile scrivere una formula generale che valga per qualsiasi rango del merletto di Sierpinsky:
Il rapporto tra l'area del merletto di rango 3 rispetto all'area del triangolo iniziale risulta:
A questo punto dovrebbe essere agevole determinare il rango per fissato rapporto.. -
.
Per rispondere al secondo quesito basta partire dalla relazione:
Ricorrendo ai logaritmi otteniamo:
da cui:
Una curiosità.
Il perimetro del merletto di rango n vale:
L'area del merletto di rango n vale:
Per n che tende ad infinito si ha che il perimetro tende a infinito mentre l'area tende a zero. Pertanto il merletto di Sierpinsky di rango infinnito è una linea spezzata di lunghezza infinita che delimita una superficie di area nulla.
Questa è una delle tante stranezze che vanno contro la ragione collegate ai frattali!. -
.
Per risolvere il quesito sul merletto di Sierpinsky ho trascorso piacevolmente un po' di tempo dedicandolo alla lettura di documenti e articoli inerenti alla questione dei frattali. Nel mezzo delle letture sono per caso ripassato nella home page di lazarus notando la pubblicazione della nuova release. Le due cose messe insieme mi hanno fatto risvegliare la voglia di riprendere a scrivere qualcosa in Pascal.
Ho cosi deciso di scrivere un piccolo programmino in Lazarus per il disegno di un merletto di Sierpinsky di rango prefissato (magari fornito in input dall'utente). E chissà che poi non mi venga la voglia di creare da me quelle belle immagini dei frattali di Mandelbrot e di Julia.
Se riesco a far qualcosa penso di riportare i passi a seguire.. -
.
Dopo qualche ora di frastornamento dovuto al fatto che da tempo non scrivevo più codice in pascal e in lazarus, ho iniziato a riprendere memoria di quel poco che sapevo e dopo un paio di ore era nata la bozza del programmino che disegna un merletto di Sierpinsky di rango prefissato. La bozza era ancora rudimentale e presentava qualche problema nei colori.
Li ho risolti e vi mostro l'output del piccolo programmino che descriverò appena ho tempo. In verde i triangoli che ad ogni ciclo vengono sottratti al triangolo di partenza.
E' interessante per coloro che vogliono capire come strutturare i cicli ricorsivi. Per gli altri... si farà ricorso ad un po' di pazienza.
. -
.
Installiamo Lazarus scaricandolo dal sito: Lazarus Download Stable Version
Avviamo l'IDE e iniziamo a popolare il nostro dialogo.
al dialogo ho dato come etichetta "Sierpinsky" agendo sulla proprietà "caption" del dialogo.
Poi ho inserito un componente TChart prelevato dai componenti "Chart". Questo componente mi serve solo per avere a disposizione la sua Canvas dove poter disegnare il merletto.
Avrei potuto scegliere qualsiasi altro componente munito di canvas, come per esempio un TImage, o addirittura sfruttare la stessa canvas del dialogo.
Ho dimensionato il componente ad una larghezza e una altezza di 420 pixel agendo sulle proprietà "Width" e "Height"
quindi ho fissato l'apparenza delle suddivisioni degli assi, agendo sulle proprietà:"AxisList-Left" e "AxisList-Bottom" fissando i valori min e max a +210 e -210 agendo su Range.
Ho quindi aggiunto un bottone con etichetta "merletto di Siepinky" alla pressione del quale si deve attivare la procedura del disegno del merletto, un pulsante "esci" per poter terminare l'applicazione e infine uno TSpinEditEx prelevato dali "lazControl"
Quest'ultimo serve ad inserire in input il rango del merletto che si intende disegnare. Inoltre ad un qualsisi cambiamento del valore del controllo deve avvenire il ridisegno del merletto.
Per evitare l'input di numeri negativi o rango eccessivamente grande tale da fare impallare il programma, ho limitato l'input da 0 (minValue) a 10 (MaxValue) con incremento/decremento di 1.
Tenete conto che già dal rango 5 a seguire non si riesce a distinguere il rango di ordine superiore.
Prima di andare avanti salviamo il progetto attribuendogli il nome "Sierpinsky01" in apposita dedicata cartelletta. Alla unit che contiene il codice fin'ora autogenerato ho dato il nome Sp_Main
Edited by afazio - 21/8/2019, 12:50. -
.
Come primo codice scritto da noi, togliamoci il pensiero del Pulsante Esci a cui ho dato il nome "BtnEsci" Ricordare che nome di un componente e sua caption sono due cose differenti.
Il nome è la variabile con cui viene riconosciuto e manipolato da codide, mentre la Caption è l'etichetta collegata al componente che appare a video.
Doppio clik sul pulsante in esame e appare l'ambiente del codice con gia scritta la procedura che gestisce l'evento OnClik.
Aggiungiamo la semplice riga: Close;CODICEprocedure TForm1.BtnEsciClick(Sender: TObject);
begin
close;
end;
In questo modo in qualsiasi istante delle nostre numerose prove abbiamo pronta la via di uscita.. -
.
Occupiamoci adesso di fissare la struttura che deve avere la procedura da attivare alla pressione del pulsante "Merletto di Sierpinsky" a cui ho dato il nome "Disegna".
Doppio clik sul componente e come al solito siamo catapultati in ambiente del codice all'interno della procedura che gestisce l'evento.
Qui, dovremmo leggere i l contenuto del conrollo Spin e depositare il valore in una variabile che chiamo Rango di tipo integer.
a seguire dovremmo richiamare la procedura che disegna il triangolo di rango 0 e poi la procedura ricorsiva per il disegno del merletto di rango prefissato.
In questa fase dovremmo anche fissare il lato del triangolo (in modo che stia all'interno della canvas del componente Chart") e il punto di ancoraggio del triangolo (per esempio le coordinate del suo vertice inferiore sinistro)
Questi ulteriori dati potrebbero anche essere oggetto di input, ma per gli scopi del presente ho preferito fissarli in maniera che il triangolo sia tutto contenuto all'interno della canvas.CODICEprocedure TForm1.DisegnaClick(Sender: TObject);
var Rango: integer ;
Var Lato: Double;
Var x0, y0 : double;
begin
// leggo il rango del merletto
Rango:=spinRango.Value;
// fisso lato del triangolo epunto di ancoraggio
Lato:=360;
x0:=45;
y0:=365;
// disegno il triangolo equilatero di rango 0
//Richiamo la procedura ricorsiva
end;
Tenete conto che stiamo ragionando in termini di coordinate Canvas in cui l'origine è in alto a sinistra, asse x verso destra e asse y verso il basso. Quindi non c'è corrispondenza tra coordinate canvas e coordinate della Chart. Non stiamo utilizzando la chart in quanto tale, ma solo la sua canvas.. -
.
Ecco la procedura che disegna il triangolo equilatero di partenza ossia il merletto di rango 0 (colorato di rosso): CODICEprocedure TForm1.DisegnaTriangolo(BoxCanvas: TCanvas; x, y, Lato: double);
var
h: double;
w: double;
PuntiT :array of Tpoint;
begin
w := Lato;
h := Lato*sqrt(3)/2;
SetLength(PuntiT, 3);
PuntiT[0] := Point(round(x),Round(y));
PuntiT[1] := Point(round(x+w),round(y));
PuntiT[2] := Point(round(x+w/2),Round(y-h));
BoxCanvas.Pen.Width := 1;
BoxCanvas.Pen.Color := clBlack;
BoxCanvas.Brush.Color := clred;
BoxCanvas.Polygon(PuntiT);
end;
in cui per lecoordinate dei vertici si fa riferimento al seguente schema della Canvas:
Le coordinate dei tre vertici sono:
Vertice A:
xA=x
yA=y
vertice B
xB= xA+w
yB=yA
vertice C
xC=xA+w/2
yC=YA-h
In cui le coordinate del punto di ancoraggio e il lato vengono passati come parametri alla sub.
Oltre a questi è anche necessario passare la canvas dove disegnare il triangolo.
Da notare che per poter disegnare un triangolo pieno (colorato internamente) ho dovuto usare ilmetodo canvas.Polygon() che richiede come parametro una lista di punti.. -
.
Passiamo adesso alla parte algoritmica più interessante:
La procedura ricorsiva
Per prima cosa occorre bene pianificare come intendiamo procedere per evitare inevitabili Stak Overflow dovuti a ricorsioni infinite.
Decido quindi di fissare un parametro che indico con n, inizialmente fissato pari al rango del merletto e che ad ogni ciclo deve essere passato alla stessa procedura ridotto di uno.
In questo modo abbiamo certezza che prima o poi questo parametro giungerà a zero.
E proprio quando n assume valore nullo inseriamo il punto di uscita dalla ricorsione.
Quindi ci va una istruzione:
Se n=0 allora esci.
Quando n=1 la procedura deve disegnare il triangolo interno i cui vertici sono i punti medi dei lati del triangolo di rango 0
Quando n=2 la procedura deve disegnare i tre triangoli interni i cui vertici sono i punti medi dei lati dei triangoli del merletto di rango 1, e cosi via al crescere di n.
Il valore iniziale di n è pari al rango, ma in effetti ne rappresenta il complementare dato che n va via via decrescendo ad ogni chiamata ricorsiva.
Pertanto l'effettiva sequenza è:
Quando n=rango la procedura deve disegnare il triangolo interno i cui vertici sono i punti medi dei lati del triangolo di rango 0
Quando n=rango-1 la procedura deve disegnare i tre triangoli interni i cui vertici sono i punti medi dei lati dei triangoli del merletto di rango 1
Quando n=rango-2 la procedura deve disegnare i nove triangoli interni i cui vertici sono i punti medi dei lati dei triangoli del merletto di rango 2, e cosi via al diminuire di n.
E' necessario quindi definire tre punti di ancoraggio relativi ai tre triangoli generati al passo precedente e determinare le coordinate dei vertici del triangolo interno da riempire di bianco.
Mi riferisco, per i prossimi messaggi e per la spiegazione del codice, al seguente schema:
Edited by afazio - 22/8/2019, 07:30. -
.
La prima bozza della procedura ricorsiva è stata la seguente: CODICEprocedure TForm1.DisegnaMerletto(BoxCanvas: TCanvas; x, y, Lato: double; n: integer);
var
h: double;
w: double;
x1, y1: double;
PuntiTC :array of Tpoint;
begin
// punto di uscita dalla ricorsione.
// N.B.:il triangolo di rango 0 è già stato disegnato
if n=0 then exit;
// Determino le coordinate di ancoraggio del triangolo in basso a sinistra
w := Lato;
h := Lato*sqrt(3)/2;
x1 := x;
y1 := y;
// disegno triangolo centrale col colore bianco
SetLength(PuntiTC, 3);
PuntiTC[0] := Point(round(x1+w/2),round(y1));
PuntiTC[1] := Point(round(x1+w/4),Round(y1-h/2));
PuntiTC[2] := Point(round(x1+3*w/4),Round(y1-h/2));
BoxCanvas.Brush.Color := clWhite;
BoxCanvas.Polygon(PuntiTC);
//Ricorsione : passo n-1 e il lato pari a meta del precedente
if n > 0 then
begin
DisegnaMerletto(BoxCanvas, x1, y1, Lato/2, n-1); //la procedura richiama se stessa
end;
end;
ottenendo come risultato il seguente:
che è quello che mi aspettavo dato che nella ricorsione ho passato solo il punto di ancoraggio del primo triangolo, quello in basso a sinistra ed ogni volta dimezzando la misura del lato e decrementando n.
A questo punto basta aggiungere le coordinate dei punti di ancoraggio del triangolo in basso a destra e di quello in alto, e aggiungere altre due chiamate con i diversi punti di ancoraggio nella parte ricorsiva, ed il gioco è fatto.. -
.
Ecco infine la procedura ricorsiva nella sua versione finale: CODICEprocedure TForm1.DisegnaMerletto(BoxCanvas: TCanvas; x, y, Lato: double; n: integer);
var
h: double;
w: double;
x1, y1, x2, y2, x3, y3: double;
PuntiTC :array of Tpoint;
begin
// punto di uscita dalla ricorsione.
// N.B.:il triangolo di rango 0 è già stato disegnato
if n=0 then exit;
// Determino le dimensioni del triangolo
w := Lato;
h := Lato*sqrt(3)/2;
// Punto di ancoraggio triangolo in basso a sinistra
x1 := x;
y1 := y;
// Punto di ancoraggio triangolo in basso a destra
x2 := x1+w/2;
y2 := y1;
// Punto di ancoraggio triangolo in alto
x3 := x2-w/4;
y3 := y2-h/2;
// disegno triangolo centrale col colore bianco
SetLength(PuntiTC, 3);
PuntiTC[0] := Point(round(x1+w/2),round(y1));
PuntiTC[1] := Point(round(x1+w/4),Round(y1-h/2));
PuntiTC[2] := Point(round(x1+3*w/4),Round(y1-h/2));
BoxCanvas.Brush.Color := clWhite;
BoxCanvas.Polygon(PuntiTC);
//Ricorsione : passo n-1 e il lato pari a meta del precedente
if n > 0 then
begin
DisegnaMerletto(BoxCanvas, x1, y1, Lato/2, n-1);
DisegnaMerletto(BoxCanvas, x2, y2, Lato/2, n-1);
DisegnaMerletto(BoxCanvas, x3, y3, Lato/2, n-1);
end;
end;
e il risultato:
Fantastico. Direi un risultato davvero notevole (per me). -
.
Sembra che questo Sierpinsky come mestiere, munito di ferretti e uncinetti, lavorasse i filati.
Infatti a suo nome, oltre al merletto già visto, c'è anche un tappeto di Sierpinsky e una trina di Sierpinsky e chissà che non ci siano anche pizzi e tricot di Sierpinsky.
Dato che la cosa mi è piaciuta, ho modificato in appena dieci minuti il programmino che disegnava il merletto per poter disegnare un tappeto di Sierpinsky.
Eccolo:
Il tappeto è ottenuto partendo da un quadrato (ma potremmo anche partire da un rettangolo o da un parallelogramma e, penso anche, da un qualsiasi quadrilatero convesso).
Il quadrato di partenza è il tappeto di rango 0.
Da questo viene sottratto un quadrato centrale avente lato pari ad 1/3 del lato di partenza, ottenendo il tappeto di rango 1. Ripetendo l'operazione sugli 8 quadrati rimanenti otteniamo il tappeto di rango 2 e cosi via.
Ad ogni passo, da ciascun quadrato, escono fuori 8 quadrati con rapporto tra i lati pari a 1/3.
La sua dimensione è data da:
e si avvicina alle dimensioni di un piano..