sviluppo di una libreria orientata agli oggetti per il calcolo di nurbs con applicazioni alla...

179
Universit` a degli Studi di Salerno Facolt`a di Scienze Matematiche Fisiche e Naturali Tesi di Laurea di I livello in Informatica Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D Relatore Candidato Dott. Mario Annunziato Antonio Sanfelice Matricola 05102/00997 Anno Accademico 2009 - 2010

Upload: antonio-sanfelice

Post on 14-May-2015

2.642 views

Category:

Technology


19 download

DESCRIPTION

Tesi di laurea triennale in cui si discute l\'implementazione di una libreria orientata agli oggetti in Python per il calcolo di curve e superfici parametriche, in particolare NURBS, che trova applicazioni nell\'ambito della grafica 2D e 3D.

TRANSCRIPT

Page 1: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Universita degli Studi di Salerno

Facolta di Scienze Matematiche Fisiche e Naturali

Tesi di Laurea di I livello in

Informatica

Sviluppo di una libreria orientataagli oggetti per il calcolo di NURBS

con applicazioni alla grafica 2D e3D

Relatore CandidatoDott. Mario Annunziato Antonio Sanfelice

Matricola 05102/00997

Anno Accademico 2009 - 2010

Page 2: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

ii

Page 3: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Ringraziamenti

Ai miei genitori, che mi hanno dato fiducia e mi hanno concesso la possibilitadi raggiungere questo obiettivo.

A Mariapia, che ha condiviso le mie gioie e i miei dolori.A Demia, Pasquale, Stefania, Enzo, Gianni e a tutti coloro che hanno reso

questi quattro anni i piu divertenti e spensierati della mia vita nonostanteil carico di lavoro che ho dovuto affrontare.

A Tonino, i cui cornetti e caffe, mi hanno tenuto in piedi in questi anni.A tutti coloro che si chiedono come mai, in una facolta il cui nome e

“Scienze Matematiche, Fisiche e Naturali”, vengono torturati con sommato-rie, derivate ed integrali, e che non importa come glielo si spieghi, continuanoa porsi bizzarri interrogativi.

Ai Dream Theater, agli Iron Maiden, ai Within Temptation, ai Night-wish, agli AC/DC, ai Gotthard, ai Guns ’n Roses, ai Three Doors Down,ai Black Sabbath, ai Metallica, ai Testament, ai Children Of Bodom, agliEdguy, ai Blind Guardian, ai Sonata Artica, ai Dire Straits, ai Queen, agliX-Japan, a Joe Satriani, ad Eric Clapton, ad Eric Johnson, agli Alter Brid-ge, agli Europe, ai Firewind, a Mozart, a Beethoven, a Bach, che da unavita mi danno l’adrenalina necessaria ad affrontare i momenti belli e menobelli.

Alla birra che, ne dicano quel che vogliono, mi ha aiutato a risolverealcuni dei peggiori rompicapo che ho incontrato durante lo studio.

A Leonardo Da Vinci, da cui ho imparato che al mondo ci sono troppecose interessanti per dedicare la vita ad una sola di esse.

A tutti voi,

Grazie

iii

Page 4: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

iv

Page 5: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

v

Sommario

Le NURBS (Non Uniform Rational B-Splines) sono lo standard attuale perquanto riguarda la grafica 2D e 3D e rappresentano il modo piu generale dirappresentare una curva o una superficie. Sebbene esistano diverse librerie esoftware gratuiti che consentono di calcolare questo tipo di curve e superfici,alcune di esse sono state abbandonate (come ad esempio la libreria nurbs++[13]), sono poco documentate o eccessivamente complesse ( come la libreriaopenNURBS [5]), altre invece sono progettate per il calcolo numerico (esem-pio modulo NURBS per octave [15]). L’obiettivo di questa tesi e lo sviluppodi una libreria orientata agli oggetti portabile, semplice da usare e che possaessere utilizzata in diversi ambiti. La suddetta libreria e stata sviluppata inPython, in quanto e presente per tutte i principali sistemi operativi, e sup-portato da un gran numero di librerie e consente di risolvere con poche righedi codice problemi complessi. Nella tesi vengono riassunte le conoscenze ne-cessarie per lavorare con curve e superfici parametriche, in particolare con leNURBS e viene illustrata la libreria da me sviluppata che oltre a permetteredi calcolare diversi tipi di curve e superfici utilizando una rappresentazionepolinomiale a tratti (curve di Bezier, B-Spline, NURBS), offre strumenti perrisolvere il problema dell’approssimazione di un insieme di punti, sia in dueche in tre dimensioni, utilizzando le B-Spline. Quest’ultima caratteristicasi rivela di particolare interesse e trova applicazioni anche al di fuori dellagrafica, permettendo ad esempio di calcolare traiettorie per robot mobili, odi approssimare (o ricostruire) segnali audio precedentemente campionati,inoltre versioni piu complesse di B-Spline e NURBS trovano applicazionein analisi nuemerica e in altri campi di ricerca avanzati come strumenti diapprossimazione di funzioni.

Page 6: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

vi

Page 7: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Indice

1 Introduzione 1

2 Spline, B-Spline e NURBS 5

2.1 Le basi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.1.1 Rappresentazione di curve . . . . . . . . . . . . . . . . 5

2.1.2 Tipi di parametrizzazione . . . . . . . . . . . . . . . . 7

2.1.3 Rappresentazione a tratti . . . . . . . . . . . . . . . . 8

2.1.4 Proprieta delle curve . . . . . . . . . . . . . . . . . . . 9

2.1.5 Continuita . . . . . . . . . . . . . . . . . . . . . . . . 9

2.1.6 Rappresentazione Matriciale di Polinomi . . . . . . . . 10

2.1.7 Funzioni di base . . . . . . . . . . . . . . . . . . . . . 13

2.1.8 Spline cubiche di uso comune . . . . . . . . . . . . . . 13

2.2 Curve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.2.1 Curve di Bezier . . . . . . . . . . . . . . . . . . . . . . 17

2.2.2 Curve B-Spline . . . . . . . . . . . . . . . . . . . . . . 19

2.2.3 Interpolazione 2D tramite B-Spline . . . . . . . . . . . 24

2.2.4 Curve NURBS . . . . . . . . . . . . . . . . . . . . . . 26

2.3 Superfici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

2.3.1 Superfici di Bezier . . . . . . . . . . . . . . . . . . . . 29

2.3.2 Superfici B-Spline . . . . . . . . . . . . . . . . . . . . 30

2.3.3 Interpolazione 3D tramite B-Splines . . . . . . . . . . 33

2.3.4 Superfici NURBS . . . . . . . . . . . . . . . . . . . . . 34

3 Cenni di Python 37

3.1 Caratteristiche basilari . . . . . . . . . . . . . . . . . . . . . . 38

3.2 Le liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

3.3 I dizionari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

3.4 Programmazione orientata agli oggetti . . . . . . . . . . . . . 42

3.5 Le eccezioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

3.6 Librerie utilizzate . . . . . . . . . . . . . . . . . . . . . . . . . 46

vii

Page 8: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

viii INDICE

3.6.1 Numpy . . . . . . . . . . . . . . . . . . . . . . . . . . 46

3.6.2 Matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . 48

4 Classi ed Algoritmi 53

4.1 La struttura delle classi . . . . . . . . . . . . . . . . . . . . . 54

4.2 La classe Points . . . . . . . . . . . . . . . . . . . . . . . . . 54

4.3 La classe Curve . . . . . . . . . . . . . . . . . . . . . . . . . . 57

4.4 La classe Bezier . . . . . . . . . . . . . . . . . . . . . . . . . 60

4.4.1 Calcolo tramite definizione . . . . . . . . . . . . . . . 60

4.4.2 L’algoritmo di De Casteljau . . . . . . . . . . . . . . . 61

4.5 La classe Spline . . . . . . . . . . . . . . . . . . . . . . . . . 64

4.6 Esempi di estensione di Spline . . . . . . . . . . . . . . . . . . 66

4.6.1 Spline cubica naturale . . . . . . . . . . . . . . . . . . 66

4.6.2 Spline di Hermite . . . . . . . . . . . . . . . . . . . . . 67

4.6.3 Spline cardinale . . . . . . . . . . . . . . . . . . . . . . 67

4.7 La classe BSpline . . . . . . . . . . . . . . . . . . . . . . . . 68

4.7.1 Calcolo da definizione . . . . . . . . . . . . . . . . . . 68

4.7.2 Calcolo con notazione matriciale . . . . . . . . . . . . 71

4.8 La classe Nurbs . . . . . . . . . . . . . . . . . . . . . . . . . . 74

4.9 La classe CurveFit . . . . . . . . . . . . . . . . . . . . . . . . 76

4.10 La classe Surface . . . . . . . . . . . . . . . . . . . . . . . . 79

4.11 La classe BezSurf . . . . . . . . . . . . . . . . . . . . . . . . 80

4.12 La classe BSplineSurf . . . . . . . . . . . . . . . . . . . . . . 82

4.12.1 Calcolo tramite definizione . . . . . . . . . . . . . . . 82

4.12.2 Calcolo con notazione matriciale . . . . . . . . . . . . 83

4.13 La classe NurbsSurf . . . . . . . . . . . . . . . . . . . . . . . 86

4.13.1 Algoritmo naive . . . . . . . . . . . . . . . . . . . . . 86

4.13.2 Algoritmo efficiente . . . . . . . . . . . . . . . . . . . 87

4.13.3 L’implementazione . . . . . . . . . . . . . . . . . . . . 88

4.14 La classe SurfaceFit . . . . . . . . . . . . . . . . . . . . . . 92

5 Esempi di utilizzo e applicazioni 97

5.1 Esempi di utilizzo . . . . . . . . . . . . . . . . . . . . . . . . . 97

5.1.1 Curve di Bezier . . . . . . . . . . . . . . . . . . . . . . 98

5.1.2 Curve B-Spline . . . . . . . . . . . . . . . . . . . . . . 100

5.1.3 Curve Nurbs . . . . . . . . . . . . . . . . . . . . . . . 102

5.1.4 Approssimazione di curve tramite B-Spline . . . . . . 103

5.1.5 Superfici di Bezier . . . . . . . . . . . . . . . . . . . . 103

5.1.6 Superfici B-Spline . . . . . . . . . . . . . . . . . . . . 105

5.1.7 Superfici NURBS . . . . . . . . . . . . . . . . . . . . . 106

5.1.8 Approssimazione di superfici tramite B-Spline . . . . . 107

5.2 Confronto con altre librerie . . . . . . . . . . . . . . . . . . . 109

5.2.1 Nurbs++ . . . . . . . . . . . . . . . . . . . . . . . . . 110

5.2.2 openNURBS . . . . . . . . . . . . . . . . . . . . . . . 110

Page 9: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

INDICE ix

5.3 Possibili applicazioni . . . . . . . . . . . . . . . . . . . . . . . 1105.3.1 Scanner 3D . . . . . . . . . . . . . . . . . . . . . . . . 1115.3.2 Ricostruzione paesaggi . . . . . . . . . . . . . . . . . . 1115.3.3 Applicazioni mediche . . . . . . . . . . . . . . . . . . . 112

5.4 Conclusioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

Appendici 117

A Casi d’uso e Class Diagrams 117A.1 Casi d’uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

A.1.1 Curve . . . . . . . . . . . . . . . . . . . . . . . . . . . 117A.1.2 Superfici . . . . . . . . . . . . . . . . . . . . . . . . . . 118

A.2 Class Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . 119

B Codice 121B.1 Curve.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121B.2 Surface.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142B.3 Util.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

Page 10: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

x INDICE

Page 11: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Capitolo 1Introduzione

Scienza e tutto cio che comprendiamo abbastazabene da spiegarlo ad un computer, tutto il resto earte.

Donald E. Knuth

Le NURBS (Non Uniform Rational B-Spline) rappresentano lo standardattuale per la rappresentazione di curve e superfici in computer grafica. Laloro capacita di rappresentare con accuratezza anche le forme piu complesse,le rende uno strumento particolarmente utile qualora si abbia la necessitadi disegnare modelli 2D e 3D, come nelle applicazioni di prototipazione ra-pida: applicazioni volte alla creazione automatica di prototipi solitamenteutilizzate in ambito industriale. Il loro sviluppo inizio negli anni ’50 con loscopo di creare un modello matematico in grado di descrivere con precisionesuperfici a forma libera, da poter utilizzare ad esempio per modellare chigliedi navi, ali di aerei, carrozzerie di automobili. Pionieri nello sviluppo diqueste tecniche di approssimazione di curve e superfici furono Pierre Beziere Paul de Casteljau, i quali lavorarono praticamente in parallelo entrambiall’oscuro del lavoro dell’altro [25]. Sebbene siano nate come strumento dimodellazione CAD e rappresentino tutt’oggi uno degli strumenti fondamen-tali nella grafica 2D e 3D, le B-Spline e le NURBS trovano applicazioneanche in campi diversi dalla grafica. Esse infatti trovano applicazione inanalisi numerica e in altri ambiti di ricerca avanzata come strumento perapprossimare funzioni. Esistono diverse librerie gratuite che permettono ilcalcolo di curve e superfici NURBS, tutta via alcune di esse presentano alcu-ni problemi: alcune librerie non vengono aggiornate da anni, altre sono poco(o per niente) documentate, rendendo difficile qualsiasi tipo di intervento sudi esse o semplicemente mancano le funzionalita di cui si aveva bisogno. Inquesto lavoro ci proponiamo di sviluppare una libreria orientata agli oggettiper il calcolo di NURBS e di altri tipi di curve e superfici parametriche,

1

Page 12: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2 CAPITOLO 1. INTRODUZIONE

Figura 1.1: La creazione di una superfice NURBS all’interno del software dimodellazione 3D Blender [1]

che consenta inoltre di risolvere il problema dell’approssimazione di punti,sia sul piano che nello spazio. Quest’ultima caratteristica e di particolareinteresse. Sebbene siano nate come strumento per la grafica, le B-Spline, leNURBS e versioni piu complesse di entrambe trovano diverse applicazionianche in ambiti molto diversi, ad esempio in analisi numerica ed altri campidi ricerca avanzati. Basti pensare che esse vengono utilizzate per:

� calcolare la traiettoria da far seguire ad un robot per fargli raggiungereuna data destinazione [21, 22];

� approssimare (o nel caso migliore ricostruire) un segnale audio o videocampionato in precedenza [23];

� controllare le funzioni di densita di probabilita output di sistemi linearistocastici [26].

Nella tesi verranno illustrati diversi approcci per il calcolo delle principalicurve e superfici parametriche, mettendo a confronto tecniche di calcolonaive con tecniche piu complesse. Saranno infine proposti alcuni esempidi utilizzo della libreria sviluppata in campi d’applicazione molto diversifra loro, per sottolineare l’ampio bacino di applicazioni che le NURBS chepossono trovare.

Il paradigma

Le curve e le superfici trattate, oltre ad essere in numero discreto, presentanoattributi e schemi comuni: per questi motivi abbiamo optato per il paradig-ma di programmazione orientata agli oggetti. Questo paradigma, rispetto aquello procedurale, consente di racchiudere in un’unica entita (denominata

Page 13: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3

classe) sia i dati che i metodi che lavorano su di essi e di definire relazioni frale entita stesse. Utilizzando questo paradigma e modellando quindi classirappresentanti i vari tipi di curve e superfici si ottiene un codice dall’elevatamodularita, il che facilita eventuali interventi di manutenzione, inoltre e piusemplice per chi conosce il contesto districarsi all’interno del codice stesso.

Il linguaggio

Per l’implementazione abbiamo scelto il Python: linguaggio versatile chepermette di sviluppare seguendo i paradigmi ad oggetti, procedurale e fun-zionale. Questo linguaggio ha guadagnato negli anni un gran numero diconsensi, in quanto offre un ottimo compromesso fra velocita e semplicita.Il Python e utilizzato negli ambiti piu disparati, dallo sviluppo web allosviluppo di videogiochi, dallo sviluppo di software di computer grafica alcalcolo scientifico. Si pensi che Google e la NASA fanno un forte uso di Py-thon nei loro rispettivi ambiti (per ulteriori dettagli vedere le sezioni “suc-cess stories” e “quotes”su [6]). Essendo Python un linguaggio interpretato,i software scritti in questo linguaggio possono essere eseguiti su qualsiasipiattaforma abbia un interprete installato, facendo sı che chi sviluppa nondebba preoccuparsi del discorso portabilita.

Sommario dei capitoli

Per facilitare la consultazione della tesi, vengono di seguito riassunti i con-tenuti dei singoli capitoli.

Il Capitolo 2 copre l’aspetto matematico del lavoro svolto, introducendole notazioni e le definizioni utilizzate per sviluppare la libreria oggettodi questa tesi.

Il Capitolo 3 riassume i concetti base del linguaggio Python, illustrandoalcuni particolari della sintassi in modo da rendere piu semplice lacomprensione delle implementazioni.

Il Capitolo 4 illustra una ad una le classi che compongono la libreria,mostrando gli algoritmi utilizzati e la loro implementazione.

Il Capitolo 5 mostra alcuni esempi di utilizzo e fornisce alcuni misurazionidel tempo di esecuzione. Inoltre vengono illustrati alcuni esempi diapplicazione della libreria.

In Appendice A e presente la documentazione della libreria, corredata didiagrammi UML dei casi d’uso e delle classi.

In Appendice B e possibile consultare il codice sorgente della librerianella sua interezza.

Page 14: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4 CAPITOLO 1. INTRODUZIONE

Page 15: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Capitolo 2Spline, B-Spline e NURBS

Se le persone non credono che la matematica siasemplice, e solo perche non hanno ancorarealizzato quanto e complicata la vita.

John von Neumann

In questo capitolo vengono presentate i vari tipi di curve e superficioggetto di questo lavoro, partendo da alcune definizioni di base fino ad ar-rivare al metodo piu generale per la rappresentazione di curve e superfici:le NURBS. Oltre a [12, 19], materiale di riferimento per questo capitolo, epossibile visitare [11] per un tutorial interattivo su curve e superfici parame-triche, mentre il materiale pubblicato dal progetto MIT OpenCourseWare([16]) contiene maggiori dettagli, dal punto di vista analitico, sulle proprietadelle curve e delle superfici qui presentate.

2.1 Le basi

In questo paragrafo verranno illustrati i concetti di base riguardanti la pa-rametrizzazione delle curve, la loro rappresentazione a tratti, i tipi di conti-nuita e altri argomenti necessari a comprendere successivamente il compor-tamento di curve di Bezier, B-Spline e Nurbs, fino ad arrivare alle superfici.

2.1.1 Rappresentazione di curve

Una curva e una successione continua di punti. Esistono tre modi principaliper rappresentare una curva. La prima, piu nota ed utilizzata nei primicorsi di matematica, e la rappresentazione esplicita. La rappresentazione informa esplicita e una rappresentazione del tipo:

y = f(x)

5

Page 16: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

6 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

Figura 2.1: Una spirale e una curva polidroma

Questo tipo di rappresentazione, che mette in evidenza la dipendenza dellavariabile y dalla variabile x ha le seguenti caratteristiche:

� Permette di disegnare facilmente la curva;

� Permette di verificare facilmente se un punto appartiene la curva;

� Non permette di rappresentare facilmente curve polidrome1;

� E dipendente dagli assi, e quindi difficile trasportare la curva in unaltro sistema di riferimento.

Proprio quest’ultima caratteristica e uno svantaggio pesante. Infatti conquesto tipo di rappresentazione e possibile rappresentare solo curve che han-no associato ad ogni valore della variabile x un unico valore della variabiley. E quindi difficile rappresentare curve come quella mostrata in figura 2.1

Per ovviare a questo problema si puo utilizzare la rappresentazione informa implicita:

f(x, y) = 0

Questo tipo di rappresentazione

� Permette di rappresentare curve polidrome;

� Permette di verificare facilmente se un punto appartiene o meno allacurva;

� Rende difficile disegnare la curva;

� E dipendente dagli assi, quindi e difficile trasportare la curva in unaltro sistema di riferimento.

1Una funzione polidroma e una funzione f : X 7→ P (Y) (dove P (Y) rappresenta l’insie-me delle parti di Y) che ad un dato valore della variabile x associa uno o piu valori dellavariabile y

Page 17: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.1. LE BASI 7

La terza rappresentazione, ovvero quella in forma parametrica, e lapiu flessibile. L’idea consiste nell’esprimere sia la variabile x che la variabiley come funzioni di un parametro esterno, che chiameremo t. Otteniamoquindi una rappresentazione del tipo:{

x = f1(t)y = f2(t)

Il motivo per cui la rappresentazione in forma parametrica e la piu flessibile,diventa ovvio con un piccolo esempio. Supponiamo di voler disegnare unacirconferenza di raggio r con centro nell’origine:

y = ±√r2 − x2

L’equazione in forma esplicita costringe a disegnare due semicirconferenze,la prima y = +

√r2 − x2, e la seconda y = −

√r2 − x2. Passando alla forma

implicita, diventa comodo generalizzare e considerare una circonferenza dicentro (x0, y0):

(y − y0)2 + (x− x0)2 = r2

y2 + y20 − 2yy0 + x2 + x20 − 2xx0 = r2

y2 + y20 − 2yy0 + x2 + x20 − 2xx0 − r2 = 0

Salta all’occhio che disegnare la circonferenza utilizzando la notazione im-plicita e tutt’altro che semplice. L’equazione in forma parametrica per unacirconferenza di raggio r e centro (x0, y0) invece e la seguente:

C =

{x(u) = x0 + r · cos(t) t ∈ [0, 2π]

y(u) = y0 + r · sin(t)

La forma parametrica e visibilmente piu semplice e consente di calcolare di-rettamente un qualsiasi punto della circonferenza, cosa che non era possibilene con la notazione esplicita ne con quella implicita.

Propio per l’estrema flessibilita e per la possibilita di poter definire for-me molto particolari in modo relativamente semplice, la rappresentazioneparametrica e molto utilizzata in computer grafica.

2.1.2 Tipi di parametrizzazione

Rappresentare una curva nel piano in forma parametrica significa creareuna applicazione f : R 7→ R2 che ad ogni valore del parametro associa unacoordinata (x, y) sul piano. Una delle comodita nell’usare questo tipo dirappresentazione e che il parametro puo rappresentare qualsiasi cosa: puoessere il tempo, un angolo, una frequenza, qualsiasi cosa si ritiene debbainfluire sulla forma della curva. Di solito si utilizza un parametro che spazi

Page 18: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

8 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

all’interno dell’intervallo [0, 1], in modo che f(0) identifichi l’inizio dellacurva e f(1) ne identifichi la fine, questo tipo di parametro viene usualmenteidentificato con u. L’affermazione precedente potrebbe portare erronamentea pensare che f(0.5) identifichi la meta della curva, ma non e cosı. Il fattoche u = 0.5 identifichi o meno la meta della curva dipende dal tipo diparametrizzazione che stiamo usando:

Naturale: In questo tipo di parametrizzazione, u non e legato alla curvain se, ma allo spazio all’interno del quale la curva e definita. Quindise u = 0.5, f(u) indica il punto della curva al centro dello spazio didefinizione.

Lunghezza d’arco: In questo tipo di parametrizzazione u segue l’anda-mento della curva, quindi se u = 0.5, f(u) identifica il punto centraledella curva.

Avendo una parametrizzazione naturale con parametro u, per conoscereil parametro s che rappresenta la lunghezza dell’arco dal punto f(0) al puntof(u).

s =

∫ u

0|f ′(u)|2 du (2.1)

Per poter utilizzare la rappresentazione a lunghezza d’arco, bisogna es-sere in grado di risolvere l’equazione (2.1) rispetto ad u dato s, il che none sempre possibile o comunque semplice.

2.1.3 Rappresentazione a tratti

Spesso puo risultare difficile trovare una funzione parametrica capace didescrivere una curva nella sua interezza. In questi casi puo risultare utileun approccio tipo divide et impera, ovvero dividere la curva in tratti la cuidescrizione tramite funzione parametrica sia piu semplice. Se ad esempiovolessimo dividere un ipotetica curva in due tratti, la sua descrizione sarebbequalcosa di simile a:

f(u) =

{f1(2 · u) se u ≤ 0.5f2(2 · u− 1) se u > 0.5

Dove f1 e la funzione parametrica che rappresenta il primo tratto e f2 equella che rappresenta il secondo, entrambe sono definite per u ∈ [0, 1]. Neldefinire le funzioni f1 e f2 bisogna assicurarsi che i due tratti combacino. Sef1(1) 6= f2(0) i due tratti non formano un unica curva continua.

Un altro problema che riguarda la rappresentazione a tratti riguardail tipo ed il numero di tratti da usare. Sicuramente, per una questionedi semplicita, conviene utilizzare tratti dello stesso tipo (ad esempio solopolinomi cubici). La scelta del numero di tratti da usare permette di deciderecon che approssimazione ricreare la curva. Col tendere del numero di tratti

Page 19: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.1. LE BASI 9

all’infinito, la curva puo essere riprodotta esattamente. Un vantaggio sicurodella rappresentazione a tratti e che permette di decidere, a seconda delleapplicazioni, se si vuole una rappresentazione piu semplice o piu precisa. Sipossono utilizzare pochi tratti piu complessi, oppure molti tratti semplici( ad esempio polinomi di primo grado) ed ottenere comunque una buonaapprossimazione della curva originale.

2.1.4 Proprieta delle curve

Nel descrivere una curva, abbiamo bisogno di conoscere alcune sue caratte-ristiche. Per le curve piu comuni, queste caratteristiche sono specifiche peril tipo di curva. Ad esempio, per un cerchio basta specificare il raggio e laposizione del centro. Per curve a forma libera pero, c’e bisogno di conoscerediverse caratteristiche. Alcune caratteristiche sono generali, ad esempio sela curva e chiusa o meno, se passa per un dato punto o se assume una certadirezione almeno una volta. Proprieta piu interessanti pero, specialmente sela curva viene rappresentata a tratti, sono le proprieta locali.

Le proprieta locali includono:

� Continuita;

� Posizione di un determinato punto della curva;

� Direzione ad un determinato punto della curva (derivata prima);

� Curvatura (derivata seconda) e altre derivate;

2.1.5 Continuita

Il discorso inerente la continuita assume una certa importanza quando sitenta di rappresentare una curva a tratti. Come detto nel paragrafo 2.1.3,siano fk e fk+1 le funzioni parametriche descriventi il tratto k-esimo e k +1-esimo della curva, se fk(1) 6= fk+1(0) la curva risulta spezzata.

Oltre alla posizione, si potrebbe verificare che anche le derivate primedei due tratti nei punti in cui esse vanno a congiungersi siano uguali. Questoperche se fk

′(1) 6= fk+1′(0), la curva potrebbe presentare un cambio di di-

rezione troppo repentino, che visivamente potrebbe tradursi, ad esempio, inuno spigolo. In generale, si dice che una curva ha una continuita di tipo Cn

se tutte le derivate fino all’n-esima combaciano fra un tratto e l’altro dellacurva. Fino a che derivata imporre la continuita e una scelta che dipendedalle applicazioni: se la curva descrive una traiettoria, una discontinuitasulla derivata 2° significherebbe un brusco cambio di accelerazione, quindiin questo caso potrebbe essere utile una continuita di tipo C3, mentre se lacurva rappresenta il profilo di un oggetto che deve attraversare un fluido (loscafo di una barca, o l’ala di un aereo), allora una discontinuita nella 4° o5° derivata potrebbe causare problemi (vedere [12]).

Page 20: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

10 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

Figura 2.2: Differenze fra diverse condizioni di continuita [12]

Questo tipo di continuita e pero un po restrittiva, e non permette dimodellare alcuni tipi di curve. Per questo si introduce una continuita piu“debole”, denominata continuita geometrica. Con questo tipo di conti-nuita si impone che la derivata nel punto finale di un tratto abbia solo ladirezione in comune con la derivata del punto iniziale del tratto successivo,mentre il modulo puo essere diverso. Cio significa che laddove una continuitadi tipo C1 richiede che

fk′(1) = fk+1

′(0)

una continuita di tipo G1 richiede che

fk′(1) = c · fk+1

′(0)

per qualche valore c. Ovviamente, una curva che e Cn continua e necessa-riamente anche Gn continua, ma non viceversa.

2.1.6 Rappresentazione Matriciale di Polinomi

Supponiamo di voler scrivere l’equazione parametrica di una linea che collegadue punti, p0 e p1. Si puo utilizzare la formula per la combinazione convessadei due punti:

f(u) = (1− u)p0 + up1

Tuttavia un modo piu semplice per rappresentare la linea puo essere quellodi scrivere il polinomio di primo grado in u:

f(u) = a0 + ua1

Specificare la linea tramite i suoi punti estremi e sicuramente piu semplice,ma la rappresentazione tramite i coefficienti a0 e a1 ha il vantaggio di essere

Page 21: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.1. LE BASI 11

piu generale. Infatti, tramite il polinomio in u e possibile rappresentare unagenerica curva di grado n-esimo nel seguente modo:

f(u) =

n∑i=0

uiai

Questa forma e detta forma canonica. Il precedente polinomio puo esse-re facilmente rappresentato in forma vettoriale definendo un vettore dellepotenze di u:

u =[

1 u u2 u3 . . . un]T

ottenendo quindi:f(u) = uTa

Sebbene la forma canonica abbia i suoi vantaggi, puo non essere la sceltapiu comoda per rappresentare una curva. Per la linea ad esempio, la stradapiu comoda rimane quella di specificare i punti estremi, e quindi imporreche p0 sia il punto dove la linea si trova quando u = 0 e p1 sia il puntodove la linea si trova quando u = 1. Per passare da una rappresentazioneall’altra possiamo scrivere:

p0 = f(0) = [1 0] [a0 a1]T

p1 = f(1) = [1 1] [a0 a1]T

Risolvendo rispetto ad a0 ed a1 otteniamo:

a0 = p0

a1 = p1 − p0

Il tutto puo essere reso piu compatto racchiudendo in singoli vettori i puntied i coefficienti del polinomio:[

p0

p1

]=

[1 01 1

] [a0a1

]oppure, se definiamo con p il vettore dei punti, con C la matrice con i valoridi u e con a il vettore dei coefficienti:

p = C a (2.2)

p e denominato vettore dei punti di controllo2, ed ogni suo elementoe definito punto di controllo. C e la matrice costante. Si puo risolverel’equazione 2.2 rispetto ad a trovando l’inversa di C. Questa matrice, che

2Si noti che dato che un punto pi e un vettore del tipo [pix piy], p e in realta una matrice2xn, dove n e il numero di punti di controllo specificati. Di conseguenza, anche il relativocoefficiente ai e un vettore, il che e ovvio se si ricorda che con la notazione parametricasi scrive un polinomio per ogni variabile posizionale, quindi ci sara un coefficiente aix perla variabile x ed un coefficiente aiy per la variabile y

Page 22: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

12 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

denoteremo con B, e denominata matrice di base. La matrice fa da pontetra i punti di controllo e i coefficienti della forma canonica, fornendo quindiun modo piu comodo per rappresentare una curva:

f(u) = u B p

Supponiamo ora di voler parametrizzare una curva di 2° grado, specifi-cando il punto iniziale, il punto centrale e il punto finale. In questo casoscriviamo:

p0 = f(0) = a0 + 01 a1 + 02 a2p1 = f(0.5) = a0 + 0.51 a1 + 0.52 a2p2 = f(1) = a0 + 11 a1 + 12 a2

La matrice costante e quindi:

C =

1 0 01 .5 .251 1 1

da cui otteniamo la matrice di base:

B = C−1 =

1 0 0−3 4 −12 −4 2

Imporre solo condizioni posizionali pero non basta. Per avere piu con-

trollo sull’andamento della curva e utile introdurre delle condizioni sullederivate.

Imporre tali condizioni e semplice, basta derivare la forma canonicarispetto ad u, quindi per una curva di secondo grado:

f(u) = a0 + a1 u + a2 u2

La derivata prima e pari a:

f ′(u) =df

du= a1 + 2 a2 u

Mentre la derivata seconda e:

f ′′(u) =d2f

du= 2 a2

Volendo parametrizzare quindi una curva di secondo grado, specificandoposizione, derivata prima e derivata seconda del suo punto centrale (u = 0.5):

p0 = f(0.5) = a0 + 0.51 a1 + 0.52 a2p1 = f ′(0.5) = a1 + 2 (0.5) a2p2 = f ′′(0.5) = 2 a2

Page 23: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.1. LE BASI 13

La matrice costante che se ne ricava e:

C =

1 .5 .251 1 10 0 2

e la matrice di base e:

B = C−1 =

1 −.5 .1250 1 −.50 0 .5

2.1.7 Funzioni di base

Se la matrice di base B e nota, la si puo moltiplicare per il vettore dellepotenze del parametro u ottenendo un vettore di funzioni:

b(u) = uB

Le funzioni che compongono questo vettore sono denominate funzioni dibase. Grazie a queste funzioni, e possibile rappresentare una curva comecombinazione lineare dei suoi punti di controllo:

f(u) =

n∑i=0

bi(u) pi

Una volta nota la matrice di base quindi, ci sono due strade per calcolare lacurva:

� moltiplicare la matrice di base per i punti di controllo, ottenendo icoefficienti della forma canonica.

� moltiplicare la matrice di base per il vettore delle potenze del parame-tro u, ottenendo le funzioni di base.

2.1.8 Spline cubiche di uso comune

In questa sezione esamineremo alcune spline di uso comune, utili anche percapire in quanti modi diversi si puo parametrizzare una curva. Le splinecubiche (che utilizzano quindi polinomi di 3° grado) sono molto utilizzatenell’ambito della grafica vettoriale. Questo perche i polinomi di terzo gradooffrono un ottimo compromesso fra versatilita e semplicita. Nell’interpolareun insieme di punti un polinomio di terzo grado disegna la curva con laminima curvatura, il che vuol dire che la curva ottenuta “oscilla” di menorispetto a quella ottenuta tramite un polinomio di grado piu alto.

Page 24: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

14 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

Spline cubica naturale

La spline cubica naturale si ottiene specificando posizione, derivata prima,derivata seconda per il punto iniziale di ogni tratto. Questo tipo di spline hauna continuita di tipo C2. Per un singolo tratto bisogna specificare anchela posizione del punto finale. Imponendo questi vincoli otteniamo:

p0 = f(0) = a0 + 01 a1 + 02 a2 + 03 a3

p1 = f ′(0) = a1 + 2 01 a2 + 3 02 a3

p2 = f ′′(0) = 2 a2 + 6 01 a3

p3 = f(1) = a0 + 11 a1 + 12 a2 + 13 a3

Quindi la matrice costante e:

C =

1 0 0 00 1 0 00 0 2 01 1 1 1

e la matrice di base e:

B = C−1 =

1 0 0 00 1 0 00 0 .5 0−1 −1 −.5 1

La figura 2.1.8 mostra il grafico delle funzioni di base per questa spline. Losvantaggio principale della spline cubica naturale, e che c’e poco controllosulla parte finale della curva. Mentre per il punto iniziale viene specificata laposizione, la derivata prima e la derivata seconda, per il punto finale vienespecificata solo la posizione, il che rende difficile modificare l’andamentodella curva a proprio piacimento.

Spline di Hermite

Le spline cubiche di Hermite si ottengono specificando posizione e derivataprima per entrambi i punti estremi. Diversi tratti di una spline di Hermi-te possono essere concatenati formando una curva con continuita C1. Unvantaggio delle spline di Hermite consiste nel fatto che modificando unodei punti di controllo, solo l’intorno di quel punto viene modificato e nonl’intera curva. Questa proprieta e nota come controllo locale. Inoltre, lespline di Hermite interpolano tutti i punti di controllo relativi alla posizione.Imponendo quindi le condizioni dette precedentemente otteniamo:

p0 = f(0) = a0 + 01 a1 + 02 a2 + 03 a3

p1 = f ′(0) = a1 + 2 01 a2 + 3 02 a3

p2 = f(1) = a0 + 11 a1 + 12 a2 + 13 a3

p3 = f ′(1) = a1 + 2 11 a2 + 3 12 a3

Page 25: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.1. LE BASI 15

Figura 2.3: Funzioni di base della spline cubica naturale

La matrice costante e:

C =

1 0 0 00 1 0 01 1 1 10 1 2 3

e la matrice di base risulta:

B = C−1 =

1 0 0 00 1 0 0−3 −2 3 −12 1 −2 1

La figura 2.4 mostra le funzioni di base per la spline cubica di Hermite.

Spline cardinale

Una spline cardinale cubica e una spline interpolante con continuita di tipoC1. Questo tipo di spline intepola tutti i suoi punti di controllo ad ecce-zione del primo e dell’ultimo. Cio che contraddistingue la spline cardinalee l’utilizzo di un parametro di tensione3 t. Questo parametro indica quanto

3Si pensi alla tensione nel senso meccanico del termine, ad esempio quella di una cordasoggetta a trazione

Page 26: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

16 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

Figura 2.4: Funzioni di base della spline cubica di Hermite

la curva deve essere tesa fra un punto di controllo e l’altro, effettuando unascalatura delle derivate nei punti di controllo. La derivata di un punto dicontrollo pi viene calcolata a partire dalla posizione dei punti di controlloadiacenti pi−1 e pi+1: la direzione e parallela alla retta che connette pi−1 epi+1, mentre il modulo e pari alla loro distanza. Il parametro di tensione tagisce scalando la derivata di un fattore 1

2(1− t).I vincoli per una spline cardinale quindi sono:

f(0) = p1

f(1) = p2

f ′(0) = 12(1− t)(p2 − p1)

f ′(1) = 12(1− t)(p3 − p1)

Risolvendo rispetto ai punti di controllo e ponendo s = 1−t2 otteniamo:

p0 = f(1)− 21−tf

′(0) = a0 + (1− 1s )a1 + a2 + a3

p1 = f(0) = a0p2 = f(1) = a0 + a1 + a2 + a3p3 = f(0) + 1

sf′(1) = a0 + 1

sa1 + 21sa2 + 31

sa3

Page 27: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.2. CURVE 17

Figura 2.5: Spline cardinale con diversi valori del parametro di tensione(Immagini ottenuta tramite la libreria da me sviluppata)

Cio porta alla matrice di base:

B = C−1 =

0 1 0 0−s 0 s 02s s− 3 3− 2s −s−s 2− s s− 2 s

Le spline cardinali sono comode in quanto rappresentano uno dei me-

todi piu semplici per interpolare un insieme di punti ottenendo una curvacon continuita C1 con in piu la proprieta del controllo locale (vedere [12]).La figura 2.5 mostra l’interpolazione di un insieme di punti tramite splinecardinale con diversi valori per il parametro di tensione.

2.2 Curve

In questa sezione verranno mostrate le tecniche di approssimazione di curveche hanno portato negli anni allo sviluppo delle NURBS.

2.2.1 Curve di Bezier

Le curve di Bezier sono un tipo di curve la cui forma e influenzata dalpoligono formato dai punti di controllo, denominato poligono di controllo.

Page 28: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

18 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

Le curve di Bezier utilizzano come funzioni di base i polinomi di Bernstein:

Jn,i(t) =

(n

i

)ti(1− t)n−i (0)0 ≡ 1 (2.3)

Dove con n su k si intende il coefficiente binomiale:(n

k

)=

n!

k!(n− k)!0! ≡ 1

La figura 2.6 mostra le funzioni di base per n = 4.

Una curva con n + 1 punti di controllo, numerati da 0 a n, viene cosırappresentata:

P (t) =n∑

i=0

Bi Jn,i(t) 0 ≤ t ≤ 1 (2.4)

ove Bi indica l’i-esimo punto di controllo. Dato che utilizzano come funzionidi base i polinomi di Bernstein, alcune proprieta delle curve di Bezier sononote.

� Le funzioni di base sono reali;

� Il grado del polinomio e pari a n (numero di punti di controllo menouno);

� La curva solitamente segue la forma del poligono di controllo;

� Il primo e l’ultimo punto della curva coincidono con il primo e l’ultimopunto del poligono di controllo;

� La curva e invariante rispetto a trasformazioni affini4;

� I vettori tangenti al primo e all’ultimo punto di controllo hanno lastessa direzione del primo e dell’ultimo lato del poligono di controllo.

� La curva rimane all’interno del piu grande poligono convesso formatodai punti di controllo, questa proprita va sotto il nome di proprietadell’involucro convesso;

� Si supponga di tracciare una retta in modo che essa “attraversi“ ilpoligono di controllo e di conseguenza la curva di Bezier ad esso asso-ciata: il numero di intersezioni fra la retta e la curva non e superioreal numero di intersezioni fra la retta e il poligono. Questa proprietava sotto il nome di variation-diminishing .

4Una trasformazione affine e una trasformazione x 7→ Ax + b, ovvero la composizionedi una trasformazione lineare determinata dalla matrice A e di una traslazione identificatadal vettore b. Una rotazione e un esempio di trasformazione affine.

Page 29: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.2. CURVE 19

Figura 2.6: Funzioni di base per una curva di Bezier con 5 punti di controllo

2.2.2 Curve B-Spline

Le curve di Bezier presentano due grossi limiti. Il primo e rappresentato dallarelazione fra il numero di punti di controllo e il grado della curva ottenuta.Per cinque punti di controllo, si otterra sempre e comunque una curva di4° grado. L’unico modo per variare il grado della curva e di aumentare odiminuire il numero di punti di controllo. Il secondo problema riguarda unacaratteristica dei polinomi di Bernstein: osservando la figura 2.6 si nota cheogni funzione Jn,i(u) copre l’intero spazio del parametro; cio significa chese si modifica un singolo punto di controllo, l’intera curva viene influenzata.Esiste un altro tipo di funzioni di base, le basi B-spline ( da basis spline ) dicui i polinomi di Bernstein sono un caso particolare. Queste funzioni di basepermettono di decidere il grado della curva indipendentemente dal numerodi punti di controllo e permettono inoltre di avere un controllo locale sullacurva.

Definizione

Sia P (t) il vettore posizione della curva espresso come funzione del parametrot. Una curva B-spline e data da 5:

P (t) =n+1∑i=1

BiNi,k(t) tmin ≤ t < tmax, 2 ≤ k ≤ n+ 1 (2.5)

5Si noti che i punti di controllo sono numerati da 1 a n + 1, e non da 0 a n

Page 30: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

20 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

dove Bi rappresenta l’i-esimo punto di controllo, Ni,k(t) e la funzione di base.La funzione di base Ni,k(t) di ordine k (grado k − 1) puo essere calcolatatramite la relazione di ricorrenza di Cox-de Boor:

Ni,1(t) =

{1 se xi ≤ t < xi+1

0 altrimenti

Ni,k(t) =(t− xi)Ni,k−1(t)

xi+k−1 − xi+

(xi+k − t)Ni+1,k−1(t)

xi+k − xi+1(2.6)

I valori xi sono componenti di un vettore, denominato vettore dei nodi,con la proprieta che xi ≤ xi+1. Una proprieta di queste funzioni di basee che per un dato ordine k, la funzione di base Ni,k(t) e diversa da 0 perxi ≤ t < xi+k. Quindi ogni punto di controllo influenza solo una zonalimitata della curva, al contrario di cio che accade cone le curve di Bezier.Una B-spline e definita come uana spline polinomiale di ordine k in quantosoddisfa le segueti proprieta:

� P (t) e un polinomio di grado k − 1 per ogni intervallo xi ≤ t < xi+1;

� P (t) e le relative derivate di ordine 1, 2, . . . , k− 2 sono continue lungol’intera curva.

Una B-spline inoltre, possiede le seguenti proprieta:

� La somma delle funzioni di base per ogni dato valore del parametro te pari a 1:

n+1∑i=1

Ni,k(t) ≡ 1

� Ni,k(t) ≥ 0 per ogni valore del parametro t;

� La curva puo essere al massimo di ordine pari al numero di punti dicontrollo n+ 1, quindi puo essere al massimo di grado n.

� La curva possiede la proprieta variation-diminishing ;

� La curva solitamente segue la forma del poligono di controllo;

� Qualsiasi trasformazione affine viene applicata alla curva applicandolaal suo poligono di controllo;

� La curva risiede sempre all’interno dei poligoni convessi creati dai puntidi controllo presi a gruppi di k.

Page 31: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.2. CURVE 21

Il vettore dei nodi

Dall’equazione (2.6) appare evidente che la scelta del vettore dei nodiinfluisce sulle funzioni di base e di conseguenza sulla forma della curva.Sebbene l’unico requisito per un vettore di nodi sia che valga la relazionexi ≤ xi+1, di solito vengono utilizzate due categorie principali di vettori deinodi, periodici e aperti, entrambi in versione uniforme o non-uniforme.

Un vettore dei nodi uniforme, e un vettore i cui elementi sono equispa-ziati, il che significa che xi+1 − xi = c con c costante arbitraria. Alcuniesempi sono [

0 1 2 3 4]

[−0.2 −0.1 0.0 0.1 0.2

]I vettori uniformi in genere iniziano da zero con incrementi unitari fino adarrivare a qualche valore massimo, oppure vengono normalizzati all’internodell’intervallo [0, 1]. Ad esempio:

[ 0 0.25 0.5 0.75 1 ]

Un vettore dei nodi uniforme periodico, per un dato ordine k porta adelle funzioni di base con la seguente proprieta:

Ni,k(t) = Ni−1,k(t− 1) = Ni+1,k(t+ 1)

Ogni funzione di base quindi altro non e che la traslazione dell’altra.Un vettore dei nodi uniforme aperto ha ai suoi estremi dei valori di nodi

ripetuti un numero di volte pari all’ordine k della B-Spline. I valori internisono equispaziati. Alcuni esempi:

k = 2 [ 0 0 1 2 3 4 4 ]k = 3 [ 0 0 0 1 2 3 3 3 ]k = 4 [ 0 0 0 0 1 2 2 2 2 ]

Un vettore uniforme aperto e cosı definito:

xi = 0 1 ≤ i ≤ kxi = i− k k + 1 ≤ i ≤ n+ 1xi = n− k + 2 n+ 2 ≤ i ≤ n+ k + 1

La figura 2.7 mostra la differenza fra l’uso di un vettore dei nodi aperto edi un vettore dei nodi periodico, entrambi uniformi.

Quando si utilizza un vettore uniforme aperto e il numero di punti dicontrollo e pari all’ordine della curva, la base delle B-spline si riduce alpolinomio di bernstein. Quindi la curva B-spline ottenuta e in realta unacurva di Bezier. Il vettore dei nodi ottenuto in questo caso consiste di k zeriseguiti da k uno.

[ 0 0 0 0 1 1 1 1 ]

Page 32: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

22 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

(a)

(b)

Figura 2.7: Due B-spline dello stesso ordine calcolate a partire dagli stessipunti di controllo: 2.7(a) con un vettore dei nodi uniforme aperto, 2.7(b)con un vettore nodi uniforme periodico (Immagini ottenute tramite la libreria da

me sviluppata)

Page 33: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.2. CURVE 23

Un vettore non uniforme e un vettore in cui l’unico vincolo e quellostandard: xi ≤ xi+1. Possono esserci valori ripetuti e possono essere equi-spaziati o meno. Esistono vettori dei nodi non uniformi aperti e non uniformiperiodici, ecco alcuni esempi:

[ 0 0 0 1 1 2 2 2 ] aperto[ 0 1 2 2 3 4 ] periodico

[ 0 0.28 0.5 0.72 1 ] periodico

Rappresentazione matriciale delle B-Spline

L’equazione (2.5) puo essere espressa in notazione matriciale (come illustratonella sezione 2.1.6). Questa “traduzione” e particolarmente semplice se siassume l’utilizzo di un vettore dei nodi uniforme periodico. Con questo tipodi vettore infatti, ogni funzione di base e traslazione dell’altra ed ognunadi esse ha influenza su esattamente k intervalli. Cio significa che tutte lefunzioni di base per 0 ≤ t∗ < 1 hanno la stessa forma, quindi puo essereutile riparametrizzare le funzioni di base all’interno di questo intervallo. Unpunto della B-spline riparametrizzata si ottiene dunque nel seguente modo:

Pj(t∗) =

k∑i=0

N∗i+1,k(t∗)Bj+i 1 ≤ j ≤ n− k + i, 0 ≤ t∗ < 1 (2.7)

Dove j indica il tratto della curva. Supponiamo ora di lavorare con unaB-spline di ordine k = 3, le funzioni di base riparametrizzate con t∗ ∈ [0, 1]sono:

N∗1,3(t∗) = (1−t∗)2

2

N∗2,3(t∗) = −2t∗2+2t∗+1

2

N∗3,3(t∗) = t∗2

2

L’equazione (2.7) diventa quindi:

2Pj(t∗) = (1− 2t∗ + t∗2)Bj + (−2t∗2 + 2t∗ + 1)Bj+1 + t∗2Bj+2

= t∗2(Bj − 2Bj+1 +Bj+2) + t∗(−2Bj + 2Bj+1 + 0Bj+2)+

+ (Bj +Bj+1 + 0Bj+2)

Passando in notazione matriciale si ottiene:

Pj(t∗) = [T ∗] [N∗] [B]

=1

2

[t∗2 t∗ 1

] 1 −2 1−2 2 0

1 1 0

Bj

Bj+1

Bj+2

Page 34: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

24 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

In modo simile e possibile passare dalle funzioni di base alla notazionematriciale per k = 4, in questo caso le funzioni di base sono:

N∗1,4(t∗) = −t∗3+3t∗2−3t∗+1

6

N∗2,4(t∗) = 3t∗3+6t∗2+4

6

N∗3,4(t∗) = −t∗3+3t∗2+3t∗+1

6

N∗4,4(t∗) = t∗3

6

e quindi la forma matriciale e:

Pj(t∗) = [T ∗] [N∗] [B]

=1

2

[t∗3 t∗2 t∗ 1

] −1 3 −3 1

3 −6 3 0−3 0 3 0

1 4 1 0

Bj

Bj+1

Bj+2

Bj+3

Per un generico ordine k, il vettore [T ∗ ] ha la forma:[

t∗k−1 t∗k−2 . . . t∗ 1]

mentre il generico ingresso della matrice [N∗ ] = [N∗i+1,j+1 ] e dato dallaseguente equazione:

N∗i+1,j+1 =1

(k − 1)!

(k − 1

i

) k−1∑l=j

(k − (l + 1))i(−1)l−j(

k

l − j

)0 ≤ i, j ≤ k − 1

(2.8)

2.2.3 Interpolazione 2D tramite B-Spline

Supponiamo di voler interpolare un insieme di punti utilizzando una B-spline. Il problema e trovare dei punti di controllo tali da generare unacurva che passi per i punti desiderati. Indichiamo con D il vettore dei puntidella curva noti. Ogni punto Dj corrispondera ad un determinato valore delparametro t, che indicheremo con tj . Per appartenere alla B-spline, i puntidevono soddifare l’equazione (2.5):

D1(t1) = N1,k(t1)B1 +N2,k(t1)B2 + · · ·+Nn+1,k(t1)Bn+1

D2(t2) = N1,k(t2)B1 +N2,k(t2)B2 + · · ·+Nn+1,k(t2)Bn+1...

Dj(tj) = N1,k(tj)B1 +N2,k(tj)B2 + · · ·+Nn+1,k(tj)Bn+1

Page 35: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.2. CURVE 25

Dove 2 ≤ k ≤ n + 1 ≤ j. Questo sistema di equazioni puo essere scritto inmodo piu compatto con la notazione matriciale:

[D ] = [N ] [B ] (2.9)

Dove[D ]T =

[D1(t1) D2(t2) . . . Dj(tj)

][B ]T =

[B1 B2 . . . Bn+1

]

[N ] =

N1,k(t1) . . . . . . Nn+1,k(t1)

.... . .

......

. . ....

N1,k(tj) . . . . . . Nn+1,k(tj)

Il valore del parametro tj associato ad ogni punto e una misura della distanzatra i punti Di lungo la curva B-spline. Un utile approssimazione di questivalori utilizza la somma delle distanze euclidee fra ogni coppia di punti.Nello specifico, per j punti dati, il valore del parametro t associato all’l-esimopunto e cosı calcolato:

t1 = 0

tltmax

=

∑ls=2 |Ds −Ds−1|∑js=2 |Ds −Ds−1|

l ≥ 2(2.10)

Dove |Di − Di−1| indica la distanza euclidea6 fra il punto i-esimo e ilprecedente. Una volta fissati il numero di punti di controllo, l’ordine e ilvettore di nodi che si desidera utilizzare, si puo utilizzare l’equazione (2.6)per calcolare gli elementi della matrice [N ]. Se si sceglie un numero dipunti di controllo pari al numero di punti appartenenti alla curva (ovveron+ 1 = j), allora la matrice [N ] e quadrata, e i punti di controllo possonoessere ottenuti dall’equazione (2.9) calcolando l’inversa della matrice [N ].

[B ] = [N ]−1[D ] 2 ≤ k ≤ n+ 1 = j (2.11)

In questo caso, la B-spline ottenuta interpola tutti i punti specificati. Seinvece di un interpolazione si desidera una approssimazione dei punti, bastascegliere un numero di punti di controllo inferiore al numero di punti dellacurva forniti (n + 1 < j). Cosı facendo la matrice [N ] non e piu quadratae il sistema di equazioni (2.9) e sovraspecificato (alcune equazioni non sononecessarie). Per risolvere il sistema moltiplichiamo entrambi i membri della(2.9) per [N ]T , ottenendo cosı una matrice quadrata7 che possiamo tentare

6La distanza euclidea fra due punti a e b e pari a√

(ax − bx)2 + (ay − by)27Data una matrice A rettangolare, il prodotto di A per la sua trasposta ATA e una

matrice quadrata

Page 36: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

26 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

di invertire:

[D ] = [N ][B ]

[N ]T [D ] = [N ]T [N ][B ]

[B ] = [[N ]T [N ]]−1[D ]

(2.12)

Il tipo di curva ottenuto quindi dipende esclusivamente dalla scelta dei valoridi n+1, k e del tipo di vettore dei nodi. Ad esempio, se si sceglie un vettoredei nodi aperto con k = n+ 1, la curva ottenuta e una curva di Bezier.

2.2.4 Curve NURBS

NURBS sta per Non Uniform Rational B-Spline, ovvero B-Spline razionalinon uniformi. Si tratta quindi, di B-Spline che utilizzano funzioni di baserazionali ed un vettore dei nodi non uniforme, aperto o periodico che sia. Cisono quindi anche B-Spline razionali uniformi aperte e uniformi periodiche.Dato che i vettori dei nodi sono stati mostrati nella sezione 2.2.2, qui milimitero a definire in generale le B-Spline razionali.

Definizione

Una B-Spline razionale e la proiezione di una B-Spline polinomiale definitain uno spazio quadridimensionale con coordinate omogenee all’interno diuno spazio tridimensionale8 (Vedere [19]). Ovvero:

P (t) =n+1∑i=1

Bhi Ni,k(t) (2.13)

Dove con Bhi si indicano i punti di controllo omogenei nello spazio quadridi-

mensionale. Ni,k(t) e l’i-esima funzione di base polinomiale cosı come e statadescritta dall’equazione (2.6). Per effettuare la proiezione, dividiamo il se-condo membro dell’equazione (2.13) per le coordinate omogenee, ottenendocosı la B-spline razionale:

P (t) =

n+1∑i=1

BihiNi,k(t)

n+1∑i=1

hiNi,k(t)

=

n+1∑i=1

BiRi,k(t) (2.14)

8Si assume che la curva sia definita nello spazio 3D invece che sul piano, il discorso ecomunque valido anche per il 2D: l’unica differenza, e che i punti Bi sono identificati dadue coordinate invece di tre.

Page 37: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.2. CURVE 27

Qui con Bi si indicano i punti di controllo tridimensionali della B-splinerazionale e le

Ri,k(t) =hiNi,k(t)

n+1∑i=1

hiNi,k(t)

(2.15)

sono le basi razionali. Le B-spline razionali contengono le B-spline comecaso particolare: se hi = 1 ∀i, allora la curva ottenuta e una B-spline nonrazionale. Le coordinate omogenee hi sono anche denominate pesi, in quan-to permettono di decidere quanta influenza deve avere un singolo punto dicontrollo sulla curva. La figura 2.2.4 mostra l’effetto che ha il cambio di unpeso su una curva NURBS.

Figura 2.8: Effetto del cambio di un peso su una curva nurbs. Tutte lecurve sono state calcolate con lo stesso vettore dei nodi e gli stessi punti dicontrollo, ma e stato variato il peso del terzo punto: dall’alto verso il basso,i valori di h3 sono rispettivamente 2, 1.5, 1, 0.75, 0.5, 0.25 e 0.(Immagini

ottenute tramite la libreria da me sviluppata)

Proprieta

In quanto generalizzazione delle B-Spline non razionali, le B-Spline razionlicondividono con esse alcune proprieta. Ad esempio:

Page 38: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

28 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

� Ogni funzione di base razionale e maggiore o uguale a 0 per ogni valoredel parametro t, ovvero:

Ri,k(t) ≥ 0 ∀t ∈ [tmin, tmax]

� La somma di tutte le funzioni di base per un dato valore del parametrot e pari a 1:

n+1∑i=1

Ri,k(t) ≡ 1

� Una B-spline razionale di ordine k ha ovunque una continuita di tipoCk−2;

� Il massimo ordine che una B-Spline razionale puo assumere e pari alnumero di punti di controllo;

� Le B-spline razionali possiedono la proprieta variation-diminishing ;

� Se tutti i pesi hi sono positivi, la B-spline razionale rimane all’internodell’unione degli involucri convessi formati da gruppi di k punti dicontrollo.

� Qualsiasi trasformazione proiettiva e applicata alla B-spline razionaleapplicandola ai vertici del poligono di controllo; La curva e invariantealle trasformazioni proiettive9. Si noti che questa condizione e piuforte rispetto a quella espressa per le B-spline non razionali, che sonoinvarianti rispetto alle trasformazioni affini.

2.3 Superfici

Rappresentare una superficie in forma parametrica non e molto diverso dalrappresentare una curva. La differenza principale e che sono necessari dueparametri invece di uno:

c1 ≤ u ≤ c2c3 ≤ w ≤ c4

Una volta fissato l’intervallo di definizione dei due parametri tramite le co-stanti c1, c2, c3 e c4

10, bisogna esprimere le coordinate dei punti come

9Una trasformazione proiettiva descrive cosa accade alla posizione percepita di unoggetto osservato quando il punto di vista dell’osservatore cambia.

10cosı definiti i parametri u e w identificano una forma rettangolare nel piano para-metrico: sebbene si possa utilizzare una qualsiasi forma nello spazio parametrico perpoi mapparla all’interno dello spazio 3D, continuero ad utilizzare per semplicita quellarettangolare.

Page 39: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.3. SUPERFICI 29

funzioni parametriche:

x = x(u,w)

y = y(u,w)

z = z(u,w)

Specificando un parametro e lasciando variare l’altro si ottiene quella cheviene definita una linea parametrica. Specificando il valore di entrambii parametri si ottiene un determinato punto della superficie. Un’ulterioredifferenza e che ora abbiamo una matrice m×n 11di punti di controllo e nonun vettore. Essi sono numerati nel modo seguente:

B0,m B1,m . . . Bn,m...

. . ....

.... . .

...B0,0 B1,0 . . . Bn,0

Dove m e n sono rispettivamente il numero di punti di controllo lungo

la direzione w e u.

2.3.1 Superfici di Bezier

Definizione e Proprieta

Una superficie di Bezier per un poliedro di controllo con n + 1 punti lungola direzione u e m + 1 punti lungo la direzione w e data dalla seguenteequazione:

Q(u ,w) =

n∑i=0

m∑j=0

Bi,jJn,i(u)Km,j(w) (2.16)

Dove Jn,i(u) e Km,j(w) sono le funzioni di base di Bernstein cosı comedescritte dall’equazione (2.3).

Dato che vengono utilizzate le funzioni di base di Bernstein, le superficidi Bezier ereditano da esse alcune proprieta:

� Il grado della superficie in ogni direzione parametrica e pari al numerodi punti di controllo in quella direzione meno 1;

� La superficie in genere segue la forma del poliedro di controllo;

� La superficie possiede lungo le direzioni u e w rispettivamente unacontinuita di tipo Cn−1 e Cm−1;

11In realta dato che ogni elemento della matrice contiene le coordinate di un punto, lamatrice e di dimensioni m× n× 3

Page 40: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

30 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

Figura 2.9: Esempio di superficie di Bezier (Immagine ottenute tramite la libreria

da me sviluppata)

� La superficie e contenuta nell’involucro convesso creato dal poligonodi controllo;

� La superficie e invariante rispetto alle trasformazioni affini;

� La superficie non mostra di avere la prorprieta variation-diminishing,essa e al contempo non definita e non conosciuta.

2.3.2 Superfici B-Spline

Definizione e proprieta

Una superficie B-spline per un poliedro di controllo con n + 1 punti lungola direzione u e m + 1 punti lungo la direzione w e data dalla seguenteequazione:

Q(u, w) =

n+1∑i=1

m+1∑j=1

Bi,jNi,k(u)Mj,l(w) (2.17)

Dove Ni,k(u)e Mj,l(w) sono le funzioni di base b-spline cosı come descrittedall’equazione (2.6). Come per le curve, la scelta dei vettori dei nodi [X] e[Y ] (relativi rispettivamente alle direzioni u e w) influisce sulla forma dellacurva. Sebbene di solito si utilizzi lo stesso tipo di vettore per entrambe ledirezioni, e possibile scegliere tipi diversi. La figura 2.10 a pagina 32 mostradelle superfici B-Spline che utilizzano diverse combinazioni dei vettori deinodi. Dato che vengono utilizzate le funzioni di base B-spline, le superficiB-Spline ereditano da esse alcune proprieta, ad esempio:

Page 41: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.3. SUPERFICI 31

� L’ordine massimo della superficie lungo le due direzioni u e w e parial numero di punti di controllo lungo quella direzione meno uno.

� La continuita della superficie e di tipo Ck−2 lungo la direzione u eC l−2 lungo la direzione w.

� La superficie e invariante rispetto alle trasformazioni affini: una tra-sformazione affine e applicata alla superficie applicandola invece alpoliedro di controllo.

� La proprieta variation-diminishing non e nota per le superfici B-Spline.

� Se l’ordine della B-spline e pari al numero di punti di controllo inentrambe le direzioni parametriche e si utilizzano dei vettori dei nodi ditipo uniforme aperto, la superficie B-spline si riduce ad una superficiedi Bezier.

� La regione di influenza di un singolo punto di controllo e limitata a±k

2 tratti lungo la direzione u e ± l2 tratti lungo la direzione w.

� La superficie risiede all’interno degli involucri convessi formati pren-dendo k, l punti di controllo adiacenti.

Rappresentazione matriciale

Una rappresentazione matriciale per una superficie B-spline periodica e deltipo:

Qs,t = [U∗ ][N∗ ][B∗s,t ][M∗ ]T [W ∗ ]T (2.18)

Dove[U∗ ] = [u∗k−1 u∗k−2 . . . u∗ 1 ][W ∗ ] = [w∗l−1 w∗l−2 . . . w∗ 1 ]

con u∗ e w∗ ad indicare i parametri scalati nell’intervallo [0 , 1]. Le matrici[N∗ ] e [M∗ ] sono date dall’equazione (2.8). La matrice [Bs,t ] rappresentauna “finestra scorrevole” che consente di calcolare delle sotto-superfici apartire da un sottoinsieme di k× l punti di controllo. Per superfici B-splineperiodiche calcolate a partire da un poliedro di punti di controllo aperto:

[B∗s,t ] = [Bi,j ] (2.19)

Dove

1 ≤ s ≤ n− k + 2 s ≤ i ≤ s+ k − 1

1 ≤ t ≤ m− l + 2 t ≤ j ≤ t+ l − 1(2.20)

Bi,j rappresenta un elemento della matrice dei punti di controllo.

Page 42: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

32 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

(a) (b)

(c) (d)

Figura 2.10: Effetto dell’uso di diversi tipi di vettori dei nodi su una su-perficie B-Spline: 2.10(a) il poliedro di controllo; 2.10(b) entrambi i vettoridi tipo uniforme aperto; 2.10(c) [X] di tipo uniforme periodico e [Y ] di ti-po uniforme aperto; 2.10(d) entrambi i vettori di tipo periodico. (Immagini

ottenute tramite la libreria da me sviluppata)

Page 43: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.3. SUPERFICI 33

2.3.3 Interpolazione 3D tramite B-Splines

Come nel caso delle curve mostrato nella sezione 2.2.3, anche le superfici B-spline possono essere utilizzate per interpolare (o approssimare) un insiemedi punti nello spazio. Organizziamo idealmente i punti da interpolare inuna maglia rettangolare r× s e indichiamo con D la matrice r ∗ s× 3 che licontiene, con 2 ≤ k ≤ n + 1 ≤ r e 2 ≤ l ≤ m + 1 ≤ s. Il generico elementodella matrice Di,j sara associato a due valori dei parametri, ui e wj . Perappartenere alla superficie, un punto deve soddisfare l’equazione (2.17). Adesempio il punto D1,1 otteniamo:

D1,1(u1, w1) =N1,k(u1)[M1,l(w1)B1,1 +M2,l(w1)B1,2 + · · ·+Mm+1,l(w1)B1,m+1]+...Nn+1,k(u1)[M1,l(w1)Bn+1,1 +M2,l(w1)Bn+1,2 + · · ·+Mm+1,l(w1)Bn+1,m+1]

Scrivendo quest’equazione per ognuno dei punti da interpolare, si forma unsistema di equazioni che puo essere riscritto in forma matriciale nel seguentemodo:

[D ] = [C ][B ] (2.21)

dove Ci,j = Ni,kMj,l. Come gia detto, [D ] e una matrice r ∗ s×3, [C ] e unamatrice r ∗ s×n ∗m contenente i prodotti delle funzioni di base, e [B ] e unamatrice n ∗ m × 3 delle coordinate dei punti di controllo , rappresentantel’incognita del problema. Se [C ] e quadrata, il problema puo essere risoltodirettamente calcolandone l’inversa:

[B ] = [C ]−1[D ] (2.22)

In questo caso la superficie ottenuta passa per tutti i punti dati. Se in-vece [C ] non e quadrata, il problema e sovraspecificato, e puo essere soloapprossimato:

[B ] = [[C ]T [C ]]−1[D ] (2.23)

I valori dei parametri ui e wj possono essere ricavati in modo simile a quellodescritto dall’equazione (2.10):

u1 = 0uqumax

=

q∑g=2

|Dg,p −Dg−1,p|

r∑g=2

|Dg,p −Dg−1,p|1 ≤ p ≤ s 1 ≤ q ≤ r (2.24)

Page 44: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

34 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

Lo stesso discorso vale per il parametro w:

w1 = 0wq

wmax=

q∑g=2

|Dp,g −Dp,g−1|

s∑g=2

|Dp,g −Dp,g−1|1 ≤ p ≤ r 1 ≤ q ≤ s (2.25)

dove umax e wmax sono, rispettivamente, i valori massimi dei vettori dei nodi[X] e [Y ].

2.3.4 Superfici NURBS

Definizione e proprieta

Una superficie B-Spline razionale con coordinate omogenee quadridimensio-nale si ottiene dalla seguente equazione:

Q(u,w) =n+1∑i=1

m+1∑j=1

Bhi,jNi,k(u)Mj,l(w) (2.26)

Dove con Bhi,j si indicano i punti di controllo omogenei, e con Ni,k(u)Mj,l(w)

le funzioni di base b-spline cosı come definite dall’equazione (2.6). La pro-iezione della B-spline non razionale definita nello spazio omogeneo quadri-dimensionale all’interno dello spazio tridimensionale si ottiene tramite laseguente equazione:

Q(u,w) =

n+1∑i=1

m+1∑j=1

hi,jBi,jNi,k(u)Mj,l(w)

n+1∑i=1

m+1∑j=1

hi,jNi,k(u)Mj,l(w)

=n+1∑i=1

m+1∑j=1

Bi,jSi,j(u,w) (2.27)

dove Bi,j indica il punto di controllo tridimensionale di posizione (i, j), eSi,j(u,w) e la funzione di base della superficie B-spline razionale:

Si,j(u,w) =hi,jNi,k(u)Mj,l(w)

n+1∑i1=1

m+1∑j1=1

hi1,j1Ni1,k(u)Mj1,l(w)

=hi,jNi,k(u)Mj,l(w)

Somma(u,w)(2.28)

con

Somma(u,w) =

n+1∑i1=1

m+1∑j1=1

hi1,j1Ni1,k(u)Mj1,l(w)

E comodo, sebbene non necessario, assumere hi,j ≥ 0 ∀i, j. Essendo co-struite a partire dalle funzioni di base B-spline, le superfici B-spline razionaliereditano da esse alcune caratteristiche, ad esempio:

Page 45: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

2.3. SUPERFICI 35

� La somma delle funzioni di base Si,j(u,w) e pari a uno per ogni valoredei parametri u e w;

� L’ordine massimo della superficie lungo le due direzioni u e w e parial numero di punti di controllo lungo quella direzione meno uno;

� La continuita della superficie e di tipo Ck−2 lungo la direzione u eC l−2 lungo la direzione w.

� La superficie e invariante rispetto alle trasformazioni proiettive: unatrasformazione proiettiva e applicata alla superficie applicandola inve-ce al poliedro di controllo;

� La proprieta variation-diminishing non e nota per le superfici B-Splinerazionali.

� Se hi,j = 1 ∀i, j, la superficie B-spline razionale si riduce alla contro-parte non razionale. Se inoltre k = n+1, l = m+1 e i vettori dei nodiutilizzati sono di tipo uniforme aperto, la superficie B-spline razionalesi riduce ad una superficie di Bezier non razionale.

� La regione di influenza di un singolo punto di controllo e limitata a±k

2 tratti lungo la direzione u e ± l2 tratti lungo la direzione w.

� Se hi,j ≥ 0 ∀i, j, la superficie risiede all’interno degli involucri con-vessi formati prendendo k, l punti di controllo adiacenti.

Una superficie B-spline razionale che utilizza dei vettori dei nodi di tiponon-uniforme, e la forma piu generale di rappresentazione di una superficie.La possibilita di utilizzare dei pesi (sia positivi che negativi) per i punti dicontrollo, permette di disegnare superfici che non possono essere disegnatecon delle superfici b-spline non-razionali. La figura 2.11 mostra gli effettidella variazione di un peso su una superficie B-spline razionale.

Page 46: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

36 CAPITOLO 2. SPLINE, B-SPLINE E NURBS

(a) h2,3 = 0 (b) h2,3 = 1

(c) h2,3 = 5 (d) h2,3 = 10

Figura 2.11: Effetto della variazione di un peso su una superficie B-splinerazionale. Il valore di h2,3 (il punto di controllo piu alto) e stato variatomentre tutti gli altri sono pari a uno. Nella figura 2.11(a) il punto vienecompletamente ignorato, mentre nella figura 2.11(b) la superficie ottenuta euna B-spline non razionale. (Immagini ottenute tramite la libreria da me sviluppata)

Page 47: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Capitolo 3Cenni di Python

Ed ora passiamo a qualcosa di completamentediverso.

Monthy Python

In questo capitolo verranno illustrate alcune peculiarita del linguaggioPython. La scelta di questo linguaggio e dovuta a diversi fattori: innan-zitutto, il Python, per essere un linguaggio interpretato, e molto veloce, emulti-piattaforma e permette di risolvere problemi complessi con poche ri-ghe di codice. Questo linguaggio e corredato da un vasto insieme di librerie,come ad esempio NumPy (vedi [4]), che offre funzioni per il calcolo scientifi-co e l’algebra lineare.L’uso di Python permette l’uso della libreria in diversiambiti. Software di modellazione 3D come Blender (vedi [1]) permettono diincludere script in Python, rendendo gli utenti di Blender potenziali uten-ti della libreria. La versione di Python utilizzata e la 2.6.5. Sul Pythonci sarebbe da scrivere davvero, davvero molto. Questo linguaggio, creatoda Guido Van Rossum nei tardi anni ’80 con l’intento di correggere buonaparte dei difetti a suo dire presenti negli altri linguaggi di programmazione,e diventato ad oggi uno dei linguaggi piu apprezzati, trovando applicazio-ni ovunque. Python e un linguaggio molto versatile, si possono scrivereprogrammi utilizzando il paradigma di programmazione procedurale, quelloorientato agli oggetti, il paradigma funzionale, ed e anche possibile scriveresemplici script. Dato che una presentazione approfondita delle funzionalitadel Python va oltre gli obiettivi di questo lavoro, di seguito saranno elencatesolo alcune caratteristiche, funzioni ed esempi di sintassi necessari a com-prendere al meglio il lavoro svolto. Per approfondimenti e disponibile sottolicenza GPL 1 il libro [18] che rappresenta l’attuale punto di riferimento

1Gnu General Public License, http://www.gnu.org/licenses/gpl.html

37

Page 48: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

38 CAPITOLO 3. CENNI DI PYTHON

per chi vuole imparare Python. La pagina [10] presenta, oltre ad una breveguida di stile, alcune tecniche e caratteristiche proprie del linguaggio.

3.1 Caratteristiche basilari

Una delle peculiarita del Python e che esso non utilizza caratteri specialiper individuare blocchi di codice, ma utilizza il livello di indentazione. Uncodice male indentato non viene interpretato da python. Il linguaggio quin-di e pensato in modo da costringere il programmatore a scrivere codiceordinato. Il Python inoltre utilizza la tipizzazione dinamica, caratteristicapropria di diversi linguaggi di alto livello come Matlab e Ruby. Cio vuoldire che non c’e vincolo di tipo per una data variabile. Ad esempio2:

1 >>> a = 5

2 >>> print a

3 5

4 # type restituisce il tipo della variabile fornita come parametro

5 >>> type(a)

6 <type ’int’>

7 >>> a = ’Hello World!’

8 >>> print a

9 Hello World!

10 >>> type(a)

11 <type ’str’>

12 >>> a = range(5)

13 >>> print a

14 [0, 1, 2, 3, 4]

15 >>> type(a)

16 <type ’list’>

Cio e reso possibile trattando le variabili in un modo fondamentalmentediverso da quello utilizzato dai linguaggi di livello piu basso. Quando, adesempio in C, si dichiarano tre variabili a1, a2, a3, viene riservato in me-moria dello spazio, la cui quantita dipende dal tipo di variabili. Questospazio viene riservato anche se le variabili sono tutte e tre dello stesso tipoe contengono tutte lo stesso valore. Quindi se le tre variabili sono di tipointero e hanno tutte valore 10, in memoria vengono riservati 3 × 4 = 12bytes. Se sono 1’000’000, vengono conservati 4’000’000 di bytes. Pythonmemorizza il valore una volta, ed assegna a quella zona di memoria tante“etichette” quante sono le variabili che hanno quel valore. Quindi in pythonad ogni variabile non corrisponde una zona di memoria, una variabile e unetichetta che viene assegnata alla zona di memoria che contiene il valoreattuale della variabile.

2’>>>’ rappresenta il prompt dell’ interprete python

Page 49: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3.2. LE LISTE 39

3.2 Le liste

Uno dei punti di forza del Python risiede nelle strutture che offre. Una diqueste strutture e la lista. La lista, in Python, e l’alternativa ai vettori.Mentre un vettore e una collezione di oggetti omogenei, ovvero tutti dellostesso tipo, ed e di taglia fissata, una lista in Python puo contenere oggettieterogenei e la sua taglia e dinamica. Gli elementi di una lista con n elementisono numerati da 0 a n− 1. Qualche esempio:

1 >>> l = [] # inizializzo l come lista

2 >>> l.append(4) # aggiungo alla lista il numero 4

3 >>> l

4 [4]

5 >>> l.append(’Hello World!’) # aggiungo la stringa "Hello World!"

6 >>> l

7 [4, ’Hello World!’]

8 >>> l.append(5.9) # aggiungo alla lista il numero 5.9

9 >>> l

10 [4, ’Hello World!’, 5.9000000000000004]

11 >>> l[2] # accedo all’elemento di indice 2

12 5.9000000000000004

13 >>> l[-1] # accedo all’ultimo elemento della lista

14 5.9000000000000004

15 >>> l[-2] # accedo al penultimo elemento della lista

16 ’Hello World!’

17 >>> l[1] # che l’elemento di indice 1

18 ’Hello World!’

19 >>> for item in l: # per ogni elemento della lista

20 ... type(item) # controllo il tipo dell’elemento

21 ...

22 <type ’int’>

23 <type ’str’>

24 <type ’float’>

I comandi dell’esempio precedente mostrano come la lista venga estesa manmano aggiungendo oggetti. Si noti che una lista e un oggetto iterabile,infatti nel ciclo presente alla fine del listato si chiede di verificare il tipo diogni elemento della lista, che contiene alla fine un intero, una stringa ed unnumero in virgola mobile. L’indicizzazione degli elementi e di tipo circolare.Infatti se si chiede di accedere all’elemento di indice −1 Python restituiscel’ultimo elemento della lista. Supponiamo ora di voler costruire un vettoredi potenze, tale che p[i] = i2 ∀i ∈ [0, 9]. In linguaggio C la cosa puo essererisolta nel seguente modo:

1 int p[10];

2 int i;

3

4 for( i=0; i < 10; i++){

5 p[i] = i*i;

Page 50: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

40 CAPITOLO 3. CENNI DI PYTHON

6 }

In Python:

1 >>> p = [i**2 for i in range(10)]

2 >>> p

3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

in una sola riga di codice, dove range(n) e una funzione che genera una listacontenente i valori dell’intervallo [0, n− 1] e ** e l’operatore di elevamentoa potenza. In generale per costruire una lista fatta di oggetti determinati( o selezionati) da una funzione f(n) a partire la una lista di elementi, inpython si puo scrivere:

1 risultato = [f(elemento) for elemento in lista]

E anche possibile introdurre delle condizioni in questa notazione. Ad esem-pio, supponendo che si voglia inserire nella lista l’ipotetica funzione f seelemento e compreso fra 0 e k, 0 altrimenti:

1 risultato = [f(elemento) if elemento >= 0 and elemento < k else 0

for elemento in lista]

Questa notazione puo essere annidata in modo da creare liste di liste.Ad esempio se volessi creare una lista, il cui i-esimo elemento e una listadelle potenze ij , con i ∈ [0, 5], j ∈ [0, 9] , posso scrivere:

1 >>> potenze = [[ i**j for j in range(10)] for i in range(6) ]

2 >>> potenze

3 [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],

4 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],

5 [1, 2, 4, 8, 16, 32, 64, 128, 256, 512],

6 [1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683],

7 [1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144],

8 [1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125]]

Un’altra comodita e il sistema di slicing, ovvero la possibilita di accederea “fette” della lista. Alcuni esempi:

1 >>> lista = range(10)

2 >>> lista

3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

4 >>> lista[2:6] # prendo gli elementi dal 2 al 5

5 [2, 3, 4, 5]

6 >>> lista[-3:] # solo gli ultimi 3

7 [7, 8, 9]

8 >>> lista[:3] # solo i primi 3

9 [0, 1, 2]

10 >>> lista[::2] # un elemento ogni due

11 [0, 2, 4, 6, 8]

12 >>> lista[2:9:3] # un elemento ogni tre dal 2 all’8

Page 51: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3.3. I DIZIONARI 41

13 [2, 5, 8]

Un’altra funzione che ho usato spesso durante il mio lavoro e stata lafunzione enumerate. Questa funzione e utile quando nell’iterare una lista,si vuole utilizzare anche l’indice di posizione degli elementi. Normalmenteuna soluzione sarebbe

1 >>> for i in range(len(lista)):

2 ... print i, lista[i]

che utilizza la funzione len, la quale restituisce il numero di elementi con-tenuti in una collezione. Utilizzando enumerate

1 >>> for i, elemento in enumerate(lista):

2 ... print i, elemento

si ottiene un codice piu elegante e facile da comprendere, ed anche piu veloce.

Da ricordare inoltre e l’operatore in, che permette di verificare se unoggetto si trova all’interno di una collezione:

1 >>> lista = range(10)

2 >>> tupla = (’Python’,’C’,’C++’)

3 >>> 5 in lista

4 True

5 >>> 50 in lista

6 False

7 >>> ’Python’ in tupla

8 True

9 >>> ’Java’ in tupla

10 False

3.3 I dizionari

Un dizionario e un array associativo, ovvero un array all’interno del quale adogni elemento e associata una chiave che lo identifica. L’implementazioneche Python offre di questa struttura dati di alto livello e tanto comodaquanto semplice da utilizzare. Un classico esempio e quello della rubrica:

1 >>> rubrica = dict()

2 >>> rubrica[’bianchi’] = 99654321

3 >>> rubrica[’verdi’] = 11123456

4 >>> rubrica[’rossi’] = 10123456

5 >>> rubrica

6 {’bianchi’: 99654321, ’rossi’: 10123456, ’verdi’: 11123456}

Dal dizionario e possibile ottenere liste iterabili delle chiavi e dei valorimemorizzati:

Page 52: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

42 CAPITOLO 3. CENNI DI PYTHON

1 >>> rubrica.keys()

2 [’bianchi’, ’rossi’, ’verdi’]

3 >>> rubrica.values()

4 [99654321, 10123456, 11123456]

e controllare se esiste una determinata chiave:

1 >>> rubrica.has_key(’rossi’)

2 True

3 >>> rubrica.has_key(’python’)

4 False

e possibile creare un dizionario prendendo chiavi e valori da due liste separatenel seguente modo:

1 >>> nomi = [’nome1’,’nome2’,’nome3’]

2 >>> valori = [ 5, 10, 15 ]

3 >>> dizionario = dict(zip(nomi,valori))

4 >>> dizionario

5 {’nome1’: 5, ’nome2’: 10, ’nome3’: 15}

zip e una funzione che a partire da un insieme di liste, restituisce una listadi tuple, ovvero delle ennuple non modificabili:

1 >>> zip(nomi,valori)

2 [(’nome1’, 5), (’nome2’, 10), (’nome3’, 15)]

3.4 Programmazione orientata agli oggetti

Una classe in python e definita nel seguente modo:

1 class NomeClasse:

2 def __init__(self, attributo1, attributo2, ..., attributoN):

3 self.attributo1 = attributo1

4 self.attributo2 = attributo2

5 ...

6 def metodo1(self,parametro1, parametro2, ..., parametroN):

7 # corpo del metodo1

8 ...

9

10 oggetto = NomeClasse(valore1, valore2, ..., valoreN)

init e il metodo costruttore. Il primo parametro che viene passatoad ogni metodo della classe e self, che e un riferimento all’oggetto stesso(come this in Java). Da notare che non c’e nessun tipo di riferimento aimodificatori di accesso per attributi e metodi; questo perche python nonprevede alcun tipo di modificatore di accesso, ergo non esistono attributi ometodi privati. Per indicare che un attributo o un metodo e privato esiste

Page 53: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3.4. PROGRAMMAZIONE ORIENTATA AGLI OGGETTI 43

una convenzione di nomenclatura, che consiste nell’anteporre un doppio un-derscore (il carattere ’ ’) al nome dell’attributo o del metodo in questione.Un piccolo esempio:

1 >>> class Prova:

2 ... def __init__(self,valore):

3 ... self.__attributo = valore

4 ...

5 >>> p = Prova(’ciao’)

6 >>> p.attributo

7 Traceback (most recent call last):

8 File "<stdin>", line 1, in <module>

9 AttributeError: Prova instance has no attribute ’attributo’

10 >>> p.__attributo

11 Traceback (most recent call last):

12 File "<stdin>", line 1, in <module>

13 AttributeError: Prova instance has no attribute ’__attributo’

14 >>> dir(p)

15 [’_Prova__attributo’, ’__doc__’, ’__init__’, ’__module__’]

16 >>> p._Prova__attributo

17 ’ciao’

L’attributo non e a tutti gli effetti privato, utilizzando la convenzio-ne di nomenclatura, python cambia il nome dell’attributo / metodo inNomeClasse NomeAttributo. Questo e probabilmente uno dei pochi di-

fetti di python, anche se gli utenti di questo linguaggio spiegano la cosa conun secco “We are all consenting adults here”, ovvero “Siamo tutti adulticonsenzienti”, ad indicare che non dovrebbe essere il linguaggio ad impedirel’accesso ad un attributo o ad un metodo, ma il buon senso di chi utilizza laclasse. Che si sia d’accordo o meno con questa filosofia, esiste un piccolo truc-co per proteggere almeno gli attributi. Tra i vari metodi standard che fannoparte di una classe python, compare il metodo set attribute . Il corpodi questo metodo contiene il codice da esegure quando si tenta di assegnareun valore ad un attributo con oggetto.attributo = valore. Il problemae che anche self.attributo = valore e una assegnazione diretta, quindidopo aver sovrascritto set attribute anche all’interno della classe stes-sa non possono assegnare direttamente dei valori agli attributi. Bisogna,invece, agire direttamente sul dizionario interno alla classe che conserva leassociazioni nome / valore degli attributi. Se ad esempio volessi impedi-re che l’attributo ’voto’ di un ipotetica classe ’Esame’ venga modificato, lasuddetta classe dovrebbe essere scritta all’incirca nel seguente modo:

1 class Esame:

2 def __init__(self,voto):

3 self.__dict__[’voto’] = voto

4 ...

5 def __set_attribute__(self,nome,valore):

6 if nome == ’voto’:

Page 54: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

44 CAPITOLO 3. CENNI DI PYTHON

7 print "Impossibile modificare l’attributo voto"

8 else:

9 self.__dict__[nome] = valore

Per quanto riguarda l’ereditarieta, Python offre sia l’ereditarieta singolache quella multipla. Non tutti i linguaggi possiedono questa caratteristica:in Java, ad esempio, per ottenere qualcosa di simile bisogna utilizzare il mec-canismo delle interfacce. Un interfaccia Java pero contiene solo la firma deimetodi, non la loro implementazione, quindi ogni volta che si ereditano deimetodi da un interfaccia essi vanno implementati anche se l’implementazio-ne e gia disponibile in un’altra classe, il che comporta ridondanza di codice.Per far ereditare attributi e metodi in Python, la sintassi e la seguente

1 class ClasseDerivata(Classe1, Classe2, ..., ClasseN):

2 ...

Se un metodo, che per esempio chiameremo metodo1, e presente in piudi una classe genitore, per decidere quale versione di metodo1 richiamarebisogna sovrascrivere il metodo nella classe figlia, per poi richiamare espli-citamente l’implementazione che vogliamo. Supponendo di voler utilizzarel’implementazione offerta da Classe2:

1 class ClasseDerivata(Classe1, Classe2, ..., ClasseN):

2 ...

3 def metodo1(self, parametro 1, ..., parametroN):

4 Classe2.metodo1(self, parametro 1, ..., parametroN)

Per controllare se un oggetto e istanza di una classe (o se una variabileha un valore di un dato tipo) si puo utilizzare l’istruzione isistance, cheprende come parametro l’oggetto da verificare e la classe. La caratteristicacomoda di questa istruzione e che puo essere utilizzata per verificare piuclassi alla volta passando come secondo parametro una collezione di classi(una lista o una tupla). Ad esempio:

1 >>> a = 5

2 >>> isinstance(a,(int,list,dict))

3 True

4 >>> a = ’HelloWorld!’

5 >>> isinstance(a,(int,list,dict))

6 False

7 >>> a = [1,2,3]

8 >>> isinstance(a,(int,list,dict))

9 True

3.5 Le eccezioni

Il modo in cui Python gestisce le eccezioni e simile a quello utilizzato da altrilinguaggi orientati agli oggetti come C++ e Java. La sintassi e la seguente:

Page 55: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3.5. LE ECCEZIONI 45

1 try:

2 # codice che potrebbe generare l’errore

3 except TipoEccezione:

4 # codice da esegure se si solleva l’eccezione

5 else:

6 # codice da esegure solo se non stata sollevata

7 # NESSUNA eccezione

8 finally:

9 # codice da eseguire in ogni caso

Ci sono diverse eccezioni standard disponibili con Python, ad esempio:

IOError Eccezione che si solleva in caso di errore di I/O;

TypeError Eccezione che si solleva quando si tenta di applicare un opera-zione o una funzione ad una variabile del tipo sbagliato (ad esempioinvocare len su una variabile che contiene un intero);

ImportError Eccezione sollevata quando si tenta di importare un modulonon installato;

ValueError Eccezione sollevata quando si passa ad una funzione un pa-rametro del tipo giusto ma con un valore inaspettato (ad esempio siriceve un valore negativo quando ci si aspettava un valore maggiore di0);

IndexError Eccezione sollevata quando si tenta di accedere ad un indicefuori limite in una lista.

E possibile creare eccezioni personalizzate estendendo la classe Exception:

1 class MiaEccezione(Exception):

2 def __init__(self, descrizione):

3 self.descrizione = descrizione

4 def __str__(self):

5 print descrizione

str e l’equivalente del metodo toString di Java. L’eccezione puo esseresollevata tramite l’istruzione raise:

1 if valore < 0:

2 raise ValueError(’Il valore deve essere > 0’)

Per accedere alla descrizione di una eccezione si puo utilizzare l’operatoredi ridenominazione as:

1 try:

2 # codice che potrebbe sollevare l’errore

3 except IOError as descrizione:

4 print "Errore: ", descrizione

Page 56: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

46 CAPITOLO 3. CENNI DI PYTHON

3.6 Librerie utilizzate

Nello scrivere la libreria per il calcolo delle nurbs, ho utilizzato principal-mente due librerie:

NumPy Principalmente per le classi e i metodi per l’algebra lineare edaltre funzioni di comodo;

Matplotlib Per il plot 2D e 3D.

Le versioni utilizzate sono numpy-1.3.0 e matplotlib-0.99.1.1. Per importareun modulo in python si utilizza l’istruzione import. E possibile anche im-portare singole componenti di un modulo, invece di del modulo intero, edeventualmente rinominarle utilizzando l’operatore di rideominazione as. Adesempio:

1 # senza utilizzare as

2 import modulo1.classe1

3

4 modulo1.classe1.metodo()

5

6 # utilizzando as

7

8 import modulo1.classe1 as cl

9

10 cl.metodo()

11

12 # posso importo direttamente la classe

13 from modulo1 import classe1

14

15 # oppure posso importare una singola classe e ridenominarla

16 from modulo1 import classe1 as cl

3.6.1 Numpy

Numpy fornisce classi e metodi utili per l’algebra lineare oltre a funzioniche rendono meno traumatico il passaggio da matlab (come ad esempiolinspace). La classe principale di questa libreria e ndarray, la quale modellaun array n-dimensionale. Gli attributi piu importanti della classe ndarray

sono:

ndim numero di dimensioni dell’array

shape La “forma” dell’array: per una matrice con n righe ed m colonne, ilvalore di shape e (n,m).

size Il numero di elementi dell’array;

dtype Oggetto che descrive il tipo di dati contenuti dall’array.

Page 57: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3.6. LIBRERIE UTILIZZATE 47

Un esempio:

1 >>> import numpy as np

2 >>> a = np.arange(5)

3 >>> b = np.arange(6,10)

4 >>> a

5 array([0, 1, 2, 3, 4])

6 >>> b

7 array([6, 7, 8, 9])

8 >>> import numpy as np

9 >>> a = np.arange(5)

10 >>> b = np.arange(5,10)

11 >>> print a

12 [0 1 2 3 4]

13 >>> print b

14 [5 6 7 8 9]

15 >>> c = np.append(a,b)

16 >>> print c

17 [0 1 2 3 4 5 6 7 8 9]

18 >>> a = np.array([[1,2,3],[4,5,6]])

19 >>> print a

20 [[1 2 3]

21 [4 5 6]]

22 >>> b = np.array([7,8,9])

23 >>> print b

24 [7 8 9]

25 >>> print np.append(a,b)

26 [1 2 3 4 5 6 7 8 9]

27 >>> c = np.append(a,b,axis=0)

28 Traceback (most recent call last):

29 File "<stdin>", line 1, in <module>

30 File "/usr/lib/python2.6/dist-packages/numpy/lib/function_base.py"

, line 3234, in append

31 return concatenate((arr, values), axis=axis)

32 ValueError: arrays must have same number of dimensions

33 # i valori di ndim devono essere uguali per entrambi gli array, per

risolvere basta scrivere

34 # fra parentesi quadre il secondo vettore

35 >>> print np.append(a,[b],0)

36 [[1 2 3]

37 [4 5 6]

38 [7 8 9]]

Un’utile funzione e append che permette di concatenare due array, permet-tendo di specficare lungo quale “asse” effettuare l’operazione. Un esempiopuo chiarire meglio questa funzionalita:

1 # arange funziona come range, ma restituisce

2 # un’istanza di ndarray

3 >>> import numpy as np

Page 58: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

48 CAPITOLO 3. CENNI DI PYTHON

4 >>> a = np.array([[1,2,3],[4,5,6]])

5 >>> print a

6 [[1 2 3]

7 [4 5 6]]

8 >>> print a.ndim

9 2

10 >>> print a.shape

11 (2, 3)

12 >>> print a.dtype

13 int32

14 >>> a = np.array([5,7,9],np.double)

15 >>> print a

16 [ 5. 7. 9.]

17 >>> print a.ndim

18 1

19 >>> print a.shape

20 (3,)

21 >>> print a.dtype

22 float64

E possibile effettuare il prodotto vettoriale fra due vettri a e b tramitela funzione dot:

1 >>> a = np.arange(5)

2 >>> b = np.arange(10,15)

3 >>> print a

4 [0 1 2 3 4]

5 >>> print b

6 [10 11 12 13 14]

7 >>> print np.dot(a,b)

8 130

9 >>> a = np.array([[1,2,3],[4,5,6],[7,8,9]])

10 >>> print a

11 [[1 2 3]

12 [4 5 6]

13 [7 8 9]]

14 >>> b = np.array([0,1,2])

15 >>> print b

16 [0 1 2]

17 >>> print np.dot(a,b)

18 [ 8 17 26]

3.6.2 Matplotlib

Matplotlib e una libreria che fornisce funzioni necessarie per eseguire plot2D e 3D. L’utilizzo e molto semplice, per il caso 2D:

1 >>> import numpy as np

2 >>> import pylab as pl

Page 59: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3.6. LIBRERIE UTILIZZATE 49

Figura 3.1: Esempio di plot della funzione sin(x) con matplotlib

3 # genera un insieme di valori compresi fra 0 e 2 * pigreco

4 >>> x = np.linspace(0,2 * np.pi)

5 >>> y = np.sin(x)

6 >>> pl.plot(x,y)

7 [<matplotlib.lines.Line2D object at 0xa7c11cc>]

8 >>> pl.show()

L’immagine 3.1 a mostra l’output del codice precedente.Per quanto riguarda il plot di grafici 3D matplotlib, offre diversi me-

todi tra cui plot wireframe che visualizza solo i lati della figura che sivuole visualizzare e plot surface che visualizza la superfice completa. Unesempio:

1 import numpy as np

2 from mpl_toolkits.mplot3d.axes3d import Axes3D

3 import pylab as pl

4

5 X = np.linspace(-10,10)

6 Y = np.linspace(-10,10)

7

8 # Ottengo la griglia di punti

9 X,Y = np.meshgrid(X,Y)

10

11 # Inizializzo l’oggetto immagine che conterr il plot

12 fig = pl.figure(1,dpi=100)

13 ax = Axes3D(fig)

14

15 Z = X**2 - Y**2

16

Page 60: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

50 CAPITOLO 3. CENNI DI PYTHON

17 # A scelta si utilizza una delle due

18 ax.plot_wireframe(X, Y, Z, cstride = 1, rstride = 1)

19 ax.plot_surface(X, Y, Z, cstride = 1, rstride = 1)

20

21 pl.show()

cstride e rstride permettono di scegliere la precisione del plot. La figura3.2 mostra la differenza fra i due tipi di plot.

Page 61: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

3.6. LIBRERIE UTILIZZATE 51

(a) Wireframe

(b) Surface

Figura 3.2: Plot della funzione z = x2−y2, in wireframe e superfice completa

Page 62: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

52 CAPITOLO 3. CENNI DI PYTHON

Page 63: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Capitolo 4Descrizione delle classi ed algoritmiimplementati

Se, fra dieci anni, mentre state facendo qualcosain modo veloce e sporco, improvvisamente miimmaginerete dietro le vostre spalle mentre vidico:“A Dijkstra questo non sarebbe piaciuto”,quella sara l’immortalita che mi basta.

Edsger W. Dijkstra

In questo capitolo saranno illustrate le classi che compongono la libreriache ho sviluppato e gli algoritmi utilizzati. Si noti che la libreria numpy estata importata utilizzando una ridenominazione:

1 import numpy as np

quindi nei fammenti di codice che seguiranno ogni riferimento a np e un rife-rimento alla libreria numpy. Buona parte degli algoritmi sono stati adattatidal testo [19]. Dico “adattati” in quanto gli algoritmi illustrati sul testo, ol-tre ad essere scritti in uno pseudocodice eccessivamente vicino al linguaggioC, a volte presentano degli errori e delle incongruenze, probabilmente dovutialla loro funzione di linea guida. I codici esposti in questo capitolo non sonocompleti, viene mostrato solo cio che e di interesse per la discussione. Perconsultare il codice nella sua interezza fare riferimento all’appendice B. Hodiviso le classi componenti la libreria in due files: Curve.py e Surface.py.In Python ogni file e un modulo, ovvero ogni file puo essere incluso comese si trattasse di una libreria. Volendo, ad esempio, includere nel propriocodice la classe Nurbs dal file Curve.py, si puo scrivere:

1 from Curve import Nurbs

2 c = Nurbs(...)

53

Page 64: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

54 CAPITOLO 4. CLASSI ED ALGORITMI

oppure si puo importare l’intero file:

1 import Curve

2 c = Curve.Nurbs(...)

e anche utilizzare ridenominazioni

1 import Curve as crv

2 c = crv.Nurbs(...)

4.1 La struttura delle classi

Proseguendo nella lettura si puo notare che la gerarchia delle classi nonrispecchia le relazioni presenti tra le curve e le superfici illustrate nel ca-pitolo 2. Questo perche nel decidere la gerarchia delle classi ho cercatodi massimizzare il riutilizzo di codice. Infatti si puo notare che metodi cheavrebbero potuto lavorare direttamente sugli attributi della classe utilizzanoinvece dei parametri passati in input. Ad esempio, il calcolo delle funzionidi base b-spline e lo stesso sia che si tratti di curve che di superfici, quindiil metodo computeBasis della classe BSpline viene utilizzato nella classeBSplineSurf, il che rende BSplineSurf classe figlia della classe BSpline.Sebbene le B-Spline contengano le curve di Bezier come caso particolare, nel-l’implementazione di questa libreria non vi e alcuna relazione di ereditarietafra di esse, in quanto non condividono metodi.

4.2 La classe Points

Sebbene la classe ndarray fornita da numpy basti per modellare un punto,essa non contiene alcune funzioni necessarie come il calcolo della distanzaeuclidea. Ho quindi esteso la classe ndarray definendo la classe Points,all’interno della quale ho aggiunto i metodi che ho ritenuto piu opportuni.Si noti che dal punto di vista semantico la funzione della classe Points eambigua, in quanto essa e utilizzata per rappresentare sia un singolo puntodi uno spazio n-dimensionale sia un array di punti. Creare due classi distin-te sarebbe sı stato piu corretto, ma una classe rappresentante un array dipunti sarebbe stata composta da un singolo metodo (metodo chordLength

descritto piu avanti), un po poco per complicare, anche se di poco, la ge-rarchia delle classi. Ho quindi trovato piu vantaggioso modellare la classePoints in modo che potesse essere utilizzata sia per modellare un singolopunto che un array di punti.

Costruttore

Il costruttore di questa classe e diverso da quello illustrato nella sezione 3.4.Il metodo new e un metodo che, se presente, viene chiamato prima di

Page 65: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.2. LA CLASSE POINTS 55

init . Utilizzando questo metodo, che ha come primo parametro la classestessa, e possibile agire direttamente sull’oggetto, piuttosto che assegnaresolo degli attributi.

1 class Points(np.ndarray):

2 def __new__(subclass,data,dtype = np.double):

3 obj = np.asarray(data,dtype).view(subclass)

4 return obj

Il metodo non fa altro che creare un oggetto obj come ndarray. Il metodoview di ndarray permette di trasformare un’istanza di ndarray in un istanzadi una qualsiasi sottoclasse di ndarray, in questo caso particolare trasformaobj da istanza di ndarray ad istanza di Points. Per ulteriori dettagli vederela documentazione di numpy [4].

Metodo distance

Questo metodo calcola la distanza Euclidea fra il punto rappresentato dallaclasse e un punto p passato per parametro. Ricordiamo che la distanza Eu-clidea fra due punti n-dimensionali a = (a1, a2, . . . , an) e b = (b1, b2, . . . , bn)e: √√√√ n∑

i=1

(bi − ai)2

1 def distance(self,p):

2 return np.sqrt(sum(pow(p-self,2)))

L’implementazione del metodo segue pari passo la definizione, ndarray so-vrascrive gli operatori aritmetici, quindi p-self restituisce un vettore il cuii-esimo elemento e pari alla differenza dell’i-esimo elemento di p con l’i-esimoelemento di self. pow(x,y) e una funzione di Python che restituisce xy,ed sqrt e la funzione offerta da numpy per il calcolo della radice quadrata.A questo punto abbiamo un vettore il cui elemento i-esimo e

√(pi − selfi)2.

La funzione sum calcola la somma degli elementi del vettore passato perparametro.

Metodo chordLength

Nei problemi di interpolazione descritti nelle sezioni 2.2.3 e 2.3.3, per stimareil valore del parametro si e usata l’equazione (2.10). Il numeratore di quellaequazione rappresenta la somma delle distanze fra ogni coppia di punti. Ilmetodo chordLengt calcola la somma delle distanze dal punto di indice i alpunto di indice j. Se non si passano parametri, il metodo calcola la sommadelle distanze di tutti i punti presenti nel vettore.

1 def chordLength(self,i=0,j=None):

Page 66: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

56 CAPITOLO 4. CLASSI ED ALGORITMI

2 return sum([self[k].distance(self[k+1]) for k in xrange(len(

self[i:j])-1)])

xrange e simile a range, solo piu efficiente nel caso di intervalli molto ampi.L’argomento di sum e una lista creata con la sintassi mostrata nella sezione3.2, la lista creata e una lista il cui elemento k-esimo e la distanza fra ilpunto k e il punto k+1, con k che va da 0 fino alla lunghezza della sottolistacontenente gli elementi dall’i-esimo al j-esimo.

Metodo convexComb

Questo metodo molto semplice restituisce la combinazione convessa in u conun punto p passato per parametro. Nel caso il valore di u ecceda i limitidell’intervallo [0, 1].

1 def convexComb(self,p,u):

2 if u < 0 or u > 1:

3 raise ValueError("il parametro u deve essere compreso fra

0 e 1")

4 return (1-u)*self + u*p

Esempio d’uso

Seguono alcuni esempi basilari d’uso della classe Points.

1 >>> import numpy as np

2 >>> from Curve import Points

3 # creazione di un punto di coordinate (3, 5)

4 >>> a = Points([3,5],np.double)

5 >>> a

6 Points([ 3., 5.])

7 # creazione di un punto di coordinate (9, 4)

8 >>> b = Points([9,4],np.double)

9 >>> b

10 Points([ 9., 4.])

11 # calcolo della distanza fra a e b

12 >>> a.distance(b)

13 6.0827625302982193

14 # combinazione convessa tra a e b con u = 0.5

15 >>> a.convexComb(b,0.5)

16 Points([ 6. , 4.5])

17 # creo un vettore di punti che contiene a, b e altri due punti

specificati manualmente

18 >>> punti = Points([a,b,(12,5),(15,9)],np.double)

19 >>> punti

20 Points([[ 3., 5.],

21 [ 9., 4.],

22 [ 12., 5.],

23 [ 15., 9.]])

Page 67: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.3. LA CLASSE CURVE 57

24 # calcolo la somma delle di stanze fra tutti i punti presenti nell’

array

25 >>> punti.chordLength()

26 14.245040190466598

4.3 La classe Curve

Questa classe contiene attributi e metodi principali comuni a tutti i tipi dicurve implementati in questa libreria.

Costruttore

I principali attributi di una curva sono i punti di controllo necessari percalcolarla, identificati dal parametro cntrl e il numero di punti della curvada calcolare, attributo identificato dal parametro npts.

1 class Curve:

2 def __init__(self,cntrl,npts):

3

4 if isinstance(cntrl,str):

5 self.loadFromFile(cntrl)

6 else:

7 try:

8 self.__dict__[’cntrl’] = Points(cntrl)

9 except Exception as detail:

10 raise PyNurbsError("Errore formato punti di controllo

:{0}".format(detail))

11

12 self.__dict__[’npts’] = npts

13

14 self.__dict__[’points’] = np.zeros((self.npts, 2)).view(

Points)

nel costruttore controllo se cntrl e una stringa tramite l’istruzione isinstance(vedere la sezione 3.4). In caso positivo, il valore di cntrl viene inteso comenome del file contenente i punti di controllo, viene invocato quindi il me-todo loadFromFile per caricare da file i suddetti punti. In caso contrario,cntrl e una lista contenente i punti di controllo della curva. Nel bloccotry/except tento di assegnare all’attributo della classe un oggetto di tipoPoints costruito a partire dai punti di controllo passati come parametro.

Metodo calculate

Il metodo calculate e il metodo che ogni classe dovra implementare percalcolare la curva.

Page 68: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

58 CAPITOLO 4. CLASSI ED ALGORITMI

1 def calculate(self):

2 pass

L’istruzione pass permette di dichiarare un metodo lasciando il corpo vuoto.

Metodo plot

Questo metodo consente di effettuare il plot della curva, utilizzando i metodidella libreria matplotlib. Di default, insieme alla curva, viene visualizzatoil poligono di controllo da cui la curva stessa e stata generata. Nel caso sivolesse visualizzare solo la curva basta settare il parametro cpgrid a False.

1 def plot(self, cpgrid = True):

2

3 pl.plot(self.points[:, 0], self.points[:, 1])

4

5 if cpgrid:

6 pl.plot(self.cntrl[:, 0],self.cntrl[:, 1],’ro’)

7 pl.plot(self.cntrl[:, 0], self.cntrl[:, 1],’r--’)

8

9 return

Sovrascrittura operatore somma

La sovrascrittura dell’operatore somma consente di concatenare due curve.Sebbene il metodo sia molto semplice, i miei sforzi si sono concentrati sulriuscire a rendere questo metodo generico in modo da non doverlo riscrivereper ogni curva. L’idea di base e quella di “avvicinare” la seconda curva allaprima intervenendo sul vettore dei punti di controllo, per poi creare unanuova curva utilizzando i punti di controllo di entrambe.

1 def __add__(self, c):

2 if not(isinstance(c,type(self))):

3 raise TypeError("Il secondo operando deve essere un

istanza di {0}".format(type(self)))

4

5 other_curve = c.cntrl.copy()

6

7 # calcolo la differenza di posizione fra l’ultimo punto della

prima curva e il primo della seconda

8 diff = self.cntrl[-1] - other_curve[0]

9

10 # traslo i singoli punti della seconda curva

11 for pt in other_curve:

12 pt += diff

13

14 # creo un nuovo insieme di punti di controllo unendo i punti

di controllo delle due curve

Page 69: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.3. LA CLASSE CURVE 59

15 new_cntrl_points = np.append(self.cntrl,other_curve[1:],0).

view(Points)

16

17 # deepcopy un metodo di python (contenuto nel pacchetto

copy) che permette di duplicare un oggetto

18 from copy import deepcopy

19

20 new_curve = deepcopy(self)

21 new_curve.cntrl = new_cntrl_points

22

23 return new_curve

In primo luogo l’istruzione:

1 if not(isinstance(c,type(self))):

2 raise TypeError("Il secondo operando deve essere un

istanza di {0}".format(type(self)))

controlla che la curva che si vuole concatenare sia del tipo corretto e incaso contrario lancia un’eccezione per informare l’utente dell’errore. Sebbe-ne, per esempio, concatenare una curva di Bezier ad una B-Spline non siaerrato, dato che l’una e un caso particolare dell’altra, la scrittura di un co-dice che convertisse il tipo di curva in modo da adattarlo all’altra richiedevapiu tempo di quanto ce ne fosse a disposizione. L’istruzione other curve =

c.cntrl.copy() crea una copia dei punti di controllo della seconda curva.Se avessi utilizzato un semplice assegnamento (other curve = c.cntrl),python avrebbe assegnato i nomi delle due variabili allo stesso vettore, pervia del modo in cui gestisce le variabili ( vedere la sezione 3.1) quindi avreierronamente modificato anche i punti di controllo della seconda curva. Aquesto punto calcolo la differenza tra l’ultimo punto di controllo della primacurva e il primo punto di controllo della seconda, per poi utilizzare questadifferenza per “avvicinare” la seconda curva. Una volta ottenuta la conca-tenazione dei due vettori dei punti di controllo, creo una copia dell’oggettocorrente e modifico il vettore dei punti di controllo della copia. Ho utilizzatouna copia, invece di creare un nuovo oggetto, perche non posso sapere sel’oggetto corrente e una curva di Bezier, una B-Spline, una Nurbs o altro e,di conseguenza, non avrei potuto passare i parametri corretti al costruttore.La strada piu semplice quindi, e stata proprio quella di duplicare l’oggettocorrente, in modo da lasciare inalterati tutti i parametri e poi modificare ilvettore dei punti di controllo.

Metodo setattr

La sovrascrittura di questo metodo serve principalmente a proteggere alcu-ni attributi da eventuali tentativi di modifica manuale da parte dell’utente(vedere la sezione3.4). A meno che non ci sia codice particolare, le future so-

Page 70: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

60 CAPITOLO 4. CLASSI ED ALGORITMI

vrascritture di questo metodo non saranno illustrate essendo solo estensionidi questa implemetazioni ad attributi specifici delle altre classi.

1 def __setattr__(self,name,value):

2

3 if name not in (’npts’,’cntrl’) and self.__dict__.has_key(

name):

4 self.__dict__[name]=value

5 return

6

7 elif name is ’npts’:

8

9 if not isinstance(value,int):

10 raise TypeError("npts deve essere un intero")

11 else:

12 self.__dict__[name] = value

13 elif name is ’cntrl’:

14 if not (isinstance(value,Points) and value.ndim == self.

cntrl.ndim):

15 raise PyNurbsError("cntrl deve essere un istanza di

numpy.ndarray di %d dimensioni"%(self.cntrl.ndim))

16 else:

17 self.__dict__[name] = value

4.4 La classe Bezier

Ho sviluppato la classe Bezier in modo da permettere di calcolare una curvadi Bezier in due modi:

Tramite la definizione formale data dall’equazione (2.4);

Tramite l’algoritmo di De Casteljau.

4.4.1 Calcolo tramite definizione

L’algoritmo 1 consente di calcolare una curva di Bezier utilizzando la defini-zione formale. Per poter implementare questo algoritmo e necessario poter

Algoritmo 1 Bezier algoritmo base

1: per ogni valore del parametro t:2: P (t)← 03: per i che va da 0 a n:4: Calcola il polinomio di Bernstein Jn,i(t)5: P (t)← P (t) +BiJn,i(t)6: fine ciclo7: fine ciclo

Page 71: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.4. LA CLASSE BEZIER 61

calcolare i polinomi di base di Bernstein, per questo scopo ho implementatoall’interno della classe Bezier il metodo bernstein, che utilizza la funzionecomb della libreria scipy per calcolare i coefficienti binomiali

(nk

):

1 from scipy.misc import comb

2

3 def bernstein(self,n,i,t):

4 return comb(n, i)*pow(t, i)*pow(1-t, n-i)

Il codice e una trasposizione uno a uno dell’equazione (2.3). Il metodocalcWithBernstein consente di calcolare i punti della curva di Bezier conl’algoritmo 1:

1 def calcWithBernstein(self):

2 tvalues = np.linspace(0,1,self.npts)

3 for i,t in enumerate(tvalues):

4 self.points[i] = sum([self.cntrl[j]*self.bernstein(self.deg,j

,t) for j in xrange(self.deg+1)])

Per prima cosa ottengo il vettore contenente i valori del parametro in ba-se al numero di punti che si desidera calcolare tramite la funzione linspace,quindi per ognuno dei valori in tvalues calcolo il relativo punto, effettuan-do la somma degli elementi di un vettore contenente i prodotti dei puntidi controllo per le funzioni di base. L’algoritmo 1 implementa pari passo ladefinizione data dall’equazione (2.4). Il corpo del ciclo piu esterno viene ese-guito per un numero di volte pari al numero di valori del parametro t. Questovalore dipende quindi dal grado di precisione desiderata. Indichiamo conε il numero di punti della curva che si desidera ottenere: la valutazione delpolinomio di Bernstein viene quindi esguita εn volte. La valutazione di que-sto polinomio richiede il calcolo di tre fattoriali e due elevamenti a potenza.Con il crescere del numero di punti di controllo, l’algoritmo tende all’insta-bilita numerica. Se cio che serve e una bassa complessita computazionale,allora l’algoritmo 1 e una buona scelta, se invece cio che si desidera e unamaggiore precisione, esiste un altro algoritmo.

4.4.2 L’algoritmo di De Casteljau

L’algoritmo di De Casteljau e un algoritmo ricorsivo con una maggiore stabi-lita numerica rispetto all’algoritmo 1. Questa stabilita viene pero pagata daun costo computazionale piu alto. L’algoritmo effettua ricorsivamente del-le combinazioni convesse, partendo dai vertici del poligono di controllo, lecombinazioni ottenute vengono a loro volta combinate fino a che non rimaneun unico punto, che e un punto appartenente alla curva. Se indichiamo conPi,j il punto di controllo j-esimo all’i-esima chiamata ricorsiva, l’algoritmodi De Casteljau e descritto dalla seguente relazione di ricorrenza:

P0,j = Pj i = 0, 1, 2, . . . , nPi,j = (1− u)Pi−1,j + uPi−1,j+1 j = 0, 1, . . . , n− i (4.1)

Page 72: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

62 CAPITOLO 4. CLASSI ED ALGORITMI

Dall’equazione (4.1) e possibile ricavare l’algoritmo 2.

Algoritmo 2 De Casteljau ricorsivo

1: funzione deCasteljau(i, j, u)2: se i = 0 allora3: restituisci P0,j

4: altrimenti5: restituisci (1 − u) deCasteljau(i − 1, j, u) + u deCastel-

jau(i− 1, j + 1, u)6: fine se7: fine funzione

Questa stesura dell’algoritmo presenta un grosso problema, lo stesso chesi insegna ai primi corsi di programmazione prendendo come esempio l’al-goritmo ricorsivo per il calcolo della sequenza di Fibonacci. La strutturadi questa ricorsione porta al ricalcolare piu volte un elemento che probabil-mente era gia stato calcolato in precedenza. Utilizzando la programmazionedinamica, questo problema e facilmente aggirato. L’idea e di conservare ipunti di controllo in un vettore e di eseguire tutte le combinazioni convessesu quello stesso vettore, portando l’algoritmo da ricorsivo ad iterativo.

Algoritmo 3 De Casteljau iterativo

1: funzione deCasteljau2: P ← B . P e il vettore dei punti di controllo3: per i che va da 0 a n4: per j che va da 0 a n− i5: Pj = (1− u)Pj + uPj+1

6: fine ciclo7: fine ciclo8: restituisci P0 . P0 e il punto appartenente alla curva di Bezier9: fine funzione

La classe Bezier nel suo metodo deCasteljau utilizza la versione itera-tiva dell’algoritmo, cosı implementata:

1 def deCasteljau(self,cntrl,deg,t):

2

3 #inizializzo un vettore contenente una prima combinazione

convessa di parametro t dei punti di controllo

4 tmp = Points([cntrl[i].convexComb(cntrl[i+1],t) for i in xrange(

deg)])

5

6 #itero il calcolo delle combinazioni lineari fino ad ottenere un

unico punto

7 for i in xrange(deg -1):

8 for j in xrange(deg -1 -i):

Page 73: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.4. LA CLASSE BEZIER 63

9 tmp[j] = tmp[j].convexComb(tmp[j+1],t)

10

11 #tmp[0] contiene il punto appartenente alla curva di Bezier

12 return tmp[0]

Il metodo calculate invoca il metodo deCasteljau per ogni valore delparametro t in modo da calcolare l’intera curva:

1 def calculate(self):

2 self.__dict__[’points’] = Points([self.deCasteljau(self.cntrl,

self.deg,t) for t in np.linspace(0,1,self.npts)])

L’algoritmo 3 ha un costo computazionale approsimativamente pari adO(n2),piu alto rispetto a quello dell’algoritmo 1. Tuttavia, questo algoritmo ridu-ce tutto ad una serie di somme e prodotti, evitando il calcolo di fattoriali,potenze e divisioni, che facilitano l’amplificazione di errori. Un altro vantag-gio dell’algoritmo di De Casteljau e che i punti intermedi calcolati possonoessere utilizzati per creare due nuovi poligoni di controllo dal poligono origi-nale, il che significa dividere la curva in due parti, utilizzando il punto finalerestituito dall’algoritmo come punto di divisone fra i due segmenti. Questacaratteristica diventa utile quando si vuole semplificare la curva, lascian-do inalterata la forma ma utilizzando piu curve di grado piu basso. Se adesempio si ha una curva con otto punti di controllo, la curva ottenuta sara disettimo grado. Utilizzando l’algoritmo di De Casteljau si possono otteneredue curve con quattro punti di controllo ciascuna, ovvero due curve di terzogrado.

Esempio d’uso

Di seguito un breve esempio d’utilizzo della classe Bezier

1 >>> import numpy as np

2 >>> from Curve import Bezier

3 # istanzio una curva di Bezier specificando manualmente i punti di

controllo

4 >>> c = Bezier([(1,1),(4,4),(6,7),(10,1)],200)

5 # per calcolare la curva con l’algoritmo di de Casteljau invoco il

metodo calculate

6 >>> c.calculate()

7 # invece per utilizzare la definizione formale

8 >>> c.calcWithBernstein()

9 # per visualizzare la curva basta invocare il metodo plot

10 >>> c.plot()

Page 74: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

64 CAPITOLO 4. CLASSI ED ALGORITMI

4.5 La classe Spline

Questa classe modella una curva polinomiale a tratti utilizzando la notazionematriciale mostrata nella sezione 2.1.6. Sebbene questa implementazionepoteva essere fatta direttamente all’interno della classe BSpline, ho preferitoscrivere questa classe intermedia in modo da fornire una base su cui costruirecurve personalizzate. Se si vuole implementare il proprio tipo di splineutilizzando la notazione matriciale, basta estendere questa classe e definiregli opportuni parametri.

Costruttore

Il costruttore della classe prende in input i seguenti parametri:

cntrl vettore dei punti di controllo;

npts Numero di punti della curva che si vogliono calcolare;

rpp Parametro che indica il numero di righe della matrice di base che dannoinformazioni su un singolo punto posizione;

k Ordine della curva che si intende calcolare (grado + 1).

1

2 class Spline(Curve):

3 def __init__(self,cntrl,npts,rpp,k):

4

5 Curve.__init__(self,cntrl,npts)

6 self.__dict__[’k’] = k

7 self.__dict__[’rpp’] = rpp

8 self.__dict__[’coeff’] = np.array([[]],np.double).reshape

(0,2)

9 self.__dict__[’B’] = np.eye(self.k)

Inoltre il costruttore inizializza un vettore coeff che conterra i valori deicoefficienti della forma canonica, e una matrice di base B come matriceidentita k × k. Per creare il proprio tipo di spline personalizzato, bastaestendere la classe Spline modificando i parametri a proprio piacere.

Metodo Calculate

Questo metodo implementa un semplice algoritmo per calcolare la spline,ricavando prima i coefficienti della forma canonica per ogni tratto della curvaper poi utilizzarli per valutare i polinomi per i singoli tratti. Innanzitutto, ilmetodo inizializza un vettore points che dovra contenere i punti della curvauna volta che saranno stati calcolati,

Page 75: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.5. LA CLASSE SPLINE 65

1 def calculate(self):

2

3 self.points = np.zeros((self.npts,2))

quindi utilizza un ciclo per contare il numero di tratti di cui e compostala curva e determinare i coefficienti per ogni tratto,

1 numTratti = 0

2 for i in xrange(0,len(self.cntrl)-self.k+1,self.rpp):

3 numTratti+=1

4 self.coeff = np.append(self.coeff,np.dot(self.B,self.

cntrl[i:i+self.k]),0)

I coefficienti per il tratto i-esimo sono ottenuti effettuando il prodotto frala matrice di base e il sottovettore dei punti di controllo contenente i puntidall’i-esimo al i+ k-esimo.

Determino quindi il passo di discretizzazione eps, dividendo il numerodi tratti per il numero di punti della curva da ricavare (sebbene questometodo non sia preciso, il numero di punti calcolati alla fine e nell’intornodel numero richiesto) e utilizzo eps come parametro per la funzione arange

fornita da numpy. Questa funzione e simile a linspace, con la differenzache restituisce gli elementi di un intervallo aperto, in questo caso [0, 1[. Cioe necessario per evitare di considerare due volte i punti che separano untratto dall’altro.

1 eps = float(numTratti)/self.npts

2

3 upts = np.arange(0,1,eps)

In questo segmento di codice, per ogni tratto, valuto il polinomio in u (laforma canonica) utilizzando i relativi coefficienti. L’uso della variabile dicomodo c e stata necessaria in quanto lo slicing (vedere la sezione 3.2)[i*self.k:i * self.k + self.k], necessario per determinare il sottoin-sieme dei coefficienti relativi al tratto k-esimo, non funzionava utilizzandodirettamente i*self.k.

1 pts = np.array([[]],np.double).reshape(0,2)

2 for i in xrange(numTratti):

3 c = i*self.k

4 pts = np.append(pts,[np.dot(upoly(u,self.k),self.coeff[c:

c+self.k]) for u in upts],0)

5 self.points = pts

6 return

La funzione upoly (definita nel modulo util contenente alcune funzioni nonstrettamente correlate ad alcuna delle classi sviluppate) e una funzione dicomodo che restituisce un array di potenze di u, cosı definita:

Page 76: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

66 CAPITOLO 4. CLASSI ED ALGORITMI

1 def upoly(u,k):

2 try:

3 u = float(u)

4 k = int(k)

5 except ValueError as detail:

6 print "Errore di tipo: ",detail

7 return

8 return np.asarray([pow(u,i) for i in xrange(k-1,1,-1)]+[u,1],np.

double)

Per utilizzare questa funzione e necessario specificare il valore di u e il nu-mero di termini k (compreso il termine noto quindi) che si desidera ottenere.Il vettore ottenuto tramite questa funzione viene moltiplicato per il vettoredei coefficienti ottenendo il punto appartenente alla curva.

4.6 Esempi di estensione di Spline

In questo paragrafo verranno illustrati alcuni esempi di estensione della clas-se Spline. Essa verra utilizzata per implementare le spline mostrate nellasezione 2.1.8.

4.6.1 Spline cubica naturale

La spline cubica naturale (vedere la sezione 2.1.8) si ottiene specificandoposizione, derivata prima e derivata seconda del punto iniziale di un trattoe la posizione del punto finale. La matrice di base pertanto, contiene trerighe che fanno riferimento ad un unico punto posizione, il valore di rpp equindi tre. La curva desiderata e una curva di terzo grado, ergo il valore dik e quattro.

1 class NaturalCubicSpline(Spline):

2 def __init__(self,cntrl,npts):

3

4 Spline.__init__(self,cntrl,npts,3,4)

5 self.__dict__[’B’] = np.array

([[-1,-1,-.5,1],[0,0,.5,0],[0,1,0,0],[1,0,0,0]],np.double

)

Dato che il vettore cntrl non contiene solo punti di controllo “posiziona-li”, ma anche i versori delle derivate prima e seconda, e stato necessariosovrascrivere il metodo plot. Per prima cosa creo tre vettori distinti checontengono rispettivamente i punti posizione, i versori della derivata primae quelli della derivata seconda.

1 def plot(self):

2 positionPoints = self.cntrl[::self.rpp]

3 firstDerivatives = self.cntrl[1::self.rpp]

Page 77: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.6. ESEMPI DI ESTENSIONE DI SPLINE 67

4 secondDerivatives = self.cntrl[2::self.rpp]

Quindi effettuo il plot della curva:

1 pl.plot(self.points[:,0],self.points[:,1])

L’istruzione crea un’istanza di matplotlib. Con pl.gca() recupero quest’i-stanza in modo da aggiungervi delle frecce che mostrino le derivate, passandoal metodo add patch oggetti di tipo pl.Arrow.

1 ax = pl.gca()

2

3 for pt,d1,d2 in zip(positionPoints,firstDerivatives,

secondDerivatives):

4 ax.add_patch(pl.Arrow(pt[0],pt[1],d1[0],d1[1],label=’d1’,

color=’r’,width=0.2,alpha=0.3))

5 ax.add_patch(pl.Arrow(pt[0],pt[1],d2[0],d2[1],label=’d2’,

color=’g’,width=0.2,alpha=0.3))

6

7 return

4.6.2 Spline di Hermite

La spline di Hermite (vedere la sezione 2.1.8) si ottiene specificando posizionee derivata prima per ogni punto di controllo, quindi il valore di rpp e due,mentre per ottenere una curva di terzo grado il valore di k deve esserequattro.

1 class HermiteSpline(Spline):

2 def __init__(self,cntrl,npts):

3 Spline.__init__(self,cntrl,npts,2,4)

4 self.__dict__[’B’] = np.array

([[2,1,-2,1],[-3,-2,3,-1],[0,1,0,0],[1,0,0,0]],np.double)

Anche qui e stato sovrascrito il metodo plot, che non verra mostrato inquanto molto simile a quello della classe NaturalCubicSpline.

4.6.3 Spline cardinale

La spline cardinale (vedere la sezione 2.1.8) si ottiene specificando i punti diposizione e un parametro di “tensione”, la matice di base e fatta in mododa determinare automaticamente le tangenti al punto iniziale e finale deltratto. Qui ogni riga della matrice riguarda un diverso punto di controllo,quindi il valore di rpp e uno, mentre come per le altre spline, per ottenereuna curva di terzo grado il valore di k deve essere 4.

1 class CardinalSpline(Spline):

2 def __init__(self,cntrl,npts,t):

3 Spline.__init__(self,cntrl,npts,1,4)

Page 78: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

68 CAPITOLO 4. CLASSI ED ALGORITMI

4 s = (1-t)/2

5 self.__dict__[’B’] = np.array([[-s,(2-s), (s-2), s],[2*s,(s

-3),(3-2*s),-s],[-s,0,s,0],[0,1,0,0,]],np.double)

4.7 La classe BSpline

Questa classe estende la classe spline per modellare una b-spline. Il calcolodella b-spline viene effettuato in due modi, tramite la definizione e tramitenotazione matriciale, come spiegato nella sezione 2.2.2. Al costruttore vannopassati i seguenti parametri:

cntrl Vettore dei punti di controllo;

npts Numero di punti della curva da calcolare;

k Ordine della B-Spline;

x Vettore dei nodi.

1 class BSpline(Spline):

2 def __init__(self,cntrl,npts,k,x):

3

4 Spline.__init__(self,cntrl,npts,1,k)

5 self.__dict__[’n’] = len(self.cntrl)

6 self.__dict__[’x’] = self.knot(x,self.k,self.n)

7 self.__dict__[’B’] = None

self.B e utilizzata nel caso venga utilizzata la notazione matriciale permemorizzare la matrice di base.

4.7.1 Calcolo da definizione

Calcolare una b-spline utilizzando la definizione richiede il poter calcolarele funzioni di base. Per comodita viene anche fornito un metodo per calco-lare due tipi standard di vettore dei nodi, l’uniforme periodico e l’uniformeaperto.

Metodo knot

Il parametro x passato al costruttore oltre poter essere un vettore decisodall’utente, puo assumere come valori le stringhe “periodic” e “open”. Ilmetodo knot prende in input i seguenti parametri:

x Stringa indicante il tipo di vettore dei nodi o vettore dei nodi esplicito;

k Ordine della B-spline;

Page 79: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.7. LA CLASSE BSPLINE 69

n Numero di punti di controlo.

In questo caso viene calcolato un vettore dei nodi uniforme del tipo indicato,come illustrato nella sezione 2.2.2. In primo luogo determino la lunghezzadel vettore pari all’ordine della b-spline piu il numero di punti di controllo.

1 def knot(self,x,k,n):

2 knotlen = k + n

Nel caso il valore di x sia “periodic”, restituisco un’istanza di ndarray

contenente gli interi da 0 a n+ k − 1.

1 if x is ’periodic’:

2 return np.array(range(knotlen+1),np.double)

Se invece il valore di x e “open”, calcolo prima la parte sinistra e la partedestra del vettore, contenenti rispettivamente il valore minimo e il valoremassimo del parametro con molteplicita k.

1 elif x is ’open’:

2 left = [0]*k

3 right = [n-k+1]*k

Quindi calcolo la parte centrale dell’array, per poi concatenare le tre parti.

1 center = range(1,right[0])

2 return np.array(left+center+right)

Se invece x e un vettore dei nodi personalizzato, controllo che sia un’istanzadi una lista, una tupla o un ndarray, in caso contrario lancio un eccezione.

1 elif not isinstance(x,(list,tuple,np.ndarray)):

2 raise TypeError("Il vettore dei knot deve essere passato

sotto forma di lista, tupla o numpy.ndarray")

Se il controllo ha successo, si effettua un ulteriore controllo per verificareche gli elementi del vettore siano valori numerici, in caso positivo tali valorivengono ordinati (si ricordi che l’unica condizione per un vettore di nodi eche xi ≤ xi+1).

1 else:

2 for element in x:

3 if not isinstance(element,(int,float)):

4 raise TypeError("Il vettore knot deve essere

composto di interi o float")

5 x.sort()

6 return x

Page 80: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

70 CAPITOLO 4. CLASSI ED ALGORITMI

Metodo computeBasis

Il metodo computeBasis calcola le funzioni di base della b-spline utilizzandola ricorsione di Cox-De Boor (equazione (2.6)). Nel testo [19] e presenteuna funzione scritta in linguaggio C che calcola l’equazione (2.6) in modoiterativo. La funzione prende in input i seguenti parametri:

n Il numero di punti di controllo;

k Ordine della B-spline;

x Il vettore dei nodi;

t Valore del parametro.

Si inizia definendo un vettore temp che alla posizione i-esima contiene 1se xi ≤ t < xi+1, 0 altrimenti, ovvero le funzioni di base di ordine k = 1.

1 def computeBasis(self,n,k,x,t):

2

3 numKnots = len(x)

4

5 temp = np.array([1 if t>=x[i] and t < x[i+1] else 0 for i in

xrange(numKnots - 1)],np.double)

Quindi inizia un processo iterativo che calcola le funzioni di base dal grado2 al grado k-esimo.

1 for k in xrange(2,k+1):

2 for i in xrange(numKnots-k):

Se la relative funzioni di base sono diverse da 0 calcolo i due addendi, term1e term2, altrimenti li pongo pari a 0, infine li sommo:

1 if temp[i]:

2 term1 = ( (t - x[i]) / (x[i+k-1] - x[i])) * temp[i]

3 else:

4 term1 = 0

5 if temp[i+1]:

6 term2 = ((x[i+k] - t) / (x[i+k] - x[i+1])) * temp[i+1]

7 else:

8 term2 = 0

9

10 temp[i] = term1 + term2

Infine verifico se il valore del parametro t e pari al valore massimo, in questocaso l’ultimo valore deve essere 1.

1 if t == x[-1]:

2 temp[n-1] = 1

3 return temp[:n]

Page 81: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.7. LA CLASSE BSPLINE 71

Metodo calculate

Il metodo calculate la curva B-spline applicando la definizione data dall’e-quazione (2.5). Si determinano i valori del parametro t con linspace (notareche t varia da xk−1 a xn, e non da x0 a xn+k−1). Dopodiche per ogni valoredi t si calcola il vettore N contenente le funzioni di base e si determina ilpunto effettuando il prodotto vettore fra il vettore dei punti di controllo e ilvettore N.

1 def calculate(self):

2

3 self.points = np.zeros((self.npts, 2))

4 tvalues = np.linspace(self.x[self.k - 1],self.x[self.n],self.

npts)

5

6 for i,t in enumerate(tvalues):

7 N = self.computeBasis(self.n, self.k, self.x, t)

8 self.points[i] = np.dot(N, self.cntrl)

4.7.2 Calcolo con notazione matriciale

La notazione matriciale per il calcolo delle B-spline illustrata nella sezione2.2.2, con alcuni accorgimenti, puo presentare diversi vantaggi rispetto al-l’approccio funzionale. Le funzioni di basi b-spline dipendono dal valore delparametro t oltre che dall’ordine k e dal vettore dei nodi. Cio vuol dire chese si utilizzano valori diversi del parametro per calcolare due volte la stes-sa curva, le funzioni di base vanno ricalcolate. Utilizzando una notazionematriciale, la matrice di base e il vettore delle potenze del parametro sonodue entita indipendenti l’una dall’altra. Se quindi, una volta calcolata unamatrice di base per un certo ordine k, essa puo essere salvata per essereriutilizzata in seguito, permettendo di risparmiare parecchi calcoli.

Metodo computePeriodicMatrix

Il primo passo e quello di calcolare la matrice di base. Questo metodosi occupa proprio di questo, dapprima controllando se la matrice relativaall’ordine k richiesto e stata gia calcolata in precedenza, in caso negativotale matrice viene calcolata da zero. Nel seguente segmento di codice, epossibile vedere come si tenti a priori di caricare la da file la matrice diordine k: se non si verificano errori di lettura, allora la matrice e statacaricata correttamente e il metodo si ferma.

1 def computePeriodicMatrix(self,k):

2

3 fileName = ’order_’+str(k)+’_periodic_BSpline_Basis_Matrix.

npy’

4

Page 82: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

72 CAPITOLO 4. CLASSI ED ALGORITMI

5 try:

6 B = np.load(fileName)

7

8 except IOError as detail:

9 pass

10 else:

11 return B

Se invece la matrice non e presente fra quelle salvate su disco, si utilizzauna funzione illustrata sul testo [19] per il calcolo dell’equazione (2.8), quiriportata per comodita:

Bi,j =1

(k − 1)!

(k − 1

i

) k−1∑l=j

(k − (l + 1))i(−1)l−j(

k

l − j

)0 ≤ i, j ≤ k − 1

(4.2)

Osservando l’equazione si nota che e presente un fattore moltiplicativo, 1(k−1)!

che non dipende ne dall’indice i ne dall’indice j. Come prima cosa quindi,dopo aver inizializzato B come matrice k × k ed aver preparato i vettoricontenenti gli indici di righe e colonne, calcolo e conservo in una variabile ilfattore moltiplicativo 1

(k−1)! :

1 B = np.zeros((k,k))

2

3 rows = cols = range(k)

4

5 fact_coeff = 1/fact(k-1)

si noti che l’equazione (4.2) puo essere spezzata in due parti:

a =1

(k − 1)!

(k − 1

i

)b =

k−1∑l=j

(k − (l + 1))i(−1)l−j(

k

l − j

)Bi,j =a · b

(4.3)

Nel ciclo for piu esterno calcolo la parte a, mentre in quello piu interno laparte b:

1 for i in rows:

2 a = fact_coeff * comb(k-1,i)

3 for j in cols:

4 b=0

5 for l in range(j,self.k):

6 b += pow((self.k - (l+1)),i)*pow(-1,l-j)*comb(self

.k,l-j)

7 B[i,j] = a*b

Page 83: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.7. LA CLASSE BSPLINE 73

Una volta che la matrice e stata calcolata, la salvo per poterla riutilizzarein futuro.

1 np.save(fileName,B)

2 return B

Metodo calculateWithMatrixNotation

Il metodo calculateWithMatrixNotation determina la curva utilizzandoil metodo calculate della classe Spline. Prima di richiamare tale metodopero, il codice controlla se e il caso di aggiornare (o di calcolare se non estato ancora fatto) la matrice di base, verificando che la forma della matriceattualmente in memoria sia pari all’ordine attuale della curva.

1 def calculateWithMatrixNotation(self):

2 # r,c sono pari al numero di righe e colonne della matrice B

3 # se essa un istanza di ndarray, altrimenti valgono

4 # entrambi -1

5 r,c = self.B.shape if isinstance(self.B,np.ndarray) else

(-1,-1)

6

7 # se r e c sono diversi da k

8 if (r,c) != (self.k,self.k):

9 # ricalcola la matrice di base

10 self.__dict__[’B’] = self.computePeriodicMatrix(self.k)

11

12 Spline.calculate(self)

13 return

Esempio d’uso

Di seguito un breve esempio d’utilizzo della classe BSpline.

1 >>> import numpy as np

2 >>> from Curve import BSpline

3 # creo una B-Spline di quarto ordine che utilizza un vettore dei

nodi aperto.

4 >>> c = BSpline([(1,1),(4,4),(6,8),(10,1)],200,4,’open’)

5 # volendo possibile passare il vettore dei nodi esplicitamente

come parametro

6 >>> c = BSpline([(1,1),(4,4),(6,8),(10,1)],200,4,[0,0,0,0,1,1,1,1])

7 # per effettuare il calcolo tramite definizione formale

8 # si invoca il metodo calculate

9 >>> c.calculate()

10 # se invece si vuole utilizzare la notazione matriciale:

11 >>> c.calculateWithMatrixNotation()

12 # per visualizzare la curva ottenuta basta invocare il metodo plot

13 >>> c.plot()

Page 84: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

74 CAPITOLO 4. CLASSI ED ALGORITMI

4.8 La classe Nurbs

La classe Nurbs estende la classe BSpline per poter calcolare B-Spline razio-nali. Per effettuare il calcolo, la classe fa uso delle funzioni di base definitedall’equazione 2.15.

Costruttore

Il costruttore della classe Nurbs prende in input i seguenti parametri:

cntrl Vettore dei punti di controllo;

npts Numero di punti della curva da calcolare;

k Ordine della spline;

x Vettore dei nodi;

h Vettore contenente i pesi relativi ai punti di controllo.

Gli unici controlli che il costruttore effettua riguardano il tipo del parametroh (che deve essere una lista, una tupla oppure un’istanza di ndarray) e lalunghezza del vettore che deve essere pari a quella del vettore dei punti dicontrollo.

1 class Nurbs(BSpline):

2

3 def __init__(self,cntrl,npts,k,x,h):

4

5 BSpline.__init__(self,cntrl,npts,k,x)

6

7 if not isinstance(h,(list,tuple,np.ndarray)):

8 raise TypeError("h deve essere una lista, una tupla o un

numpy.ndarray, non di tipo {0}\n".format(type(h)))

9 elif len(h) != len(self.cntrl):

10 raise ValueError("h ha lunghezza {0} mentre cntrl ha

lunghezza {1}".format(len(h),len(self.cntrl)))

11 self.__dict__[’h’] = h

Metodo computeBasis

Il metodo computeBasis consente di calcolare le funzioni di base razionaliutilizzando l’equazione (2.15). Come primo passo calcolo le funzioni di basenon razionali:

1 def computeBasis(self,n,k,x,h,t):

2

3 temp = BSpline.computeBasis(self,n,k,x,t)

Page 85: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.8. LA CLASSE NURBS 75

Quindi determino un vettore temp il cui elemento i-esimo e pari al prodottodel peso i-esimo per la funzione di base i-esima.

1 temp = np.asarray([h[i]*basis for i,basis in enumerate(temp)

])

Quindi divido ogni elemento di temp per la somma degli elementi, ottenendole basi razionali.

1 temp = temp/sum(temp)

2 return temp

Metodo calculate

Il metodo calculate e identico a quello della classe BSpline, ma e stato ne-cessario sovrascriverlo in quanto e cambiata la firma del metodo computeBasische ora include tra i parametri anche il vettore dei pesi:

1 def calculate(self):

2 tvalues = np.linspace(self.x[self.k - 1],self.x[self.n],self.

npts)

3

4 for i,t in enumerate(tvalues):

5 N = self.computeBasis(self.n,self.k,self.x,self.h,t)

6 self.points[i] = np.dot(N,self.cntrl)

Sovrascrittura operatore somma

Per far funzionare l’operatore di somma anche per le b-spline razionali, oltreai vettori dei punti bisogna concatenare anche i vettori dei pesi.

1 def __add__(self,c):

2 new_curve = Curve.__add__(self,c)

3 new_curve.h = np.append(self.h,c.h[1:])

4 return new_curve

Esempio d’uso

Di seguito un breve esempio d’utilizzo della classe BSpline.

1 >>> import numpy as np

2 >>> from Curve import Nurbs

3 # creo una curva Nurbs di ordine 4, vettore dei nodi aperto

4 # con pesi [1,3,3,1]

5 >>> c = Nurbs([(1,1),(4,4),(6,8),(10,1)],200,4,’open’,[1,3,3,1])

6 # per effettuare il calcolo

7 >>> c.calculate()

8 # per visualizzare la curva ottenuta basta invocare il metodo plot

9 >>> c.plot()

Page 86: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

76 CAPITOLO 4. CLASSI ED ALGORITMI

4.9 La classe CurveFit

La classe CurveFit permette di interpolare un insieme di punti utilizzandole tecniche illustrate nella sezione 2.2.3.

Costruttore

Il costruttore della classe CurveFit prende in input i seguenti parametri:

curvePoints Vettore contenente i punti per i quali deve passare la curva;

numCpoints Numero di punti di controllo da calcolare;

npts Numero di punti della curva da calcolare;

k Ordine della B-Spline.

Il vettore dei nodi utilizzato e di tipo uniforme aperto, in modo che ancheil primo e l’ultimo punto vengano interpolati.

1 class CurveFit(BSpline):

2 def __init__(self,curvePoints,numCpoints,npts,k):

3

4 BSpline.__init__(self,np.zeros((numCpoints,2)),npts,k,’open’)

5 if isinstance(curvePoints,str):

6 try:

7 self.__dict__[’curvePoints’] = np.loadtxt(curvePoints)

8 except IOError as detail:

9 print "Errore nel leggere dal file {0}: {1}".format(

curvePoints,detail)

10 raise

11 else:

12 self.__dict__[’curvePoints’] = np.asarray(curvePoints).

view(Points)

Una volta fatti i controlli sull’input determino i punti di controllo invocandoil metodo getControlPoints.

1 self.__dict__[’cntrl’] = self.getControlPoints()

Metodo getParametersValue

Come visto nella sezione 2.2.3 per poter determinare i vertici del poligonodi controllo e necessario stimare i valori del parametro t associati ai puntinoti della curva. Il metodo getParametersValue utilizza l’equazione (2.10).L’unico input del metodo e il parametro pvec che contiene i punti della curva

Page 87: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.9. LA CLASSE CURVEFIT 77

noti 1. In primo luogo il metodo determina il numero n di punti della curvanoti e inizializza un vettore t di n zeri:

1 def getParametersValue(self,pvec):

2 n = len(pvec)

3 t = np.zeros(n,np.double)

a questo punto sommo le distanze dal secondo punto (t0 deve essere pari a0, vedere eq (2.10)) all’ultimo:

1 for i in range(1,n):

2 t[i] = t[i-1] + pvec[i].distance(pvec[i-1])

infine divido tutti gli elementi del vettore per il valore massimo, in modoche i valor del parametro siano compresi nell’intervallo [0, 1].

1 t = t/t[-1]

2 return t

Metodo getNMatrix

Questo metodo determina la matrice contenente le funzioni di base. Il me-todo prende in input il vettore dei nodi, il valore del parametro t e il numerodi punti di controllo da calcolare. Il primo passo e quello di scalare il vettoredei nodi nell’intervallo [0, 1]. Dato che c’e la possibilita che il vettore deinodi contenga valori interi e non in virgola mobile, effettuo una sorta di cast,che trasforma x in un’istanza di ndarray i cui valori sono di tipo double.

1 def getNMatrix(self,x,t,n):

2

3 x = np.asarray(x,np.double)

4 x = x / x[-1]

Determino quindi il numero numPoints di punti della curva noti trami-te la funzione len di Python ed inizializzo la matrice N come matricenumPoints× n di zeri:

1 numPoints = len(t)

2 N = np.zeros((numPoints,n))

Infine calcolo le righe che compongono la matrice invocando il metodocomputeBasis

1 for i in range(numPoints):

2 N[i] = self.computeBasis(n,self.k,x,t[i])

3 return N

1e stato utilizzato un parametro e non un riferimento interno a self.curvePoints inmodo da poter riutilizzare il metodo anhce per la classe SurfaceFit dove e presente unamatrice di punti e non un vettore.

Page 88: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

78 CAPITOLO 4. CLASSI ED ALGORITMI

Metodo getControlPoints

Questo metodo si occupa di determinare i vertici del poligono di controllo concui generare la curva interpolante, risolvendo il sistema di equazioni (2.9)con il metodo solve, presente nel pacchetto linalg della libreria numpy.Per prima cosa determino il vettore contenente i valori del parametro e lamatrice con le funzioni di base (L’assegnazione D = self.curvePoints estata fatta solo per migliorare la leggibilita del codice).

1 def getControlPoints(self):

2 t = self.getParametersValue(self.curvePoints)

3

4 N = self.getNMatrix(self.x, t, self.n)

5 D = self.curvePoints

Controllo se la matrice N e quadrata, in caso contrario moltiplico sia N cheD per NT , come da equazione (2.12):

1 r,c = N.shape

2

3 if(r != c):

4 D = np.dot(N.T,D)

5 N = np.dot(N.T,N)

a questo punto inizializzo la matrice B come matrice n×2 di zeri, e tento dirisolvere il sistema [N ][B] = [D]. Il metodo solve potrebbe lanciare un’ec-cezione nel caso la matrice N si riveli essere singolare, pertanto racchiudola riga di codice per la risoluzione del sistema in un blocco try / except:

1 B = np.zeros((self.n,2))

2

3 try:

4 B = np.linalg.solve(N,D)

5

6 except np.linalg.LinAlgError as detail:

7 print "Errore: la matrice N singolare: {0}".format(

detail)

8

9 return B

Esempio d’uso

Di seguito un breve esempio d’uso della classe CurveFit

1 >>> import numpy as np

2 >>> from Curve import CurveFit

3

4 # creo un istanza di CurveFit passando come parametro

5 # i punti da approssimare e il numero di punti di

6 # controllo da determinare

Page 89: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.10. LA CLASSE SURFACE 79

7 >>> c = CurveFit([(4,4),(7,6),(12,4),(8,2),(4,4)],5,200,4)

8 # a questo punto i punti di controllo sono gia’ stati

9 # determinati, devo solo calcolare la curva

10 >>> c.calculate()

11 # e visualizzarla

12 >>> c.plot()

4.10 La classe Surface

La classe Surface serve ad aggiungere e modificare alcuni parametri dellaclasse Curve. Una classe che vuole modellare una superficie quindi, deveestendere sia la classe Curve (o una sua sottoclasse) che la classe Surface,ed invocare i rispettivi costruttori.

Costruttore

Il costruttore della classe Surface prende in input i seguenti parametri:

cntrl Matrice dei punti di controllo;

npts Numero di punti di controllo lungo la direzione u;

mpts Numero di punti di controllo lungo la direzione w.

Il costruttore controlla che la matrice dei punti di controllo abbia tre di-mensioni e re-inizializza il vettore points, che dovra contenere i punti dellasuperficie, come matrice npts ·mpts× 3

1 class Surface:

2

3 def __init__(self, cntrl, npts, mpts):

4

5 if not(self.cntrl.ndim == 3):

6 raise PyNurbsError("I punti di controllo della superficie

devono essere tridimensionali")

7

8 self.__dict__[’mpts’] = mpts

9

10 self.__dict__[’points’] = np.zeros((self.npts * self.mpts, 3)

).view(Points)

Metodo plot

La classe Surface offre inoltre un metodo per il plot delle superfici. Il meto-do recupera dalla matrice self.points tre vettori, uno per ogni coordinata,e trasforma questi vettori in matrici npts×mpoints.

Page 90: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

80 CAPITOLO 4. CLASSI ED ALGORITMI

1 def plot(self, plotcp=False, eps=1, dpi=100):

2 X = self.points[:, 0].reshape(self.npts, self.mpts)

3 Y = self.points[:, 1].reshape(self.npts, self.mpts)

4 Z = self.points[:, 2].reshape(self.npts, self.mpts)

Quindi si istanzia la classe figure offerta dalla libreria matplotlib, questooggetto conterra il plot della superficie.

1 fig = pl.figure(1, dpi = dpi)

2 ax = Axes3D(fig)

3

4 ax.plot_surface(X, Y, Z,cstride = eps, rstride = eps)

Opzionalmente si puo settare il parametro plotcp a True per visualizzaresul plot anche il poliedro di controllo.

1 if(plotcp):

2 ax.plot_wireframe(self.cntrl[:, :, 0], self.cntrl[:, :,

1], self.cntrl[:, :, 2], color="#cc0000")

3 pl.show()

4 return

4.11 La classe BezSurf

La classe BezSurf permette di calcolare una superficie di Bezier utilizzandol’equazione (2.16). Essa estende le classi Bezier e Surface, in modo da po-ter utilizzare il metodo bernstein contenuto nella classe Bezier ed ottenerei parametri aggiuntivi e le modifiche contenute nella classe Surface.

Costruttore

Il costruttore prende in input i seguenti parametri:

cntrl Matrice dei punti di controllo;

npts Numero di linee parametriche da calcolare lungo la direzione u;

mpts Numero di linee parametriche da calcolare lungo la direzione w.

Vengono richiamati i costruttori delle classi Bezier e Surface, all’internodei quali vengono effettuati i controlli dell’input:

1 class BezSurf(Bezier, Surface):

2 def __init__(self, cntrl, npts, mpts):

3

4 Bezier.__init__(self, cntrl, npts)

5 Surface.__init__(self, cntrl, npts, mpts)

Page 91: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.11. LA CLASSE BEZSURF 81

Quindi determino il numero di punti di controllo e i gradi lungo le direzioniu e w:

1 self.__dict__[’n’], self.__dict__[’m’] = self.cntrl.shape[:2]

2

3 self.__dict__[’u_degree’] = self.n - 1

4 self.__dict__[’w_degree’] = self.m - 1

Il metodo calcWithBernstein

L’algoritmo 4 mostra come calcolare una superficie di Bezier utilizzando ladefinizione formale.

Algoritmo 4 Calcolo di una superficie di Bezier

1: per ogni valore del parametro u:2: per ogni valore del parametro w:3: S(u,w)← 04: per i che va da 0 a n:5: Calcola il polinomio di Bernstein Jn,i(u)6: per j che va da 0 a m:7: Calcola il polinomio di Bernstein Km,j(w)8: S(u,w)← S(u,w) +Bi,jJn,i(u)Km,j(w)9: fine ciclo

10: fine ciclo11: fine ciclo12: fine ciclo

Il metodo calcWithBernstein implementa l’algoritmo 4: dopo averazzerato il vettore points, vengono calcolati i valori dei parametri u e w:

1 def calcWithBernstein(self):

2

3 self.points = np.zeros((self.npts * self.mpts, 3))

4

5 upts = np.linspace(0, 1, self.npts)

6 wpts = np.linspace(0, 1, self.mpts)

Dato che i punti non sono organizzati in una matrice npts×mpts ma in unvettore di lunghezza npts ·mpts, utilizzo un contatore, denominato iCount,come indice per i punti da calcolare.

1 iCount = 0

2

3 for u in upts:

4 for w in wpts:

5

6 for i in xrange(self.n):

Page 92: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

82 CAPITOLO 4. CLASSI ED ALGORITMI

7

8 jni = self.bernstein(self.u_degree, i, u)

9

10 for j in xrange(self.m):

11

12 kmj = self.bernstein(self.w_degree, j, w)

13

14 self.points[iCount] += self.cntrl[i, j] * jni

* kmj

15 iCount+=1

Esempio d’uso

Segue un breve esempio d’utilizzo della classe BezSurf.

1 >>> import numpy as np

2 >>> from Surface import BezSurf

3 # per una questione di spazio non faccio un esmepio completo

4 # dei punti di controllo

5 >>> cntrlPts = [[ (0,0,0), (5,0,5), ..., ],[ (0,5,0), (5,5,5), ...]]

6 # creo un istanza di BezSurf che calcoli 20 linee parametriche in

ogni direzione

7 >>> s = BezSurf(cntrlPts, 20, 20)

8 # calcolo la superficie

9 >>> s.calcWithBernstein()

10 # e la visualizzo

11 >>> s.plot()

4.12 La classe BSplineSurf

La classe BSplineSurf permette di calcolare una superficie B-Spline siautilizzando la definizione formale (data dall’equazione (2.17)), sia utiliz-zando la notazione matriciale (vedere (2.18)). La classe estende la classeBSpline, in modo da poter utilizzare il knot per il calcolo dei vettori deinodi, il metodo computeBasis per calcolare le funzioni di base e il metodocomputePeriodicMatrix per poter determinare le matrici di base per lanotazione matriciale.

4.12.1 Calcolo tramite definizione

L’algoritmo utilizzato in questo metodo e simile all’algoritmo 4. La differen-za e che, dato che il metodo computeBasis restituisce un vettore contenentele funzioni di base relative ad ogni punto di controllo, e possibile utilizzare ilprodotto vettore per calcolare il singolo punto della superficie. Vi e un pro-blema di compatibilita di dimensioni fra il vettore delle funzioni di base e ilvettore dei punti di controllo, per cui le coordinate (x, y, z) vengono trattate

Page 93: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.12. LA CLASSE BSPLINESURF 83

separatamente. In primo luogo azzero il vettore dei punti della superficie edetermino i valori dei parametri u e w:

1 def calculate(self):

2

3 self.points = np.zeros((self.npts * self.mpts, 3)).view(

Points)

4

5 uvalues = np.linspace(self.x[self.k - 1], self.x[self.n],

self.npts)

6 wvalues = np.linspace(self.y[self.l - 1], self.y[self.m],

self.mpts)

Dato che i punti della superficie sono memorizzati in un vettore di punti enon in una matrice, utilizzo un contatore ptCount per determinare qualepunto della superficie si sta calcolando. Per ogni valore dei parametri u e wcalcolo le funzioni di base:

1 ptCount = 0

2 for u in uvalues:

3

4 u_basis = self.computeBasis(self.n, self.k, self.x, u)

5

6 for w in vvalues:

7 w_basis = self.computeBasis(self.m, self.l, self.y, w)

Quindi determino separatamente le coordinate x, y e z, effettuando il pro-dotto vettore fra il vettore delle funzioni di base per il parametro u, lacolonna della matrice self.cntrl contenente la coordinata e il vettore dellefunzioni di base per il parametro w:

1 x = np.dot(u_basis, np.dot(self.cntrl[:,:,0], w_basis)

)

2 y = np.dot(u_basis, np.dot(self.cntrl[:,:,1], w_basis)

)

3 z = np.dot(u_basis, np.dot(self.cntrl[:,:,2], w_basis)

)

4 self.points[ptCount] = Points([[x,y,z]])

5 ptCount+=1

4.12.2 Calcolo con notazione matriciale

Il metodo calculateWithMatrixNotation permette di calcolare una su-perficie B-Spline utilizzando la notazione matriciale, come illustrato nellasezione 2.3.2. La caratteristica di questo metodo di calcolo e che la super-ficie non viene calcolata subito per intero, ma viene calcolata “a pezzi”,utilizzando delle sotto maglie k × l di punti di controllo. Non avendo tro-vato algoritmi pronti ho stilato una bozza di algoritmo (algoritmo 5) per

Page 94: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

84 CAPITOLO 4. CLASSI ED ALGORITMI

calcolare la superficie B-Spline, basandomi sulle equazioni (2.18) e (2.20).Nell’implementare l’algoritmo 5 in python l’unica modifica fatta riguarda i

Algoritmo 5 Calcolo di una superficie B-Spline con notazione matriciale

1: Determina i valori del parametro u∗ ∈ [0, 1[2: Determina i valori del parametro w∗ ∈ [0, 1[3: Determina la matrice di base N per il grado k4: Determina la matrice di base M per il grado l5: per s = 1 fino a n− k + 2:6: B∗s ← Bs:s+k−1,∗ . Prelevo le righe dalla s alla s+ k − 17: per ogni valore del parametro u∗:8: Determina il vettore U∗

9: S∗ ← [U∗][N ] . Calcolo parte dell’equazione (2.18)10: per t = 1 fino a m− l + 2:11: B∗s,t ← B∗s,t:t+l−1 . Prelevo le colonne dalla t alla t+ l − 1

12: S∗ ← [S∗][B∗s,t][M ]T

13: per ogni valore del parametro w:14: Determina il vettore W ∗

15: S(u,w)← [S∗][W ∗]T

16: fine ciclo w17: fine ciclo t18: fine ciclo u19: fine ciclo s

valori degli attributi npts e mpts, indicanti il numero di linee parametricheda calcolare lungo le direzioni u e w. Se i valori di npts e mpts non sonodivisibili per il numero di tratti da calcolare lungo le due direzioni, sorge poiun problema di inconsistenza in quanto non vengono calcolati esattamentenpts× mpts punti della superficie ma un valore molto prossimo.

1 def calculateWithMatrixNotation(self):

2 num_tratti_u = self.n - self.k + 1

3 num_tratti_w = self.m - self.l + 1

4

5 scaled_npts = self.npts / num_tratti_u

6 scaled_mpts = self.mpts / num_tratti_w

Una volta effettuata la scalatura del numero di linee parametriche da calco-lare lungo le due direzioni, bisogna aggiornare i valori totali di npts e mpts,in quanto questi valori vengono utilizzati nel metodo plot per calcolare lagriglia di coordinate x e y.

1 self.__dict__[’npts’] = scaled_npts * num_tratti_u

2 self.__dict__[’mpts’] = scaled_mpts * num_tratti_w

Page 95: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.12. LA CLASSE BSPLINESURF 85

Il resto del codice e una trasposizione 1:1 dell’algoritmo 5, l’unica differenzae che le coordinate x, y e z vengono trattate separatamente2

1 N = self.computePeriodicMatrix(self.k)

2 M = self.computePeriodicMatrix(self.l)

3

4 u = np.arange(0, 1, 1/float(scaled_npts))

5 w = np.arange(0, 1, 1/float(scaled_mpts))

6

7 ptCount = 0

8 for s in xrange(num_tratti_u):

9

10 cntrl_subnet_rows = self.cntrl[s:s + self.k, :, :]

11

12 for i in u:

13

14 ipoly = upoly(i, self.k)

15 left = np.dot(ipoly, N)

16

17 for t in xrange(num_tratti_w):

18

19 cntrl_subnet = cntrl_subnet_rows[:, t:t + self.l,

:]

20

21 left_x = np.dot(left, np.dot(cntrl_subnet[:, :,

0], M.T))

22 left_y = np.dot(left, np.dot(cntrl_subnet[:, :,

1], M.T))

23 left_z = np.dot(left, np.dot(cntrl_subnet[:, :,

2], M.T))

24

25 for j in w:

26

27 jpoly = upoly(j, self.l)

28

29 x = np.dot(left_x, jpoly)

30 y = np.dot(left_y, jpoly)

31 z = np.dot(left_z, jpoly)

32

33 self.points[ptCount] = Points([[x, y, z]])

34 ptCount += 1

Esempio d’uso

Di seguito un breve esempio d’utilizzo della classe BSplineSurf.

1 >>> import numpy as np

2Se gli indici sembrano errati e perche oltre al fatto che gli indici iniziano da 0 e nonda 1, in python istruzioni come Matrice[a:b,:] restituiscono le righe dalla a alla b− 1.

Page 96: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

86 CAPITOLO 4. CLASSI ED ALGORITMI

2 >>> from Surface import BSplineSurf

3 # per una questione di spazio non faccio un esmepio completo

4 # dei punti di controllo

5 >>> cntrlPts = [[ (0,0,0), (5,0,5), ..., ],[ (0,5,0), (5,5,5), ...]]

6 # creo una superficie B-Spline di ordine 4 con vettore

7 # dei nodi periodico lungo la direzione u e di ordine 3

8 # con vettore dei nodi aperto lungo la direzione w

9 >>> s = BSplineSurf(cntrlPts, 20, 20, 4, 3, ’periodic’,’open’)

10 # posso calcolare la superficie utilizzando la definizione

11 # formale invocando il metodo calculate

12 >>> s.calculate()

13 # oppure utilizzando la notazione matriciale invocando

14 # il metodo calculateWithMatrixNotation, in questo caso

15 # pero’ saranno utilizzati vettori dei nodi periodici

16 # in entrambe le direzioni

17 >>> s.calculateWithMatrixNotation()

18 # infine visualizzo la superficie

19 >>> s.plot()

4.13 La classe NurbsSurf

La classe NurbsSurf consente di calcolare delle superfici NURBS. Essa nonestende la classe Nurbs come ci si sarebbe potuto aspettare: osservando leequazioni (2.15) e (2.27) si nota che le funzioni di base utilizzate per le curvenon sono riutilizzabili per le superfici. Vengono utilizzate, invece, le funzionidi base delle B-Spline. Dato che nella libreria e gia presente una classe cheestende sia BSpline che Surface, ovvero la classe BSplineSurf, la classeNurbsSurf eredita da essa tutti i metodi e gli attributi necessari.

4.13.1 Algoritmo naive

L’algoritmo 6 calcola una superficie NURBS a partire dall’equazione (2.27).Questo semplice algoritmo e parecchio inefficiente: in particolare il denomi-natore della funzione di base razionale “Somma” viene calcolato npts×mptsvolte, in piu nel ciclo piu interno viene effettuata la divisione Somma. Nelciclo piu interno vengono quindi effettuati una somma, due prodotti ed unadivisone fra numeri in virgola mobile.

Si ricordi (vedere la sezione 2.2.2) che una funzione di base b-spline ediversa da zero solo su un intervallo limitato dei valori del parametro, buonaparte di questi calcoli sono fatti praticamente a vuoto. Per capire meglio ilconcetto, consideriamo l’equazione (2.27) con k = l = n+ 1 = m+ 1 = 3:

Page 97: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.13. LA CLASSE NURBSSURF 87

Algoritmo 6 Algoritmo naive per le superfici NURBS

1: per ogni valore del parametro u:2: Determina le funzioni di base Ni,k(u)3: per ogni valore del parametro w:4: Determina le funzioni di base Mj,l(w)5: Somma←

∑n+1i1=1

∑m+1j1=1 hi1,j1Ni1,k(u)Mj1,l(w)

6: per i = 0 a n:7: per j = 0 a m:8: S(u,w)← S(u,w) + hi,jNi,k(u)Mj,l(w)/Somma9: fine ciclo j

10: fine ciclo i11: fine ciclo w12: fine ciclo u

Q(u,w) =

N1,3(u)

Somma(u,w){h1,1B1,1M1,3(w) + h1,2B1,2M2,3(w) + h1,3B1,3M3,3(w)}+

N2,3(u)

Somma(u,w){h2,1B2,1M1,3(w) + h2,2B2,2M2,3(w) + h2,3B2,3M3,3(w)}+

N3,3(u)

Somma(u,w){h3,1B3,1M1,3(w) + h3,2B3,2M2,3(w) + h3,3B3,3M3,3(w)}

E facile notare che se Ni,k(u) = 0, l’intero calcolo contenuto fra le paren-tesi e superfluo, come la divisione per Somma(u,w). Allo stesso modo seMj,l(w) = 0, il calcolo del singolo addendo puo essere evitato. Cio si traducenell’inserimento di due controlli all’interno dell’algoritmo, che si occupino diverificare se Ni,k(u) o Mj,l(w) sono diversi da zero. L’algoritmo 7 mostraqueste modifiche.

Sebbene queste semplici modifiche migliorino di circa il 25% le presta-zione di questo algoritmo “naive”, esso rimane comunque non ottimale.

4.13.2 Algoritmo efficiente

Il calcolo delle funzioni di base razionali dipende dagli ordini della super-ficie b-spline k e l lungo le due direzioni, dal numero di punti di controllon e m, dal numero di linee parametriche da calcolare, npts e mpts e daipesi hi,j . Se non cambia nessuno di questi parametri, allora non cambia-no ne le funzioni di base, Ni,k(u) e Mj,l(w), quindi neanche ne la funzioneSomma(u,w). Precalcolare le funzioni di base e la funzione Somma(u,w)per poi conservarle in dei vettori consentirebbe di migliorare notevolmentele prestazioni dell’algoritmo. L’algoritmo 8 a pagina 89 mostra le modificheapportate secondo il criterio sopra descritto.

Page 98: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

88 CAPITOLO 4. CLASSI ED ALGORITMI

Algoritmo 7 Algoritmo naive per le superfici NURBS migliorato

1: per ogni valore del parametro u:2: Determina le funzioni di base Ni,k(u)3: per ogni valore del parametro w:4: Determina le funzioni di base Mj,l(w)5: Somma←

∑n+1i1=1

∑m+1j1=1 hi1,j1Ni1,k(u)Mj1,l(w)

6: per i = 0 a n:7: se Ni,k(u) 6= 0 allora8: per j = 0 a m:9: se Mj,l(w) 6= 0 allora

10: S(u,w)← S(u,w) + hi,jNi,k(u)Mj,l(w)/Somma11: fine se12: fine ciclo j13: fine se14: fine ciclo i15: fine ciclo w16: fine ciclo u

4.13.3 L’implementazione

In questo paragrafo verra mostrata l’implementazione della classe NurbsSurfcon particolare attenzione all’implementazione dell’algoritmo 8.

Costruttore

Il costruttore della classe NurbsSurf prende in input i seguenti parametri:

cntrl Matrice n × m × 4 contenente le coordinate omogenee dei punti dicontrollo;

npts Numero di linee parametriche da calcolare lungo la direzione u;

mpts Numero di linee parametriche da calcolare lungo la direzione w;

k Ordine della B-Spline razionale lungo la direzione u;

l Ordine della B-Spline razionale lungo la direzione w;

x Vettore dei nodi per la direzione u;

y Vettore dei nodi per la direzione w.

Gli attributi itest e weightsAreChanged sono utilizzati nell’algoritmo dicalcolo per evitare calcoli inutili, mentre gli attributi niku, mjlw e sumuw

sono vettori che conterranno rispettivamente le funzioni di base Ni,k(u), lefunzioni di base Mj,l(w) e le funzioni Somma(u,w).

Page 99: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.13. LA CLASSE NURBSSURF 89

Algoritmo 8 Algoritmo efficiente per le superfici NURBS

1: niku← array(npts)2: mjlw ← array(mpts)3: somma← array(npts ·mpts)4: se itest 6= (npts+mpts+ k + l + n+m) allora5: Determina i valori del parametro u6: Determina i valori del parametro w7: count← 18: per ogni valore del parametro u9: niku(count)← N∗,k(u)

10: count = count+ 111: fine ciclo12: count← 113: per ogni valore del parametro w14: mjlw(count)←M∗,l(w)15: count = count+ 116: fine ciclo17: itest = npts+mpts+ k + l + n+m18: fine se19: se sono cambiati i pesi hi,j allora20: count← 121: per ogni N∗,k(u) in niku:22: per ogni M∗,l(w) in mjlw:23: s← 024: per i = 1 a n+ 1:25: per j = 1 a m+ 1:26: s← s+ hi,jNi,k(u)Mj,l(w)27: fine ciclo j28: fine ciclo i29: somma(count)← s30: count← count+ 131: fine ciclo M∗,l(w)32: fine ciclo N∗,k(u)33: fine se34: count← 135: per ogni N∗,k(u) in niku:36: per ogni M∗,l(w) in mjlw:37: per i = 1 a n+ 1:38: se Ni,k(u) 6= 0 allora39: per j = 1 a m+ 1:40: se Mj,l(w) 6= 0 allora41: R← hi,jNi,k(u)Mj,l(w)/somma(count)42: S(count)← S(count) +Bi,jR43: fine se44: fine ciclo j45: fine se46: fine ciclo i47: count = count+ 148: fine ciclo M∗,l(w)49: fine ciclo N∗,k(u)

Page 100: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

90 CAPITOLO 4. CLASSI ED ALGORITMI

1 class NurbsSurf(BSplineSurf):

2 def __init__(self,cntrl,npts,mpts,k,l,x,y):

3

4 BSplineSurf.__init__(self,cntrl,npts,mpts,k,l,x,y)

5

6 self.__dict__[’itest’] = -1

7

8 self.__dict__[’weightsAreChanged’] = True

9

10 self.__dict__[’niku’] = []

11 self.__dict__[’mjlw’] = []

12

13 self.__dict__[’sumuw’] = []

14

15 self.__dict__[’weightsAreChanged’] = True

16

17 if not(self.cntrl.shape[2] == 4):

18 raise PyNurbsError("I punti di controllo della superficie

devono essere quadridimensionali")

Metodo changeWeight

Questo semplice metodo serve per monitorare le modifiche ai pesi dei puntidi controllo, non fa altro che modificare il peso per il punto di controllo diindice i, j e settare l’attributo weightsAreChanged a True.

1 def changeWeight(self,i,j,h):

2 self.cntrl[i,j,3] = h

3 self.__dict__[’weightsAreChanged’] = True

Metodo calculate

Questo metodo implementa l’algoritmo 8 per calcolare la superficie NURBS.La prima parte del metodo calcola e salva nei rispettivi array le funzioni dibase qualora uno dei parametri venga modificato:

1 def calculate(self):

2 if self.itest != (self.n + self.m + self.npts + self.mpts +

self.k + self.l):

3

4 upts = np.linspace(self.x[self.k - 1], self.x[self.n],

self.npts)

5 wpts = np.linspace(self.y[self.l - 1], self.y[self.m],

self.mpts)

6

7 for u in upts:

8 base = self.computeBasis(self.n, self.k, self.x, u)

Page 101: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.13. LA CLASSE NURBSSURF 91

9 self.niku.append(base)

10

11 for w in wpts:

12 base = self.computeBasis(self.m, self.l, self.y, w)

13 self.mjlw.append(base)

14

15 self.itest = self.n + self.m + self.k + self.l + self.

npts + self.mpts

La seconda parte del metodo calcola, nel caso ci sia stata una modifica deipesi dei punti di controllo, la funzione Somma(u,w) e ne salva il reciprocoin un array, in modo da evitare divisioni nel ciclo principale dove vienecalcolato il punto della superficie.

1 if self.weightsAreChanged:

2 for nbasis in self.niku:

3 for mbasis in self.mjlw:

4 s = 0

5 for i in xrange(self.n):

6 for j in xrange(self.m):

7 s += (self.cntrl[i,j,3] * nbasis[i] *

mbasis[j])

8 self.sumuw.append(1./s)

9 self.__dict__[’weightsAreChanged’] = False

L’ultima parte del metodo calcola la superficie, sulla falsa riga dell’algoritmo7.

1 ptCount = 0

2 for nbasis in self.niku:

3 for mbasis in self.mjlw:

4 for i in range(self.n):

5 if nbasis[i] != 0:

6 for j in range(self.m):

7 if mbasis[j] != 0:

8 rbasis = self.cntrl[i,j,3] * nbasis[i]

* mbasis[j] * self.sumuw[ptCount]

9

10 self.points[ptCount] += Points(self.

cntrl[i,j,:3] * rbasis)

11

12 ptCount += 1

Esempio d’uso

Di seguito un breve esempio d’utilizzo della classe NurbsSurf.

1 >>> import numpy as np

2 >>> from Surface import NurbsSurf

3 # per una questione di spazio non faccio un esmepio completo

Page 102: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

92 CAPITOLO 4. CLASSI ED ALGORITMI

4 # dei punti di controllo. La quarta coordinata il peso

5 # relativo al punto

6 >>> cntrlPts = [[(0,0,0,1), (5,0,5,3), ... ],[(0,5,0,3), (5,5,5,10)

,...]]

7 # creo una superficie NURBS di ordine 4 con vettore

8 # dei nodi periodico lungo la direzione u e di ordine 3

9 # con vettore dei nodi aperto lungo la direzione w

10 >>> s = NurbsSurf(cntrlPts, 20, 20, 4, 3, ’periodic’,’open’)

11 # posso calcolare la superficie invocando il metodo calculate

12 >>> s.calculate()

13 # infine visualizzo la superficie

14 >>> s.plot()

4.14 La classe SurfaceFit

La classe SurfaceFit consente di approssimare un insieme di punti appar-tenente ad una superficie utilizzando le superfici B-Spline, come illustratonella sezione 2.3.3. Essa e quindi un’estensione della classe BSplineSurf,in quanto modella comunque una superficie B-Spline, ma con in aggiun-ta i metodi necessari per approssimare dei punti della superficie gia noti.Inoltre SurfaceFit estende CurveFit, ereditando in particolare il metodogetParametersValue.

Costruttore

Il costruttore della classe SurfaceFit prende in input i seguenti parametri:

surfacePoints Punti della superficie da approssimare;

n Numero di punti di controllo da determinare lungo la direzione u;

m Numero di punti di controllo da determinare lungo la direzione w;

npts Numero di punti della superficie da calcolare lungo la direzione u;

mpts Numero di punti della superficie da calcolare lungo la direzione w;

k Ordine della B-Spline per la direzione u;

l Ordine della B-Spline per la direzione w;

Viene richiamato il costruttore di BSplineSurf, che inizializza la matricedei punti di controllo come matrice n ×m di zeri. Si noti che i vettori deinodi utilizzati sono di tipo aperto, tale scelta non e casuale: l’utilizzo divettori dei nodi di tipo periodico porta (nel caso specifico di questa libreria)ad un errata approssimazione dei punti dati in input.

Page 103: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.14. LA CLASSE SURFACEFIT 93

1 class SurfaceFit(CurveFit,BSplineSurf):

2 def __init__(self, surfacePoints, n, m, npts, mpts, k, l):

3

4 BSplineSurf.__init__(self, np.zeros((n, m, 3)), npts, mpts, k

, l, ’open’,’open’)

5 if isinstance(surfacePoints, str):

6 try:

7 self.__dict__[’surfacePoints’] = np.loadtxt(

surfacePoints)

8 except IOError as detail:

9 print "Errore nel leggere i dati dal file {0}: {1}".

format(surfacePoints,detail)

10 raise

11 elif not isinstance(surfacePoints, (list, tuple, np.ndarray))

:

12 raise TypeError("Errore, il parametro surfacePoints deve

essere una lista,\

13 una tupla o un istanza di np.ndarray, non

di tipo {0}".format(type(surfacePoints

)))

14 else:

15 self.__dict__[’surfacePoints’] = np.asarray(surfacePoints

).view(Points)

16

17 self.__dict__[’cntrl’] = self.getControlPoints()

Metodo getParametersMatrix

Questo metodo costruisce una matrice contenente le stime dei valori deiparametri u e w relativi ai punti noti della superficie dati in input. Vieneutilizzato il metodo getParametersValue ereditato dalla classe CurveFit.Si noti che ad ogni punto di controllo sono associate le stime di entrambi iparametri u e w, la matrice e quindi creata in modo tale che matricei,j,0 = ue matricei,j,1 = w.

1 def getParametersMatrix(self):

2

3

4 r,s = self.surfacePoints.shape[:2]

5

6 uw = np.zeros((r, s, 2),np.double)

7

8 for i in range(s):

9 uw[:,i,0] = self.getParametersValue(self.surfacePoints[:,

i])

10

11 for i in range(r):

Page 104: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

94 CAPITOLO 4. CLASSI ED ALGORITMI

12 uw[i,:,1] = self.getParametersValue(self.surfacePoints[i

,:])

13

14 return uw

Metodo getNMatrix

Questo metodo consente di calcolare la matrice contenente le funzioni dibase. Il primo passo e quello di scalare i vettori dei nodi nell’intervallo [0 , 1],dato che le stime dei parametri calcolate con il metodo getPametersMatrix

risiedono in quell’intervallo.

1 def getNMatrix(self):

2

3 x = np.asarray(self.x,np.double)

4 x = x / x[-1]

5

6 y = np.asarray(self.y,np.double)

7 y = y / y[-1]

Quindi determino il numero di righe e colonne r, s della matrice surfacePoints,quindi inizializzo N come matrice r · s× n ·m.

1 r,s = self.surfacePoints.shape[:2]

2

3 N = np.zeros((r * s, self.n * self.m), np.double)

Una volta determinata la matrice contenente le stime dei parametri u e w,per ogni coppia di valori si calcolano le funzioni di base Ni,k(u) e Mj,l(w), icui prodotti vengono salvati all’interno della matrice N .

1 uw = self.getParametersMatrix()

2

3 rowCount = 0

4

5 for uparam in range(r):

6

7 for wparam in range(s):

8

9 u = uw[uparam, wparam, 0]

10 w = uw[uparam, wparam, 1]

11

12 Nbasis = self.computeBasis(self.n, self.k, x, u)

13 Mbasis = self.computeBasis(self.m, self.l, y, w)

14

15 colCount = 0

16

17 for Nik in Nbasis:

18

Page 105: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

4.14. LA CLASSE SURFACEFIT 95

19 for Mjl in Mbasis:

20

21 N[rowCount, colCount] = Nik * Mjl

22

23 colCount+=1

24

25 rowCount+=1

26

27 return N

Metodo getControlPoins

Questo e il metodo principale della classe SurfaceFit, che consente di cal-colare dei punti di controllo tali da generare una superficie B-Spline cheapprossimi i punti dati in input. Alcuni degli assegnamenti fatti in questometodo sono fatti per una pura questione di leggibilita, e per rendere piuchiara la corrispondenza con quanto detto nella sezione 2.3.3. Il codice se-gue lo stesso schema del metodo getControlPoints della classe CurveFit,l’unica differenza e che c’e stata la necessita di “trasformare” alcune matrici,modificando il numero di righe e colonne, per evitare problemi di dimensionedurante i prodotti.

1 def getControlPoints(self):

2 r, s = self.surfacePoints.shape[:2]

3

4 D = self.surfacePoints.reshape(r * s, 3)

5 cntrl = np.zeros((self.n * self.m, 3))

6

7 N = self.getNMatrix()

8

9 rows,columns = N.shape

10

11 if(rows != columns):

12 print "La matrice N non quadrata"

13 D = np.dot(N.T,D)

14 N = np.dot(N.T,N)

15

16 try:

17 cntrl = np.linalg.solve(N,D)

18 except np.linalg.LinAlgError:

19 print "Matrice Singolare"

20

21 cntrl = cntrl.reshape((self.n, self.m, 3))

22 return cntrl

Page 106: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

96 CAPITOLO 4. CLASSI ED ALGORITMI

Metodi ambigui

Utilizzando la multiereditarieta, sorge il problema dell’ambiguita dei me-todi ereditati, laddove ci siano metodi con la stessa firma in entrambe leclassi genitore. Il problema e facilmente risolto sovrascrivendo tali metodi erichiamando esplicitamente il metodo della classe desiderata.

1 def calculate(self):

2 BSplineSurf.calculate(self)

3

4 def plot(self, **args):

5 BSplineSurf.plot(self, **args)

Il parametro **args e un dizionario python che contiene i valori passati perparametro nella forma parametro1 = valore1, parametro2 = valore2 ...,in questo modo tutti i parametri passati al metodo plot della classe SurfaceFitverranno rigirati al metodo plot della classe BSplineSurf

Esempio d’uso

Di seguito un breve esempio d’utilizzo della classe SurfaceFit.

1 >>> import numpy as np

2 >>> from Surface import SurfaceFit

3 # per una questione di spazio non faccio un esmepio completo

4 # dei punti di controllo

5 >>> surfPts = [[ (0,0,0), (5,0,5), ..., ],[ (0,5,0), (5,5,5), ...]]

6 # creo una superficie B-Spline di ordine 4 lungo la direzione u

7 # e di ordine 3 lungo la direzione w, facendo determinare

8 # alla classe 5 punti di controllo lungo entrambe le direzioni

9 >>> s = SurfaceFit(cntrlPts, 5, 5,20,20, 4, 3)

10 # posso calcolare la superficie utilizzando la definizione

11 # formale invocando il metodo calculate

12 >>> s.calculate()

13 # infine visualizzo la superficie

14 >>> s.plot()

Page 107: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Capitolo 5Esempi di utilizzo e applicazioni

Un duro lavoro mirato e la vera chiave per ilsuccesso. Concentrati sull’obiettivo e avvicinatiad esso passo dopo passo, fino a completarlo. Sesei indeciso sul come fare qualcosa, fallo inentrambi i modi e vedi quale funziona meglio

John Carmack

5.1 Esempi di utilizzo

In questa sezione verranno mostrati degli esempi di utilizzo delle classi checompongono la libreria da me sviluppata. Questi esempi verranno utilizzatianche per effettuare delle misurazioni sui tempi effettivi di esecuzione. Pereffettuare tali misurazioni mi sono servito del comando time presente nellabash di linux. Tale comando produce in output tre misurazioni:

real Tempo trascorso tra il lancio del processo e la sua terminazione, du-rante questo tempo la CPU e stata utilizzata anche da altri processi,quindi questa misurazione dipende anche dal numero di processi inesecuzione e dalla loro priorita.

user Tempo effettivo di CPU utilizzato dal processo in modalita utente,l’utilizzo della CPU effettuato da altri processi e il tempo in cui ilprocesso rimane sospeso non vengono considerati.

sys Tempo effettivo di CPU utilizzato da eventuali chiamate di sistema delprocesso in spazio kernel, anche qui l’utilizzo della CPU effettuato daaltri processi e il tempo in cui il processo rimane sospeso non vengonoconsiderati.

97

Page 108: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

98 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

Di seguito le specifiche della macchina su cui sono state effettuate le misu-razioni:

Processore Intel Atom N450;

Frequenza 1.66 Ghz;

Memoria Cache L2 512 Kb;

Memoria RAM 1 Gb

Sistema operativo Ubuntu Linux 10.04

Versione del Kernel 2.6.32-25-generic

Si tenga presente che le misurazioni non riguarderanno solo il metodo checalcola effettivamente la curva o la superficie: il codice non sara lanciatogia all’interno dell’interprete Python, ma da uno script preparato appo-sitamente, quindi le misurazioni comprenderanno il tempo di caricamentodell’interprete, l’inizializzazione dell’oggetto rappresentante la curva o la su-perficie, la lettura da file dei punti di controllo e il calcolo vero e proprio.I file utilizzati come input sono semplici file di testo il cui contenuto e cosıformattato:

x0 y0 z0x1 y1 z1x2 y2 z2...xn yn zn

Nel caso i punti servano come punti di controllo per una superficie, bisognaaggiungere un ultima riga contenente il numero di righe, di colonne e dicoordinate per punto. Ad esempio, per un poliedro di controllo per unasuperficie NURBS con 5 punti di controllo lungo la direzione u, 7 puntilungo la direzione w e 4 coordinate per ogni punto, l’ultima riga del file deveessere 5 7 4.

5.1.1 Curve di Bezier

I test per la curva di Bezier saranno effettuati ricreando la curva mostratanella figura 5.1. Verranno calcolati 200 punti della curva sia utilizzando ladefinizione formale sia utilizzando l’algoritmo di De Casteljau. Questi sonogli script che verranno lanciati:

1 #!/usr/bin/python

2 from Curve import Bezier

3

4 c = Bezier(’infinito.txt’,200)

5 c.calcWithBernstein()

Page 109: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.1. ESEMPI DI UTILIZZO 99

Listing 5.1: bezier definition.py

1 #!/usr/bin/python

2 from Curve import Bezier

3

4 c = Bezier(’infinito.txt’,200)

5 c.calculate()

Listing 5.2: bezier decasteljau.py

Figura 5.1: Curva di Bezier utilizzata per i test

Le tabelle 5.1 e 5.2 mostrano i tempi relativi a tre esecuzioni consecutivedei due script. Come ci si poteva aspettare vista la maggiore complessitacomputazionale (vedere la sezione 4.4.2), l’algoritmo di De Casteljau impiegaun po di tempo in piu.

Esecuzione real user sys

1° 0m 2.877s 0m 2.452s 0m 0.216s2° 0m 2.817s 0m 2.504s 0m 0.196s3° 0m 2.942s 0m 2.448s 0m 0.212s

Tabella 5.1: Tempi di esecuzione per il calcolo di una curva di Bezier tramitetramite definizione formale.

Controlliamo ora la differenza presente fra i punti calcolati tramite ladefinizione formale e tramite l’algoritmo di de Casteljau:

Page 110: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

100 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

Esecuzione real user sys

1° 0m 3.463s 0m 3.140s 0m 0.208s2° 0m 3.469s 0m 3.092s 0m 0.240s3° 0m 3.395s 0m 3.156s 0m 0.180s

Tabella 5.2: Tempi di esecuzione per il calcolo di una curva di Bezier tramitealgoritmo di de Casteljau

1 >>> c1 = Bezier(’infinito.txt’,200)

2 >>> c2 = Bezier(’infinito.txt’,200)

3 >>> c1.calculate()

4 >>> c2.calcWithBernstein()

5 >>> diff = c1.points - c2.points

6 >>> diff.max()

7 1.5987211554602254e-14

L’errore e sicuramente accettabile in quanto ci sono differenze solo a partiredalla 15° cifra decimale, bisogna pero ricordare che in questo test i puntidi controllo sono solo dodici, al crescere del numero di punti di controllol’errore del metodo che utilizza la definizione formale aumenta.

5.1.2 Curve B-Spline

I test per le curve B-Spline verranno effettuati ricreando la curva mostra-ta nella figura 5.2, saranno messi a confronto i risultati ottenuti tramite ilcalcolo tramite definizione con quelli ottenuti dal calcolo con notazione ma-triciale. Entrambi i metodi utilizzano vettori dei nodi periodici e calcolano200 punti di una b-spline di ordine quattro.

Ecco i due script che verranno lanciati:

1 #!/usr/bin/python

2 from Curve import BSpline

3 c = BSpline(’fungo.txt’,200,4,’periodic’)

4 c.calculate()

Listing 5.3: periodic bspline basis.py

1 #!/usr/bin/python

2 from Curve import BSpline

3 c = BSpline(’fungo.txt’,200,4,’periodic’)

4 c.calculateWithMatrixNotation()

Listing 5.4: periodic bspline basis matrix.py

Le tabelle 5.3 e 5.4 mostrano i tempi relativi a tre esecuzioni consecutivedei due script. Come ci si poteva aspettare il calcolo con notazione matricialee piu veloce, il fatto e ovvio se si ricorda che questo metodo salva le matrici

Page 111: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.1. ESEMPI DI UTILIZZO 101

Figura 5.2: Curva calcolata utilizzando una b-spline periodica di ordine 4

di base una volta calcolate, per poi riutilizzarle. Infatti si puo notare uncerto distacco fra la prima e la seconda esecuzione, proprio in virtu del fattoche nella prima esecuzione la matrice di base e stata calcolata e salvata sudisco. Purtroppo non e possibile effettuare un confronto fra i risultati inquanto i due metodi non calcolano lo stesso numero di punti (vedere sezione4.5).

Esecuzione real user sys

1° 0m 2.208s 0m 1.620s 0m 0.180s2° 0m 1.810s 0m 1.584s 0m 0.176s3° 0m 1.884s 0m 1.576s 0m 0.196s

Tabella 5.3: Tempi di esecuzione per il calcolo di una curva B-Spline tramitedefinizione formale

Esecuzione real user sys

1° 0m 1.843s 0m 1.448s 0m 0.256s2° 0m 1.722s 0m 1.416s 0m 0.224s3° 0m 1.773s 0m 1.576s 0m 0.244s

Tabella 5.4: Tempi di esecuzione per il calcolo di una curva B-Spline tramitenotazione matriciale

Page 112: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

102 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

5.1.3 Curve Nurbs

Il test della classe Nurbs e stato effettuato ricreando la curva mostrata nellafigura 5.3. Vengono calcolati 200 punti di una B-Spline razionale di sestoordine, con i pesi dei sedici punti di controllo tutti pari ad uno. Sebbeneil risultato visivo sara una B-Spline non razionale, la scelta dei pesi noninfluisce sul calcolo, quindi la scelta dei pesi unitari e equivalente a qualsiasialtra. Di seguito lo script utilizzato per

1 #!/usr/bin/python

2 from Curve import Nurbs

3

4 c = Nurbs(’stella.txt’,200,6,’periodic’,[1]*16)

5 c.calculate()

Listing 5.5: Nurbs test.py

La tabella 5.5 mostra i tempi di tre esecuzioni consecutive dello script.

Esecuzione real user sys

1° 0m 3.860s 0m 2.252s 0m 0.316s2° 0m 3.540s 0m 2.236s 0m 0.264s3° 0m 2.891s 0m 2.116s 0m 0.252s

Tabella 5.5: Tempi di esecuzione per il calcolo di una curva NURBS

Figura 5.3: Curva utilizata per testare la classe Nurbs

Page 113: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.1. ESEMPI DI UTILIZZO 103

5.1.4 Approssimazione di curve tramite B-Spline

Per testare la classe CurveFit, verranno approssimati 18 punti dati con unaB-Spline di ordine 4 generata a partire dai 14 punti di controllo generati dal-l’algoritmo. La curva sara composta di 200 punti. La figura 5.4 a pagina 104mostra il percorso dall’idea iniziale del test al risultato finale. Inizialmenteho provato a far determinare alla classe un numero di punti di controllo parial numero di punti dati, ovvero 18. Ogni punto dato e stato effettivamenteinterpolato, ma la curva risultante e molto distante dall’idea iniziale. Dimi-nuendo il numero di punti di controllo a 14, il risultato rispecchia meglio lacurva originale. Di seguito lo script utilizzato per i test:

1 #!/usr/bin/python

2 from CurveFitting import CurveFit

3

4 c = CurveFit(’letteraG.txt’,14,200,4)

5 c.calculate()

Listing 5.6: curvefit test.py

La tabella 5.6 mostra i tempi di tre esecuzioni consecutive dello script. Si

Esecuzione real user sys

1° 0m 2.704s 0m 1.532s 0m 0.236s2° 0m 1.623s 0m 1.388s 0m 0.176s3° 0m 1.664s 0m 1.460s 0m 0.148s

Tabella 5.6: Tempi di esecuzione per l’approssimazione di una curva tramiteB-Spline

ricordi che questi tempi comprendono sia la generazione dei punti di controlloche il calcolo della curva.

5.1.5 Superfici di Bezier

Per il test delle superfici di Bezier, B-Spline e Nurbs verra utilizzato il po-ligono di controllo mostrato nella figura 5.5. Verranno calcolati 20 × 20 =400 punti appartenenti alla superficie. Di seguito viene illustrato lo scriptutilizzato per i test.

1 #!/usr/bin/python

2 from Surface import BezSurf

3 s = BezSurf(’bezier_surf.txt’,20,20)

4 s.calcWithBernstein()

Listing 5.7: bezsurf test.py

La tabella 5.7 mostra i tempi di tre esecuzioni consecutive dello script,mentre la superficie generata e mostrata nella figura 5.6.

Page 114: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

104 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

(a) L’idea iniziale (b) 18 punti di controllo

(c) 14 punti di controllo (d) Il risultato finale

Figura 5.4: Test di approssimazione di curva tramite B-Spline. In verde ipunti dati, in rosso i punti di controllo calcolati. 5.4(a)Ho provato a scrivereuna lettera su un foglio e a prendere alcuni punti campione, i quali sonostati dati in input alla classe. 5.4(b) numero di punti di controllo pari alnumero di punti dati. 5.4(c) e 5.4(d) numero di punti di controllo inferioreal numero di punti dati.

Page 115: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.1. ESEMPI DI UTILIZZO 105

Figura 5.5: Poliedro di controllo utilizzato per i test

Esecuzione real user sys

1° 0m 10.760s 0m 9.065s 0m 0.336s2° 0m 9.410s 0m 8.841s 0m 0.212s3° 0m 10.027s 0m 9.037s 0m 0.284s

Tabella 5.7: Tempi di esecuzione per il calcolo di una superficie di Bezier

5.1.6 Superfici B-Spline

Come test per la classe BSpline verra calcolata una superficie di ordine 4lungo entrambe le direzioni composta da 20 × 20 = 400 punti, che utilizzavettori dei nodi periodici. Verranno utilizzati entrambi i metodi di calcolo:quello che utilizza la definizione formale e quello che utilizza la notazionematriciale. Di seguito gli script utilizzati:

1 #!/usr/bin/python

2 from Surface import BSplineSurf

3 s = BSplineSurf(’surf.txt’,20,20,4,4,’periodic’,’periodic’)

4 s.calculate()

Listing 5.8: bsplinesurf test.py

1 #!/usr/bin/python

2 from Surface import BSplineSurf

3 s = BSplineSurf(’surf.txt’,20,20,4,4,’periodic’,’periodic’)

4 s.calculate()

Listing 5.9: bsplinesurf matrix test.py

Le tabelle 5.8 e 5.9 mostrano i tempi di tre esecuzioni consecutive dei duescript.

Non e possibile mettere a confronto i due metodi in quanto non vienecalcolato lo stesso numero di punti della superficie, in particolare, in questo

Page 116: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

106 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

Figura 5.6: Output della classe BezSurf

Esecuzione real user sys

1° 0m 1.948s 0m 1.524s 0m 0.200s2° 0m 1.768s 0m 1.484s 0m 0.236s3° 0m 1.819s 0m 1.484s 0m 0.220s

Tabella 5.8: Tempi di esecuzione per il calcolo di una superficie B-Splinetramite definizione formale

Esecuzione real user sys

1° 0m 1.652s 0m 1.344s 0m 0.224s2° 0m 1.678s 0m 1.328s 0m 0.208s3° 0m 1.629s 0m 1.484s 0m 0.272s

Tabella 5.9: Tempi di esecuzione per il calcolo di una superficie B-Splinetramite notazione matriciale

caso il metodo calculateWithMatrixNotation calcola 324 punti invece di400. E da notare comunque la differenza di tempi rispetto al calcolo dellasuperficie di Bezier. L’immagine 5.7 mostra l’output della classe BSpline

utilizzando il metodo calculate.

5.1.7 Superfici NURBS

La superficie NURBS utilizzata per il test utilizza vettori dei nodi periodici,gli unici pesi variati sono quelli dei 4 punti piu alti, il cui valore e statosettato a 3. Saranno calcolati 20 × 20 = 400 punti della superficie, chee di ordine 4 in entrambe le direzioni parametriche. Di seguito lo scriptutilizzato:

1 #!/usr/bin/python

2 from Surface import NurbsSurf

Page 117: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.1. ESEMPI DI UTILIZZO 107

Figura 5.7: Output della classe BSplineSurf

3 s = NurbsSurf(’surf.txt’,20,20,4,4,’periodic’,’periodic’)

4 s.calculate()

Listing 5.10: nurbs test.py

La tabella 5.10 mostra i tempi relativi a tre esecuzioni consecutive delloscript, mentre la figura 5.8 mostra la superficie generata.

Esecuzione real user sys

1° 0m 2.237s 0m 2.092s 0m 0.144s2° 0m 2.242s 0m 2.096s 0m 0.140s3° 0m 2.235s 0m 2.088s 0m 0.140s

Tabella 5.10: Tempi di esecuzione per il calcolo di una superficie NURBS

5.1.8 Approssimazione di superfici tramite B-Spline

Dato che preparare manualmente un esempio di superficie da approssimaree un compito un po arduo, per testare la classe SurfaceFit verra dapprimagenerata una superficie tramite uno script, i punti di questa superficie ver-ranno salvati in un file che rappresentera l’input della classe SurfaceFit.La superficie parametrica utilizzata per i test e descritta dalle seguentiequazioni:

x(u,w) = 2(

1− eu6π

)cos(u) cos2(

w

2)

y(w) = 2(−1 + e

u6π

)sin(u) cos2(

w

2)

z(u,w) = 1− eu3π − sin(w) + e

u6π sin(w)

0 ≤ u ≤ 6π

0 ≤ w ≤ 2π

Page 118: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

108 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

Figura 5.8: Output della classe NurbsSurf

Lo script utilizzato per generare i punti e il seguente:

1 #-*- coding: utf-8 -*-

2 import numpy as np

3

4 def seashell(upts,vpts):

5 u = np.linspace(0,6*np.pi,upts)

6 v = np.linspace(0,2*np.pi,vpts)

7

8 U,V = np.meshgrid(u,v)

9

10 pt = np.zeros((upts,vpts,3),np.double)

11

12 pt[:,:,0] = 2*(1-np.exp(U/(6*np.pi)))*np.cos(U)*pow(np.cos(V/2)

,2)

13 pt[:,:,1] = 2*(-1+np.exp(U/(6*np.pi)))*np.sin(U)*pow(np.cos(V/2)

,2)

14 pt[:,:,2] = 1 - np.exp(U/(3*np.pi))-np.sin(V)+np.exp(U/(6*np.pi)

)*np.sin(V)

15

16 return pt

17

18

19 surf = seashell(20,20)

20 np.save(’seashell.npy’,surf)

Listing 5.11: genera superficie.py

La funzione save della libreria numpy permette di salvare un array in un filebinario, invece che in un file di testo. Lo script su cui verra effettuato il teste il seguente:

1 #!/usr/bin/python

2 import numpy as np

Page 119: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.2. CONFRONTO CON ALTRE LIBRERIE 109

3 from SurfaceFitting import SurfaceFit

4

5 seashell = np.load(’seashell.npy’)

6

7 s = SurfaceFit(seashell,9,10,30,30,4,4)

8 s.calculate()

Listing 5.12: test surfacefit.py

La tabella 5.11 mostra i tempi relativi a tre esecuzioni consecutive delloscript, mentre la figura 5.9 mostra la superficie “originale” e l’approssima-zione ottenuta tramite la classe SurfaceFit.

Esecuzione real user sys

1° 0m 2.672s 0m 2.464s 0m 0.196s2° 0m 2.669s 0m 2.452s 0m 0.208s3° 0m 2.696s 0m 2.504s 0m 0.164s

Tabella 5.11: Tempi di esecuzione per l’approssimazione di una superficietramite B-Spline

(a) (b)

Figura 5.9: Esempio di approssimazione di superficie: 5.9(a) i punti daapprossimare. 5.9(b) il risultato dell’approssimazione.

5.2 Confronto con altre librerie

In rete e possible trovare diverse librerie gratuite che consentono di calcolarecurve e superfici NURBS. In questa sezione elenchero sommariamente lecaratteristiche principali di due librerie disponibili in rete: Nurbs++ [13]e openNURBS [5].

Page 120: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

110 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

5.2.1 Nurbs++

La libreria Nurbs++ e una libreria open source sviluppata in C++. Sebbe-ne sia progettata per l’uso in ambito grafico, essa fornisce diverse funzioni diinteresse generale, permettendo ad esempio determinare la derivata di unacurva in un determinato punto, di effettuare l’approssimazione di un insiemedi punti sia tramite interpolazione generale che tramite approssimazione aiminimi quadrati. E importante notare che nurbs++ non modella diret-tamente curve e superfici di Bezier e B-Spline: le uniche curve e superficigestite sono le NURBS. Cio significa che non sono presenti algoritmi efficientiper il calcolo di spline di Bezier e B-Spline, per ottenerle bisogna comunqueutilizzare le NURBS sfruttando le relazioni che le legano agli altri tipi dicurve e superfici (vedere capitolo 2). La libreria supporta diversi formati difile, in particolare lo standard VRML (Virtual Reality Modeling Language[8]), formato studiato per la rappresentazione di ambienti 3D sul web. Ilprogetto e attualmente fermo, l’ultima versione (3.0.11) e stata rilasciata il24 maggio 2002. La libreria viene ancora utilizzata grazie alla manutenzio-ne effettuata da alcuni utilizzatori che rilasciano sporadicamente delle patchper far sı che il codice venga compilato anche dalle nuove versioni dei com-pilatori C++. La documentazione e presente ma incompleta e tratta solo laparte relativa alle curve, inclusa la loro approssimazione.

5.2.2 openNURBS

La libreria OpenNurbs costituisce il motore del software di modellazione3D Rhinoceros [7], il quale utilizza le NURBS come unico strumento per lamodellazione. L’azienda che sviluppa questo software ha deciso di rilasciaregratuitamente i sorgenti della libreria che si occupa del calcolo di NURBS.La libreria, sviluppata in C++, e molto complessa e poco documentata: ciorende molto difficile effettuare modifiche al suo interno od estenderla conclassi personalizzate. Inoltre essa utilizza il formato file di Rhinoceros persalvare le curve e le superfici calcolate, costringendo coloro che desideranoutilizzarla a studiare anche il suddetto formato. Essendo stata sviluppatacome cuore di un programma per la modellazione 3D, la quasi totalita del-le funzioni offerte dalla libreria openNURBS riguarda esclusivamente lagrafica.

5.3 Possibili applicazioni

In questa sezione verranno illustrati alcuni campi d’applicazione in cui eutilizzabile la libreria sin’ora illustrata.

Page 121: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.3. POSSIBILI APPLICAZIONI 111

5.3.1 Scanner 3D

La pagina [17] illustra il progetto di uno scanner 3D che utilizza le Nurbs.L’idea e quella di riprendere in controluce un oggetto posizionato su una ba-se che viene fatta ruotare da un motore a passo. Le immagini cosı ottenutesono date in input ad un programma scritto in matlab che utilizza funzionisoglia e filtri fino ad isolare i contorni dell’oggetto. Questi contorni vengonosalvati in un file come curve NURBS. Nel progetto non vi e un vero e proprio

Figura 5.10: Scanner 3D

calcolo di NURBS, l’autore utilizza il formato file del software di modella-zione 3D Maya([3]) per salvare i punti estrapolati dai contorni dell’oggettocome punti di una curva NURBS, con lo svantaggio di salvare anche datiperturbati (disturbo durante l’acquisizione delle immagini o bassa qualitadelle immagini stesse). Tali punti potrebbero invece essere utilizzati comeinput per la classe SurfaceFit, essi sarebbero cosı approssimati , ottenen-do un effetto di smussatura che diminuirebbe l’effetto visivo di eventualidati perturbati. Inoltre cosı facendo si otterrebbe direttamente la superfi-cie descrivente l’oggetto scansionato, invece di salvare una per una le curverappresentanti i vari contorni dell’oggetto stesso.

5.3.2 Ricostruzione paesaggi

L’approssimazione di superfici puo essere utilizzata per creare un semplicesoftware che permetta di “ricostruire” dei paesaggi. Avendo a disposizioneuna mappa topografica, si potrebbero utilizzare le curve di livello in essapresenti per ottenere dei punti da dare in input alla classe SurfaceFit.La parte complessa consiste proprio nell’estrarre dalla mappa le curve dilivello per poi prelevare da esse dei punti campione. La risoluzione di questoproblema richiede la conoscenza di tecniche di elaborazione delle immagini

Page 122: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

112 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

Figura 5.11: Mappa topografica del Monte Fuji, Giappone [2]

che esulano dallo scopo di questa tesi. L’articolo [20] descrive un metodoper estrarre le curve di livello da una mappa topografica.

5.3.3 Applicazioni mediche

Gli articoli [14] e [27] illustrano alcuni utilizzi delle NURBS in campo medico.In particolare, [14] mostra come applicare tecniche di prototipazione rapidaal campo medico utilizzando volumi NURBS per modellare ossa, mentre [27]illustra una tecnica per la modellazione dei muscoli e per la loro simulazio-ne meccanica, sempre utilizzando volumi NURBS. Un volume NURBS siottiene aggiungendo un terzo parametro all’equazione (2.27):

S(u, v, w) =

n∑i=0

m∑j=0

l∑k=0

Bi,j,khi,j,kNi,p(u)Nj,q(v)Nk,r(w)

n∑i=0

m∑j=0

l∑k=0

hi,j,kNi,p(u)Nj,q(v)Nk,r(w)

(5.1)

Dall’equazione (5.1) e possibile ricavare la definizione delle funzioni razionalidi forma:

Ri,j,k(u, v, w) =hi,j,kNi,p(u)Nj,q(v)Nk,r(w)

n∑i=0

m∑j=0

l∑k=0

hi,j,kNi,p(u)Nj,q(v)Nk,r(w)

(5.2)

Nelle equazioni precedenti Ni,p(u), Nj,q(v), Nk,r(w) indicano le funzioni di

base B-Spline cosı come descritte dall’equazione (2.6). E facile intuire che

Page 123: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

5.4. CONCLUSIONI 113

estendere la libreria introdotta in questa tesi per far sı che gestisca anche i vo-lumi NURBS e molto semplice, cio ne permetterebbe l’uso nelle applicazionidescritte dagli articoli [14] e [27].

5.4 Conclusioni

La libreria introdotta con questa tesi permette di calcolare curve e super-fici di Bezier, B-Spline e NURBS, oltre a permettere l’approssimazione dicurve e superfici tramite B-Spline. Essa rispecchia i requisiti di semplicitae portabilita che erano stati prefissati oltre ad essere sufficientemente ro-busta grazie all’elevato uso che si e fatto delle eccezioni. Il Python si erivelato un linguaggio atipico come linguaggio orientato agli oggetti, vistala totale assenza di modificatori di accesso. Per quanto riguarda i tempidi esecuzione, e possibile ottenere miglioramenti significativi riscrivendo inC i metodi di importanza critica, utilizzando le API che Python offre perestendere il linguaggio. Tra le classi e presente un basso accoppiamento,mentre la coesione potrebbe essere migliorata: una possibilita e di ridefini-re le classi secondo l’architettura MVC (Model-View-Controller). Questovuol dire creare classi che si occupino solo di gestire i dati (i Model), classiche offrano i metodi necessari per lavorare su di essi (i Controller) e classiper la visualizzazione delle curve e delle superfici (i View). Questa archi-tettura ha il chiaro vantaggio di rendere indipendenti il modo in cui i dativengono trattati e conservati dal modo in cui essi vengono visualizzati, edaiuta inoltre nell’aumentare la coesione. Di contro pero, questo approccioaumenterebbe l’accoppiamento tra le classi, rendendo la libreria, anche sedi poco, piu complessa.

Page 124: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

114 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI

Page 125: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Appendici

115

Page 126: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D
Page 127: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Appendice ACasi d’uso e Class Diagrams

In questa appendice e possibile consultare la documentazione e i diagrammiUML che descrivono la libreria.

A.1 Casi d’uso

A.1.1 Curve

Figura A.1: Diagramma dei casi d’uso riguardante le curve

Calcola curva di Bezier

Questa funzionalita permette all’utente di calcolare un numero arbitrario dipunti di una curva di Bezier a partire da un insieme di punti di control-lo fornito in input. L’utente deve poter scegliere se il calcolo deve essere

117

Page 128: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

118 APPENDICE A. CASI D’USO E CLASS DIAGRAMS

effettuato utilizzando la definizione formale delle curve di Bezier o tramitel’algoritmo di de Casteljau.

Calcola curva B-Spline

Questa funzionalita permette all’utente di calcolare un numero arbitrario dipunti di una curva B-Spline fornendo in input un insieme di punti di con-trollo, l’ordine della curva e il vettore dei nodi. L’utente deve poter sceglierese il calcolo deve essere effettuato utilizzando la notazione funzionale o lanotazione matriciale. Nel primo caso, l’utente deve inoltre poter scegliere seutilizzare un vettore dei nodi standard (periodico o aperto) o fornirne unopersonalizzato.

Calcola curva NURBS

Questa funzionalita consente all’utente di determinare un numero arbitrariodi punti di una curva NURBS dopo aver fornito in input l’insieme dei puntidi controllo, l’ordine della curva, il vettore dei nodi e i pesi dei punti dicontrollo.

Concatena curve

Questa funzionalita consente all’utente di concatenare due curve dello stessotipo utilizzando l’operazione di somma. Il risultato dell’operazione e unasingola curva generata dalla concatenazione dei punti di controllo delle duecurve originali.

Approssima curva

Questa funzionalita consente all’utente di approssimare una curva fornendoin input un insieme di punti noti della curva stessa. L’utente deve inoltrespecificare l’ordine della curva approssimante che desidera ottenere.

A.1.2 Superfici

Calcola superfice di Bezier

Questa funzionalita permette all’utente di determinare, a partire da un insie-me di punti di controllo dato in input, una superfice di Bezier il cui numerodi linee parametriche lungo le due direzioni e arbitrario.

Calcola superfice B-Spline

Questa funzionalita consente all’utente di calcolare, a partire da un insiemedi punti di controllo, una superfice B-Spline il cui numero di linee parame-triche lungo le due direzioni e arbitrario. L’utente deve poter scegliere il

Page 129: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

A.2. CLASS DIAGRAM 119

Figura A.2: Diagramma dei casi d’uso riguardante le superfici

metodo di calcolo della superfice tra l’utilizzo della notazione funzionale equello della notazione matriciale. Nel primo caso l’utente deve poter sce-gliere una qualsiasi combinazione di vettori dei nodi per le due direzioni u ew.

Calcola superice NURBS

Questa funzionalita permette all’utente di calcolare una superfice NURBS apartire da un insieme di punti di controllo descritti da coordinate omogenee.Il numero di linee parametriche lungo le due direzioni deve essere arbitrarioe l’utente deve poter scegliere una qualsiasi combinazione di vettori dei nodiper le due direzioni u e w.

Approssima superfice

Questa funzionalita consente all’utenete di approssimare una superfice speci-ficando un insieme di punti noti della superfice stessa. L’utente deve inoltrespecificare il numero di punti di controllo e l’ordine della superfice lungo ledue direzioni u e w.

A.2 Class Diagram

La figura A.3 mostra il diagramma completo delle classi che compongonola libreria. Dal diagramma si evince il basso accoppiamento presente frale classi, risultato di una attenta progettazione della loro gerarchia. Neldiagramma sono mostrati, per ogni classe, solo alcuni metodi di particolareinteresse che aiutino a capire come essi sono stati ereditati o sovrascritti aseconda delle esigenze.

Page 130: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

120 APPENDICE A. CASI D’USO E CLASS DIAGRAMS

Figura A.3: Class Diagram completo delle classi componenti la libreria

Page 131: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Appendice BCodice

In questa appendice e possibile consultare il codice sorgente della libreria dame sviluppata nella sua interezza. Il file Curve.py contiene tutte le classiriguardanti le curve 2D, mentre il file Surface.py contiene tutte le classiriguardanti le superfici. Il file Util.py contiene alcune funzioni di comododa me sviluppate che non erano strettamente legate alle curve e alle superfici.Ad esempio e presente un metodo che permette di ottenere una matrice ditrasformazione omogenea specificando gli angoli di rotazione intorno ai treassi, la traslazione, la scalatura e il vettore di prospettiva.

B.1 Curve.py

1

2 #-*- coding: utf-8 -*-

3 #

4 # Scritto da Sanfelice Antonio

5 # (c) 2010

6 """

7 @package PyNurbsCurve

8 @brief Insieme di classi che fornisce metodi per calcolare curve

parametriche.

9 @author Antonio Sanfelice

10

11 Questa parte della libreria consente di calcolare diversi tipi di

curve

12 parametriche, in particolare:

13 - Natural Cubic Spline

14 - Hermite Spline

15 - Cardinal Spline

16 - Bzier

17 - B-Spline

18 - NURBS

121

Page 132: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

122 APPENDICE B. CODICE

19 Inoltre permette di approssimare curve tramite B-Spline

20 """

21 try:

22 import numpy as np

23 from Util import *

24 from scipy.misc import comb

25 from scipy import factorial as fact

26 import pylab as pl

27 except ImportError as detail:

28 print "Errore nell’importare le librerie:\n",detail

29

30

31

32

33 class Points(np.ndarray):

34 """

35 Classe che modella un array di punti

36 Aggiunge a numpy.ndarray i metodi "distance","chordLength" e

"convexComb"

37 """

38 def __new__(subclass,data,dtype = np.double):

39 obj = np.asarray(data,dtype).view(subclass)

40 return obj

41

42

43 def distance(self,p):

44 """

45 Metodo che calcola la distanza euclidea fra due punti.

46 Si noti che con questa notazione il numero di dimensioni

47 irrilevante.

48 @param p il punto con cui calcolare la distanza

49 """

50 return np.sqrt(sum(pow(p-self,2)))

51

52

53 def chordLength(self,i=0,j=None):

54 """

55 Metodo che calcola la somma delle distanze fra

56 l’i-esimo e il j-esimo punto dell’array

57 Chiamato senza argomenti calcola la somma delle distanze

58 dal primo all’ultimo punto

59 @param i l’indice del primo punto della sequenza

60 @param j l’indice dell’ultimo punto della sequenza

61 """

62 return sum([self[k].distance(self[k+1]) for k in xrange(len(

self[i:j])-1)])

63

64

65

Page 133: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 123

66 def convexComb(self,p,u):

67 """

68 Metodo che restituisce la combinazione convessa u

69 con un punto p

70 @param p il punto con cui si vuole calcolare la

combinazione convessa

71 @param u il valore della combinazione, compreso fra 0 e 1

72 """

73 if u < 0 or u > 1:

74 raise ValueError("il parametro u deve essere compreso fra

0 e 1")

75 return (1-u)*self + u*p

76

77

78

79 class Curve:

80 """

81 Classe interfaccia che definisce i parametri e i metodi

82 di base che devono essere implementati per modellare

83 una curva.

84 """

85

86 def __init__(self,cntrl,npts):

87

88 """

89 Costruttore

90

91 INPUT:

92 @param cntrl vettore dei punti di controllo o nome del

file che contiene i punti

93 @param npts numero di punti della curva da calcolare

94

95 ATTRIBUTI:

96 @var points punti che costituiscono la curva

97 """

98

99 if isinstance(cntrl,str):

100 self.loadFromFile(cntrl)

101 else:

102 try:

103 self.__dict__[’cntrl’] = Points(cntrl)

104 except Exception as detail:

105 raise PyNurbsError("Errore formato punti di controllo

:{0}".format(detail))

106

107 self.__dict__[’npts’] = npts

108 #inzializzo il vettore dei punti come vettore di zeri npts x

2

Page 134: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

124 APPENDICE B. CODICE

109 self.__dict__[’points’] = np.zeros((self.npts, 2)).view(

Points)

110

111

112 def calculate(self):

113 """

114 Metodo che effettua i calcoli necessari a determinare

115 self.points

116 """

117 pass

118

119

120 def loadFromFile(self,nomeFile):

121 """

122 Metodo che carica da file i punti di controllo

123 """

124 if not isinstance(nomeFile,str):

125 raise TypeError("Il nome del file deve essere una stringa

, non di tipo {0}".format(type(nomeFile)))

126 try:

127 self.__dict__[’cntrl’] = np.loadtxt(nomeFile).view(Points

)

128 except IOError as detail:

129 print "Errore nel caricare i dati dal file {0}: {1}".

format(nomeFile,detail)

130 raise

131

132

133

134 def plot(self, plotcp = True):

135 """

136 Metodo che esegue il plot dei valori contenuti in self.

points

137 @param plotcp flag booleano, se True allora visualizza

anche le linee di collegamento fra i punti di

controllo

138 @param show flag booleano, se True visualizza il plot(se

false, utile per overwriting,show() deve essere l’

ultima operazone di plot)

139 """

140

141 pl.plot(self.points[:, 0], self.points[:, 1])

142

143 if plotcp:

144 pl.plot(self.cntrl[:, 0],self.cntrl[:, 1],’ro’)

145 pl.plot(self.cntrl[:, 0], self.cntrl[:, 1],’r--’)

146

147 pl.axis(’equal’)

148 pl.show()

Page 135: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 125

149

150 return

151

152 def __add__(self, c):

153 """

154 Metodo che sovrascrive l’operatore di somma

155 """

156

157 if not(isinstance(c,type(self))):

158 raise TypeError("Il secondo operando deve essere un

istanza di {0}".format(type(self)))

159

160 # copio i punti di controllo della seconda curva, dato che

devo modificarli per

161 # "avvicinare" la seconda curva alla prima

162 other_curve = c.cntrl.copy()

163

164 # calcolo la differenza di posizione fra l’ultimo punto della

prima curva e il primo della seconda

165 diff = self.cntrl[-1] - other_curve[0]

166

167 # traslo i singoli punti della seconda curva

168 for pt in other_curve:

169 pt += diff

170

171 # creo un nuovo insieme di punti di controllo unendo i punti

di controllo delle due curve

172 new_cntrl_points = np.append(self.cntrl,other_curve[1:],0).

view(Points)

173

174 # deepcopy un metodo di python (contenuto nel pacchetto

copy) che permette di duplicare un oggetto

175 from copy import deepcopy

176

177 new_curve = deepcopy(self)

178 new_curve.cntrl = new_cntrl_points

179

180 #new_curve.calculate()

181

182 return new_curve

183

184

185 def __setattr__(self,name,value):

186 """

187 Metodo che sovrascrive l’operatore di assegnazione

188 per gli attributi, permettendo di eseguire funzioni

189 in caso di cambiamenti

190 @param name il nome dell’attributo che deve essere

settato

Page 136: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

126 APPENDICE B. CODICE

191 @param value il valore che si vuole assegnare al

parametro

192 """

193

194 # se il nome dell’attributo non npts o cntrl, ma comunque

un

195 # attributo esistente, setta attributo = value

196 if name not in (’npts’,’cntrl’) and self.__dict__.has_key(

name):

197 self.__dict__[name]=value

198 return

199 # se l’attributo da modificare npts

200 elif name is ’npts’:

201 # controllo se value un intero

202 if not isinstance(value,int):

203 raise TypeError("npts deve essere un intero")

204 else:

205 self.__dict__[name] = value

206 elif name is ’cntrl’:

207 if not (isinstance(value,Points) and value.ndim == self.

cntrl.ndim):

208 raise PyNurbsError("cntrl deve essere un istanza di

numpy.ndarray di %d dimensioni"%(self.cntrl.ndim))

209 else:

210 self.__dict__[name] = value

211 elif name is ’points’:

212 print "mannaggia"

213 object.__setattr__(self,name,value)

214

215

216

217 class Bezier(Curve):

218 """

219 Classe che modella una curva di Bzier

220 """

221 def __init__(self,cntrl,npts):

222 """

223 Metodo costruttore: cntrl va immesso nella forma [(x0,y0)

,(x1,y1),...,(xn,yn)]

224 INPUT:

225 @param cntrl punti di controllo da cui ottenere la curva

226 @param npts numero di punti da calcolare

227

228 ATTRIBUTI:

229 @var deg grado della curva (num_punti_di_controllo -1)

230 """

231

232 Curve.__init__(self,cntrl,npts)

233 self.__dict__[’deg’] = len(self.cntrl)-1

Page 137: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 127

234

235 def bernstein(self,n,i,t):

236 """

237 Metodo che calcola la funzione di base di bernstein

238 @param n il grado della curva (ordine della curva di

Bzier)

239 @param i l’indice della funzione di base da calcolare

240 @param t il valore del parametro

241 """

242

243 return comb(n, i)*pow(t, i)*pow(1-t, n-i)

244

245 def calcWithBernstein(self):

246

247 """

248 Metodo che calcola la curva come da definizione formale

249 """

250

251 tvalues = np.linspace(0,1,self.npts)

252 for i,t in enumerate(tvalues):

253 # l’i-esimo punto uguale alla sommatoria per i da 0 a

deg di cntrl[i]*bernstein(deg,i,t)

254 self.points[i] = sum([self.cntrl[j]*self.bernstein(self.

deg,j,t) for j in xrange(self.deg+1)])

255

256 def deCasteljau(self,cntrl,deg,t):

257

258 """

259 Metodo che calcola il punto della curva corrispondente al

valore di parametro t

260 @param t il valore del parametro corrispondente al punto

desiderato

261 """

262

263 #inizializzo un vettore points contenente una prima

combinazione convessa in t dei punti di controllo

264 tmp = Points([cntrl[i].convexComb(cntrl[i+1],t) for i in

xrange(deg)])

265

266 #itero il calcolo delle combinazioni lineari fino ad ottenere

un unico punto

267 for i in xrange(deg -1):

268 for j in xrange(deg -1 -i):

269 tmp[j] = tmp[j].convexComb(tmp[j+1],t)

270

271 #points[0] contiene il punto appartenente alla curva di

Bezier

272 return tmp[0]

273

Page 138: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

128 APPENDICE B. CODICE

274 def calculate(self):

275 #points un array contenente gli output di deCasteljau per

ogni valore di t

276 self.__dict__[’points’] = Points([self.deCasteljau(self.cntrl

,self.deg,t) for t in np.linspace(0,1,self.npts)])

277

278 # sovrascrivo l’operatore di assegnamento in modo da proteggere

deg da modifiche

279 def __setattr__(self,name,value):

280 if name is ’deg’:

281 raise PyNurbsError("Il grado della curva non pu essere

modificato manualmente")

282 elif name is ’cntrl’:

283 Curve.__setattr__(self,name,value)

284 self.__dict__[’deg’] = len(self.cntrl)-1

285 Curve.__setattr__(self,name,value)

286

287

288

289 class Spline(Curve):

290 """

291 Classe che modella una generica spline

292 utilizza la notazione matriciale per effettuare

293 i calcoli

294 """

295

296 def __init__(self,cntrl,npts,rpp,k):

297 """

298 Metodo costruttore

299 INPUT:

300 @param cntrl vettore dei punti di controllo da cui

ottenere la curva

301 @param npts numero di punti della curva da calcolare

302 @param rpp numero di righe della matrice di base dedicate

ad un singolo\

303 punto di controllo

304 @param k ordine della curva desiderato (grado desiderato

+ 1)

305

306 ATTRIBUTI:

307 @var B matrice di base che identifica il tipo di spline

308 @var coeff coefficienti del polinomio in u

309 """

310

311 Curve.__init__(self,cntrl,npts)

312 self.__dict__[’k’] = k

313 self.__dict__[’rpp’] = rpp

314 self.__dict__[’coeff’] = np.array([[]],np.double).reshape

(0,2)

Page 139: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 129

315 #inizializzo la matrice di base come matrice di base k x k

316 self.__dict__[’B’] = np.eye(self.k)

317

318 def calculate(self):

319 """

320 Metodo che effettua il calcolo.

321 L’algoritmo determina i coefficienti ’a’ del polinomio

322 ak-1*u^k-1 + ak-2*u^k-2 + ... + a2*u^2 + a1*u + a0

323 fatto ci valuta i polinomi ottenendo i punti della curva

324 """

325

326

327 self.points = np.zeros((self.npts,2))

328

329 # Il calcolo deve fermarsi k punti prima dell’ultimo

330 # in modo che il dot product non vada oltre la grandezza dell

’array

331 # numTratti un contatore che conter il numero di tratti di

cui

332 # la spline composta

333 numTratti = 0

334 for i in xrange(0,len(self.cntrl)-self.k+1,self.rpp):

335 numTratti+=1

336 self.coeff = np.append(self.coeff,np.dot(self.B,self.

cntrl[i:i+self.k]),0)

337

338

339 # determino l’eps nello spazio del parametro u

340 eps = float(numTratti)/self.npts

341

342

343 # determino il valore di u. Utilizzo "arange" in luogo di

linspace perch

344 # arange calcola un intervallo [a,b[ mentre linspace calcola

un intervallo chiuso

345 # che comprende gli estremi. dato che la fine di un tratto

coincide con l’inizio del

346 # successivo, utilizzare arange evita di calcolare due volte

lo stesso punto

347 upts = np.arange(0,1,eps)

348

349 pts = np.array([[]],np.double).reshape(0,2)

350 for i in xrange(numTratti):

351 c = i*self.k

352 # Calcolo i punti dell’intero tratto effettuando il dot

product fra il polinomio in u

353 # e la sotto matrice costituita dalle righe dalla i alla

i+self.k della matrice dei

354 # coefficienti e lo aggiungo alla lista dei punti

Page 140: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

130 APPENDICE B. CODICE

355 pts = np.append(pts,[np.dot(upoly(u,self.k),self.coeff[c:

c+self.k]) for u in upts],0)

356 self.points = pts

357 return

358

359

360

361 class NaturalCubicSpline(Spline):

362 """

363 Classe che modella una spline cubica naturale

364 """

365 def __init__(self,cntrl,npts):

366 """

367 Costruttore

368 @param cntrl punti di controllo indicanti punto posizione

, vettore della derivata prima e

369 della derivata seconda nella forma [(x0,y0),(d

’0x,d’0y),(d’’0x,d’’0y),(x1,y1),...]

370 @param npts il numero di punti della curva che si vuole

calcolare

371 """

372

373 Spline.__init__(self,cntrl,npts,3,4)

374 self.__dict__[’B’] = np.array

([[-1,-1,-.5,1],[0,0,.5,0],[0,1,0,0],[1,0,0,0]],np.double

)

375

376 def plot(self):

377 """

378 Metodo che effettua il plot della curva, mostrando le

condizioni di controllo

379 (derivata prima e seconda) come frecce

380 """

381

382 positionPoints = self.cntrl[::self.rpp]

383 firstDerivatives = self.cntrl[1::self.rpp]

384 secondDerivatives = self.cntrl[2::self.rpp]

385

386 xmin = positionPoints[:,0].min()

387 xmax = positionPoints[:,0].max()

388 ymin = positionPoints[:,1].min()

389 ymax = positionPoints[:,1].max()

390

391 offset = max(abs(np.append(firstDerivatives,secondDerivatives

)))

392

393 pl.plot(self.points[:,0],self.points[:,1])

394

395 ax = pl.gca()

Page 141: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 131

396

397 for pt,d1,d2 in zip(positionPoints,firstDerivatives,

secondDerivatives):

398 ax.add_patch(pl.Arrow(pt[0],pt[1],d1[0],d1[1],label=’d1’,

color=’r’,width=0.2,alpha=0.3))

399 ax.add_patch(pl.Arrow(pt[0],pt[1],d2[0],d2[1],label=’d2’,

color=’g’,width=0.2,alpha=0.3))

400

401 axis_limits = [xmin-offset,xmax+offset,ymin-offset,ymax+

offset]

402

403 pl.axis(axis_limits)

404 return

405

406

407 class HermiteSpline(Spline):

408 """

409 Classe che modella una spline di Hermite

410 """

411 def __init__(self,cntrl,npts):

412 """

413 Costruttore

414 @param cntrl punti di controllo indicanti punto posizione

e vettore derivata prima nella forma:\

415 [(x0,y0),(d0x,d0y),(x1,y1),(d1x,d1y),...]

416 @param npts numero di punti della curva da calcolare

417 """

418

419 Spline.__init__(self,cntrl,npts,2,4)

420 self.__dict__[’B’] = np.array

([[2,1,-2,1],[-3,-2,3,-1],[0,1,0,0],[1,0,0,0]],np.double)

421

422 def plot(self):

423 """

424 Metodo che effettua il plot della curva, mostrando le

condizioni di controllo

425 (derivata prima) come frecce

426 """

427

428 positionPoints = self.cntrl[::self.rpp]

429 firstDerivatives = self.cntrl[1::self.rpp]

430

431 xmin = positionPoints[:,0].min()

432 xmax = positionPoints[:,0].max()

433 ymin = positionPoints[:,1].min()

434 ymax = positionPoints[:,1].max()

435

436 offset = max(abs(np.ravel(firstDerivatives)))

437

Page 142: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

132 APPENDICE B. CODICE

438 pl.plot(self.points[:,0],self.points[:,1])

439

440 ax = pl.gca()

441

442 for pt,d1 in zip(positionPoints,firstDerivatives):

443 ax.add_patch(pl.Arrow(pt[0],pt[1],d1[0],d1[1],label=’d1’,

color=’r’,width=0.2,alpha=0.3))

444

445 axis_limits = [xmin-offset,xmax+offset,ymin-offset,ymax+

offset]

446

447 pl.axis(axis_limits)

448

449 pl.show()

450

451 return

452

453

454 class CardinalSpline(Spline):

455 """

456 Classe che modella una Cardinal spline

457 """

458

459 def __init__(self,cntrl,npts,t):

460 """

461 Costruttore

462 @param cntrl vettore indicante la posizione dei punti di

controllo

463 @param npts numero di punti della curva da calcolare

464 @param t parametro di \’tensione\’ della curva

465 """

466

467 Spline.__init__(self,cntrl,npts,1,4)

468 s = (1-t)/2

469 self.__dict__[’B’] = np.array([[-s,(2-s), (s-2), s],[2*s,(s

-3),(3-2*s),-s],[-s,0,s,0],[0,1,0,0,]],np.double)

470

471

472 class BSpline(Spline):

473 """

474 Classe che modella una BSpline

475 """

476 def __init__(self,cntrl,npts,k,x):

477 """

478 Costruttore

479 INPUT:

480 @param cntrl vettore dei vertici del poligono di

controllo

481 @param npts numero di punti della curva da calcolare

Page 143: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 133

482 @param k ordine della bspline (grado polinomio +1)

483 @param x pu essere una stringa di valore "periodic" o "

open": in questo caso

484 viene generato un vettore knot periodico o aperto

, con molteplicit k agli estremi.

485 Altrimenti x pu essere un vettore knot deciso

dall’utente, passato sotto forma di

486 lista, tupla o numpy.ndarray

487

488 ATTRIBUTI:

489 @var n numero di punti di controllo

490 @var B matrice di base nel caso si scelga il calcolo

matriciale

491 """

492

493 Spline.__init__(self,cntrl,npts,1,k)

494 self.__dict__[’n’] = len(self.cntrl)

495 self.__dict__[’x’] = self.knot(x,self.k,self.n)

496 self.__dict__[’B’] = None

497

498

499 def knot(self,x,k,n):

500 """

501 Metodo che genera un vettore knot

502 il metodo genera un nuovo vettore se il parametro x una

stringa

503 di valore ’periodic’ o ’open’. Se invece x una lista,

una tupla

504 o un istanza di numpy.ndarray che definisce un vettore

knot,

505 il metodo controlla che tale vettore sia valido,

ordinandolo

506 a priori e verificando se ci sono elementi diversi da

507 int o float all’interno di esso.

508 @param x stringa indicante il tipo di knot vector

desiderato, o un knot vector

509 @param k l’ordine della bspline

510 @param n il numero di punti di controllo

511 """

512 knotlen = k + n

513 if x is ’periodic’:

514 return np.array(range(knotlen+1),np.double)

515 elif x is ’open’:

516 left = [0]*k

517 right = [n-k+1]*k

518 center = range(1,right[0])

519 return np.array(left+center+right)

520 elif not isinstance(x,(list,tuple,np.ndarray)):

Page 144: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

134 APPENDICE B. CODICE

521 raise TypeError("Il vettore dei knot deve essere passato

sotto forma di lista, tupla o numpy.ndarray")

522 else:

523 x.sort()

524 for element in x:

525 if not isinstance(element,(int,float)):

526 raise TypeError("Il vettore knot deve essere

composto di interi o float")

527 return x

528

529 def computeBasis(self,n,k,x,t):

530 """

531 Metodo che restituisce un vettore delle funzioni di base

Ni,k(t)

532 per ogni i da 0 a numero punti di controllo -1, calcolati

tramite

533 la ricorsione di Cox - De Boor

534 INPUT:

535 @param n numero di punti di controllo

536 @param k ordine della bspline

537 @param x vettore knot

538 @param t valore del parametro

539

540 Variabili:

541 @var numKnots grandezza del vettore knot

542 @var temp vettore temporaneo su cui verr effettuata la

ricorsione di Cox-De Boor

543 """

544

545 numKnots = len(x)

546

547 # inizializzo temp con la base della ricorsione, Ni,1 = 1 se

x[i] <= t < x[i+1], 0 altrimenti

548 temp = np.array([1 if t>=x[i] and t < x[i+1] else 0 for i in

xrange(numKnots - 1)],np.double)

549

550 for k in xrange(2,k+1):

551 for i in xrange(numKnots-k):

552 # se Ni,k-1 diverso da 0

553 if temp[i]:

554 # calcolo il primo termine della ricorsione di Cox

- De Boor

555 term1 = ( (t - x[i]) / (x[i+k-1] - x[i])) * temp[i

]

556 else:

557 # altrimenti lo pongo direttamente a 0

558 term1 = 0

559 # se Ni+1,k-1 diverso da 0

560 if temp[i+1]:

Page 145: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 135

561 # calcolo il secondo termine della ricorsione di

Cox - De Boor

562 term2 = ((x[i+k] - t) / (x[i+k] - x[i+1])) * temp[

i+1]

563 else:

564 # altrimenti lo pongo direttamente a 0

565 term2 = 0

566

567 # calcolo Ni,k sommando i due termini della ricorsione

di Cox - De Boor

568 temp[i] = term1 + term2

569

570 # se il parametro coincide con l’ultimo valore knot

571 if t == x[-1]:

572 # recupero l’ultimo punto

573 temp[n-1] = 1

574

575 # restituisco i primi n elementi di temp

576 return temp[:n]

577

578 # metodo che calcola la matrice di base per una bspline

periodica,

579 # utilizzando la formula descritta a pag. 76 di Introduction to

Nurbs

580 # B[i,j] = 1/(k-1)! * comb(k-1,i) * sum per l=j a k di (k-(l+1))

^i * -1^(l-j) * comb(k,l-j)

581 # per ogni i,j appartenenti a [0,k-1]

582 # il metodo inoltre, salva in un file denominato "

order_k_periodic_BSpline_Base_Matrix.npy"

583 # dove k l’ordine della bspline, la matrice calcolata. Il primo

tentativo che il metodo

584 # effettua di caricare da file la matrice se esiste, altrimenti

la calcola e la salva,

585 # creando, utilizzo dopo utilizzo, un archivio di matrici di

base per BSpline periodiche.

586 def computePeriodicMatrix(self,k):

587 # imposto il nome del file

588 fileName = ’order_’+str(k)+’_periodic_BSpline_Basis_Matrix.

npy’

589

590 # provo a leggere il file

591 try:

592 B = np.load(fileName)

593

594 except IOError as detail:

595 # se il file non esiste, stampo un messaggio di avviso e

la matrice verr calcolata

596 print "La matrice di ordine %d non presente fra quelle

salvate, la calcolo..."%(k)

Page 146: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

136 APPENDICE B. CODICE

597 else:

598 # se nessuna eccezione ha avuto luogo, restituisco la

matrice letta da file

599 #print "La matrice di ordine %d presente fra quelle

salvate"%(k)

600 return B

601

602 # inizializzo una matrice k x k di zeri

603 B = np.zeros((k,k))

604

605 # calcolo per primo il fattore moltiplicativo 1/(k-1)! che

fisso

606 fact_coeff = 1/fact(k-1)

607

608 # inizializzo gli iteratori di indici per righe e colonne

609 rows = cols = range(k)

610

611 for i in rows:

612 # calcolo le combinazioni di k-1 su i

613 temp = comb(k-1,i)

614 # e le moltiplico per il coefficienti moltiplicativo 1/(k

-1)!

615 # ottenendo il valore della parte della formula che

moltiplica la sommatoria

616 a = fact_coeff * temp

617 for j in cols:

618 # b un accumulatore che conterr il valore della

sommatoria

619 b=0

620 # il ciclo seguente calcola la sommatoria

621 for l in range(j,self.k):

622 b += pow((self.k - (l+1)),i)*pow(-1,l-j)*comb(self

.k,l-j)

623 B[i,j] = a*b

624

625 # salvo la matrice

626 np.save(fileName,B)

627 return B

628

629 def calculateWithMatrixNotation(self):

630 # con il seguente controllo verifico se la matrice self.B

esiste e ne recupero il numero di righe e colonne

631 # se non esiste pongo il numero di righe e di colonne pari a

-1

632 r,c = self.B.shape if isinstance(self.B,np.ndarray) else

(-1,-1)

633 # se le dimensioni della matrice non corrispondono all’

attuale ordine della bspline o,

Page 147: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 137

634 # implicitamente, la matrice B ancora non esiste, calcolo la

matrice di base

635 if (r,c) != (self.k,self.k):

636 self.__dict__[’B’] = self.computePeriodicMatrix(self.k)

637 # e invoco il metodo calculate della classe Spline

638 Spline.calculate(self)

639 return

640

641

642 def calculate(self):

643

644 self.points = np.zeros((self.npts, 2))

645

646 #in caso contrario, determino i valori del parametro per cui

calcolare la curva

647 tvalues = np.linspace(self.x[self.k - 1],self.x[self.n],self.

npts)

648

649 for i,t in enumerate(tvalues):

650 N = self.computeBasis(self.n, self.k, self.x, t)

651 self.points[i] = np.dot(N, self.cntrl)

652

653

654 # sovrascrivo l’operatore di assegnazione, in modo da proteggere

gli attributi B e n

655 # e da effettuare i dovuti controlli per i rimanenti

656 def __setattr__(self, name, value):

657 if name in (’B’, ’n’):

658 print "L’attributo {0} non pu essere modificato dall’

utente direttamente.".format(name)

659 elif name is ’x’:

660 self.__dict__[’x’] = self.knot(value, self.k, self.n)

661 self.calculate()

662 elif name is ’k’:

663 if not isinstance(value,int):

664 raise TypeError("Il grado k deve essere di tipo int,

non {0}".format(type(value)))

665 elif value > len(self.cntrl):

666 raise ValueError("Il grado k pu essere al pi uguale al

numero di control points, che {0}".format(len(

self.cntrl)))

667 else:

668 self.__dict__[’k’] = value

669 self.calculate()

670 else:

671 Spline.__setattr__(self,name,value)

672 if name is ’cntrl’:

673 self.__dict__[’n’] = len(self.cntrl)

674 self.__dict__[’x’] = self.knot(’periodic’,self.k,self.n)

Page 148: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

138 APPENDICE B. CODICE

675

676

677 class Nurbs(BSpline):

678 """

679 Classe che modella una curva NURBS

680 """

681 def __init__(self,cntrl,npts,k,x,h):

682 """

683 Costruttore

684 @param h lista, tupla o numpy.ndarray contenente i pesi

dei punti di controllo

685 """

686

687 BSpline.__init__(self,cntrl,npts,k,x)

688

689 # se h non n una lista, n una tupla, n un numpy.ndarray

690 if not isinstance(h,(list,tuple,np.ndarray)):

691 raise TypeError("h deve essere una lista, una tupla o un

numpy.ndarray, non di tipo {0}\n".format(type(h)))

692 elif len(h) != len(self.cntrl):

693 raise ValueError("h ha lunghezza {0} mentre cntrl ha

lunghezza {1}".format(len(h),len(self.cntrl)))

694 self.__dict__[’h’] = h

695

696 def computeBasis(self,n,k,x,h,t):

697

698 # calcolo le funzioni di base non razionali

699 temp = BSpline.computeBasis(self,n,k,x,t)

700 # moltiplico le funzioni di base per i pesi

701 temp = np.asarray([h[i]*basis for i,basis in enumerate(temp)

])

702 # divido tutto per la somma degli hi*Ni,k(t), ottenendo la

base razionale

703 temp = temp/sum(temp)

704 return temp

705

706 def calculate(self):

707 tvalues = np.linspace(self.x[self.k - 1],self.x[self.n],self.

npts)

708

709 for i,t in enumerate(tvalues):

710 N = self.computeBasis(self.n,self.k,self.x,self.h,t)

711 self.points[i] = np.dot(N,self.cntrl)

712

713 def __setattr__(self,name,value):

714 if name is ’h’:

715 if not isinstance(value,(list,tuple,np.ndarray)):

716 raise TypeError("h deve essere una lista, una tupla o

un istanza di numpy.ndarray, non di tipo {0}".

Page 149: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 139

format(type(h)))

717 elif len(value) != len(self.cntrl):

718 raise ValueError("h ha lunghezza {0} mentre cntrl ha

lunghezza {1}".format(len(value),len(self.cntrl)))

719 self.__dict__[name] = value

720 self.calculate()

721 else:

722 BSpline.__setattr__(self,name,value)

723

724 def __add__(self,c):

725 new_curve = Curve.__add__(self,c)

726 new_curve.h = np.append(self.h,c.h[1:])

727 return new_curve

728

729

730 class CurveFit(BSpline):

731 ’’’

732 Classe che offre i metodi necessari

733 a determinare i vertici di un poligono di controllo

734 tale da generare una curva BSpline che interpoli

735 i punti della curva dati in input.

736 ’’’

737

738 def __init__(self,curvePoints,numCpoints,npts,k):

739 ’’’

740 Metodo costruttore

741 INPUT:

742 @param curvePoints lista contenente i punti della curva

noti, nella forma [[x0,y0],[x1,y1],...,[xn,yn]]

743 oppure nome del file contenente i punti.

744 @param numCpoints numero di vertici desiderato per il

poligono di controllo

745 @param npts numero di punti della curva da calcolare

746 @param k ordine della curva

747 @param x vettore knot o stringa indicante il tipo di

vettore knot desiderato, ’periodic’ o ’open’

748 ’’’

749

750

751 BSpline.__init__(self,np.zeros((numCpoints,2)),npts,k,’open’)

752 # se curvePoints una stringa

753 if isinstance(curvePoints,str):

754 try:

755 # provo a leggere i punti dal file il cui nome

indicato da curvePoints

756 self.__dict__[’curvePoints’] = np.loadtxt(curvePoints)

.view(Points)

757 except IOError as detail:

Page 150: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

140 APPENDICE B. CODICE

758 print "Errore nel leggere dal file {0}: {1}".format(

curvePoints,detail)

759 raise

760 else:

761 self.__dict__[’curvePoints’] = np.asarray(curvePoints).

view(Points)

762

763 self.__dict__[’cntrl’] = self.getControlPoints()

764

765

766 def getParametersValue(self,pvec):

767 ’’’ Metodo che calcola il valori del parametro associati

768 ai punti noti della curva.

769 @param pvec vettore contente i punti della curva

770 ’’’

771 # determino il numero di punti noti della curva

772 n = len(pvec)

773

774 # inizializzo il vettore che alla fine dell’esecuzione

conterr i valori del parametro

775 t = np.zeros(n,np.double)

776

777 # dato che t0 = 0, inizio il ciclo da 1

778 for i in range(1,n):

779

780 # il valore del parametro associato a t[i] uguale al

valore del parametro

781 # associato a p[i-1] pi la distanza fra p[i] e p[i-1]

782 t[i] = t[i-1] + pvec[i].distance(pvec[i-1])

783

784 # divido il vettore per l’ultimo valore che contiene (che il

valore pi grande)

785 # in modo da normalizzare il vettore nell’intervallo [0,1]

786 t = t/t[-1]

787

788 return t

789

790 def getNMatrix(self,x,t,n):

791 """

792 Metodo che calcola la matrice N contenente

793 le funzioni di base Ni,k(t)

794 @param x vettore knot

795 @param t valore del parametro

796 @param n numero di punti di controllo

797 """

798

799 #scalo il vettore knot nell’intervallo [0,1]

800 x = np.asarray(x,np.double)

801 x = x / x[-1]

Page 151: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.1. CURVE.PY 141

802

803 # determino il numero di punti

804 numPoints = len(t)

805

806 # inizializzo la matrice come matrice numPoints x n di zeri

807 N = np.zeros((numPoints,n))

808

809 # per i che va da 0 a numPoints-1

810 for i in range(numPoints):

811 # l’i-esima riga di N uguale alle funzioni di base

associate al parametro t[i]

812 # si ricordi che computeBasis restituisce un vettore

contenete le funzioni base

813 # Ni,k(u) per ogni i che va da 0 a n

814 N[i] = self.computeBasis(n,self.k,x,t[i])

815 return N

816

817

818 def getControlPoints(self):

819 ’’’

820 Metodo che determina i vertici del poligono di controllo

821 ’’’

822

823 # calcolo i valori del parametro associati ai punti della

curva noti

824 t = self.getParametersValue(self.curvePoints)

825

826 # determino la matrice N contenente le funzioni di base

827 N = self.getNMatrix(self.x, t, self.n)

828

829 # Pongo D = al vettore dei punti della curva noti.

830 # assegnazione fatta solo per una quetione di leggibili

831 D = self.curvePoints

832

833 # determino la dimensione della matrice N

834 r,c = N.shape

835

836 # se la matrice non quadrata

837 if(r != c):

838 # moltiplico la matrice dei termini noti e la matrice N

per la trasposta di N,

839 # in modo da ottenere una matrice dei coefficienti

quadrata

840 D = np.dot(N.T,D)

841 N = np.dot(N.T,N)

842

843 # inizializzo la matrice B, che conterr i punti di controllo

844 B = np.zeros((self.n,2))

845

Page 152: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

142 APPENDICE B. CODICE

846 # provo a risolvere il sistema di equazioni

847 try:

848 B = np.linalg.solve(N,D)

849

850 # catturo l’eccezione dovuta ad un eventuale matrice

singolare

851 except np.linalg.LinAlgError as detail:

852 print "Errore: la matrice N singolare: {0}".format(

detail)

853

854 return B

855

856 def plot(self,**args):

857 pl.plot(self.curvePoints[:,0],self.curvePoints[:,1],’go’)

858 BSpline.plot(self,**args)

859 return

860

861 def __setattr__(self,name,value):

862 if name is ’curvePoints’:

863 if not isinstance(value,(list,tuple,np.ndarray)):

864 raise TypeError("{0} deve essere una lista,tupla o

istanza di numpy.ndarray, non {1}".format(name,

type(value)))

865 else:

866 self.__dict__[name] = value

867 self.__dict__[’cntrl’] = self.getControlPoints()

868 self.calculate()

869 elif name is ’n’:

870 if not isinstance(value,int):

871 raise TypeError("L’attributo n deve essere un intero,

non un {0}".format(type(value)))

872 else:

873 self.__dict__[name] = value

874 self.__dict__[’cntrl’] = self.getControlPoints()

875 self.calculate()

876 else:

877 BSpline.__setattr__(self,name,value)

B.2 Surface.py

1 #-*- coding: utf-8 -*-

2 #

3 # Scritto da Sanfelice Antonio

4 # (c) 2010

5

6 """

7 @package PyNurbsSurface

Page 153: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.2. SURFACE.PY 143

8 @brief Insieme di classi che consentono di calcolare diversi tipi di

superfici parametriche.

9

10 Questa parte della libreria consente di calcolare i seguenti tipi di

superfici parametriche:

11 - Bzier

12 - BSpline

13 - NURBS

14 """

15 try:

16 import numpy as np

17 from Util import *

18 from Curve import Points,Bezier,Spline,BSpline,CurveFit,Nurbs

19 from mpl_toolkits.mplot3d.axes3d import Axes3D

20 import pylab as pl

21 except ImportError as detail:

22 print "Errore nell’importare le librerie:\n",detail

23

24

25 class Surface:

26 """

27 Classe interfaccia che definisce i parametri e i metodi

28 di base che devono essere implementati per modellare

29 una superfice.

30 """

31

32 def __init__(self, cntrl, npts, mpts):

33 """

34 Metodo Costruttore

35

36 INPUT:

37 @param cntrl vettore dei punti di controllo da cui

generare la superfice

38 @param npts numero di linee parametriche lungo la

direzione u

39 @param mpts numero di linee parametriche lungo la

direzione w

40 """

41

42 self.__dict__[’mpts’] = mpts

43

44 #self.points ora ha 3 dimensioni e dovr contenere npts * mpts

punti

45 self.__dict__[’points’] = np.zeros((self.npts * self.mpts, 3)

).view(Points)

46

47 if isinstance(cntrl,str):

48 self.loadFromFile(cntrl)

49

Page 154: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

144 APPENDICE B. CODICE

50 elif not(self.cntrl.ndim == 3):

51 raise PyNurbsError("I punti di controllo della superfice

devono essere tridimensionali")

52

53

54 def __setattr__(self, name, value):

55 """

56 Metodo che sovrascrive l’operatore di assegnazione

57 per gli attributi, permettendo di eseguire funzioni

58 in caso di cambiamenti

59 @param name il nome dell’attributo che deve essere

settato

60 @param value il valore che si vuole assegnare al

parametro

61 """

62

63

64 if name is ’mpts’:

65 self.__dict__[name] = value

66 self.calculate()

67

68 def loadFromFile(self, nomeFile):

69 if not isinstance(nomeFile,str):

70 raise TypeError(’nomeFile non una stringa ma un {0}’.

format(type(nomeFile)))

71

72 try:

73 tmp = np.loadtxt(nomeFile)

74 except IOError as detail:

75 print "Errore nel caricare i dati dal file {0}: {1}".

format(nomeFile,detail)

76

77 n, m, k, c = tmp[-1]

78 self.__dict__[’cntrl’] = tmp[:-1].reshape((n,m,k))

79

80

81

82

83 def plot(self, plotcp=False, eps=1, dpi=100):

84 ’’’

85 Metodo che effettua il plot della superfice

86 INPUT:

87 @param dpi dot per inch desiderati per l’immagine del

plot

88 @param eps spaziatura fra una linea e la successiva della

superfice

89

90

Page 155: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.2. SURFACE.PY 145

91 se plotcp == True mostra nel plot anche il poligono di

controllo

92 che ha generato la superfice

93 eps indica quanto preciso si vuole la superfice, pi eps si

avvicina ad 1

94 pi strette saranno le mesh

95 dpi indica la grandezza che si desidera per il plot, in punti

per pollice

96 ’’’

97

98

99 X = self.points[:, 0].reshape(self.npts, self.mpts)

100 Y = self.points[:, 1].reshape(self.npts, self.mpts)

101 Z = self.points[:, 2].reshape(self.npts, self.mpts)

102

103 fig = pl.figure(1, dpi = dpi)

104 ax = Axes3D(fig)

105

106 ax.plot_surface(X, Y, Z,cstride = eps, rstride = eps)

107 if(plotcp):

108 ax.plot_wireframe(self.cntrl[:, :, 0], self.cntrl[:, :,

1], self.cntrl[:, :, 2], color="#cc0000")

109 ax.set_xlabel("X")

110 ax.set_ylabel("Y")

111 ax.set_zlabel("Z")

112 pl.axis(’equal’)

113 pl.show()

114 return

115

116

117 class BezSurf(Bezier, Surface):

118 """

119 Classe che modella una superfice di Bzier

120

121 """

122 def __init__(self, cntrl, npts, mpts):

123 ’’’

124 Metodo costruttore

125 @param cntrl matrice contenente le coordinate dei vertici

del poligono di controllo

126 @param npts numero di curve parametriche da calcolare

lungo la direzione u

127 @param mpts numero di curve parametriche da calcolare

lungo la direzione w

128 ’’’

129 Bezier.__init__(self, cntrl, npts)

130 Surface.__init__(self, cntrl, npts, mpts)

131

132 self.__dict__[’n’], self.__dict__[’m’] = self.cntrl.shape[:2]

Page 156: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

146 APPENDICE B. CODICE

133

134 self.__dict__[’u_degree’] = self.n - 1

135 self.__dict__[’w_degree’] = self.m - 1

136

137 def loadFromFile(self, nomeFile):

138 Surface.loadFromFile(self,nomeFile)

139

140

141 def calcWithBernstein(self):

142 ’’’

143 Metodo che calcola la superfice di Bzier utilizzando la

definizione formale

144 ’’’

145

146 self.points = np.zeros((self.npts * self.mpts, 3))

147

148 # determino i valori dei parametri

149 upts = np.linspace(0, 1, self.npts)

150 wpts = np.linspace(0, 1, self.mpts)

151

152 # inizializzo un contatore di riga

153 iCount = 0

154

155 # per ogni valore del parametro u

156 for u in upts:

157 # per ogni valore del parametro w

158 for w in wpts:

159 # per ogni i da 0 a n-1

160 for i in xrange(self.n):

161 # calcolo la funzione di base per la direzione u

162 jni = self.bernstein(self.u_degree, i, u)

163

164 # per ogni j da 0 a m-1

165 for j in xrange(self.m):

166 # calcolo la funzione di base per la direzione

w

167 kmj = self.bernstein(self.w_degree, j, w)

168 # l’iCount-esimo punto della superfice uguale

al prodotto tra il punto di controllo

169 # i,j per la funzione di base i nella

direzione u e la funzione di base j nella

direzione w

170 self.points[iCount] += self.cntrl[i, j] * jni

* kmj

171 iCount+=1

172

173 def __setattr__(self, name, value):

174 if name in(’u_degree’, ’w_degree’):

Page 157: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.2. SURFACE.PY 147

175 print "Impossibile impostare manualmente il valore di {0}

".format(name)

176 else:

177 Bezier.__setattr__(self, name, value)

178

179 def plot(self, plotcp = False):

180 Surface.plot(self, plotcp)

181 return

182

183

184 class BSplineSurf(Surface, BSpline):

185 """

186 Classe che modella una superfice Spline

187 """

188 def __init__(self, cntrl, npts, mpts, k, l, x, y):

189 """

190 Costruttore

191 @param cntrl matrice dei punti di controllo

192 @param npts numero di punti della superfice da calcolare

lungo la direzione u

193 @param mpts numero di punti della superfice da calcolare

lungo la direzione v

194 @param k ordine della bspline lungo la direzione u

195 @param l ordine della bspline lungo la direzione v

196 @param x vettore knot per la direzione u o stringa di

valore ’open’ oppure ’periodic’

197 @param y vettore knot per la direzione w o stringa di

valore ’open’ oppure ’periodic’

198 """

199 BSpline.__init__(self, cntrl, npts, k, x)

200 Surface.__init__(self, cntrl, npts, mpts)

201 self.__dict__[’n’], self.__dict__[’m’] = self.cntrl.shape[:2]

202 self.__dict__[’l’] = l

203 self.__dict__[’y’] = self.knot(y, self.l, self.m)

204

205 def calculate(self):

206

207 self.points = np.zeros((self.npts * self.mpts, 3)).view(

Points)

208

209 # determino i valori per i parametri u e v

210 uvalues = np.linspace(self.x[self.k - 1], self.x[self.n],

self.npts)

211 vvalues = np.linspace(self.y[self.l - 1], self.y[self.m],

self.mpts)

212

213 ptCount = 0

214 for u in uvalues:

215 # calcolo le basi per la direzione u, Ni,k(u)

Page 158: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

148 APPENDICE B. CODICE

216 u_basis = self.computeBasis(self.n, self.k, self.x, u)

217

218 for v in vvalues:

219 # calcolo la funzione di base per la direzione v, Mj,l

(v)

220 v_basis = self.computeBasis(self.m, self.l, self.y, v)

221

222 # calcolo le coordinate del punto, moltiplicando la

matrice

223 # dei punti di controllo per i vettori delle funzioni

di base

224 x = np.dot(u_basis, np.dot(self.cntrl[:,:,0], v_basis)

)

225 y = np.dot(u_basis, np.dot(self.cntrl[:,:,1], v_basis)

)

226 z = np.dot(u_basis, np.dot(self.cntrl[:,:,2], v_basis)

)

227 self.points[ptCount] = Points([[x,y,z]])

228 ptCount+=1

229

230

231 def calculateWithMatrixNotation(self):

232 """

233 Metodo che calcola i punti della superfice

234 tramite il prodotto matriciale

235 $[u][N][cntrl][M]^T[v]$

236 """

237

238 # dato che il calcolo viene effettuato generando delle "

sottosuperfici" k*l

239 # devo determinare il numero di tratti in entrambe le

direzioni u e v

240 # in modo da determinare il numero di punti da calcolare per

ogni singola

241 # sotto superfice

242 num_tratti_u = self.n - self.k + 1

243 num_tratti_v = self.m - self.l + 1

244

245 # divido il numero di punti da calcolare per il numero di

tratti lungo le due direzioni

246 scaled_npts = self.npts / num_tratti_u

247 scaled_mpts = self.mpts / num_tratti_v

248

249 # aggiorno npts e mpts, dato che nel determinare il numero di

punti

250 # da calcolare per singolo tratto, ci sono stati degli

arrotondamenti

251 # e quindi non verranno calcolati esattamente npts e mpts

punti lungo le due direzioni

Page 159: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.2. SURFACE.PY 149

252 # lasciare invariati npts e mpts porterebbe alla

visualizzazione di artefatti durante

253 # plotting

254 self.__dict__[’npts’] = scaled_npts * num_tratti_u

255 self.__dict__[’mpts’] = scaled_mpts * num_tratti_v

256

257 # aggiorno la grandezza del vettore points

258 self.__dict__[’points’] = np.zeros((self.npts * self.mpts,3))

259

260 # determimo le matrici di base per entrambe le direzioni, N e

M

261 N = self.computePeriodicMatrix(self.k)

262 M = self.computePeriodicMatrix(self.l)

263

264 # determino i valori dei parametri per cui calcolare la curva

265 u = np.arange(0, 1, 1/float(scaled_npts))

266 v = np.arange(0, 1, 1/float(scaled_mpts))

267

268 ptCount = 0

269 for s in xrange(num_tratti_u):

270

271 # determino la sottomatrice di controllo contenente le

righe dalla s alla s+k

272 cntrl_subnet_rows = self.cntrl[s:s + self.k, :, :]

273

274 # per ogni u

275 for i in u:

276 # determino il polinomio in u

277 ipoly = upoly(i, self.k)

278 left = np.dot(ipoly, N)

279

280 for t in xrange(num_tratti_v):

281 # determino la sottomatrice di cntrl_subnet_rows

contenente solo le colonne dalla t alla t+l

282 cntrl_subnet = cntrl_subnet_rows[:, t:t + self.l,

:]

283

284 # calcolo la prima parte del prodotto matriciale

285 left_x = np.dot(left, np.dot(cntrl_subnet[:, :,

0], M.T))

286 left_y = np.dot(left, np.dot(cntrl_subnet[:, :,

1], M.T))

287 left_z = np.dot(left, np.dot(cntrl_subnet[:, :,

2], M.T))

288

289 # for j in v

290 for j in v:

291

292 # determino il polinomio in v

Page 160: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

150 APPENDICE B. CODICE

293 jpoly = upoly(j, self.l)

294

295 # completo il calcolo moltiplicando la parte

sinistra per la parte mancante,

296 # ovvero il polinomio in v

297 x = np.dot(left_x, jpoly)

298 y = np.dot(left_y, jpoly)

299 z = np.dot(left_z, jpoly)

300

301 # aggiungo le coordinate x,y,z trovate ai

punti della superfice

302 self.points[ptCount] = Points([[x, y, z]])

303 ptCount += 1

304

305

306 def __setattr__(self, name, value):

307 if name in (’k’,’l’):

308 maxOrder = {’k’: self.n, ’l’: self.m}[name]

309 if not isinstance(value, int):

310 raise TypeError("{0} deve essere di tipo int, non {1}"

.format(name, type(value)))

311 elif value > maxOrder:

312 raise ValueError("{0} pu essere al pi {1} con l’

attuale poligono di controllo".format(name,

maxOrder))

313 else:

314 BSpline.__setattr__(self, name, value)

315 Surface.__setattr__(self, name, value)

316

317

318 class NurbsSurf(BSplineSurf):

319 """

320 Classe che modella una superfice NURBs

321 """

322 def __init__(self,cntrl,npts,mpts,k,l,x,y):

323 ’’’

324 Metodo costruttore

325 @param cntrl array nxmx4 contenente le coordinate

omogenee dei punti di controllo, pesi compresi

326 @param npts numero di linee parametriche lungo la

direzione u

327 @param npts numero di linee parametriche lungo la

direzione w

328 @param k ordine della b-spline razionale lungo la

direzione u

329 @param l ordine della b-spline razionale lungo la

direzione w

330 @param x vettore knot per la direzione u, si possono

passare i valori ’open’, ’periodic’ o direttamente il

Page 161: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.2. SURFACE.PY 151

vettore

331 @param y vettore knot per la direzione w, si possono

passare i valori ’open’, ’periodic’ o direttamente il

vettore

332 ’’’

333 BSplineSurf.__init__(self,cntrl,npts,mpts,k,l,x,y)

334

335 # variabile test che serve a controllare se uno dei parametri

della superfice stato cambiato

336 # utile nell’algoritmo che ricava la superfice nurbs per

evitare calcoli inutili

337 self.__dict__[’itest’] = -1

338

339 # array che conterranno le funzioni di base per ogni punto

della superfice lungo

340 # le due direzioni

341 self.__dict__[’niku’] = []

342 self.__dict__[’mjlw’] = []

343

344 # array che conterr la i valori della funzione Sum(u,w) per

ogni valore di u e w

345 # la funzione sum(u,w) la funzione che compare al

denominatore nel calcolo della base

346 # razionale delle nurbs

347 self.__dict__[’sumuw’] = []

348

349 # variabile booleana che controlla se c’ stato un cambiamento

nei pesi

350 self.__dict__[’weightsAreChanged’] = True

351

352 # se la matrice dei punti di controllo non ha 4 coordinate

per punto

353 if not(self.cntrl.shape[2] == 4):

354 raise PyNurbsError("I punti di controllo della superfice

devono essere quadridimensionali")

355

356 def changeWeight(self,i,j,h):

357 ’’’

358 Metodo che consente di variare il singolo peso di un

punto di controllo

359 Sebbene sia possibile effettuare questo cambio accedendo

direttamente

360 all’attributo cntrl, ci porterebbe a dei problemi con l’

algoritmo

361 utilizzato dal metodo calculate. Questo metodo infatti

segnala se

362 c’ stato un cambiamento nei pesi.

363 ’’’

364 self.cntrl[i,j,3] = h

Page 162: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

152 APPENDICE B. CODICE

365 self.__dict__[’weightsAreChanged’] = True

366

367 def calculate(self):

368 # se uno fra n, m, k, l, npts, mpts cambia, ricalcolo le

funzioni di base

369 if self.itest != (self.n + self.m + self.npts + self.mpts +

self.k + self.l):

370

371 upts = np.linspace(self.x[self.k - 1], self.x[self.n],

self.npts)

372 wpts = np.linspace(self.y[self.l - 1], self.y[self.m],

self.mpts)

373

374 # calcolo le funzioni di base per ogni valore di u e le

salvo in un array

375 for u in upts:

376 base = self.computeBasis(self.n, self.k, self.x, u)

377 self.niku.append(base)

378

379 # calcolo le funzioni di base per ogni valore di w e le

salvo in un array

380 for w in wpts:

381 base = self.computeBasis(self.m, self.l, self.y, w)

382 self.mjlw.append(base)

383

384 self.itest = self.n + self.m + self.k + self.l + self.

npts + self.mpts

385

386 if self.weightsAreChanged:

387 # calcolo la funzione somma di prodotti di funzioni di

base per pesi dei punti di controlli

388 for nbasis in self.niku:

389 for mbasis in self.mjlw:

390 s = 0

391 for i in xrange(self.n):

392 for j in xrange(self.m):

393 s += (self.cntrl[i,j,3] * nbasis[i] *

mbasis[j])

394 # conservo il reciproco in modo da non effettuare

divisoni durante il loop principale

395 # per il calcolo della superfice

396 self.sumuw.append(1./s)

397 self.__dict__[’weightsAreChanged’] = False

398

399 ptCount = 0

400 for nbasis in self.niku:

401 for mbasis in self.mjlw:

402 for i in range(self.n):

Page 163: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.2. SURFACE.PY 153

403 # se la funzione di base i-esima 0 evito il

calcolo

404 if nbasis[i] != 0:

405 for j in range(self.m):

406 # se la funzione di base j-esima 0 evito

il calcolo

407 if mbasis[j] != 0:

408 # calcolo la base razionale (self.cntrl

[i,j,3] il peso del punto di

controllo i,j)

409 rbasis = self.cntrl[i,j,3] * nbasis[i]

* mbasis[j] * self.sumuw[ptCount]

410

411 # aggiorno il punto della superfice che

si sta calcolando

412 # si noti che self.cntrl[i,j,:3]

contiene solo le coordinate x,y,z e

non il peso

413

414 self.points[ptCount] += Points(self.

cntrl[i,j,:3] * rbasis)

415

416 ptCount += 1

417

418

419 class SurfaceFit(CurveFit,BSplineSurf):

420 """

421 Classe che consente di determinare

422 una superfice BSpline che approssima

423 dei punti dati in input

424 """

425

426 def __init__(self, surfacePoints, n, m, npts, mpts, k, l):

427

428 ’’’

429 Metodo Costruttore:

430 INPUT:

431 @param surfacePoints punti noti appartenenti alla

superfice o nome del file che li contiene

432 @param n numero di punti di controllo da calcolare lungo

la direzione u

433 @param m numero di punti di controllo da calcolare lungo

la direzione v

434 @param npts numero di punti della superfice da calcolare

lungo la direzione u

435 @param mpts numero di punti della superfice da calcolare

lungo la direzione v

436 @param k ordine della BSpline per la direzione u

437 @param l ordine della BSpline per la direzione v

Page 164: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

154 APPENDICE B. CODICE

438 ’’’

439 BSplineSurf.__init__(self, np.zeros((n, m, 3)), npts, mpts, k

, l, ’open’,’open’)

440 if isinstance(surfacePoints, str):

441 try:

442 self.__dict__[’surfacePoints’] = np.loadtxt(

surfacePoints)

443 except IOError as detail:

444 print "Errore nel leggere i dati dal file {0}: {1}".

format(surfacePoints,detail)

445 raise

446 elif not isinstance(surfacePoints, (list, tuple, np.ndarray))

:

447 raise TypeError("Errore, il parametro surfacePoints deve

essere una lista,\

448 una tupla o un istanza di np.ndarray, non

di tipo {0}".format(type(surfacePoints

)))

449 else:

450 self.__dict__[’surfacePoints’] = np.asarray(surfacePoints

).view(Points)

451

452 self.__dict__[’cntrl’] = self.getControlPoints()

453

454 def loadFromFile(self, nomeFile):

455 BSplineSurf.loadFromFile(self, nomeFile)

456

457 def getParametersMatrix(self):

458 ’’’

459 Metodo che calcola i valori del parametro

460 associati a tutti i punti contenuti in self.surfacePoints.

461 La matrice ha dimensione r x s, dove r il numero di punti

462 della superfice noti lungo la direzione u, e s il numero

di

463 punti della superfice noti lungo la direzione w.

464 ’’’

465 # determino la dimensione della matrice dei punti noti della

superfice

466 r,s = self.surfacePoints.shape[:2]

467

468 # inizializzo uw come matrice r x s x 2 di zeri

469 # la dimensione 2 dovuta al fatto che l’ingresso i,j della

matrice

470 # conterr il valore del parametro ui e del parametro wj

471 uv = np.zeros((r, s, 2),np.double)

472

473 # per ogni colona della matrice self.surfacePoints

474 for i in range(s):

475

Page 165: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.2. SURFACE.PY 155

476 # determino il valore del parametro u

477 uv[:,i,0] = self.getParametersValue(self.surfacePoints[:,

i])

478

479 # per ogni riga della matrice self.surfacePoints

480 for i in range(r):

481

482 # determino il valore del parametro w

483 uv[i,:,1] = self.getParametersValue(self.surfacePoints[i

,:])

484

485 return uv

486

487 def getNMatrix(self):

488 ’’’

489 Metodo che calcola la matrice contenente i prodotti delle

funzioni di base

490 Nik*Mjl, che verr utilizzata come matrice dei

coefficienti nella risoluzione del sistema

491 di equazioni N cntrl = D

492 ’’’

493

494 # scalo i vettori knot nell’intervallo [0,1]

495 x = np.asarray(self.x,np.double)

496 x = x / x[-1]

497

498 y = np.asarray(self.y,np.double)

499 y = y / y[-1]

500

501

502 # determino righe e colonne della matrice self.surfacePoints

503 r,s = self.surfacePoints.shape[:2]

504

505 # inizializzo N come matrice di zeri r*s x n*m

506 N = np.zeros((r * s, self.n * self.m), np.double)

507

508 # determino i valori dei parametri u e w per i punti noti

della superfice

509 uv = self.getParametersMatrix()

510

511 # inizializzo un contatore riga per la matrice C

512 rowCount = 0

513

514 # per uparam che va da 0 a r-1

515 for uparam in range(r):

516

517 # per wparam che va da 0 a s-1

518 for wparam in range(s):

519

Page 166: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

156 APPENDICE B. CODICE

520 # Qui calcolo una riga della matrice C, contenente

tutti i prodotti Ni,k(uparam)*Mj,l(wparam)

521

522 # estraggo dalla matrice uw i valori u e w che mi

servono, pi per una questione di leggibilit

523 # che per altro

524 u = uv[uparam, wparam, 0]

525 v = uv[uparam, wparam, 1]

526

527 #determino le funzioni di base Nk e Ml

528 Nbasis = self.computeBasis(self.n, self.k, x, u)

529 Mbasis = self.computeBasis(self.m, self.l, y, v)

530

531 # inizializzo un contatore colonna per la matrice C

532 colCount = 0

533

534 # per ogni funzione di base N

535 for Nik in Nbasis:

536

537 # e per ogni funzione di base M

538 for Mjl in Mbasis:

539

540 # calcolo l’entrata della matrice N

541 N[rowCount, colCount] = Nik * Mjl

542

543 colCount+=1

544

545 rowCount+=1

546

547 return N

548

549

550 def getControlPoints(self):

551 ’’’

552 Metodo che calcola i punti di controllo necessari per

calcolare la superfice

553 ’’’

554 r, s = self.surfacePoints.shape[:2]

555

556 # nel seguente frammento di codice, opero dei

redimensionamenti alle matrici

557 # che compongono il sistema di equazioni. Dato che N una

matrice r*s x n*m

558 # D deve essere una matrice r*s x 3, e B deve essere una

matrice n*m x 3

559 # ricordiamo che r x s la dimensione della matrice

contenente i punti noti

560 # della superfice, mentre n e m sono il numero di punti di

controllo

Page 167: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.3. UTIL.PY 157

561 # lungo le direzioni u e v

562

563 D = self.surfacePoints.reshape(r * s, 3)

564 cntrl = np.zeros((self.n * self.m, 3))

565

566 # determino la matrice C

567 N = self.getNMatrix()

568

569 # recupero il numero di righe e colonne della matrice C

570 rows,columns = N.shape

571

572 #se la matrice non quadrata

573 if(rows != columns):

574 print "La matrice N non quadrata"

575 # Aggiusto le matrici C e D moltiplicandole per C

trasposto

576 D = np.dot(N.T,D)

577 N = np.dot(N.T,N)

578

579 # determino B risolvendo il sistema N cntrl = D

580 try:

581 cntrl = np.linalg.solve(N,D)

582 except np.linalg.LinAlgError as detail:

583 print "Matrice Singolare: {0}".format(detail)

584

585 # sistemo le dimensioni di cntrl, dato che nel metodo

calculate() cntrl deve essere una matrice n x m x 3

586 cntrl = cntrl.reshape((self.n, self.m, 3))

587 return cntrl

588

589 def calculate(self):

590 BSplineSurf.calculate(self)

591

592 def calculateWithMatrixNotation(self):

593 BSplineSurf.calculateWithMatrixNotation(self)

594

595 def plot(self, **args):

596 BSplineSurf.plot(self, **args)

B.3 Util.py

1 # -*- coding: utf-8 -*-

2 #

3 # Scritto da Sanfelice Antonio

4 # (c) 2010

5

6 import numpy as np

Page 168: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

158 APPENDICE B. CODICE

7

8 class PyNurbsError(Exception):

9 def __init__(self,description):

10 self.description = description

11 def __str__(self):

12 return self.description

13

14

15 def upoly(u,k):

16 """

17 Metodo che restituisce un array di k elementi

18 contenente le potenze di u da 0 a k-1

19 nella forma array([pow(u,k-1), pow(u,k-2),...,pow(u,2),u,1])

20 """

21 try:

22 u = float(u)

23 k = int(k)

24 except ValueError as detail:

25 print "Errore di tipo: ",detail

26 return

27 # restituisco un array contenente pow(u,i) per ogni i da k-1 a

2,

28 # concatenato a u,1. (evito di fargli calcolare le potenze per i

= 1,0)

29 return np.asarray([pow(u,i) for i in xrange(k-1,1,-1)]+[u,1],np.

double)

30

31 def deg2rad(angle):

32 return np.pi * angle / 180.

33

34 def rad2deg(angle):

35 return angle * 180./np.pi

36

37 def htm(rot_x = 0, rot_y = 0, rot_z = 0, t_x = 0, t_y = 0, t_z = 0,

px = 1, py = 1, pz = 1, scale = 0):

38

39 """

40 Metodo che restituisce una matrice di trasformazione omogenea

41 @param rot_x angolo di rotazione intorno all’asse x

42 @param rot_y angolo di rotazione intorno all’asse y

43 @param rot_z angolo di rotazione intorno all’asse z

44 @param t_x unit traslazione lungo l’asse x

45 @param t_y unit traslazione lungo l’asse y

46 @param t_z unit traslazione lungo l’asse z

47 @param px cambio di prospettiva rispetto a x

48 @param py cambio di prospettiva rispetto a y

49 @param pz cambio di prospettiva rispetto a z

50 @param scale fattore di scalatura

51 """

Page 169: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

B.3. UTIL.PY 159

52 mat = np.eye(4)

53 mat[3,3] = scale

54 mat[3,:3] = np.asarray(px, py, pz)

55 mat[:3,3] = np.asarray(t_x, t_y, t_z)

56

57 mat[1,1] = mat[2,2] = np.cos(rot_x)

58 sn = np.sin(rot_x)

59 mat[1,2] = -sn

60 mat[2,1] = sn

61

62 cs = np.cos(rot_y)

63 mat[0,0] = cs

64 mat[2,2] += cs

65 sn = np.sin(rot_y)

66 mat[0,2] = sn

67 mat[2,0] = -sn

68

69 cs = np.cos(rot_z)

70 mat[0,0] += cs

71 mat[1,1] += cs

72 sn = np.sin(rot_z)

73 mat[0,1] = -sn

74 mat[1,0] = sn

75

76 return mat

Page 170: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

160 APPENDICE B. CODICE

Page 171: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Elenco delle figure

1.1 Esempio di superfice NURBS in Blender [1] . . . . . . . . . . 2

2.1 Una spirale e una curva polidroma . . . . . . . . . . . . . . . 6

2.2 Differenze fra diverse condizioni di continuita [12] . . . . . . . 10

2.3 Funzioni di base della spline cubica naturale . . . . . . . . . . 15

2.4 Funzioni di base della spline cubica di Hermite . . . . . . . . 16

2.5 Spline cardinale con diversi valori del parametro di tensione(Immagini ottenuta tramite la libreria da me sviluppata) . . . . . . . 17

2.6 Funzioni di base per una curva di Bezier con 5 punti di controllo 19

2.7 Effetto del vettore dei nodi sulle B-Spline . . . . . . . . . . . 22

2.8 Nurbs con diversi valori dei pesi . . . . . . . . . . . . . . . . . 27

2.9 Esempio di superficie di Bezier (Immagine ottenute tramite la

libreria da me sviluppata) . . . . . . . . . . . . . . . . . . . . . . 30

2.10 Superfici B-Spline con diversi vettori dei nodi . . . . . . . . . 32

2.11 Superfici B-spline razionali con variazione di un peso . . . . . 36

3.1 Esempio di plot della funzione sin(x) con matplotlib . . . . . 49

3.2 Esempi di plot wireframe e superfice completa . . . . . . . . . 51

5.1 Esempio di output della classe Bezier . . . . . . . . . . . . . 99

5.2 Esempio di output della classe B-Spline . . . . . . . . . . . . 101

5.3 Esempio di output della classe Nurbs . . . . . . . . . . . . . . 102

5.4 test della classe CurveFit . . . . . . . . . . . . . . . . . . . . 104

5.5 Poliedro di controllo utilizzato per i test . . . . . . . . . . . . 105

5.6 Output della classe BezSurf . . . . . . . . . . . . . . . . . . . 106

5.7 Output della classe BSplineSurf . . . . . . . . . . . . . . . . . 107

5.8 Output della classe NurbsSurf . . . . . . . . . . . . . . . . . 108

5.9 Esempio di approssimazione di superficie . . . . . . . . . . . . 109

5.10 Scanner 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

5.11 Mappa topografica del Monte Fuji, Giappone [2] . . . . . . . 112

161

Page 172: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

162 ELENCO DELLE FIGURE

A.1 Casi d’uso: Curve . . . . . . . . . . . . . . . . . . . . . . . . . 117A.2 Casi d’uso: Superfici . . . . . . . . . . . . . . . . . . . . . . . 119A.3 Class Diagram completo delle classi componenti la libreria . . 120

Page 173: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Lista degli algoritmi

1 Bezier algoritmo base . . . . . . . . . . . . . . . . . . . . . . 602 De Casteljau ricorsivo . . . . . . . . . . . . . . . . . . . . . . 623 De Casteljau iterativo . . . . . . . . . . . . . . . . . . . . . . 624 Calcolo di una superficie di Bezier . . . . . . . . . . . . . . . 815 Calcolo di una superficie B-Spline con notazione matriciale . 846 Algoritmo naive per le superfici NURBS . . . . . . . . . . . . 877 Algoritmo naive per le superfici NURBS migliorato . . . . . . 888 Algoritmo efficiente per le superfici NURBS . . . . . . . . . . 89

163

Page 174: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

164 LISTA DEGLI ALGORITMI

Page 175: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Elenco delle tabelle

5.1 Tempi di esecuzione per il calcolo di una curva di Beziertramite tramite definizione formale. . . . . . . . . . . . . . . . 99

5.2 Tempi di esecuzione per il calcolo di una curva di Beziertramite algoritmo di de Casteljau . . . . . . . . . . . . . . . . 100

5.3 Tempi di esecuzione per il calcolo di una curva B-Spline tra-mite definizione formale . . . . . . . . . . . . . . . . . . . . . 101

5.4 Tempi di esecuzione per il calcolo di una curva B-Spline tra-mite notazione matriciale . . . . . . . . . . . . . . . . . . . . 101

5.5 Tempi di esecuzione per il calcolo di una curva NURBS . . . 1025.6 Tempi di esecuzione per l’approssimazione di una curva tra-

mite B-Spline . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035.7 Tempi di esecuzione per il calcolo di una superficie di Bezier . 1055.8 Tempi di esecuzione per il calcolo di una superficie B-Spline

tramite definizione formale . . . . . . . . . . . . . . . . . . . 1065.9 Tempi di esecuzione per il calcolo di una superficie B-Spline

tramite notazione matriciale . . . . . . . . . . . . . . . . . . . 1065.10 Tempi di esecuzione per il calcolo di una superficie NURBS . 1075.11 Tempi di esecuzione per l’approssimazione di una superficie

tramite B-Spline . . . . . . . . . . . . . . . . . . . . . . . . . 109

165

Page 176: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

166 ELENCO DELLE TABELLE

Page 177: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

Bibliografia

[1] Blender official site. http://www.blender.org/.

[2] Japan gps navigation and topographic maps for garmin. http://uud.info/en/topo/.

[3] Maya 3D Animation, Visual Effects and Compositing Software. http://www.autodesk.it/maya.

[4] Numpy official site. http://numpy.scipy.org/.

[5] Opennurbs official site. htp://www.opennurbs.com.

[6] Python official site. http://www.python.org/.

[7] Rhino3d official site. http://www.rhino3d.com/.

[8] VRML specification, Web3D Consortium. http://www.web3d.org/

x3d/specifications/vrml/.

[9] Marco Beri. Python. Pocket. Apogeo, Gennaio 2007.

[10] David Goodger. Code like a Pythonista: Idiomatic Python.http://python.net/~goodger/projects/pycon/2007/idiomatic/

handout.html.

[11] Evgeny Demidov. An Interactive Introduction to Splines. http://www.ibiblio.org/e-notes/Splines/Intro.htm.

[12] Michael Gleicher. A Curve Tutorial for Introductory ComputerGraphics. http://www.cs.wisc.edu/graphics/Courses/559-f2004/docs/cs559-splines.pdf, 2004.

[13] Philippe Lavoie. Nurbs++ page on sourceforge. http://libnurbs.

sourceforge.net/index.shtml.

167

Page 178: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

168 BIBLIOGRAFIA

[14] D. Ma, F. Lin, and CK Chua. Rapid prototyping applications in medi-cine. part 1: nurbs-based volume modelling. The International Journalof Advanced Manufacturing Technology, 18(2):103–117, 2001.

[15] Carlo de Falco Rafael Vazquez Mark Spink, Daniel Claxton. Nurbspackage for octave. http://octave.sourceforge.net/nurbs/index.

html.

[16] Nicholas Patrikalakis, Takashi Maekawa. Cour-se on Computational Geometry. http://

ocw.mit.edu/courses/mechanical-engineering/

2-158j-computational-geometry-spring-2003/.

[17] Christopher Nielsen. Generating nurbs surfaces through 3d silhouettescanning. http://www.odec.ca/projects/2008/niel8c2/.

[18] Mark Pilgrim. Dive into python. http://diveintopython.org/.

[19] D.F. Rogers. An introduction to NURBS: with historical perspective.Morgan Kaufmann, 2001.

[20] WU Rui-Qing, X.R. CHENG, and Y. Cun-Jian. Extracting contourlines from topographic maps based on cartography and graphics kno-wledge. Journal of Computer Science and Technology, 9(2), October2009.

[21] A.J. Schmid and H. Woern. Path planning for a humanoid robotusing NURBS curves. In IEEE Conference on Automation Science andEngineering, IEEE CASE, 2005.

[22] Enzhong Shan, Bin Dai, Jinze Song, and Zhenping Sun. A dynamicrrt path planning algorithm based on b-spline. volume 2, pages 25–29,dec. 2009.

[23] Michael Unser, Akram Aldroubi, Murray Eden, and Life Fellow. B-spline signal processing: Part i-theory. IEEE Trans. Signal Processing,41:821–833, 1993.

[24] Wikipedia. Algoritmo di de Casteljau. http://it.wikipedia.org/

wiki/Algoritmo_di_de_Casteljau.

[25] Wikipedia. NURBS. http://en.wikipedia.org/NURBS.

[26] J. Zhou, H. Yue, and H. Wang. Shaping Of Output PDF Based OnThe Rational Square-Root B-Spline Model. ACTA Automatica Sinica,31(3):343, 2005.

Page 179: Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D

BIBLIOGRAFIA 169

[27] X. Zhou and J. Lu. NURBS-based Galerkin method and application toskeletal muscle modeling. In Proceedings of the 2005 ACM symposiumon Solid and physical modeling, pages 71–78. ACM, 2005.