4. Configurazioni iterative

Strutture di controllo iterative

Controllo del flusso delle istruzioni

Ogni algoritmo può essere definito usando e combinando tre strutture di base.

Sequenza semplice successione di istruzioni
(ottenuta con: ordine delle istruzioni)

Decisione esecuzione al verificarsi di una condizione
(ottenuta con: if, switch o ?:)

Ripetizione esecuzione ciclica di una o più istruzioni
(ottenuta con: for o while)

La struttura for


	for (inizializza; condizione; aggiorna)
	{
		istruzioni da ripetere
	}
	

una o più operazioni iniziali eseguite una sola volta

il for inizia o ripete le istruzioni se la condizione è true, altrimenti finisce

operazioni eseguite dopo ogni ciclo prima di una nuova verifica della condizione

Le parentesi graffe possono essere tolte se viene usata un'unica istruzione... ma si consiglia di tenerle.

Ordine di esecuzione delle parti del for


	for (inizializza; condizione; aggiorna) {
		istruzioni
	}
	

	- inizializza
	- condizione (==true)
	- istruzioni

	- aggiorna
	- condizione (==true)
	- istruzioni

	  ...

	- aggiorna
	- condizione (==false)
	- uscita dal for
	

Se la condizione è false al 1° controllo, non viene eseguita nessuna istruzione.

Dalla sequenza al for

Immagine generata con una sequenza di istruzioni:


	function setup() {
		background( 200 );

		var x = 10;
		rect( x,20, 20,60 );

		x = x+30;
		rect( x,20, 20,60 );

		x = x+30;
		rect( x,20, 20,60 );

		// x = x+30;
		// x < 100  == false
	}
		

La stessa immagine generata con un for:


	function setup() {
		background( 200 );

		for (var x = 10;  x < 100;  x = x+30) {
			rect( x,20, 20,60 );
		}
	}
	

si può scrivere anche: x += 30.

Provare ad allargare il canvas (ad es., con createCanvas(400,100); prima di background(200)) e aumentare il numero di rettangoli intervenendo sui parametri del for.

Provare ad allargare il canvas e aumentare le spaziature fra i rettangoli intervenendo sui parametri del for.

Interruzione forzata del for: break


	function setup() {
		background( 200 );

		for (var x = 5;  x < 100;  x = x+20) {
			if (x == 65) {
				break;
			}
			rect( x,20, 10,60 );
		}
	}
	

interrompe il for ed evita l'esecuzione dei cicli successivi.

Salto di un ciclo: continue


	function setup() {
		background( 200 );

		for (var x = 5;  x < 100;  x = x+20) {
			if (x == 65) {
				continue;
			}
			rect( x,20, 10,60 );
		}
	}
	

interrompe il ciclo corrente e passa a quello successivo.

Uso di for annidati


	function setup() {
		background( 200 );

		for (var x = 5;  x < 86;  x = x+20) {
			for (var y = 5;  y < 66;  y = y+20) {
				rect( x,y, 10,10 );
			}
		}
	}
	

esegue 5 cicli con x che va da 5 a 85.

esegue 4 cicli (con y che va da 5 a 65) per 5 volte (una per ogni variazione di x).

Provare a modificare il codice in modo da far disegnare anche l'ultima riga di quadratini.

Array e iterazioni sui dati

Gli array

Struttura dati che permette di memorizzare e gestire sequenze di valori.

Vengono identificati da un nome (come le variabili) e il riferimento ai singoli elementi avviene attraverso un indice numerico.

Creazione degli array


	var gradi = [];  // array vuoto
	gradi[0] = 24;   // imposta 1° elemento
	gradi[1] = 16;   // imposta 2° elemento
	gradi[2] = 27;   // imposta 3° elemento
	

nome dell'array.

indice dell'elemento, a partire da 0 (zero).

Se i valori sono noti fin dall'inizio, il codice può essere scritto anche così:


	var gradi = [ 24, 16, 27 ];
	

valori iniziali separati da virgole.

Lettura e uso degli elementi di un array


	function setup() {
		var gradi = [ 24, 16, 27 ];

		text( gradi[0], 10, 20 );
		text( gradi[1], 10, 35 );
		text( gradi[2], 10, 50 );
	}
	

indice dell'elemento di cui leggere il valore.

Lo stesso codice si puo riscrivere con un for:


	function setup() {
		var gradi = [ 24, 16, 27 ];

		for (var i=0; i<gradi.length; ++i) {
			text( gradi[i], 10, 20+i*15 );
		}
	}
	

proprietà degli array che restituisce il numero di elementi; nell'esempio, gradi.length corrisponde a 3.

l'operatore ++ somma 1 al valore della variabile; ++i corrisponde quindi a i=i+1.

calcola i valori della coordinata y:
       - 20 (20+0*15)
       - 35 (20+1*15)
       - 50 (20+2*15).

Aggiunta di elementi a un array


	function setup() {
		var gradi = [ 24, 16, 27 ];

		gradi.unshift(10);  // inserisci all'inizio
		gradi.push(30);     // aggiungi in coda

		for (var i=0; i<gradi.length; ++i) {
			text( gradi[i], 10, 20+i*15 );
		}
	}
	

metodo degli array che permette di inserire un elemento iniziale facendo slittare automaticamente tutti gli altri.

metodo degli array che permette di aggiungere un elemento dopo tutti gli altri.

Eliminazione di elementi a un array


	function setup() {
		var gradi = [ 24, 16, 27 ];

		gradi.splice( 1, 1 );  // togli 2° elemento
		gradi.pop();           // togli ultimo elemento

		for (var i=0; i<gradi.length; ++i) {
			text( gradi[i], 10, 20+i*15 );
		}
	}
	

il primo parametro indica l'indice dell'elemento da eliminare, il secondo indica il numero di elementi da eliminare.

metodo degli array che permette di eliminare l'ultimo elemento.

Il metodo splice() permette anche di inserire elementi in una determinata posizione facendo slittare quelli successivi.

Gli array JavaScript hanno anche altri metodi che permettono di fare operazioni più complesse: w3schools.com/js/js_array_methods.asp.

Iterazioni cromatiche

Generazione di una sequenza casuale di colori


	function setup() {
		// disegna barre colorate
		noStroke();
		for (var x = 0;  x < width;  x += 5) {
			fill( random(255), random(255), random(255) );
			rect( x,0, 5,100 );
		}
	}
	

l'operatore += permette di sommare il valore a destra a quello della variabile a sinistra, nel caso specifico corrisponde a x=x+5.

4 risultati possibili

Creazione e uso di una palette predefinita


	function setup() {
		// definisci la palette
		var palette = [
			color(255,0,0), color(255,255,0),
			color(0,255,0), color(0,255,255),
			color(0,0,255), color(255,0,255)
		];
		// disegna barre colorate
		noStroke();
		for (var x = 0;  x < width;  x += 5) {
			var c = x/width * palette.length;
			fill( palette[ int(c) ] );
			rect( x,0, 5,100 );
		}
	}
	

color() è una funzioni di p5.js che permette di compattare le informazioni di un colore in un unico intero utilizzabile nelle istruzioni di impostazione del colore.

x/width restituisce un valore con decimali che va da 0 (0/100) a 0.99 (99/100); moltiplicato per il numero di elementi dell'array (palette.length) permette di ottenere l'intera sequenza degli indici della palette;  i valori possono essere ricavati anche con:
c = map( x, 0,width, 0,palette.length );

gli indici degli array devono essere sempre numeri interi.

Creazione e uso di una palette con gradienti


	function setup() {
		colorMode( HSL, 360,100,100 );
		// crea la palette
		var palette = [];
		for (var n = 0;  n < 20;  ++n) {
			palette[n] = color( 30, 100, n*5 );
		}
		// disegna barre colorate
		noStroke();
		for (var x = 0;  x < width;  x += 5) {
			var c = x/width * palette.length;
			fill( palette[ int(c) ] );
			rect( x,0, 5,100 );
		}
	}
	

uso della modalità cromatica HSL per rendere più semplice impostare una variazione di luminosità di qualsiasi colore.

colore sempre arancione (30), al massimo della saturazione (100) e con un valore di luminosità variabile da 0 (0*5) a 95 (19*5).

Provare a modificare la sfumatura intervenendo sui parametri della funzione color() e usando la variabile n.

Scelta a caso da una palette


	function setup() {
		colorMode( HSL, 360,100,100 );
		// crea la palette
		var palette = [];
		for (var n = 0;  n < 20;  ++n) {
			palette[n] = color( 30, 100, n*5 );
		}
		// disegna barre colorate
		noStroke();
		for (var x = 0;  x < width;  x += 5) {
			var c = random( palette.length );
			fill( palette[ int(c) ] );
			rect( x,0, 5,100 );
		}
	}
	

valore a caso nell'intervallo dei valori di indice validi.

4 risultati possibili

Iterazioni vettoriali

Creazione poligoni irregolari


	function setup() {
		background( 200 );

		var vertici = 4;

		beginShape();
		for (var i=0; i<vertici; ++i) {
			var x = random( width );
			var y = random( height );
			vertex( x, y );
		}
		endShape( CLOSE );
	}
	

la variabile vertici definisce il numero di vertici da calcolare e da usare per disegnare il poligono.

il parametro CLOSE permette di chiudere il poligono senza indicare di nuovo il primo vertice.

3 risultati possibili

Creazione poligoni regolari: metodo

I poligoni regolari vengono disegnati calcolando la successione dei vertici sulla circonferenza in cui il poligono è inscritto.

Partendo da un angolo iniziale e un raggio noti, il problema è ricavare la successione degli angoli e quindi le coordinate dei vertici sulla circonferenza.

Radianti, coseni e seni

Ipotizzando un segmento:

il coseno di rad corrisponde alla coordinata x della seconda estremità

il seno di rad corrisponde alla coordinata y della seconda estremità

Funzioni cos() e sin()


	x = cos( rad );
	y = sin( rad );
	

Entrambe le funzioni restituiscono valori che vanno da -1 a 1.

Per modificare la posizione e il raggio della circonferenza di riferimento è sufficiente moltiplicare il nuovo raggio ai valori ottenuti e sommare le coordinate del nuovo centro:


	x = 300 + cos( rad ) * 200;
	y = 100 + sin( rad ) * 200;
	

sposta il centro alle coordinate 300,100.

porta il raggio a 200 pixel.

Sketch interattivo: openprocessing.org/sketch/145782

Creazione poligoni regolari: algoritmo


	function setup() {
		background( 200 );

		var vertici = 5;

		beginShape();
		for (var i=0; i<vertici; ++i) {
			var angolo = i * TWO_PI/vertici;
			var x = 50 + cos( angolo )*40;
			var y = 50 + sin( angolo )*40;
			vertex( x, y );
		}
		endShape( CLOSE );
	}
	

ricava l'angolo fra un vertice e l'altro; potrebbe essere calcolato una sola volta, prima del for, e memorizzato in una variabile da usare al posto della divisione.

coordinate riferite a una circonferenza posizionata al centro del canvas (50,50) e con un raggio di 40 pixel.

Per ruotare il vertice iniziale di 90° in senso antiorario, si può usare:
angolo = i*TWO_PI/vertici - HALF_PI;

Variazioni dei poligoni regolari: stella


	function setup() {
		background( 200 );

		var vertici = 16;

		beginShape();
		for (var i=0; i<vertici; ++i) {
			var angolo = i * TWO_PI/vertici;
			var raggio = (i%2 == 0)? 40 : 10;
			var x = 50 + cos(angolo) * raggio;
			var y = 50 + sin(angolo) * raggio;
			vertex( x, y );
		}
		endShape( CLOSE );
	}
	

la condizione i%2 == 0 è vera se i è pari (resto di i/2 uguale a 0(); se la condizione è vera raggio diventa 40, altrimenti diventa 10.

Iterazioni di pattern

Traslazione delle coordinate


	translate( 20,10 );
	rect( 0,0, 60,60 );
	

modifica le coordinate di riferimento in modo che l'origine si trovi alle coordinate 20,10.

Variazione di scala delle coordinate


	scale( 1.5, 0.5 );
	rect( 0,0, 60,60 );
	

modifica le coordinate di riferimento in modo che l'asse orizzontale sia allungato al 150% (1.5) e quello verticale sia ridotto al 50% (0.5).

Rotazione delle coordinate


	rotate( TWO_PI/12 );
	rect( 0,0, 60,60 );
	

modifica le coordinate di riferimento in modo che siano ruotate di 30° in senso orario (TWO_PI/12).

Somma e reset delle trasformazioni


	function setup() {
		background( 200 );

		translate(20,10);
		rect(0,0, 50,50);

		translate(20,10);
		rect(0,0, 50,50);

		resetMatrix();
		rect(0,0, 50,50);
	}
	

il secondo translate() si somma a quelli precedenti; in questo caso l'origine delle coordinate risulterà spostato di 40 pixel (20+20) verso destra e di 20 (10+10) verso il basso.

l'istruzione riporta le coordinate alla situazione iniziale.

Provare a sostituire uno o entrambi i translate() con le istruzioni scale() o rotate()

Ripetizione di trasformazioni


	function setup() {
		createCanvas( 400, 300 );
		background( 200 );

		for (var i=0;  i<200;  ++i)
		{
			translate( 2, 8 );
			rotate( -0.02 );
			scale( 0.986 );

			rect( 0,0, 120,120 );
		}
	}
	

Trasformazioni con for annidati


	function setup() {
		createCanvas( 300, 300 );
		background( 200 );

		rectMode( CENTER );
		var angolo = 0;

		for (var y=0;  y<=height;  y+=20) {
			for (var x=0;  x<=width;  x+=20) {

				angolo += 0.006;
				resetMatrix();
				translate( x,y );
				rotate( angolo );

				rect( 0,0, 20,20 );
			}
		}
	}
	
Successione del disegno dei moduli.

Pattern casuali con for annidati


	function setup() {
		background( 200 );

		for (var y=0;  y<height;  y+=10) {
			for (var x=0;  x<width;  x+=10) {

				if (random(100) < 50) {
					line( x,y+5, x+5,y );        // disegna  /
					line( x+5,y+10, x+10,y+5 );  // disegna     /
				} else {
					line( x+5,y, x+10,y+5 );     // disegna     \
					line( x,y+5, x+5,y+10 );     // disegna  \
				}
			}
		}
	}
	
Con una probabilità del 50% può essere disegnato casualmente un modulo anziché l'altro.