La teoria frattale mi ha sempre affascinato, sin da quanto un bel po' di anni fa ho letto "How Long Is the Coast of Britain ?" del matematico Mandelbrot e spesso, in vari linguaggi, mi diverto a realizzare piccole rappresentazioni di semplici insiemi o figure autosimilanti. Per testare anche le animazioni in Flutter, ho riprodotto il Triangolo di Sierpiński.
Cosa sono i frattali ?
I frattali sono figure geometriche caratterizzate dal ripetersi sino all'infinito di uno stesso motivo su scala sempre più ridotta. Un frattale è un insieme F che abbia proprietà simili a quelle elencate qui di seguito:
- Autosimilitudine: F è unione di un numero di parti che, ingrandite di un certo fattore, riproducono tutto F; in altri termini F è unione di copie di se stesso a scale differenti.
- Struttura fine: F rivela dettagli ad ogni ingrandimento. Non è possibile definire in modo netto ed assoluto i confini dell'insieme (i bordi dell'immagine)
Il Triangolo di Sierpiński
Il triangolo di Sierpiński è un frattale, così chiamato dal nome di Wacław Sierpiński che lo descrisse nel 1915. È un esempio base di insieme auto-similare, cioè matematicamente generato da un pattern che si ripete allo stesso modo su scale diverse, basato su un triangolo equilatero.
- Livello 0 Si parte da un triangolo equilatero di lato a.
- Livello 1 Si congiungono i punti medi di ciascun lato individuando quattro triangoli simili al primo (di lato a/2) di cui tre ugualmente orientati e uno capovolto.
- Livello 2 Si ripete l'operazione di scomposizione precedente su ciascuno dei tre triangoli non capovolti ottenendo 9 triangolini non capovolti di lato a/4.
- Livello 3 Si ripete la stessa operazione sui 9 triangoli ottenendone 27 di lato a/8.
- Livello 4 Si ripete la stessa operazione sui 27 triangoli ottenendone 81 di lato a/16.
- ....
- Livello n Si ottengono 3^n triangoli di lato a*2^(-n) (dove a è il lato del triangolo al livello 0).
L'implementazione in Flutter
Partiamo da un nuovo progetto e sostituiamo il codice standard nel file main.dart con questo:
import 'package:flutter/material.dart';
void main() { runApp(MyApp());}
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Fractal Sierpiński', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); }}
class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState();}
class _MyHomePageState extends State<MyHomePage> {}
Per disegnare la figura frattale abbiamo bisogno di un un widget CustomPaint() e di una variabile che indichi il livello di dettaglio, quindi iniziamo a definire il metodo build in questo modo:
Il painter personalizzato
Per la definizione del painter personalizzato, nella cartella lib creiamo un nuovo file triangle_painter.dart con la definizione iniziale della classe che useremo:
Questo codice disegna il triangolo del livello 0, e per farlo
- determina la dimensione minima del canvas (per poter ottenere la base del nostro triangolo tale che non fuoriesca dal canvas),
- determina l'altezza del triangolo equilatero associato (applicando il teorema di Pitagora),
- determina l'offset verticale ed orizzontale necessario a centrare il nostro triangolo nel canvas,
- definisce la Path() del triangolo,
- infine disegna il triangolo sul canvas.
Il risultato è questo:
Al livello 1, dobbiamo disegnare il triangolo capovolto unendo i punti medi dei lati. Aggiungiamo quindi il codice necessario, definendo una funzione che potremo richiamare all'occorrenza:
Questo funzione,
- verifica che il livello sia maggiore o uguale a zero, altrimenti non fa nulla,
- determina l'altezza del nuovo triangolo equilatero a partire dalla base,
- definisce la Path() del triangolo,
- disegna il triangolo sul canvas.
Non ci resta che richiamarla all'interno del nostro metodo paint() in questo modo:
(decrementiamo il livello, dimezziamo la base e passiamo le coordinate del vertice inferiore del triangolo da disegnare):
E' chiaro a questo punto che applicando la ricorsività, potremo ripetere questo procedimento per ogni livello. La nostra funzione _drawTriangle() dovrà ricorsivamente richiamare se stessa (decrementando il livello e dimezzando la base ad ogni step), per il triangolo di sinistra, quello di destra e quello superiore. Aggiungiamo quindi queste righe:
Per un livello 2 e 5 otteniamo rispettivamente questi due risultati
Miglioriamo il codice
Miglioriamo l'interfaccia della nostra applicazione aggiungendo uno slider che permetta di scegliere facilmente il livello desiderato. Lavoriamo sul file main.dart e modifichiamo quanto segue:
con questo risultato.
Aggiungiamo l'animazione
Per come è scritto il codice finora, il disegno del triangolo avviene in un solo momento. Aggiungiamo quindi un effetto di animazione per testare anche questa funzionalità di Flutter.
Aggiungiamo il mixin:
un controller, un listenable e la variabile di appoggio
l'interpolazione (al momento inizio e fine coincidono con il valore iniziale del livello):
inizializziamo il controller e l'animazione, fissando il periodo in 1500 millisecondi e una curva Curves.easeOut :
una funzione che aggiorna il livello ad ogni tween:
e modifichiamo lo slider in modo che intervenga sul valore massimo dell'interpolazione, resettando il controller ogni volta che si modifica il valore del livello desiderato :
Possiamo differenziare i livelli anche per colore. Nel triangle_painter.dart, aggiungiamo una lista di colori:
e modifichiamo le funzioni drawPath() per cambiare colore ad ogni livello:
Il risultato finale che si ottiene è:
Conclusioni
Questo codice è chiaramente solo un punto di partenza per studiare i grafici frattali e le animazioni. Si può cambiare il painter per esempio per disegnare l'insieme di Jiulia altre forme note o si può modificare la curva dell'animazione per avere effetti diversi.
Come per gli articoli precedenti potete trovare il codice tra le mie repo GitHub: https://github.com/luigimicco/flutter_sierpinski Nella repo troverete anche uno slider per implementare lo zoom sul canvas.
Come per gli articoli precedenti potete trovare il codice tra le mie repo GitHub:
Commenti
Posta un commento