6. Trasformazioni 2D e 3D

Programmazione orientata agli oggetti

Oggetti, proprietà e metodi

Gli "oggetti" sono costituiti da:

proprietà (strutture dati che memorizzano valori)

e

metodi (istruzioni che gestiscono le proprietà)

Definire la "classe" di un "oggetto" in JavaScript


	function NomeClasse( prop1, ... ) {
		this.nomeProprietà = prop1;
		this.nomeMetodo = function() {
			// istruzione del metodo
		};
	}
	

eventuali valori iniziali passati al momento della creazione dell'oggetto.

inizializzazioni delle proprietà, eseguite al momento della creazione dell'oggetto.

definizione dei metodi, eseguiti solo dopo una chiamata diretta.

Per convenzione, i nomi delle classi hanno l'iniziale maiuscola.

La nuova versione di JavaScript (ECMAScript 6) prevede un modo più esplicito di definire le classi.

Creazione e uso di un "oggetto"


	// creazione oggetto
	var nomeOggetto = new NomeClasse( prop1, ... );

	// uso di una proprietà
	var nomeVariabile = nomeOggetto.nomeProprietà;

	// uso di un metodo
	nomeOggetto.nomeMetodo();
	

crea un oggetto basato sulla classe NomeClasse inizializzandola con gli eventuali valori passati fra parentesi tonde.

riferimento alla proprietà interna all'oggetto creato.

riferimento al metodo definito nella classe dell'oggetto.

Uso di oggetti con proprietà


	// definizione classe (con sole prorietà)
	function Circle( x, y ) {
		this.x = x;
		this.y = y;
	}

	// creazione oggetti
	var circle1 = new Circle( 30,30 );
	var circle2 = new Circle( 70,70 );

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

		// uso proprietà oggetti
		ellipse( circle1.x,circle1.y, 40,40 );
		ellipse( circle2.x,circle2.y, 40,40 );
	}
	

Uso di oggetti con proprietà e metodi


	// definizione classe
	function Circle( x, y ) {
		this.x = x;
		this.y = y;
		this.display = function() {
			ellipse( this.x,this.y, 40,40 );
		};
	}

	// creazione oggetti
	var circle1 = new Circle( 30,30 );
	var circle2 = new Circle( 70,70 );

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

		// uso metodi oggetti
		circle1.display();
		circle2.display();
	}
	

Uso di array di oggetti


	function Circle( x, y ) {
		this.x = x;
		this.y = y;
		this.display = function() {
			ellipse( this.x,this.y, 11,11 );
		};
	}

	var circles = [];

	function setup() {
		// creazione 1° oggetto
		circles[0] = new Circle( random(90),random(90) );
	}

	function draw() {
		background(200);
		// visualizzazione oggetti
		for (var i=0;  i<circles.length;  ++i) {
			circles[i].display();
		}
	}

	function mousePressed() {
		// aggiunta oggetto
		circles.push( new Circle( mouseX,mouseY ) );
	}
	

array di oggetti Circle.

Operazioni vettoriali

La classe p5.Vector

I vettori rappresentano la differenza fra un punto iniziale e uno finale e possono essere utilizzati anche per memorizzare la posizione e i vertici delle forme geometriche.

La classe p5.Vector contiene le proprietà x, y e z e una serie di metodi/funzioni che permettono di fare operazioni e ottenere informazioni sulle componenti del vettore. Attraverso le funzioni mag() e heading(), ad esempio, è possibile ottenere la lunghezza e la direzione del vettore.

Spostamento con la classe p5.Vector

Attraverso il metodo add() è possibile sommare a un vettore (ad es. pos) a un altro vettore (ad es. vel) che ne aggiorni le coordinate (pos.x, pos.y) in modo da simulare uno spostamento.

Array di oggetti in movimento


	function Circle( xVel, yVel ) {
		this.pos = createVector();
		this.vel = createVector( xVel, yVel );
		this.display = function() {
			this.pos.add( this.vel );
			ellipse( this.pos.x,this.pos.y, 11,11 );
		};
	}

	var circles = [];

	function setup() {
		// creazione 50 oggetti
		for (var i=0;  i<50;  ++i) {
			var sq = new Circle(random(1), random(1));
			circles.push( sq );
		}
	}

	function draw() {
		background(200);
		// visualizzazione oggetti
		for (var i=0;  i<circles.length;  ++i) {
			circles[i].display();
		}
	}
	

vettore della posizione, preimpostato a 0,0 [p5.js prevede una funzione che usa automaticamente il new].

vettore della velocità (spostamento a ogni fotogramma), impostato con i parametri passati al costruttore della classe Circle.

somma le componenti x e y (e z) del vettore vel a quelle del vettore pos.

Array di vettori come vertici


	var vertices = [];

	function setup() {
		for (var i=0;  i<10;  ++i) {
			var vec = createVector(random(width), random(height));
			vertices.push( vec );
		}
	}

	function draw() {
		background(200);
		beginShape();
			for (var i=0;  i<vertices.length;  ++i) {
				vertex( vertices[i].x, vertices[i].y );
			}
		endShape(CLOSE);
	}
	

array di p5.Vector usati per memorizzare i vertici di un poligono.

Alcuni possibili risultati.

Interpolazioni di vettori e morphing


	var vertsFrom = [];  // vertici poligono iniziale
	var vertsTo = [];    // vertici poligono finale

	function setup() {
		createCanvas(200,200);
		// crea vettori dei vertici
		for (var i=0;  i<6;  ++i) {
			var vFrom = createVector(random(width), random(height));
			vertsFrom.push( vFrom );
			var vTo = createVector(random(width), random(height));
			vertsTo.push( vTo );
		}
	}

	function draw() {
		background(200);
		// visualizza l'interpolazione
		var t = norm( sin(frameCount/50), -1,1 );
		beginShape();
		for (var i=0;  i<vertsFrom.length;  ++i) {
			var vLerp = p5.Vector.lerp(vertsFrom[i], vertsTo[i], t);
			vertex( vLerp.x, vLerp.y );
		}
		endShape(CLOSE);
	}
	

normalizza un valore con andamento sinusoidale dipendente da frameCount: il risultato oscillerà fra 0 e 1 anziché fra -1 e 1.

calcola le interpolazioni fra vertsFrom e vertsTo alla posizione t.

Interpolazioni e figure risultanti.

Grafica 3D e trasformazioni geometriche

Coordinate 3D di riferimento

Nel canvas 3D (contrariamente a quanto succede in Processing) l'origine delle coordinate cartesiane (il punto 0,0) si trova al centro e non in alto a sinistra.

Fonte immagine: Safari CSS Visual Effects Guide

Primitive grafiche 3D

P5.js permette di creare direttamente sei tipologie di solidi attraverso le istruzioni box(), cylinder(), cone(), torus(), sphere() e ellipsoid().

Le istruzioni non prevedono l'indicazione della posizione che va impostata attraverso l'istruzione translate() inserita prima dell'istruzione di disegno.

Uso delle primiteve grafiche 3D

P5.js prevede anche la possibilità di disegnare piani nello spazio 3D.


	function setup() {
		createCanvas( 200, 200, WEBGL );
	}

	function draw() {
		background(200);

		rotateY( frameCount/100 );
		plane( 100, 100 );
	}
	

costante da usare per attivare la visualizzazione in 3D.

permette di far ruotare continuamente le coordinate di riferimento sull'asse delle y.

disegna un piano di 100x100 pixel frontale (che però verrà visualizzato con l'angolazione imposata dall'istruzione rotateY()).

Uso di beginShape() e endShape()

La possibilità di usare la coordinata z non è documentate ma sembra funzionare come in Processing


	function setup() {
		createCanvas( 200, 200, WEBGL );
	}

	function draw() {
		background(200);
		rotateY( frameCount/100 );

		beginShape();
			fill( 255,255,0 );     // giallo
			vertex( 0, -50, 50 );
			fill( 0,255,255 );     // cyan
			vertex( 75, 50, 0 );
			fill( 255,0,255 );     // magenta
			vertex( -75, 50, 0 );
		endShape(CLOSE);
	}
	

l'istruzione fill() permette di definire un colore diverso per ogni vertice ma può essere usata anche solo all'inzio del beginShape() per ottenere un colore unico.

coordinate z dei vertici.

Uso di push() e pop()


	function setup() {
		createCanvas( 400, 200, WEBGL );
	}

	function draw() {
		background(200);

		push();
		translate( -80,0,0 );
		rotateX( frameCount/100 );
		box( 50, 50, 100 );
		pop();

		translate( 80,0,0 );
		rotateX( -frameCount/100 );
		cylinder( 25, 100 );
	}
	

salva l'insieme delle impostazioni grafiche e geometriche.

ripristina le impostazioni grafiche e geometriche salvate dal push() precedente.

Provare a cancellare (o disattivare con //) l'istruzione pop() per vedere l'effetto sulle trasformazioni del cilindro.

Impostazione delle luci


	function setup() {
		createCanvas( 200, 200, WEBGL );
	}

	function draw() {
		background(200);

		ambientLight( 100,100,200 );
		pointLight( 100,200,100, -100,60,0 );

		rotateX( frameCount/100 );
		cylinder( 25, 100 );
	}
	

imposta una luce diffusa blu.

imposta una luce verde (100,200,100) emessa a partire dal punto -100,60,0 verso tutte le direzioni.

P5.js prevede anche la possibilità di impostare una luce che si diffonde con raggi paralleli [vedi directionalLight()].

Materiali degli oggetti 3D


	function setup() {
		createCanvas( 200, 200, WEBGL );
	}

	function draw() {
		background(200);
		ambientLight( 100,100,200 );
		pointLight( 100,200,100, -100,60,0 );

		specularMaterial( 255,200,200 );

		rotateX( frameCount/100 );
		cylinder( 25, 100 );
	}
	

imposta un materiale riflettente rosa (255,200,200) che interagirà con le luci d'ambiente e puntiforme.

Per migliorare la resa del materiale è necessario far disegnare il cilindro con un numero più alto di facce, ad esempio usando: cylinder( 25,100, 48,32 );

Altri materiali si possono impostare con le istruzioni normalMaterial() e ambientMaterial().

Mappature su oggetti 3D


	var texImage;  // immagine da mappare

	function preload() {
		texImage = loadImage( "assets/texture.png" );
	}

	function setup() {
		createCanvas( 200, 200, WEBGL );
	}

	function draw() {
		background(200);
		ambientLight( 100,100,200 );
		pointLight( 100,200,100, -100,60,0 );

		texture( texImage );

		rotateX( frameCount/100 );
		cylinder( 25, 100 );
	}
	

carica l'immagine "texture.png" che si trova nella sottocartella "assets" e salva tutti i dati nella variable texImage.

imposta l'immagine texImage come materiale da mappare sui solidi successivi.

Immagine da mappare e mappatura.

Impostazione della videocamera


	function setup() {
		createCanvas( 200, 200, WEBGL );
	}

	function draw() {
		background(200);
		ambientLight( 100,100,200 );
		pointLight( 100,200,100, -100,60,0 );

		camera( mouseX-100, mouseY-100, 0 );

		rotateX( frameCount/100 );
		cylinder( 25, 100 );
	}
	

sposta la videocamera seguendo la posizione del mouse tenendo conto il cursore ha come origine l'angolo in alto a sinistra (nel caso specifico: -100,-100).

Altri parametri della videocamera si possono impostare attraverso le istruzioni perspective() e ortho().

Un modo veloce per far spostare il punto di vista con il mouse è inserire l'istruzione orbitControl() (invece di camera()) all'inizio del draw().

Trasformazioni geometriche e raster

Codice di base


	var vertices = [];  // vertici del poligono

	function setup() {
		createCanvas(500,300);
		// crea 20 vertici a caso
		for( var i=0; i<20; ++i ) {
			vertices.push( createVector(random(width),random(height)) );
		}
	}

	function draw() {
		background( 220, 4 );

		// disegna poligono
		beginShape();
		for( var i=0; i<vertices.length; ++i ) {
			var vert = vertices[i];
			vertex( vert.x, vert.y);
		}
		endShape(CLOSE);
	}
	

cancella il canvas con un grigio chiaro molto trasparente lasciando intravedere il contenuto dei fotogrammi precedenti.

Trasformazioni geometriche


	var vertices = [];  // vertici del poligono

	function setup() {
		createCanvas(500,300);
		// crea 20 vertici a caso
		for( var i=0; i<20; ++i ) {
			vertices.push( createVector(random(width),random(height)) );
		}
	}

	function draw() {
		background( 220, 4 );

		// trasformazione geometrica
		shearX( sin(frameCount/40)/3 );

		// disegna poligono
		beginShape();
		for( var i=0; i<vertices.length; ++i ) {
			var vert = vertices[i];
			vertex( vert.x, vert.y);
		}
		endShape(CLOSE);
	}
	

attiva uno "stiramento" orizzontale delle forme che verranno disegnate in seguito; l'entità dello "stiramento" oscillerà con un andamento sinusoidale.

Trasformazioni dei vertici


	var vertices = [];  // vertici del poligono

	function setup() {
		createCanvas(500,300);
		// crea 20 vertici a caso
		for( var i=0; i<20; ++i ) {
			vertices.push( createVector(random(width),random(height)) );
		}
	}

	function draw() {
		background( 220, 4 );

		// trasformazione geometrica
		shearX( sin(frameCount/40)/3 );

		// disegna poligono
		beginShape();
		for( var i=0; i<vertices.length; ++i ) {
			// trasformazione dei vertici
			var vert = vertices[i].copy();
			vert.add( random(-5,5), random(-5,5) );
			vertex( vert.x, vert.y);
		}
		endShape(CLOSE);
	}
	

crea una copia del vertice, per evitare di accumulare le modifiche fotogramma dopo fotogramma.

aggiunge una variazione casuale da -5 a +5 pixel a ogni coordinata.

Impostazioni di visualizzazione


	var vertices = [];  // vertici del poligono

	function setup() {
		createCanvas(500,300);
		// crea 20 vertici a caso
		for( var i=0; i<20; ++i ) {
			vertices.push( createVector(random(width),random(height)) );
		}
	}

	function draw() {
		background( 220, 4 );

		// trasformazione geometrica
		shearX( sin(frameCount/40)/3 );

		// impostazioni visualizzazione
		blendMode( DIFFERENCE );

		// disegna poligono
		beginShape();
		for( var i=0; i<vertices.length; ++i ) {
			// trasformazione dei vertici
			var vert = vertices[i].copy();
			vert.add( random(-5,5), random(-5,5) );
			vertex( vert.x, vert.y);
		}
		endShape(CLOSE);

		blendMode( BLEND );
	}
	

imposta il tipo di fusione dei colori da disegnare con quelli già sul canvas; nel caso specifico i colori da disegnare verranno sottratti a quelli del canvas.

ripristina l'impostazione di fusione di default: pixel sommati in base al valore del canale alfa.

Applicazione di filtri al canvas


	var vertices = [];  // vertici del poligono

	function setup() {
		createCanvas(500,300);
		// crea 20 vertici a caso
		for( var i=0; i<20; ++i ) {
			vertices.push( createVector(random(width),random(height)) );
		}
	}

	function draw() {
		background( 220, 4 );

		// trasformazione geometrica
		shearX( sin(frameCount/40)/3 );

		// impostazioni visualizzazione
		blendMode( DIFFERENCE );

		// disegna poligono
		beginShape();
		for( var i=0; i<vertices.length; ++i ) {
			// trasformazione dei vertici
			var vert = vertices[i].copy();
			vert.add( random(-5,5), random(-5,5) );
			vertex( vert.x, vert.y);
		}
		endShape(CLOSE);

		blendMode( BLEND );

		// modifica dei colori dei pixel del canvas
		filter( DILATE );
	}
	

modifica i pixel del canvas in base ai loro valori attuali e (a volte) in base ai valori dei pixel adiacenti; nel caso specifico i pixel tendono ad assumere il colore dei pixel adiacenti più luminosi.

Spostamento di pixel del canvas


	var vertices = [];  // vertici del poligono

	function setup() {
		createCanvas(500,300);
		// crea 20 vertici a caso
		for( var i=0; i<20; ++i ) {
			vertices.push( createVector(random(width),random(height)) );
		}
	}

	function draw() {
		background( 220, 4 );

		// trasformazione geometrica
		shearX( sin(frameCount/40)/3 );

		// impostazioni visualizzazione
		blendMode( DIFFERENCE );

		// disegna poligono
		beginShape();
		for( var i=0; i<vertices.length; ++i ) {
			// trasformazione dei vertici
			var vert = vertices[i].copy();
			vert.add( random(-5,5), random(-5,5) );
			vertex( vert.x, vert.y);
		}
		endShape(CLOSE);

		blendMode( BLEND );

		// modifica dei colori dei pixel del canvas
		filter( DILATE );

		// spostamento di blocchi di pixel
		var img = get(random(width),random(height),random(width),random(height));
		image( img, random(width),random(height) );
	}
	

posizione iniziale a caso dell'area rettangolare di pixel da copiare.

estensione orizzontale e verticale a caso dell'area di pixel da copiare.

disegna in una posizione a caso la porzione di canvas ricavata (img).