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
179
Embed
Sviluppo di una libreria orientata agli oggetti per il calcolo di NURBS con applicazioni alla grafica 2D e 3D
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.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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
ii
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
iv
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.
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
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
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.
4 CAPITOLO 1. INTRODUZIONE
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
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
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
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
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]).
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
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
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:
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.
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
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
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:
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.
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.
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
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.
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:
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 ]
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)
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:
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:
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):
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
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.
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:
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.
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
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:
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:
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.
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)
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:
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)
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:
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.
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)
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
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
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;
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) ]
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
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:
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
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’
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’:
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):
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:
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
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.
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
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
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
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.
3.6. LIBRERIE UTILIZZATE 51
(a) Wireframe
(b) Surface
Figura 3.2: Plot della funzione z = x2−y2, in wireframe e superfice completa
52 CAPITOLO 3. CENNI DI PYTHON
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
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
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):
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.]])
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
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.
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.
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
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-
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
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:
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:
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):
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:
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()
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).
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,
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):
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.
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:
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.
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]
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,
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.
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.
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;
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
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:
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]
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.
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.
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
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
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()
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)
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:
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
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.
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
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
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.
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.
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)
4.11. LA CLASSE BEZSURF 81
Quindi determino il numero di punti di controllo e i gradi lungo le direzioniu e w:
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:
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):
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
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
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:
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:
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
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.
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.
86 CAPITOLO 4. CLASSI ED ALGORITMI
2 >>> from Surface import BSplineSurf
3 # per una questione di spazio non faccio un esmepio completo
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:
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
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.
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).
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)
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:
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
92 CAPITOLO 4. CLASSI ED ALGORITMI
4 # dei punti di controllo. La quarta coordinata il peso
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.
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
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.
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 .
13 Mbasis = self.computeBasis(self.m, self.l, y, w)
14
15 colCount = 0
16
17 for Nik in Nbasis:
18
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
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
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()
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
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()
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.
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
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).
Tabella 5.4: Tempi di esecuzione per il calcolo di una curva B-Spline tramitenotazione matriciale
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.
Tabella 5.5: Tempi di esecuzione per il calcolo di una curva NURBS
Figura 5.3: Curva utilizata per testare la classe Nurbs
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
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.
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.
5.1. ESEMPI DI UTILIZZO 105
Figura 5.5: Poliedro di controllo utilizzato per i test
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
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
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.
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π
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:
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
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.
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].
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.
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
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
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.
114 CAPITOLO 5. ESEMPI DI UTILIZZO E APPLICAZIONI
Appendici
115
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
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
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.
120 APPENDICE A. CASI D’USO E CLASS DIAGRAMS
Figura A.3: Class Diagram completo delle classi componenti la libreria
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
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
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
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
164 LISTA DEGLI ALGORITMI
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
[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
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/
[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.
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.