tecniche di manipolazione intuitiva di splines...
TRANSCRIPT
Tecniche per la manipolazioneintuitiva di splines bidimensionali
di
Fabrizio Morciano
Corso di Laurea in
Ingegneria Elettronica
Universita degli studi L’Aquila
Anno 2001
Relatore: Prof. Gabriele Di Stefano
Correlatore: Dott. Gian Marco Todesco
Studente: Fabrizio Morciano
Indice
Indice i
1 Introduzione 1
1.1 Considerazioni generali . . . . . . . . . . . . . . . . . . . 1
1.1.1 Il Progetto Paperless . . . . . . . . . . . . . . . . 1
1.2 Struttura della tesi . . . . . . . . . . . . . . . . . . . . . 2
2 Il mondo dell’animazione 4
2.1 Fasi di sviluppo . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 La presenza del digitale . . . . . . . . . . . . . . . . . . . 7
2.3 L’animazione 3D . . . . . . . . . . . . . . . . . . . . . . 9
2.4 L’animazione mista . . . . . . . . . . . . . . . . . . . . . 10
3 Il progetto Paperless 12
3.1 Obiettivi . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Tecnologie . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2.1 Acquisizione dell’input da un dispositivo digitale . 14
3.2.2 Vettorializzazione di immagini raster . . . . . . . 15
3.2.3 Inbetweening . . . . . . . . . . . . . . . . . . . . 17
3.2.4 Colorazione e colorazione automatica . . . . . . . 17
4 Stato dell’arte 20
4.1 Manipolare una curva . . . . . . . . . . . . . . . . . . . . 20
4.2 Tecnologie e limiti . . . . . . . . . . . . . . . . . . . . . 21
i
INDICE INDICE
5 Curve di Bezier e splines 25
5.1 Curve di Bezier e splines . . . . . . . . . . . . . . . . . 25
5.1.1 Generalita . . . . . . . . . . . . . . . . . . . . . . 25
5.1.2 Curve di Bezier . . . . . . . . . . . . . . . . . . . 27
5.1.3 Spline e curve di Bezier . . . . . . . . . . . . . . 32
5.2 L’algoritmo di de Casteljau . . . . . . . . . . . . . . . . . 38
5.3 Calcolo della distanza minima tra una curva ed un punto
del piano . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.3.1 Risoluzione con la proprieta dell’involucro convesso 40
5.3.2 Risoluzioni con i polinomi di Bezier . . . . . . . . 41
5.4 Curvatura . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.4.1 Riferimento di Frenet . . . . . . . . . . . . . . . . 46
5.4.2 Algoritmi per il calcolo della curvatura . . . . . . 48
6 Utilizzo e progetto di tecniche di manipolazione 51
6.1 Gli strumenti . . . . . . . . . . . . . . . . . . . . . . . . 51
6.1.1 Strumenti di disegno . . . . . . . . . . . . . . . . 52
6.1.2 Strumenti di manipolazione . . . . . . . . . . . . 54
6.1.3 Strumenti per il controllo della visualizzazione . . 58
6.2 Gli Algoritmi . . . . . . . . . . . . . . . . . . . . . . . . 59
6.2.1 Ricerca di una curva . . . . . . . . . . . . . . . . 60
6.2.2 Suddivisione di una curva in tratti di lunghezza
fissata . . . . . . . . . . . . . . . . . . . . . . . . 61
6.2.3 Minimizzazione del numero di curve di una linea . 62
6.2.4 Taglio e rotazione . . . . . . . . . . . . . . . . . . 68
7 Il programma Fasped 71
7.1 Il programma Fasped . . . . . . . . . . . . . . . . . . . . 71
7.2 La struttura delle classi per l’interfaccia . . . . . . . . . 72
7.2.1 fbGrOb . . . . . . . . . . . . . . . . . . . . . . . 72
7.2.2 fbWindow . . . . . . . . . . . . . . . . . . . . . . 74
ii
INDICE INDICE
7.2.3 fbFasped . . . . . . . . . . . . . . . . . . . . . . . 74
7.3 La struttura delle classi dei tools . . . . . . . . . . . . . 75
7.3.1 fbTool . . . . . . . . . . . . . . . . . . . . . . . . 75
7.3.2 fbCursor . . . . . . . . . . . . . . . . . . . . . . . 76
7.3.3 fbFrameManager . . . . . . . . . . . . . . . . . . 76
7.4 La struttura delle classi per la grafica . . . . . . . . . . . 76
7.4.1 fbFrame . . . . . . . . . . . . . . . . . . . . . . . 77
7.4.2 fbPolyLine . . . . . . . . . . . . . . . . . . . . . . 77
7.4.3 fbCurve . . . . . . . . . . . . . . . . . . . . . . . 77
7.5 La struttura delle classi per l’analisi numerica . . . . . . 80
7.5.1 fbPoint . . . . . . . . . . . . . . . . . . . . . . . . 80
7.5.2 fbStraightLine . . . . . . . . . . . . . . . . . . . . 81
7.5.3 fbVector . . . . . . . . . . . . . . . . . . . . . . . 81
7.5.4 fbMatrix . . . . . . . . . . . . . . . . . . . . . . . 81
7.5.5 fbOperation . . . . . . . . . . . . . . . . . . . . . 82
8 Lavori futuri 83
8.1 Line Library . . . . . . . . . . . . . . . . . . . . . . . . . 83
8.2 Feature del disegno . . . . . . . . . . . . . . . . . . . . . 83
A Appendice A 85
A.1 Codice Sorgente . . . . . . . . . . . . . . . . . . . . . . . 85
A.1.1 fbArrow . . . . . . . . . . . . . . . . . . . . . . . 85
A.1.2 fbAutoSwitch . . . . . . . . . . . . . . . . . . . . 86
A.1.3 fbBender . . . . . . . . . . . . . . . . . . . . . . . 86
A.1.4 fbBSpline . . . . . . . . . . . . . . . . . . . . . . 88
A.1.5 fbCircle . . . . . . . . . . . . . . . . . . . . . . . 90
A.1.6 fbClamp . . . . . . . . . . . . . . . . . . . . . . . 91
A.1.7 fbConfig . . . . . . . . . . . . . . . . . . . . . . . 93
A.1.8 fbCursor . . . . . . . . . . . . . . . . . . . . . . . 95
A.1.9 fbCurve . . . . . . . . . . . . . . . . . . . . . . . 96
iii
INDICE INDICE
A.1.10 fbDrawingPin . . . . . . . . . . . . . . . . . . . . 100
A.1.11 fbEllipse . . . . . . . . . . . . . . . . . . . . . . . 101
A.1.12 fbException . . . . . . . . . . . . . . . . . . . . . 102
A.1.13 fbFasped . . . . . . . . . . . . . . . . . . . . . . . 103
A.1.14 fbFrameManager . . . . . . . . . . . . . . . . . . 106
A.1.15 fbFrame . . . . . . . . . . . . . . . . . . . . . . . 106
A.1.16 fbFunction . . . . . . . . . . . . . . . . . . . . . . 108
A.1.17 fbGeometric . . . . . . . . . . . . . . . . . . . . . 111
A.1.18 fbGeoObj . . . . . . . . . . . . . . . . . . . . . . 111
A.1.19 fbGrOb . . . . . . . . . . . . . . . . . . . . . . . 112
A.1.20 fbLoader . . . . . . . . . . . . . . . . . . . . . . . 116
A.1.21 fbMath . . . . . . . . . . . . . . . . . . . . . . . . 117
A.1.22 fbOperation . . . . . . . . . . . . . . . . . . . . . 128
A.1.23 fbPan . . . . . . . . . . . . . . . . . . . . . . . . 137
A.1.24 fbPencil . . . . . . . . . . . . . . . . . . . . . . . 138
A.1.25 fbPen . . . . . . . . . . . . . . . . . . . . . . . . 139
A.1.26 fbPointManipulator . . . . . . . . . . . . . . . . . 140
A.1.27 fbPoint . . . . . . . . . . . . . . . . . . . . . . . . 141
A.1.28 fbPolyLine . . . . . . . . . . . . . . . . . . . . . . 145
A.1.29 fbPotential . . . . . . . . . . . . . . . . . . . . . . 149
A.1.30 fbRectangle . . . . . . . . . . . . . . . . . . . . . 150
A.1.31 fbSaver . . . . . . . . . . . . . . . . . . . . . . . . 151
A.1.32 fbSelector . . . . . . . . . . . . . . . . . . . . . . 151
A.1.33 fbStraightLine . . . . . . . . . . . . . . . . . . . . 152
A.1.34 fbSubSelector . . . . . . . . . . . . . . . . . . . . 154
A.1.35 fbTool . . . . . . . . . . . . . . . . . . . . . . . . 155
A.1.36 fbTriangle . . . . . . . . . . . . . . . . . . . . . . 157
A.1.37 fbUtil . . . . . . . . . . . . . . . . . . . . . . . . 157
A.1.38 fbVisitAndChangeColor . . . . . . . . . . . . . . 159
A.1.39 fbVisitAndChangeDesignAspect . . . . . . . . . . 160
iv
INDICE INDICE
A.1.40 fbVisitAndChangeTolerance . . . . . . . . . . . . 161
A.1.41 fbVisitAndChangeVisibility . . . . . . . . . . . . 161
A.1.42 fbVisitor . . . . . . . . . . . . . . . . . . . . . . . 162
A.1.43 fbWindow . . . . . . . . . . . . . . . . . . . . . . 163
Bibliografia 166
v
Ringraziamenti
Numerose sono le persone che mi sento di ringraziare per questo lavo-
ro. Per primo ringrazio il Dott. Gian Marco Todesco (mentore prezioso,
prodigo di ottimi consigli), per aver seguito e supervisionato quanto rea-
lizzato; ringrazio il Prof. Gabriele Di Stefano e l’Ing. Vincenzo Vennarini
per le correzioni suggeritemi; ringrazio Francesca per gli splendidi disegni
utilizzati negli esempi dei tool e lo stuff di Digital Video per la cortesia
e l’assistenza accordatami; ringrazio inoltre Francesca, Emiliana, Franco
ed Andrea per il supporto che mi hanno fornito per il completamento di
questa tesi.
a Emiliana e Franco
Capitolo 1
Introduzione
1.1 Considerazioni generali
L’animazione e un vasto mondo che racchiude al suo interno molteplici
forme di espressioni grafiche come: i films di animazione, i video-giochi, le
animazioni per il Web. Lo sviluppo di una qualsiasi di queste espressioni,
richiede un lavoro lungo e specialistico. Nasce la necessita di avvalersi di
strumenti che siano di aiuto sia per innovare un contesto classico, sia per
velocizzare i tempi di realizzazione. Il contesto di cui ci interesseremo,
sara quello dei film di animazione bidimensionali (cel animation). Gli
strumenti richiesti possono essere offerti dalla Computer Graphics, adat-
tando le tecniche esistenti ad uno specifico campo o, soprattutto, svilup-
pando nuove tecniche da abbinare a dispositivi di interazione innovativi,
come nel progetto Paperless.
1.1.1 Il Progetto Paperless
Paperless e un Progetto Europeo, che ha come scopo la realizzazione di
uno strumento software di supporto allo sviluppo di films di animazione
2D. Paperless e basato sulla combinazione di tool user friendly, con di-
1
1. INTRODUZIONE
spositivi di interazione innovativi. Questa tesi, si prefigge di sviluppare
dei tools, da utilizzare nell’ambito di tale progetto.
Modulo di disegno
In Paperless, e previsto che venga realizzato un modulo, in cui sia pos-
sibile il disegno del fotogramma di un personaggio direttamente su uno
schermo a cristalli liquidi. Questo disegno sara composto da un insie-
me di pennellate (strokes). Il nostro compito sara di fornire dei tools,
necessari alla manipolazione di strokes, dall’utilizzo facile e dal risultato
intuitivo.
La facilita d’uso risulta necessaria in quanto stiamo realizzando uno
strumento che non sara dedicato ad un utente che abbia dimestichezza
con l’utilizzo di computer. Inoltre, il numero di linee presenti in un
disegno e molto grande e manipolarle rispettando alcune condizioni (come
la continuita) risulterebbe difficile e noioso.
D’altra parte, rispetto agli strumenti offerti da un convenzionale siste-
ma CAD1 (che permettono il controllo accurato dei parametri descriventi
il modello matematico del disegno), quelli da noi proposti agiscono come
metafora del mondo reale. I modelli da noi impiegati descrivono det-
tagliatamente come il tool deve realizzare la manipolazione, senza che
l’utente abbia la necessita di controllare ogni parametro.
1.2 Struttura della tesi
Nei capitoli successivi tratteremo i seguenti argomenti:
• nel Capitolo 2 forniremo una breve introduzione al mondo dell’a-
nimazione 2D, descrivendo le fasi di realizzazione di un film di
animazione ed il ruolo del digitale;
1Computer Aided Design
2
1. INTRODUZIONE
• nel Capitolo 3 parleremo in modo piu esteso del progetto Paperless,
quali sono i suoi obiettivi e qual e stato, nel suo ambito, il nostro
ruolo;
• nel Capitolo 4 esporremo lo stato dell’arte sulla manipolazione
di immagini bidimensionali, indicando quali tecniche risultano piu
indicate al nostro caso;
• nel Capitolo 5 tratteremo la teoria relativa alle curve di Bezier ed
alle splines, fornendo le descrizioni matematiche delle tecniche di
manipolazione introdotte nel 4;
• nel Capitolo 6 analizzeremo le tecniche ed i tools realizzati, spie-
gandone l’utilizzo e gli algoritmi;
• nel Capitolo 7 presenteremo il codice dell’ambiente di test che
abbiamo realizzato;
• in Appendice allegheremo le linee di codice piu significative.
3
Capitolo 2
Il mondo dell’animazione
Il mondo dell’animazione, in questi ultimi anni, sta subendo dei cam-
biamenti profondi. Come in molti altri campi, il computer si integra o
sostituisce le tecniche tradizionali e diventa un potente aiuto. In questo
capitolo, esporremo le fasi per la realizzazione di un film di animazione
in modo tradizionale ed i cambiamenti portati dall’utilizzo del computer.
2.1 Fasi di sviluppo
Lo sviluppo di un film di animazione prevede la realizzazione di uno story-
board (una serie di disegni in cui viene scomposta la sceneggiatura). Una
volta realizzato lo storyboard, e fatte eventuali correzioni alla sceneggia-
tura, si procede con la registrazione della colonna sonora. La colonna
sonora deve essere incisa prima, perche e necessaria alla sincronizzazione
tra disegno e parlato. Lo strumento su cui si basa la produzione di un
film, e il foglio macchina (Fig. 2.1). Il foglio macchina e una griglia, dove,
in verticale, abbiamo l’asse dei tempi e, in orizzontale, un insieme di celle
in cui sono inseriti i numeri che identificano i disegni. Ogni fotogramma
del film, sara ottenuto dalla sovrapposizione dei disegni indicati nelle cel-
le orizzontali (vedremo in seguito, come cio sia possibile). Sull’asse dei
tempi viene inserito il testo delle parole (ricavato dalla colonna sonora)
4
2. IL MONDO DELL’ANIMAZIONE
Figura 2.1: Foglio macchina
che competera a quella scena e questo permette la gia citata sincroniz-
zazione tra il parlato ed il disegno. Nel foglio macchina possono essere
aggiunte ulteriori indicazioni, quali, ad esempio, quelle per il movimento
della camera.
Ora gli animatori sono pronti a realizzare le scene principali di un’a-
nimazione; per le quali si realizza un modello di animazione (delle bozze
di studio, usate come “modello” per i movimenti di ogni personaggio).
Per realizzazioni di qualita, un personaggio puo essere animato me-
diante una tecnica detta rotoscoping. Il rotoscoping sfrutta i movimenti
di un attore umano: questo viene filmato, i fotogrammi vengono stampa-
ti su carta e vengono utilizzati dagli animatori come guida per muovere
in modo piu realistico un personaggio.
In ogni caso, i movimenti vengono scomposti in una serie di fotogram-
mi, la cui rapida successione realizza l’animazione vera e propria. Perche
l’occhio umano non avverta il passaggio da uno all’altro, e opportuno
usare almeno 24 fotogrammi (frames) ogni secondo. Questi disegni sono
5
2. IL MONDO DELL’ANIMAZIONE
realizzati da una gerarchia di animatori ed intercalatori: i primi realizza-
no le scene “chiave” (i key frames), quelle con le posizioni iniziali e finali
di un personaggio (character); i secondi provvedono a disegnare le scene
intermedie (gli inbetweens). Le tavole sono poco differenti l’una dall’altra
e per realizzazione di basso costo si puo allora decidere di utilizzare lo
stesso frame piu volte (se ripetuto due volte, parleremo di animazioni a
passo-2 , altrimenti si parlera di animazione a passo-1). Una volta rea-
lizzati i frames, viene effettuato il pencil test (l’animazione delle matite),
mediante il quale si controlla che l’animazione risulti fluida e che non
ci sia una grossa discrepanza tra le pennellate delle varie tavole. Se il
test non viene superato, si decide quali disegni devono essere sostituiti,
aggiunti o rimossi. Una volta che siano state completate le modifiche,
le tavole sono nuovamente testate, ripetendo il procedimento, finche non
si ottiene il risultato voluto. A questo punto, i disegni realizzati devo-
no essere copiati su lucido (cels), inchiostrati (a questo provvedono gli
inchiostratori) e colorati. La colorazione e realizzata a mano ed i colori
sono piatti (omogenei e privi di sfumature). Ad ogni personaggio viene
associata una palette di colori (il color model) valida lungo tutto il film ed
evita che abbia un colore diverso in scene differenti. Vengono inoltre rea-
lizzati gli sfondi e, quando tutte le tavole sono pronte, si puo cominciare
la fase di registrazione. Per ogni fotogramma si prendono i disegni neces-
sari (che sono individuati dal numero sulla riga del foglio macchina) e si
dispongono su un opportuno dispositivo per la registrazione detto rostro
(Fig. 2.2).
Il rostro si compone di:
• una camera, posta in cima ad una struttura piramidale e perpen-
dicolare ai piani sottostanti;
• una serie di vassoi trasparenti (trasparent trays), tutti fra loro
paralleli, in cui possono essere inseriti i lucidi;
6
2. IL MONDO DELL’ANIMAZIONE
Figura 2.2: Schema di un rostro
• uno sfondo fisso, in cui viene posto il disegno che realizza la sceno-
grafia del fotogramma.
La camera puo effettuare tutti i movimenti che la mantengono perpendi-
colare al piano (puo traslare e zoomare); i disegni, anche se sono vincolati
a rimanere sul vassoio, possono essere traslati o ruotati, solo per lo sfon-
do non sono possibili movimenti. Il vantaggio di utilizzare una struttura
di questo genere e che permette di associare uno (o piu) vassoi ad un
personaggio. Le animazioni in cui il personaggio muove solo una parte
del corpo (come quelle del parlato, in cui tipicamente muove solo la boc-
ca), possono essere suddivise tra due livelli: uno in cui viene mantenuta
la sagoma del personaggio; l’altro in cui viene animata la parte mobile.
In questa fase e anche possibile inserire qualche effetto, mediante luci,
cambio di lente, ecc.. Il limite fisico, al numero di livelli consentiti, e
costituito dalla trasparenza del lucido, infatti, all’aumentare del numero
di tavole sovrapposte, quelle che si trovano in secondo piano perdono di
definizione.
2.2 La presenza del digitale
Nel paragrafo precedente e stata descritta la maniera tradizionale di
realizzare un film di animazione. Oggi, grazie alle nuove tecnologie, il
7
2. IL MONDO DELL’ANIMAZIONE
digitale interviene praticamente in tutte le fasi della lavorazione.
La realizzazione dello storyboard utilizza il digitale per una prima
valutazione delle animazioni e del sonoro. Si puo animare una scena
senza necessariamente arrivare a registrare su pellicola, questo semplifica
le correzioni e gli indispensabili aggiustamenti.
La registrazione della colonna sonora, come abbiamo visto, e effettua-
ta prima dello sviluppo dei disegni, per permettere la sincronizzazione tra
parlato e disegno. L’utilizzo del digitale, in questa fase, permette di rico-
noscere automaticamente il parlato, ricavando i fonemi che sono trascritti
nel foglio macchina e di associare tali fonemi con i frames che rappresen-
tano le posizioni della bocca di un personaggio. In quest’ultimo caso,
la sincronizzazione riutilizza i frames dell’animazione del parlato (mouth
shapes) in varie parti del film, senza che sia necessario ridisegnarli.
I disegni, dopo essere stati disegnati su carta, sono scannerizzati per
poter essere utilizzati in un contesto digitale. La scannerizzazione deve
essere realizzata in modo da evitare gli effetti di seghettatura, derivan-
ti da una bassa risoluzione; per questo viene spesso effettuata, con una
risoluzione che sia superiore rispetto a quella richiesta per le fasi suc-
cessive. Alcuni software permettono inoltre di vettorializzare il disegno
(parleremo piu estesamente in seguito di quale siano i vantaggi di questa
operazione). Le immagini, vengono esaminate per la rimozione di even-
tuali disturbi o imperfezioni che siano state introdotte nel processo di
digitalizzazione.
In seguito si puo procedere alla colorazione. Grazie ad opportuno
software, vengono individuate le regioni chiuse, che possono cosı essere
colorate sfruttando dei tools specializzati. Alcuni sistemi di animazione
computerizzata, permettono anche la colorazione automatica: zone di
disegno, che in tavole succesive risultano quasi allineate, possono essere
riempite dallo stesso colore utilizzato nella tavola in cui si sta lavorando.
Il foglio macchina e naturalmente rimpiazzato dal suo equivalente di-
8
2. IL MONDO DELL’ANIMAZIONE
gitale, il cui utilizzo permette di facilitare la composizione delle immagini
per la fase di registrazione.
Il modello della camera utilizzato e ancora verticale, ma la sovrap-
posizione dei disegni e gestita utilizzando l’alpha-blending. Con tale de-
nominazione si indicano quelle tecniche che, oltre ad utilizzare il colore
(specificato per mezzo dell’intensita delle sue componenti di base rosso
verde e blu, R[ed]G[reen]B[lue]), sfruttano un campo alpha che ne indica
l’opacita, permettendo di sovrapporre diversi disegni e di realizzare effet-
ti di trasparenza. Come nel caso tradizionale, la camera non e fissa, sono
possibili tutti i movimenti che la mantengono perpendicolare ai piani in
cui sono poste le tavole (zoom, rotazioni, panoramiche, ecc.) ed, analo-
gamente, e possibile sia ruotare che traslare le tavole; inoltre, possiamo
aggiungere un vasta gamma di effetti speciali).
La registrazione permette infine di riversare il film sul supporto desi-
derato.
2.3 L’animazione 3D
Negli ultimi 20 anni la computer graphics ha permesso di fornire agli
animatori nuovi personaggi, non piu disegnati, ma modellati tridimen-
sionalmente. Accenniamo brevemente alla realizzazione di un film di
animazione tridimensionale.
In questo caso, i personaggi sono realizzati sfruttando dei programmi
software chiamati modellatori (qualche volta le sagome dei personaggi
possono essere prima scolpite su opportuni materiali e poi acquisite con
scanner tridimensionali). In questa fase (detta modelling), ad ogni model-
lo sono applicate le caratteristiche fisiche e cinematiche che lo caratteriz-
zeranno (come il colore, l’elasticita, i vincoli da rispettare nel movimento
di ogni sua parte, ecc.).
Sempre nel modelling si definiscono sia l’animazione (fase di anima-
9
2. IL MONDO DELL’ANIMAZIONE
tion, dove si scelgono le pose chiave e si lascia che un software specializ-
zato si occupi dell’inbetweening), sia le proprieta dei materiali (che dei
programmi detti shaders, sfruttano per la fase di shading). Nello shading
e possibile riprodurre una grande varieta di materiali (come legno, stoffa,
metallo, capelli).
Si puo poi passare all’illuminazione, dove vengono definiti diversi con-
tributi di luce (ambiente, luci a bulbo, ecc.), necessari alla scena per
creare un umore ed un’emozione particolare (fase di lighting).
L’ultima fase e il rendering, per ogni frame sono calcolati tutti i con-
tributi (luci, ombre, riflessi, movimenti, ecc.) ed il risultato, infine, puo
essere registrato su un supporto (pellicola, DVD, ecc.).
2.4 L’animazione mista
Sempre piu spesso, il 2D ed il 3D vengono combinati in uno stesso film
di animazione.
Il 3D permette di creare gli sfondi e le animazioni su cui far muovere
i personaggi 2D, superando i limiti rappresentati dal modello a camera
verticale e rendendo l’azione piu simile a quella di un film live action (con
attori umani). A questo proposito possiamo citare, ad esempio, Anastasia
(Fox), La strada per Eldorado (DreamWorks), Titan A.E. (Fox), Tarzan
(Walt Disney).
E anche possibile sfruttare il 3D per creare personaggi da muovere in
scenari bidimensionali. Alcuni dei vantaggi che derivano da questa scelta
sono: la facilita di gestione dei movimenti del personaggio (i movimenti
sono gestiti da un programma software); la possibilita di muovere un
numero elevato di personaggi (scene di massa); la possibilita di realizzare
effetti speciali (come l’animazione dell’acqua). Riportando degli esempi
dell’utilizzo di tali tecniche possiamo citare: per il primo punto “Iron
Giant” (Warner Bros), in cui il personaggio (un robot d’acciaio alto 15
10
2. IL MONDO DELL’ANIMAZIONE
metri) e gestito da un programma di modellazione e colorato con effetto
“a cartone animato”; per il secondo “Il Re Leone”, dove il computer
interviene nella carica delle antilopi, o “Hercules” (Walt Disney), dove
il digitale interviene nella gestione dei movimenti delle teste dell’Idra
di Lerna; per il terzo “La strada per Eldorado”, in questo film viene
utilizzato un sistema particellare per simulare i movimenti dell’acqua.
11
Capitolo 3
Il progetto Paperless
Le fasi per lo sviluppo di un film di animazione, con tecniche tradizionali,
sono lunghe e costose. Il computer, come abbiamo visto, ha permesso
di migliorare molti aspetti, ma il suo utilizzo ha trovato poco spazio
nell’acquisizione diretta del disegno. Le tavole sono realizzate su carta
ed a mano. Un disegnatore e quindi limitato nella correzione (quando si
cancella, la carta si deteriora) e l’unica possibilita di manipolazione che
ha, e ridisegnare in modo diverso la scena o il personaggio.
L’innovazione tecnologica ci fornisce dei dispositivi, come le tavolet-
te grafiche con schermo (Fig. 3.1 a pag. 14), di cui possiamo estendere
le capacita, associandogli dei tools facili da utilizzare. Possiamo allo-
ra sfruttare l’integrazione tra tecnologia innovativa e tools user friendly,
per realizzare strumenti che facilitino il compito degli artisti e dei tecni-
ci che contribuiscono allo sviluppo di un film d’animazione (disegnatori,
intercalatori, inchiostratori).
Perseguendo l’obiettivo di fornire un ambiente di animazione avan-
zato ed integrato, la Digital Video S.r.l., ed altre aziende europee, sta
portando avanti il progetto Paperless.
12
3. IL PROGETTO PAPERLESS
3.1 Obiettivi
I programmi tradizionali per il supporto dell’animazione cominciano il
loro compito subito dopo la fase di disegno a mano, permettendo la ge-
stione della scannerizzazione delle tavole. Questo passo rappresenta un
pesante limite sia per il tempo richiesto per acquisire le centinaia di mi-
gliaia di tavole necessarie, che per la limitata possibilita di modificare
quanto gia acquisito. In Paperless, questo stadio viene ad essere rimpiaz-
zato, realizzando il disegno direttamente utilizzando una speciale tavolet-
ta grafica; all’artista viene inoltre permesso di cancellare e, soprattutto,
di modificare quanto disegnato.
Cancellare un tratto di matita su un foglio di carta non e possibile
all’infinito, in quanto il supporto si deteriora e sicuramente non e facile
eliminare particolari o linee di piccole dimensioni.
La modifica di un tratto e invece qualcosa di assolutamente nuovo;
considerare i tratti come oggetti e permetterne la manipolazione, non e
fisicamente realizzabile in un contesto tradizionale.
La facilita di utilizzo e quella di manipolazione permettono a Paper-
less di aprire il mondo dell’animazione a nuove proposte come, l’anima-
zione di pagine Web o i videogiochi.
3.2 Tecnologie
Per conciliare le richieste degli utilizzatori, con gli obiettivi che si vogliono
raggiungere, il progetto dovra utilizzare diverse tecnologie, alcune nuove,
altre necessarie per l’integrazione con le attuali metodologie di sviluppo.
13
3. IL PROGETTO PAPERLESS
3.2.1 Acquisizione dell’input da un dispositivo digi-
tale
Il disegno a mano libera trova il suo supporto naturale nella carta. L’e-
sperienza che si e ottenuta, nel disegno su carta, e preziosa per un dise-
gnatore e dovra essere mantenuta. Attualmente l’interazione digitale con
il disegno e possibile o dopo la scannerizzazione dei disegni o con l’acqui-
sizione da tavoletta grafica ([1], [2]). Della scannerizzazione abbiamo gia
parlato; per quello che riguarda l’acquisizione da tavoletta, l’utilizzatore
ha uno strumento simile a quello usuale (carta e matita), pero il dise-
gno non appare sotto la penna ma su un video e, spesso, il tratto non e
sensibile alla pressione che viene esercitata.
Utilizzando un display digitale ed una penna elettronica sensibile alla
pressione (Fig. 3.1), il disegnatore non deve cambiare la sua metodologia
di lavoro. Il disegno appare sul display seguendo la penna e le linee
Figura 3.1: Tavoletta Wacom
assumono uno spessore variabile in funzione della pressione esercitata.
14
3. IL PROGETTO PAPERLESS
Al disegnatore viene poi fornita la liberta di manipolare il disegno, i cui
tratti non sono piu statici, ma interpretabili come oggetti, rigidi o elastici,
deformabili in modo intuitivo. Viene cosı facilitata la creazione di nuovi
frames da quelli esistenti.
Questa tesi trova la sua origine nell’introduzione di metodologie in-
tuitive per la manipolazione dei tratti e vedremo in seguito cosa e stato
proposto.
Per completezza, dobbiamo esporre qualche svantaggio derivante dal-
l’utilizzo della tavoletta. Il suo costo (una tavoletta costa quasi 5 milioni
di lire), che fino a quando non sara comparabile con i display tradizio-
nali, non la rendera immediatamente accessibile ad un vasto pubblico;
le sue dimensioni, che sono simili a quelle di un foglio di formato A4
e rendono necessaria l’implementazione di un contesto multi-risoluzione
per simulare un supporto di dimensioni maggiori; l’errore di parallasse
(che si presenta a causa dello spessore non nullo del vetro del display) e
non fa apparire il disegno esattamente sotto la penna, ma leggermente
piu in basso (questo potrebbe creare qualche difficolta per il disegnato-
re che non individua esattamente il punto iniziale della linea che va a
disegnare).
3.2.2 Vettorializzazione di immagini raster
Il passaggio ad un sistema innovativo sara graduale. Il supporto per le
tecniche esistenti dovra, necessariamente, essere mantenuto ed e per que-
sto che in Paperless e previsto il mantenimento dell’acquisizione tramite
scanner; cambia pero il formato uscente dalla scannerizzazione non piu
raster ma vettoriale.
15
3. IL PROGETTO PAPERLESS
Raster vs Vettori
Su un computer e possibile memorizzare le immagini utilizzando due
diverse modalita: raster o vettoriale.
Se utilizziamo una modalita raster, possiamo pensare di sovrapporre
all’immagine una griglia rettangolare composta di celle quadrate. La di-
mensione di un’immagine sara data dal numero totale di celle. Quanto
piu le celle saranno piccole e numerose, tanto maggiore sara la risolu-
zione finale dell’immagine. Le celle vengono chiamate pixels (dall’inglese
picture elements) e ad ogni cella verra associato il colore dell’immagine.
Memorizzando tale informazione, il risultato che otterremo e una matrice
costituita da pixels.
Se utilizziamo una modalita vettoriale, possiamo pensare di fissare un
sistema di coordinate solidale con il disegno (lo chiameremo world coor-
dinates, coordinate mondo)1. Una volta scelte le primitive matematiche
a cui ricondurre ogni tratto del disegno (linee, cerchi, curve, ecc.), pos-
siamo memorizzarne i parametri (ad esempio un cerchio viene espresso
come raggio e centro). Il risultato che si ottiene e una lista di primitive
con i parametri necessari a identificarle univocamente.
Gli svantaggi derivanti dall’utilizzo di un formato vettoriale, posso-
no essere ricondotti alla necessita di operare una conversione nella fase
di visualizzazione; questo, in quanto i dispositivi di output attualmen-
te disponibili, sfruttano una tecnologia di tipo raster (sono capaci di
visualizzare efficientemente delle immagini di tipo raster).
I vantaggi di un formato vettoriale sono: permette un controllo asso-
luto delle primitive (al contrario di un formato raster in cui,conoscendo
solo il colore, non sappiamo nulla, circa quello che e rappresentato); ha
la possibilita di operare con funzioni continue e non con elementi finiti e
discreti come i pixels; e indipendente dalla risoluzione; occupa poca me-
1Il sistema world coordinates verra ricondotto al sistema window coordinatessolidale con la finestra della nostra applicazione
16
3. IL PROGETTO PAPERLESS
moria, rendendolo adatto per il Web. Quest’ultimo aspetto permette di
effettuare le operazioni di zoom o di rotazione senza degrado di qualita.
3.2.3 Inbetweening
L’inbetweening e la realizzazione dei frames intermedi di un’animazione, a
partire da due posizioni chiave di un personaggio. Come accennato nel ca-
pitolo relativo al mondo dell’animazione, la realizzazione degli inbetweens
e delegata ad una gerarchia di animatori ed intercalatori che dovranno
realizzare numerose tavole molto simili tra loro. Per facilitare questa fa-
se, sono stati, quindi, realizzati dei sistemi automatici di inbetweening.
Questi sistemi hanno bisogno della presenza di un supervisore umano che
associ le curve corrispondenti in due frames chiave. Dopo questa fase, il
programma che realizza l’algoritmo d’inbetweening, e in grado di inserire
gli ulteriori frames, necessari al completamento dell’animazione.
In Paperless e previsto lo sviluppo di un algoritmo d’inbetweening. Il
nostro lavoro potrebbe essere utilizzato in tale fase, permettendo all’ani-
matore di applicare la manipolazione su di un frame, in modo progressivo
(memorizzando ogni passo intermedio) e considerando la successione di
frames ottenuta come gli inbetweens. In tal modo sara lo stesso animato-
re a realizzare e valutare l’animazione e non l’operatore che associa curva
a curva.
3.2.4 Colorazione e colorazione automatica
Colorazione
La realizzazione tramite programmi software della colorazione (fase in cui
vengono colorate le tavole), comporta la risoluzione di alcuni problemi
direttamente collegati al formato dell’immagine da colorare.
Il primo problema e la definizione di regione, cioe della zona limitata
di disegno che deve essere colorata. In ambito raster, si definisce regione
17
3. IL PROGETTO PAPERLESS
un insieme di pixels dello stesso colore. La colorazione, comportera di
cambiare il colore di tutti i pixels della regione, con quello desiderato;
per far questo esistono degli algoritmi di campitura ben noti (algoritmi
di fill) su cui non ci soffermeremo.
La definizione di regione precedentemente data non vale nel caso d’im-
magini vettoriali. In immagini vettoriali, una regione viene definita come
una zona del piano racchiusa da curve; per una regione non e, allora,
noto il numero di curve che la racchiudono e non e detto che curve molto
vicine si incontrino in un punto, permettendo la chiusura. Il riconosci-
mento di una regione chiusa e stato trattato e risolto per Paperless da
[3] in un precedente lavoro di tesi; e previsto che la chiusura automatica
di regioni “quasi” chiuse verra implementata in Paperless.
Colorazione automatica
Un’animazione si compone di molte tavole che devono essere colorate.
Visto che queste sono poco differenti l’una dall’altra, si potrebbe uti-
lizzare un opportuno programma che permetta di colorare regioni della
prima tavola, gestendo automaticamente la colorazione della stessa re-
gione nelle tavole successive. Un programma di questo genere si basa
su un algoritmo di colorazione automatica. La colorazione automatica e
resa difficile da diversi problemi: si deve riconoscere una regione in una
tavola; la regione, che durante l’animazione puo essere stata modificata,
deve essere individuata nelle tavole successive. In Paperless e previsto
lo sviluppo di un modulo che permetta la colorazione automatica. Que-
sto modulo si potra avvantaggiare delle informazioni fornite dal modulo
di inbetweening. Partendo dall’idea che, una volta stabilita una corri-
spondenza curva a curva tra due tavole successive, il colore delle linee e
delle regioni resta individuato, e possibile (in linea di principio) colorare
le tavole interpolanti utilizzando le informazioni di colore associate alla
prima. Il modulo di inbetweening si occupera, allora, di associare curva a
18
3. IL PROGETTO PAPERLESS
curva, mentre quello di colorazione aggiungera il colore negli inbetweens
via via generati.
19
Capitolo 4
Stato dell’arte
4.1 Manipolare una curva
In Paperless e previsto un modulo di disegno che permetta la manipo-
lazione intuitiva di strokes.
Una stroke verra rappresentata con un’opportuna equazione mate-
matica; in questa equazione saranno presenti dei parametri (strettamen-
te collegati alla forma assunta dalla stroke) che chiameremo punti di
controllo1. La manipolazione diretta dei punti di controllo, permette al-
l’utente di avere la padronanza assoluta della forma assunta dalla curva,
ma operare sui punti di controllo, richiede la conoscenza dell’equazione
che descrive la curva e non risulta una scelta indicata per chi non e esper-
to. Inoltre, visto che il numero di curve che possono essere presenti in
un disegno e elevato, lo spostamento dei punti di controllo risulterebbe
estremamente noioso e difficile.
Il nostro approccio, quindi, e stato quello di cercare una metafora
del mondo reale. Se riconduciamo la manipolazione di una curva alla
manipolazione di un oggetto (come il filo del telefono, il tubo da giardino,
le molle, gli elastici, ecc.), il risultato che ci si aspetta sara quello derivato
1Parleremo piu estesamente dell’aspetto matematico nel capitolo 5.
20
4. STATO DELL’ARTE
dall’esperienza quotidiana. Questa scelta comporta che non c’interessa
avere un controllo assoluto di ogni parametro (come nel caso di un utente
che utilizzi un CAD tecnico), ma avere un sistema che risponda in modo
coerente ad un modello.
Esaminiamo lo stato dell’arte, circa la manipolazione di curve bi-
dimensionali, analizzando alcuni programmi gia esistenti e le tecniche
proposte in letteratura.
4.2 Tecnologie e limiti
I programmi di manipolazione attualmente disponibili, oltre a fornire
avanzate funzioni di editing per le curve, permettono un controllo esat-
to della posizione dei punti di controllo; fra i piu noti possiamo citare
Flash[4] (vedere Fig. 4.1), o XFig[5] (vedere Fig. 4.2).
XFig fornisce molti strumenti per lo spostamento dei punti di con-
trollo e risulta piu indicato per un utente esperto.
Flash2, permette la manipolazione diretta di curve, ma l’utente non
ha la possibilita di muovere esattamente il punto selezionato, come vedia-
mo in Fig. 4.1,in cui, durante lo spostamento, il cursore si e allontanato
dal punto della curva.
Anche in letteratura sono stati proposti molti lavori per la risoluzione
del problema di manipolazione.
In [6] viene sviluppato un editor vettoriale, viene proposto un metodo
di manipolazione basato sulle wavelets. Questo metodo deriva da tecniche
usate nel trattamento dei segnali. I punti di controllo di una curva sono
visti come campioni di un segnale, tale segnale puo allora essere trattato
con dei filtri. I filtri consentono di modificare l’aspetto ed il carattere di
una linea.
2Prodotto della Macromedia e standard de facto per quello che riguarda la graficasu Web.
21
4. STATO DELL’ARTE
Figura 4.1: Un esempio di utilizzo di Flash versione 4.0.
Figura 4.2: Una esempio di utilizzo di Xfig.
22
4. STATO DELL’ARTE
Con aspetto, si intende la forma complessiva della curva e modifi-
candolo, possiamo effettuare la manipolazione di zone rappresentate da
curve con parametrizzazione particolare, o ottenere una versione meno
precisa, utile nel caso di stampa con bassa risoluzione (Fig. 4.3).
Figura 4.3: L’aspetto della linea viene variato, senza cambiarne ilcarattere.
Con carattere, si intende la forma del tratto con cui e realizzata la
curva; permetterne la modifica consente di realizzare una libreria di forme
possibili, da utilizzare sulla linea a video (Fig. 4.4).
Figura 4.4: Il carattere della curva viene variato, senza cambiarnel’aspetto.
L’utilizzo delle wavelets comporta, pero, delle difficolta di trattamento
in linee con discontinuita (caso frequente in disegni a mano), nonche per
operare in un contesto a risoluzione variabile (che vogliamo offrire ad un
disegnatore per correggere o migliorare dettagli).
In [7] viene proposto un approccio strettamente fisico. Come gia det-
to, la nostra idea di manipolazione deriva dagli oggetti quotidianamente
23
4. STATO DELL’ARTE
utilizzati, perche allora non applicare i parametri descriventi l’elasticita,
la dimensione ecc, ad una curva per poi deformarla in funzione delle for-
ze che subisce? L’idea porta ad introdurre le tecniche agli elementi finiti
(FEM, Finit Elements Methods), queste risultano computazionalmente
onerose; il maggior limite e che non sono direttamente applicabili a curve
ma soltanto a segmenti.
In [8] troviamo una tecnica basata sulla Free Form Deformation che
permette di modificare una curva, agendo in zone del disegno, individuate
da primitive semplici (cerchi, segmenti, curve). L’azione deformante sara
funzione di un certo potenziale collegato alla primitiva. Questa tecnica
presenta un grosso problema da risolvere, le equazioni che sono utilizzate
per rappresentare le curve non sono invarianti, ne rispetto a deformazioni
prospettiche, ne rispetto a deformazioni non lineari (come quelle offerte
dalle funzioni potenziale).
Il nostro contributo alla manipolazione e presentato in questo lavoro,
dove la nostra attenzione e stata attratta dalle possibilita offerte dalle
funzioni potenziale. Grazie al loro utilizzo, abbiamo ottenuto uno stru-
mento di manipolazione nuovo che basa il suo funzionamento su quelle
che abbiamo definite funzioni di modellazione (che sono specifiche funzio-
ni potenziale); inoltre, abbiamo esteso il lavoro presentato in [8] (relativo
alla Free Form Deformation) al caso bidimensionale, rendendo possibile
utilizzare questo approccio per la manipolazione non di oggetti tridimen-
sionali ma di disegni; infine, abbiamo realizzato uno strumento inedito
che permette di piegare tratti di disegno3.
Dopo questa breve introduzione, possiamo addentrarci nel problema,
analizzando le primitive utilizzate (le splines e le curve di Bezier) e le
tecniche implementate, da un punto di vista piu strettamente matemati-
co.
3Lo strumento, il cui nome e Bender, permette anche di “stirare” le linee (vedipag. 56).
24
Capitolo 5
Curve di Bezier e splines
5.1 Curve di Bezier e splines
5.1.1 Generalita
Quando abbiamo parlato dei formati raster e dei formati vettoriali 1, ab-
biamo fissato la nostra attenzione sul formato vettoriale, ma non ci siamo
soffermati sulle scelta delle primitive. In questo lavoro le primitive che
utilizzeremo saranno curve. Sorge allora la necessita di scegliere una rap-
presentazione che risulti compatta e permetta una facile manipolazione
dell’oggetto che descrive.
Una curva puo sempre essere ricondotta ad una linea spezzata, i cui
segmenti siano sufficientemente piccoli, da non dar luogo ad un effetto di
aliasing geometrico2 [9] (vedi Fig. 5.1). Descrivere una curva per mezzo
di punti e difficile, inefficiente e dispersivo, sia in termini di tempo da im-
piegare che per lo spazio di memoria da utilizzare (una buona definizione
comporta un elevato numero di punti). Sara allora preferibile utilizzare
1Si definiscono immagini raster quelle che sono descritte da una matrice di elementifondamentali detti pixels, vettoriali quelle i cui elementi sono primitive elementariquali linee, cerchi, ecc., vedi pag. 16.
2L’aliasing geometrico si presenta se riusciamo a distinguere i tratti dellapolinomiale che approssimano la curva.
25
5. CURVE DI BEZIER E SPLINES
Figura 5.1: Una curva disegnata mediante tratti sempre piu numerosi.
una rappresentazione matematica che: approssimi i punti; richieda meno
informazioni da memorizzare; sia facile da gestire.
Possiamo pensare di descrivere una curva in modo esplicito. Con que-
sta scelta avremo funzioni del tipo y = f(x). Questa rappresentazione
ha pero tre svantaggi: e impossibile avere molteplici valori di y per la
stessa x (non si possono descrivere curve come cerchi ed ellissi); la ge-
stione di trasformazioni affini (come rotazioni,ecc.) richiede l’utilizzo di
classi di funzioni diverse; non e facile gestire curve con tangente verticale
(pendenza infinita della retta tangente).
Un’altra soluzione e utilizzare una rappresentazione implicita f(x, y) =
0. Anche in questo caso abbiamo qualche problema, mentre possiamo
descrivere senza difficolta cerchi ed ellissi (x2
a2 + y2
b2− c2 = 0), applicare
trasformazioni affini risulta ancora laborioso; inoltre non abbiamo la pos-
sibilita di fissare un verso per la tangente (molto utile, ad esempio, nel
calcolo delle intersezioni).
La soluzione puo essere trovata utilizzando una rappresentazione di ti-
po parametrico (x = x(t), y = y(t) dove t e il parametro). La rappresen-
26
5. CURVE DI BEZIER E SPLINES
tazione parametrica non soffre delle limitazioni precedenti: la pendenza
geometrica (che puo essere infinita), e rimpiazzata dal vettore tangen-
te parametrico (che ha valori finiti); la curva puo passare piu volte per
lo stesso punto; siamo in grado di rappresentare senza difficolta cerchi,
semicerchi ed ellissi.
5.1.2 Curve di Bezier
Una curva di Bezier3, e una funzione continua b(t) ∈ <2 definita su di
un intervallo [0 ≤ t ≤ 1] ∈ < che puo essere scritta come:
b(t) =n∑
j=0
bjBnj (t). (5.1)
Nella Eq. 5.1, bj sono n + 1 punti che appartengono ad <2 e sono chia-
mati punti di controllo della curva; Bnj (t) risultano essere i polinomi di
Bernstein, definiti esplicitamente come:
Bni (t) =
n
i
ti(1− t)n−i (5.2)
con i coefficienti binomiali dati da:
n
i
=
n!
i!(n−i)!se 0 ≤ i ≤ n
0 altrimenti. (5.3)
La Eq. 5.1 rappresenta una curva di Bezier di grado n in forma
di Bernstein. Questa forma non e l’unica possibile, ma e utile per
le proprieta di cui gode che prima elencheremo e poi analizzeremo in
dettaglio:
• invarianza per trasformazioni affini;
3In seguito utilizzaremo curve bidimensionali e risultera <2, per il caso generalek-dimensionale avremmo dovuto scrivere <k.
27
5. CURVE DI BEZIER E SPLINES
Figura 5.2: Esempi di curve di Bezier. Lo spostamento dei punti dicontrollo influenza la forma della curva.
• invarianza sotto trasformazioni affini del parametro;
• proprieta dell’involucro convesso;
• interpolazione dei punti finali;
• simmetria;
• invarianza per combinazioni baricentriche;
• precisione lineare;
• pseudo controllo locale.
Invarianza per trasformazioni affini
Una trasformazione Φ : <2 → <2 e detta affine se verifica:
Φ(∑
αiPi) =∑
αiΦ(Pi), (5.4)
dove αi ∈ < e Pi ∈ <2. Applicando una trasformazione affine ad una
curva di Bezier abbiamo:
Φ(b(t)) = Φ(n∑
j=0
bjBnj (t)) =
n∑j=0
Bnj (t)Φ(bj). (5.5)
28
5. CURVE DI BEZIER E SPLINES
Applicare una trasformazione affine ad una curva4, significa, allora, tra-
sformare i soli punti di controllo e non ogni singolo punto della curva.
Invarianza sotto trasformazioni affini del parametro
Questo vuol dire che che l’intervallo [0, 1], del parametro t, puo essere
trasformato in [a, b] e viceversa:
n∑j=0
bjBnj (t) =
n∑j=0
bjBnj (
u− a
b− a). (5.6)
Questa proprieta permette di cambiare l’intervallo di definizione di una
curva senza cambiarne l’aspetto. L’utilizzo di questa proprieta ci per-
mette di effettuare facilmente la suddivisione di una curva in tratti di
lunghezza minore. Le curve che otteniamo risultano essere ancora curve
di Bezier il cui parametro e ricondotto a t ∈ [0, 1].
Proprieta dell’involucro convesso
Una curva di Bezier risulta sempre contenuta all’interno dell’involucro
convesso, costituito dai segmenti congiungenti i suoi punti di controllo.
Questo deriva dal fatto che i polinomi di Bernstein sono non negativi e
la loro somma risulta uguale ad 1. Il fatto che siano non negativi, nell’in-
tervallo [0, 1] di definizione, puo essere verificato analizzando il segno dei
singoli fattori della Eq. 5.7, che risultano positivi o nulli per t ∈ [0, 1]. Il
fatto che la somma dei polinomi sia 1 segue dalla definizione, infatti:
n∑i=0
n
i
(t)i(1− t)n−i = [(t) + (1− t)]n = 1. (5.7)
Una curva di Bezier e allora una combinazione lineare dei suoi punti
di controllo a coefficienti positivi e non maggiori di 1. Questa proprieta
risulta utile per operazioni di clipping, cioe, quando dobbiamo controllare
4Sono affini operazioni come lo zoom, la traslazione, la rotazione.
29
5. CURVE DI BEZIER E SPLINES
se una curva e visibile o no. Nel caso in cui, tutti i punti di controllo
sono visibili, anche la curva lo sara e potra essere disegnata, viceversa,
se nessun punto e visibile, siamo sicuri che non lo sara neanche la curva.
Interpolazione dei punti finali
Essendo Bnj (0) = δj,0 e Bn
j (1) = δj,n con δj,i l’ordinaria funzione di
Kronecker e ricordando l’Eq. 5.1 segue:
b(0) =n∑
j=0
bjBnj (0) =
n∑j=0
bjδj,0 = b0, (5.8)
ed analogamente per t = 1, vediamo allora che i punti b0 e bn apparten-
gono alla curva.
Simmetria
Prendiamo una curva a(t) che sia descritta da n punti di controllo b0a ,
b1a , b(n−1)a , bna , e una curva b(t) che sia ottenuta dagli stessi punti di
controllo, presi pero in ordine inverso. Per i nuovi (che indichiamo con
pedice b) b0b, b1b
, b(n−1)b, bnb
, sara allora:
b0a = bnb,
b1a = b(n−1)b,
...,
b(n−1)a = b1b,
bna = b0b,
(5.9)
cioe:
bia = b(n−i)b. (5.10)
30
5. CURVE DI BEZIER E SPLINES
Scriviamo l’equazione della curva b(t) in modo che il verso di percorrenza
sia lo stesso della curva a(t):
b(t) =n∑
j=0
bibBnn−j(1− t). (5.11)
Dall’Eq. 5.10 e dall’Eq. 5.11 abbiamo:
n∑j=0
bibBnn−j(1− t) =
n∑j=0
b(n−j)aBnn−j(1− t) (5.12)
risultando pero che:
Bnn−j(1− t) = Bn
j (t) (5.13)
ne deduciamo che:
n∑j=0
b(n−j)aBnj (1− t) =
n∑j=0
bjaBnj (t) = a(t), (5.14)
quindi le due curve sono uguali.
Invarianza per combinazioni baricentriche
In formule abbiamo:
n∑j=0
(αbj + βcj)Bnj (t) = α
n∑j=0
bjBnj (t) + β
n∑j=0
cjBnj (t), (5.15)
significa che possiamo costruire la media pesata di due curve, sia pren-
dendo la media pesata dei punti corrispondenti, sia facendo la media dei
punti di controllo e costruendo la curva. Questa proprieta risulta utile
per le superfici e non ci soffermeremo ulteriormente sopra.
31
5. CURVE DI BEZIER E SPLINES
Precisione lineare
Questa proprieta comporta che risulti:
n∑j=0
j
nBn
j (t) = t. (5.16)
Possiamo allora dedurre che: se prendiamo due punti a,b e una curva di
Bezier con punti di controllo uniformemente spaziati sulla congiungente
ad a,b, la curva rappresentata sara esattamente il segmento individuato
da a,b.
Pseudo controllo locale
Dato che i polinomi di Bernstein hanno il massimo per t = in
si puo
pensare che la curva risentira di uno spostamento di un punto di con-
trollo quanto piu questo pesi (peso inteso come valore del massimo del
polinomio di Bernstein).
5.1.3 Spline e curve di Bezier
Alcuni algoritmi (come quelli di minimizzazione per un insieme di curve o
quelli d’interpolazione di un insieme di punti) sono facilmente ottenibili
sfruttando le proprieta delle spline ed e per questo che nel seguito ne
tratteremo le proprieta (in particolare delle B-spline) e vedremo come e
possibile ricondurre una B-Spline ad un insieme equivalente di curve di
Bezier.
Considerazioni generali
Le funzioni di Bezier sono un potente strumento per il disegno e la mani-
polazione di curve. Se, pero, vogliamo modellare una curva dalla forma
estremamente complessa, la rappresentazione che si ottiene utilizzando
i polinomi di Bezier risulta avere un grado troppo elevato. Possiamo
32
5. CURVE DI BEZIER E SPLINES
allora pensare di utilizzare, invece di uno, un certo numero di polino-
mi di Bezier di grado minore (tipicamente grado 2 o 3). Chiameremo
curva B-spline la curva originata dall’utilizzo di tratti di curva polino-
miale di Bezier. I vantaggi che derivano da questo approccio possono
essere osservati, oltre che nell’interpolazione, anche nella manipolazione.
Nell’interpolazione: in quanto se volessimo interpolare n + 1 punti (con,
ad esempio, n ≥ 20) con un unico polinomio, questo presenterebbe un
comportamento eccessivamente oscillante che puo non essere accettabile;
mentre per una B-spline il grado e fisso e (tipicamente) basso5. Nella
manipolazione: in quanto se volessimo spostare uno dei punti interpolati
da un unico polinomio, questo spostamento si risentirebbe lungo tutta
la curva, mentre in una B-spline influenza la forma di un breve tratto
intorno al punto.
Caratteristiche e proprieta
Una spline6 e una funzione continua che trasforma ogni intervallo [ui, ui+1],
di un insieme u0 <, . . . , < uL in E2, in un tratto di curva polinomiale.
Ciascun numero reale ui e chiamato nodo e l’insieme degli intervalli e
detto sequenza dei nodi. Per ogni valore del parametro u abbiamo cosı
un corrispondente punto s(u) nella curva s. Ogni intervallo cosı definito
puo essere raffigurato da una curva di Bezier. Questo genere di rap-
presentazione e chiamato B-spline. La “B” sta per basis (base) e vuol
dire che una B-spline, puo essere rappresentata come somma pesata di
polinomi base (cosa non vera per le spline tradizionali).
Volendo descrivere una curva mediante una B-spline abbiamo bisogno
di fissare: il grado che desideriamo per la B-spline (lo indicheremo con
5A questo proposito notiamo che le B-spline godono della proprieta di minimo,sono le curve che minimizzano l’energia elastica del modello (vedi [10]), cioe sonoquelle che (a parita di grado) forniscono un’interpolazione che minimizza il quadratodell’integrale di curvatura, calcolato lungo la linea.
6In seguito con En indicheremo lo spazio euclideo n-dimensionale.
33
5. CURVE DI BEZIER E SPLINES
Figura 5.3: B-spline: una B-spline cubica con i suoi punti di controllo equelli relativi alle curve di Bezier che la rappresentano
n e sara generalmente 2 o 3); un insieme di numeri reali non decrescenti
che chiameremo nodi; un insieme di punti di controllo (che indicheremo
con di). Esistono relazioni molto strette fra il numero dei nodi, quello dei
punti di controllo ed il grado della B-spline. Nella sequenza di nodi, che
ricordiamo essere decrescente, e possibile che un valore sia ripetuto piu
volte. La ripetizione di un nodo comporta che la sua molteplicita (che
indicheremo con mi e sara semplice se pari ad 1) aumenti di 1 per ogni
ripetizione.
Dovra risultare che :
L+n−1∑i=n−1
ri = L + 1. (5.17)
Introduciamo cosı L, che (nel caso in cui tutti i nodi siano semplici) rap-
presenta il numero di domini intervallo di una B-spline. Ogni dominio
intervallo, puo essere interpolato da una curva di Bezier (vedi Fig. 5.1.3)
che mantenga con quella precedente e quella seguente condizioni di con-
34
5. CURVE DI BEZIER E SPLINES
tinuita; tali condizioni sono funzione del grado della B-spline (avremo
continuita C1 se di grado 2, C2 se di grado 3).
Con continuita di tipo C1 indichiamo l’uguaglianza, tra due curve,
dei vettori tangenti nel punto di giunzione. Se l’uguaglianza vale anche
per le derivate successive dn[b(t)]dtn
parleremo di continuita di tipo Cn. Pos-
siamo definire anche un’altro tipo di continuita, quella geometrica (Gn).
Quando due curve si incontrano in un punto (senza ulteriori condizioni)
avremo continuita geometrica G0. Se le tangenti nel punto di giunzio-
ne hanno stessa direzione, ma non necessariamente la stessa lunghezza,
allora parleremo di continuita geometrica di tipo G1.
Possiamo ora dare l’espressione completa per una B-spline:
s(u) =L+n−1∑
j=0
djNnj (u) (5.18)
dove: il parametro u puo assumere tutti i valori compresi tra i nodi
u0, · · · , uL+2n−2; n rappresenta il grado del polinomio di base Nnj (u); dj
sono i punti di controllo.
Il polinomio di base gode di alcune interessanti proprieta: e non nega-
tivo per qualsiasi valore di u, e (se i punti dj sono tutti pari ad 1) rende
la funzione s(u) identicamente uguale ad 1. Il valore di un polinomio in
un punto puo essere calcolato sfruttando delle formule di ricorsione.
Noi illustreremo quella di Cox, De Boor piu utile da un punto di vista
implementativo.
Definiamo una funzione di base per n = 0 come:
N0i (u) =
1 se ui−1 ≤ u < ui
0 altrove. (5.19)
In questo modo possiamo calcolare una funzione di grado qualsiasi uti-
35
5. CURVE DI BEZIER E SPLINES
lizzando la ricorsione di Cox, de Boor:
Nnl (u) =
u− ul−1
ul+n−1 − ul−1
Nn−1l (u) +
ul+n − u
ul+n − ul
Nn−1l+1 (u). (5.20)
Il calcolo esplicito del valore per ux puo essere effettuato ricordando la
Eq. 5.19 e procedendo nel seguente modo. Noto il valore j per cui ux
e compreso nell’intervallo [ui−1, ui], andiamo a calcolare il valore delle
funzioni che sono non nulle, organizzandole in una matrice:
......
......
......
0 0 0 0 · · · 0
N0j (ux) = 1 N1
j−1(ux) N2j−2(ux) N3
j−3(ux) · · · Nkj−k(ux)
0 N1j (ux) N2
j−1(ux) N3j−2(ux) · · · Nk
j−(k−1)(ux)... 0 N2
j (ux) N3j−1(ux) · · · · · ·
... 0 N3j (ux) · · · ...
... 0. . . . . .
.... . . Nk
j (ux)... 0
...
.
(5.21)
La somma dei valori presenti su ogni colonna e pari ad uno ed il valore
della B-spline per ux puo essere ora calcolato come:
s(ux) =j∑
i=j−k
djNkj (ux). (5.22)
In precedenza abbiamo detto che dalle B-spline possono essere otte-
nute un certo numero di curve di Bezier (pari al valore dei domini inter-
vallo), ma non abbiamo specificata la dipendenza tra i punti di controllo
della B-spline e delle curve di Bezier. Questa relazione (che tratteremo
esclusivamente per il grado 3, ma puo essere facilmente estesa ad un gra-
do qualsiasi) e ottenuta considerando le relazioni di continuita esistenti
36
5. CURVE DI BEZIER E SPLINES
tra due curve adiacenti. L’insieme dei dj prende il nome di poligono B-
Spline ed questo che in seguito verra ricondotto ad un insieme di curve
di Bezier (i cui punti di controllo indicheremo con bi). Nel seguito del
paragrafo utilizzeremo ∆i per indicare la differenza ui+1−ui tra due nodi.
Se tra due curve esiste una relazione di continuita di tipo C1 in ui il
punto di controllo nella giunzione deve rispettare una condizione di tipo
b3i =∆i
∆i−1 + ∆i
b3i−1 +∆i−1
∆i−1 + ∆i
b3i+1. (5.23)
Per esserci una continuita di tipo C2 dovra esistere un punto di, tale che
b3i−2,b3i−1,di e di,b3i+1,b3i+2, siano nel rapporto ∆i−1 : ∆i. La defini-
Figura 5.4: B-spline cubica con continuita C2: il punto ausiliario di
permette di definire il poligono B-spline della curva
zione di una B-spline (di grado 3) passa attraverso il posizionamento dei
punti di, che rappresentano il poligono B-spline. Quindi da un poligono
B-spline e dall’insieme dei nodi, ricaviamo facilmente i punti di controllo
37
5. CURVE DI BEZIER E SPLINES
delle curve di Bezier equivalenti alla B-spline. Avremo allora:
b3i−2 =∆i−1 + ∆i
∆di−1 +
∆i−2
∆di (5.24)
e
b3i−1 =∆i
∆di−1 +
∆i−2 + ∆i−1
∆di (5.25)
per tutti i valori di i = 2, L− 1, dove
∆ = ∆i−2 + ∆i−1 + ∆i. (5.26)
Qualche attenzione e richiesta per i primi e gli ultimi punti di controllo
dell’insieme delle curve di Bezier. Per questi dovra risultare:
b0 = d0
b1 = d1
b2 = ∆1
∆0+∆1d0 + ∆0
∆0+∆1d1
· · ·
b3L−2 = ∆L−1
∆L−2+∆L−1dL−1 + ∆L−1
∆L−2+∆L−1dL
b3L−1 = dL
b3L = dL+1
(5.27)
5.2 L’algoritmo di de Casteljau
L’algoritmo di de Casteljau permette di calcolare semplicemente i pun-
ti di una curva parametrica, come combinazione convessa dei punti di
controllo della stessa. Facciamo prima un esempio, per poi presentare
l’algoritmo. Prendiamo tre punti b0, b1, b2 ∈ E2 e sia t ∈ <. Possiamo
38
5. CURVE DI BEZIER E SPLINES
allora costruire:
b10 = (1− t)b0 + tb1,
b11 = (1− t)b1 + tb2,
b02 = (1− t)b1
0 + tb11.
(5.28)
Sostituendo le prime due equazioni nella terza otteniamo:
b02(t) = (1− t)2b0 + 2t(1− t)b1 + t2b2. (5.29)
Questa e l’espressione di una parabola. Al variare di t nell’intervallo
(−∞,∞), mediante un processo d’interpolazione lineare ripetuta, pos-
siamo ottenere (a partire da b0,b1,b2) tutti punti della parabola.
Poniamo ora, di voler calcolare il valore di una curva polinomiale di
grado n. Presi b0, b1,. . . , bn ∈ E3 punti e un parametro t ∈ <, sia:
bri = (1− t)br−1
i + tbr−1i+1
r = 1, . . . , n
i = 0, . . . , n− r, (5.30)
con bi0(t) = bi. Allora bn
0 (t) sara il punto di parametro t nella curva di
Bezier bn. Il poligono P formato dai b0, b1,. . . , bn e detto poligono di
Bezier o poligono di controllo. Da un punto di vista pratico, l’utilizzo
di questo algoritmo ci permette d’individuare:il punto di una curva per
un arbitrario valore del parametro; i punti di controllo delle sottocurve
relative a tale suddivisione.
5.3 Calcolo della distanza minima tra una
curva ed un punto del piano
Il problema di calcolare la distanza minima tra un punto del piano ed
una curva di Bezier puo essere riformulato come: trovare il valore del
39
5. CURVE DI BEZIER E SPLINES
parametro tP della curva, per cui la distanza del punto dalla curva sia
minima. Una volta trovato il parametro e facile individuare il punto
corrispondente sulla curva e la distanza minima.
Nel seguito descriveremo due diversi algoritmi risolutivi che risolvono
il problema d’individuare il parametro. Il primo sfrutta le proprieta del-
l’involucro convesso, il secondo formula il problema in modo da ricondurlo
ad una forma facilmente trattabile algoritmicamente.
5.3.1 Risoluzione con la proprieta dell’involucro con-
vesso
L’involucro convesso, individuabile dai punti di controllo che descrivono
la curva, contiene tutta la curva. Questa proprieta, viene mantenuta
nelle curve generate dalla suddivisione, effettuata mediante l’algoritmo
di de Casteljau (vedi pag. 38). Dati i punti di controllo di una curva
parametrica, si possono individuare i punti di controllo delle due sotto-
curve (generate dalla suddivisione della curva originale) per un valore di
parametro pari ad 12
facendo:
L0 = b0
L1 = b0+b1
2
R3 = b3
R2 = b2+b3
2
H = b1+b2
2
L2 = L1+H2
R1 = H+R2
2
L3 = R0 = L2+R1
2
(5.31)
dove con L abbiamo indicati i punti di controllo della curva a sinistra, con
R quelli della curva a destra e con bi quelli della curva iniziale. Suppo-
niamo che il punto selezionato sia molto vicino alla curva (praticamente
40
5. CURVE DI BEZIER E SPLINES
gli appartiene), effettuando un semplice test (controlliamo che il punto
sia contenuto nell’involucro convesso), potremmo facilmente trovare il piu
piccolo involucro che contiene il punto e, quindi, il corrispondente para-
metro, iterando l’algoritmo di de Casteljau sulla sottocurva in cui il test
e passato. Il metodo risulta essere abbastanza veloce, ogni volta dimez-
ziamo l’intervallo su cui effettuare la ricerca, ma presenta molti limiti.
Innanzi tutto, non e possibile trovare un punto che sia troppo distante
dalla curva (una delle ipotesi fatte e la vicinanza del punto alla curva), in
quanto, il test di appartenenza all’involucro, fallirebbe dopo pochi passi
dando un risultato errato, inoltre, puo accadere che il test di apparte-
nenza sia verificato per due poligoni contemporaneamente, rendendo la
ricerca complessa da gestire.
5.3.2 Risoluzioni con i polinomi di Bezier
La ricerca del parametro che individua il punto della curva piu vicino ad
un punto selezionato, puo essere posto formalmente dicendo: data una
curva parametrica B(t) ed un punto P, entrambi nel piano, trovare il
punto sulla curva B(t) piu vicino a P (o meglio il parametro relativo
a tale punto). La congiungente tra il punto di minimo e il punto P
sara necessariamente normale alla tangente. L’equazione, che vogliamo
risolvere rispetto a t, sara:
(B(t)−P) ·B′(t) = 0. (5.32)
Se B(t) e una curva parametrica di Bezier in forma di Bernstein (B(t) =∑ni=0 biB
ni (t)), possiamo indicare la derivata come:
B′(t) = nn−1∑i=0
(bi+1 − bi)Bn−1i (t). (5.33)
41
5. CURVE DI BEZIER E SPLINES
Le curve che utilizziamo hanno grado 3, la derivata prima sara ovviamen-
te di grado 2, il polinomio 5.32 sara generalmente di grado 5. Per tale
grado non esiste una forma chiusa e, per la ricerca delle radici reali, si
dovra utilizzare qualche algoritmo. Nel seguito, ne presenteremo uno che
utilizza le proprieta delle curve di Bezier, preoccupiamoci di riportare il
nostro polinomio in tale forma.
Separiamo i due polinomi della 5.32, avremo:
B1(t) = B(t)−P, (5.34)
B2(t) = B′(t). (5.35)
Allora la 5.32 sara riscrivibile come:
B1(t) ·B2(t) = 0. (5.36)
Il primo polinomio sara riscrivibile come:
B1(t) =
= B(t)−P =
=∑n
i=0 biBni (t)−P =
=∑n
i=0 ciBni (t)
(5.37)
con ci = bi −P. Dall’espansione della 5.35 abbiamo:
B2(t) =
= n∑n−1
i=0 (bi+1 − bi)Bn−1i (t) =
=∑n−1
i=0 diBn−1i (t),
(5.38)
42
5. CURVE DI BEZIER E SPLINES
dove abbiamo di = n(bi+1 − bi). Possiamo ora riscrivere la 5.36 come:
0 =∑n
i=0 ciBni (t) ·∑n
j=0 djBn−1j (t)
=∑n
i=0
∑n−1j=0 ci · djB
ni (t)Bn−1
j (t)
=∑n
i=0
∑n−1j=0 ci · dj
n
i
(1− t)(n−i)ti
n− 1
j
(1− t)(n−1−j)tj
=∑n
i=0
∑n−1j=0 ci · dj
n
i
n− 1
j
(1− t)(2n−1)(i−j))t(i + j)
=∑n
i=0
∑n−1j=0 ci · dj
n
i
n− 1
j
2n− 1
i + j
B2n−1
i+j
=∑n
i=0
∑n−1j=0 ci · djzi,jB
2n−1i+j
=∑n
i=0
∑n−1j=0 wi,jB
2n−1i+j ,
(5.39)
con zi,j =
n
i
n− 1
j
2n− 1
i + j
e wi,j = ci · djzi,j. Ora wi,j non sono punti,
ma numeri reali che descrivono una sola componente della curva parame-
trica (la y per l’esattezza). L’altra componente e ottenuta considerando i
punti di controllo wi,j ottenuti da i+j/n+(n−1) (semplice applicazione
della proprieta di precisione lineare di pag. 32). A questo punto l’equa-
zione di quinto grado e in forma di Bezier, possiamo, quindi, utilizzare
l’algoritmo di soluzione basato su questa particolare rappresentazione che
presentiamo nella nuova sezione.
43
5. CURVE DI BEZIER E SPLINES
Metodo per il calcolo delle radici di un polinomio in forma di
Bezier
In [10], viene definito minmax box: “il piu piccolo rettangolo con i lati pa-
ralleli agli assi coordinati, che contiene l’involucro [convesso]”. Trovare la
minmax box e molto semplice, basta individuare i massimi dell’involucro
convesso e calcolare il rettangolo che li interpola. Se abbiamo bisogno di
calcolare le intersezioni di una curva planare (i cui valori delle coordinare
x dei punti di controllo, siano uniformemente spaziati sulle ascisse), con
un segmento S, basta controllare la presenza d’intersezioni tra minmax
box della curva e segmento. Se questa intersezione e trovata si puo uti-
lizzare l’algoritmo di de Casteljau, vedere quale delle due minmax box
ha ancora intersezione e ripetere il procedimento finche l’involucro con-
vesso della curva considerata non sia talmente piccolo da poter essere
considerato un punto. La suddivisione binaria, operata dall’utilizzo del-
l’algoritmo di de Casteljau, permette di avere un errore pari ad 12r dove r
e il passo di ricorsione. Al primo passo, infatti, avremo che il parametro
varia tra 0 ed 1 (t ∈ [0, 1]) e l’errore massimo che ci potremo aspettare
sara 0.5, al passo successivo l’errore che ci aspetteremo sara la meta 0.25
e cosı via. Questo algoritmo non e attendibile per la soluzione di un po-
linomio con un grado troppo elevato, in quanto c’e un accumulo di errori
derivanti dalle suddivisioni che potrebbero fornire una soluzione errata.
E bene notare il test della minmax box e formalmente analogo a quello
presentato nella sezione precedente. L’ipotesi che ora lo rende utilizzabile
e la planarita della curva (che non e assicurata nel caso precedente).
5.4 Curvatura
Nel nostro studio particolare importanza ha rivestito l’analisi della cur-
vatura di una curva parametrica. Una curva in E3 e ottenuta da una
44
5. CURVE DI BEZIER E SPLINES
rappresentazione parametrica tipo
x = x(t) =
x(t)
y(t)
z(t)
, t ∈ [a, b] ⊂ < (5.40)
dove le coordinate cartesiane x, y, z, sono funzioni differenziabili di t e
x(t) e una curva del tipo descritto in precedenza, (curve di Bezier, curve
splines). Per evitare problemi relativi alla parametrizzazione della curva,
Figura 5.5: Una curva parametrica nello spazio.
assumiamo che risulti:
x′=
x′(t)
y′(t)
z′(t)
6= 0, t ∈ [a, b] ⊂ < (5.41)
denotando con l’apice la derivata prima rispetto a t. Una parametrizza-
zione di questo tipo e detta regolare. Effettuando un cambio di τ = τ(t)
45
5. CURVE DI BEZIER E SPLINES
del parametro t, dove τ e una funzione differenziabile di t, non cambie-
remo l’aspetto della curva. Questa riparametrizzazione sara regolare se
τ′ 6= 0 per tutte le t ∈ [a, b], in tal caso infatti possiamo ricavare l’inversa
t = τ(t). Ponendo:
s = s(t) =∫ t
a‖x′‖dt, (5.42)
vediamo che tale risultato e indipendente da qualsiasi tipo di parame-
trizzazione infatti:
x′dt =
dx
dτ
dτ
dtdt =
dx
dτdτ . (5.43)
Questo parametro invariante s e detto lunghezza dell’arco.
5.4.1 Riferimento di Frenet
Introdurremo ora uno speciale sistema di coordinate (collegato al punto
x(t) della curva), che facilitera la descrizione di alcune proprieta della
curva. Effettuiamo l’espansione in serie di Taylor di x(t) :
x(t + ∆t) = x + x′∆t + x
′′ 1
2∆t2 + x
′′′ 1
6∆t3 + . . . (5.44)
Supponendo che le prime tre derivate x′,x
′′,x
′′′, siano linearmente indi-
pendenti possiamo fissare un sistema di riferimento basato sulle derivate
con origine in x(t). La curva x(t), in questo sistema, puo essere scritta
come: ∆t + . . .
∆t12
+ . . .
∆t16
+ . . .
(5.45)
dove con i puntini intendiamo i termini di ordine superiore. Riportan-
do il sistema di riferimento individuato in forma ortonormale, possiamo
46
5. CURVE DI BEZIER E SPLINES
chiamare i tre assi individuati t, m, b e questi saranno dati da:
t = x′
‖x′‖
m = b ∧ t
b = x′∧x
′′
‖x′∧x′′‖
(5.46)
dove con∧
denotiamo l’usuale prodotto vettoriale. Il vettore t e detto
vettore tangente, il vettore m e detto normale principale e b e detto
vettore binormale. Il riferimento t, m, b, e detto riferimento di Frenet,
e varia il suo orientamento al variare del parametro t. La formulazio-
ne in termini di riferimento di Frenet e utilizzata in quanto si desidera
esprimere le variazioni locali del riferimento, in funzione del riferimento
stesso. In particolare utilizzando una parametrizzazione rispetto alla lun-
ghezza dell’arco le formule che rappresentano la curvatura e la torsione
sono semplici. Indicando la differenziazione rispetto all’arco con un apice
numerico, vediamo che x1 = t e un vettore unitario, e si trova inoltre che:
x1 · x1 = 1
x1 · x2 = 0.(5.47)
Possiamo ora ottenere le formule di Frenet− Serrel
t1 = km
m1 = −kt + τb
b1 = −τm
(5.48)
dove k e τ sono rispettivamente la curvatura e la torsione della curva in
questione.
Curvatura e torsione si calcolano mediante le
k = k(s) = ‖x2‖ (5.49)
47
5. CURVE DI BEZIER E SPLINES
k = k(t) =‖x′ ∧ x
′′‖‖x′‖3 (5.50)
τ = τ(s) =1
k2det[x
′, x
′′, x
′′′] (5.51)
τ = τ(t) =det[x
′, x
′′, x
′′′]
‖x′ ∧ x′′‖2 . (5.52)
5.4.2 Algoritmi per il calcolo della curvatura
Per i nostri scopi l’aspetto della curva che ci preme maggiormente cono-
scere e manipolare e la curvatura. Questo perche due curve che appaiono
uguali possono avere differenti curvature e questo nasconde profonde dif-
ferenze di parametrizzazione. Il grafico di curvatura si ottiene come coor-
dinate (t, s(t)) al variare del parametro t ∈ [a, b]; questo comporta che
utilizzeremo la forma 5.50, che tradotta in termini di curve bidimensionali
si riduce a
k = k(t) =x′′(t)y
′(t)− x
′(t)y
′′(t)
[(x′(t))2 + (y′(t))2]32
(5.53)
questa formula non riveste un particolare interesse computazionale, in-
fatti la sua risoluzione comporta il calcolo di quattro derivate e la riso-
luzione della radice a denominatore, per noi e piu interessante utilizzare
la formula
k =n− 1
n
w0w2
w1
b
a2, (5.54)
(vedi [10]) dove con a, indichiamo la distanza tra i punti di controllo
b0 e b1, e con b la distanza tra b2 e la tangente individuata da b0b1
tale formula (di cui non riporteremo la dimostrazione) e valida per un
polinomio razionale di Bezier di grado 3 e ci restituisce il valore della
curvatura per il valore 0 del parametro. Un polinomio di tal genere si
esprime come
br(t) =
∑ni=0 wibiBi(t)∑ni=0 wiBr(t)
, (5.55)
48
5. CURVE DI BEZIER E SPLINES
Figura 5.6: Riferimento di Frenet e significato di a e b
questa formulazione puo essere facilmente ricondotta al caso non ra-
zionale fissando i pesi wi = k, k 6= 0, in tal modo per la proprieta di
precisione lineare (vedere 5.1.2), la sommatoria del polinomio a denomi-
natore e uguale all’unita, e il polinomio a numeratore non e altro che la
rappresentazione di una curva parametrica non razionale. Se il grado e
tre otteniamo:
k =2
3
b
a2, (5.56)
o, in modo equivalente, 7
k =2 · 23
area[b0,b1,b2]
dist3[b0,b1], (5.57)
questa espressione e equivalente alla 5.54 perche indicando con b2t la
proiezione di b2 sull’asse t, l’area precedente puo essere calcolata come
differenza tra(b2t−a)·b
2e
b2t ·b2
da cui b2· b2t − (b2t − a) = a·b
2e sosti-
tuendo all’area quest’ultima espressione si ottiene esattamente la 5.54.
7 l’area nel nostro caso risulta essere il valore del seguente determinante
area(a,b, c) =ax bx cx
ay by cy
1 1 1
49
5. CURVE DI BEZIER E SPLINES
E allora possibile calcolare la curvatura per ogni valore della curva, uti-
lizzando l’algoritmo di de Casteljau. Possiamo ricavare il poligono di
controllo per il valore di parametro che ci interessa ed applicargli la
5.57.Un solo caso sembrerebbe avere difficolta, il valore estremo t = 1
(x(1) = b2); sfruttando un’ulteriore proprieta delle curve di Bezier, la
simmetria (vedere 5.1.2), basta allora invertire le posizioni dei punti con-
trollo (b0r = b3, b1r = b2, b2r = b1, b3r = b0), e applicando la formula
si ottiene esattamente il risultato cercato.
50
Capitolo 6
Utilizzo e progetto di tecniche
di manipolazione
In questo capitolo, parleremo delle tecniche realizzate per permettere la
manipolazione intuitiva di una curva. Nella prima parte presenteremo
gli strumenti ed il loro utilizzo, nella seconda gli algoritmi che ne hanno
reso possibile l’implementazione. Gli strumenti che tratteremo sono uti-
lizzati nel programma Fasped (un ambiente che e servito per il test e che
analizzeremo nel prossimo capitolo), ma la loro collocazione finale sara
all’interno di Paperless.
6.1 Gli strumenti
Fasped, analogamente ad altri programmi di disegno vettoriale, offre al-
cuni strumenti (tools) per disegnare e manipolare tratti di curva (nel
seguito considereremo un disegno, realizzato per mezzo di pennellate o
strokes), come, ad esempio, il tool Pencil, con cui possiamo tracciare una
linea a mano libera, o Arrow, con cui la possiamo muovere come se fosse
un filo. Il risultato dell’operazione eseguita da uno strumento, e ottenuto
grazie all’algoritmo che lo caratterizza.
Gli strumenti offerti da Fasped sono contenuti in diverse toolbars.
51
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
Ogni toolbar raggruppa le icone dei tools che hanno funzionalita simili.
Abbiamo una toolbar di disegno; una di manipolazione ed una per il
controllo della visualizzazione. La selezione avviene clickando sull’icona
raffigurante il tool. L’utente interagisce con uno strumento, sfruttando
opportune combinazioni degli eventi (del mouse) di click, drag e release.
6.1.1 Strumenti di disegno
La toolbar di disegno (vedi Fig. 6.1), contiene degli strumenti che per-
mettono il disegno di una linea o di semplici figure geometriche.
Figura 6.1: La toolbar con gli strumenti di disegno
Le icone presenti in Fig. 6.1 corrispondono ai tools:
• Pencil, che permette il disegno di una curva a mano libera;
• Pen, che permette di disegnare una curva specificando i punti di
controllo;
• Rectangle, che permette di disegnare un rettangolo;
• Ellipse, che permette di disegnare un’ellisse.
Pencil
Pencil permette il disegno di una linea a mano libera. I punti sono
acquisti nella fase di click and drag e vengono interpolati al release. Du-
rante l’acquisizione viene mostrata la poligonale che passa per i punti
campionati dal mouse; questa viene sostituita al release dalla linea com-
posta dalle curve interpolanti. L’algoritmo d’interpolazione e descritto in
dettaglio a pag. 62. Pencil deve riuscire a convertire in curve di Bezier, li-
nee che potrebbero avere discontinuita (l’utente potrebbe disegnare delle
52
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
cuspidi) e questo comporta di dover esaminare i punti per suddivider-
li in tratti “abbastanza” continui, da raccordare opportunamente dopo
l’interpolazione.
Pen
Pen e lo strumento per disegnare un arco di Bezier, posizionando i punti
di controllo. Al click viene lasciato il punto iniziale dell’arco, durante il
drag viene spostato il secondo punto, la cui posizione e fissata dall’azione
di release. Visto che le curve di Bezier, da noi utilizzate hanno bisogno di
quattro parametri, dovremo ripetere la procedura appena descritta per
completare l’arco.
Durante il drag sono mostrati due vettori, uno che va dal punto sele-
zionato al cursore ed un altro di verso opposto. Questi indicano all’utente
quale sara la posizione dei punti di controllo intermedi.
Possiamo costruire una linea costituita da tratti di curve, semplice-
mente iterando il procedimento di posizionamento. Le curve costrui-
te mantengono una continuita di tipo C1 (la tangente, nel punto di
giunzione, della curva che precede e uguale a quella della curva che segue).
Rectangle
Rectangle e uno dei tool realizzati al fine di facilitare il disegno di sem-
plici primitive. La costruzione del rettangolo avviene posizionando i due
estremi della diagonale, gli eventi utilizzati sono: click, drag e release. Al
click viene posizionato il primo estremo, durante il drag muoviamo la dia-
gonale e viene visualizzato il rettangolo costruito a partire dall’estremo
fissato e di diagonale variabile; al release rimane individuato il secondo
estremo ed il disegno finale.
53
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
Ellipse
Ellipse viene impiegato per disegnare ellissi o cerchi. Il suo utilizzo e
uguale a Rectangle, l’unica differenza risiede nel fatto che, invece del
rettangolo, viene disegnata la curva in esso inscritta.
6.1.2 Strumenti di manipolazione
In Fasped, un disegno puo essere realizzato a mano, oppure caricato da
un file su cui era stato precedentemente salvato. Una volta che un di-
segno e presente nella finestra di lavoro, e possibile procedere alla sua
manipolazione.
Figura 6.2: La toolbar con gli strumenti di manipolazione
La modifica del disegno e resa possibile da (Fig. 6.7):
• ControlPoints, che permette di spostare i punti di controllo;
• Arrow, che permette di spingere o tirare una linea come se fosse un
filo;
• Bender, che permette di “piegare” o “stirare” una curva;
• Deformer, che permette di selezionare una zona di disegno e defor-
marla in modo intuitivo.
ControlPoints
ControlPoints e un tool che permette di modificare la posizione di un
punto di controllo di una linea. Quando il tool viene selezionato, so-
no mostrati tutti gli estremi1 delle curve del disegno. Clickando su uno
1Per la proprieta di pag. 30 gli estremi sono punti di controllo.
54
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
di questi punti, sono mostrati i vettori che collegano l’estremo ai punti
di controllo intermedi. Effettuando il drag i vettori sono traslati nella
posizione voluta. Al successivo release i vettori continuano ad essere mo-
strati, in questo modo l’utente puo cambiare la direzione della tangente
(vedi Fig. 6.3).
Figura 6.3: In 1) la curva come appare dopo la selezione del tool; in 2) eavvenuta la selezione e vengono mostrati i segmenti; in 3) “bilancino” eruotato; in 4) il risultato finale.
Arrow
L’Arrow e uno strumento che permette di manipolare una singola pen-
nellata. La deformazione e ottenuta clickando un punto della linea e
Figura 6.4: Un esempio del’utilizzo di arrow: a sinistra l’immagineoriginale; a destra e stata “tirata” una linea del cappello.
55
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
spostandolo. La linea, durante il movimento manterra la sua continuita.
L’operazione effettuata da Arrow ha un carattere “locale”, agisce solo su
un breve tratto di linea. La lunghezza di questo tratto e fissata automa-
ticamente dal programma e varia al variare della risoluzione del disegno.
Questa astrazione comporta, da un punto di vista implementativo, di
dover risolvere il problema di rendere una linea indipendente dalla sua
parametrizzazione; per far questo abbiamo realizzato un algoritmo (che
presenteremo in seguito), che permette la parametrizzazione uniforme
mediante suddivisioni ricorsive.
Bender
Il Bender e uno strumento che lascia manipolare le linee di un disegno
in due modi: “piegandole” o “stirandole” (vedi Fig. 6.5). Diversamente
Figura 6.5: Un esempio di utilizzo del bender. A sinistra l’immagine ori-ginale mentre viene posizionato lo strumento, in centro abbiamo ruotatoparte del braccio, a destra il risultato finale.
dall’Arrow, il Bender permette di considerare come un oggetto fisico, non
piu un contorno, ma un pezzo di disegno. In questo modo siamo in grado
di spostare delle aree di disegno opportunamente individuate.
Il Bender richiede d’individuare un segmento di “taglio” che divida
la finestra di disegno in due semipiani e che intersechi le linee formanti
la zona da muovere. Questo segmento e ricavato dalla combinazione
56
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
dell’azione di click, in cui viene posizionato un estremo, di drag, che
permette di fissarne la lunghezza e di release, che rilascia il secondo. Una
volta che il segmento sia stato posizionato lo possiamo muovere in due
modi o spostando un estremo, o trasclando.
Nel primo caso, le linee che si trovano nel semipiano, verso cui avviene
lo spostamento, sono “piegate” rispetto all’estremo fisso; nel secondo caso
vengono invece “stirate” nella direzione del movimento.
Il Bender, come gia detto, permette di agire su intere zone di disegno,
questo comporta di dover raccordare in modo opportuno le regioni mobili
con quelle ferme e questo puo essere fatto solo inserendo nuove linee la
cui forma sia opportunamente controllata.
Deformer
Il Deformer permette di modificare una zona del disegno che e individuata
dalla forma di un’area semplice (nel nostro caso un cerchio). Il tool,
una volta selezionato, viene visualizzato in finestra per mezzo di una
circonferenza, il cui centro segue il cursore.
All’evento di click and release la circonferenza viene lasciata sul dise-
gno e tutte le linee che hanno intersezioni o si trovano nel cerchio vengono
selezionate e colorate di rosso; una volta posizionata la possiamo traslare
o aumentarne il raggio.
Nel primo caso basta clickare all’interno del cerchio e fare drag, in
questo modo la regione individuata dal tool viene traslata ed il disegno
selezionato deformato, mantenendo alcune caratteristiche (in particolare
viene mantenuta la continuita nella regione anulare esterna); nel secondo
caso, dobbiamo clickare in prossimita del cerchio e fare drag; l’area si
allarga ed un numero maggiore di curve sono selezionate.
Il Deformer sfrutta le potenzialita offerte dalle funzioni potenziale.
Questo (come vedremo in seguito) comporta di dover suddividere la curva
in tratti di lunghezza molto piccola, muoverli e cercare di ridurli.
57
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
Figura 6.6: Un esempio del’utilizzo di Deformer. A sinistra il tool vie-ne posizionato, in centro il tool e attivo ed e stato spostato, a destra ilrisultato della deformazione.
6.1.3 Strumenti per il controllo della visualizzazio-
ne
In questo paragrafo presentiamo alcuni tools che servono per gestire la
visualizzazione del disegno; nella toolbar (Fig. ??) troviamo:
Figura 6.7: La toolbar con gli strumenti di controllo visualizzazione.
• Zoom IN, gestisce lo zoom per visualizzare particolari;
• Zoom OUT, gestisce lo zoom per dare una vista d’insieme;
• Pan, che permette di fare una panoramica del disegno;
• FrameManager, che permette di cambiare la tavola su cui si sta
lavorando.
Zoom IN/OUT
Zoom IN/OUT gestisce lo zoom, permette cosı di esaminare particolari
o di avere una vista d’insieme. Diversamente dagli altri tool basta solo
selezionare l’icona per ingrandire o rimpicciolire il disegno.
58
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
Pan
Pan si occupa di traslare il disegno, all’interno della finestra video. Al
click viene agganciato il punto in cui si trova il cursore, durante il drag
il disegno viene traslato ed al release viene rilasciato.
FrameManager
Nel nostro programma, abbiamo fornito all’utente, la possibilita di ope-
rare con piu di un disegno in una stessa finestra; questa scelta, e dovuta
al fatto che un’animazione e composta da una sequenza di disegni. Fra-
meManager permette di passare da uno all’altro. Quando lo strumento
viene selezionato e mostrata una griglia le cui celle possono essere piene
o vuote (questa griglia, per ora, consente di gestire fino a 20 disegni con-
temporaneamente). Ogni cella e l’indice di una struttura dati contenente
le tavole di disegno. Se un cella e vuota, in quella posizione non c’e un
disegno. Quelle piene possono essere gialle o viola: se sono gialle ci sono
disegni a cui accedere; se viola il disegno della cella e quello a video (per
adesso e possibile visualizzare un solo disegno alla volta, non viene gesti-
to l’alpha-blending, vedi pag. 9). Le funzionalita potrebbero essere estese
in futuro, per consentire la copia di tavole e la preview dell’animazione
di quelle presenti.
6.2 Gli Algoritmi
In questo seconda parte del capitolo presentiamo gli algoritmi che hanno
reso possibile la realizzazione dei tools precedenti; in seguito vedremo:
• ricerca di una curva;
• suddivisione di una curva in tratti di lunghezza fissata;
• minimizzazione del numero di curve di una linea.
59
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
6.2.1 Ricerca di una curva
L’algoritmo che, all’interno del disegno, individua una curva che si trovi
a distanza minima da un punto e uno dei piu importanti, in quanto ogni
tool ha bisogno di conoscere dove l’utente desidera sia applicata l’azione.
Il nostro algoritmo prende in ingresso le curve che compongono il disegno,
il punto in cui si vuole effettuare la ricerca ed il raggio di pick.
Il raggio di pick, fissa la distanza massima per cui il punto clickato
riesce a selezionare un oggetto; linee, curve o punti a distanza maggiore
di tale valore non verranno trovati.
In uscita avremo la curva selezionata; inoltre, viene restituito il va-
lore del parametro per cui la curva ha distanza minima dal punto. Lo
pseudocodice e mostrato in Algoritmo
Algoritmo 1: Selezione
procedure Selectioninput: listOfCurves /*elenco di curve*/
point /*punto del piano che e stato selezionato*/pickRay /*raggio di pick*/
output: curve /*curva a distanza minima*/[1] for each curva di listOfCurves[2] Controlla che il punto appartenga alla minmax box
della curva, allargata di pickRay[3] if e all’interno[4] then calcola la curva a distanza minima dal punto[5] curve=curva trovata in 4
Spieghiamo meglio alcune fasi. Un disegno e composto da molte cur-
ve, abbiamo bisogno di effettuare la ricerca nel modo piu rapido possibile.
Sappiamo che una curva risulta completamente contenuta all’interno del-
l’involucro formato dai suoi punti di controllo (proprieta a pag. 29). Effet-
tuare un test che controlli se un punto appartenga all’involucro convesso
richiede numerosi calcoli; preferiamo allora utilizzare, al posto dell’invo-
60
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
lucro, la minmax box, una maggiorazione molto facile da individuare (e
il rettangolo che contiene l’involucro) e che richiede pochi confronti per
stabilire se un punto si trova all’interno o no. La minmax box e allargata
delle dimensioni del “raggio di pick”, (questo permette la selezione an-
che se il punto di click non e vicinissimo alla curva). Sulla minmax box
allargata viene effettuato il test di appartenenza e, se ha esito positivo,
si passa al calcolo del minimo vero e proprio (effettuato in linea 4) che
viene eseguito utilizzando la procedura presentata a pag. 41. Infine, e
restituita la curva che ha distanza minima ed il parametro che compete
al minimo2.
6.2.2 Suddivisione di una curva in tratti di lunghez-
za fissata
Nel nostro studio abbiamo trovato che l’utilizzo di opportune funzioni
potenziale, permette di realizzare tools di manipolazione dal risultato
intuitivo e facili da usare (come l’Arrow o il Deformer). Le funzioni
potenziale non sono, pero, trasformazioni affini ed il risultato della tra-
sformazione dei soli punti di controllo della curva, non e quello esatto
(che potrebbe essere ottenuto solamente trasformando ogni punto della
curva secondo la funzione prescelta). Nella realta non potremo utilizzare
gli infiniti punti della curva, possiamo pero pensare di dividerla, in seg-
menti di lunghezza pari alla dimensione di un pixel e muovere gli estremi
di ogni segmento (vedi [10], [1], [8]). In questo modo riusciremo ad indi-
viduare l’effetto della deformazione, anche se poi rimane il problema di
ridurre il numero di punti necessari a descrivere la linea deformata.
Nasce l’esigenza di trovare un algoritmo che permetta di suddividere
la curva in tratti di lunghezza costante. Questo non puo essere fatto,
2In alcuni casi sono restituiti anche gli indici che permettono d’individuare la curvaall’interno delle strutture dati utilizzate (indice della linea, nella lista di linee di unframe; indice della curva, nella lista di curve di una linea).
61
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
semplicemente prendendo i punti ottenuti dal campionamento uniforme
del parametro t, in quanto non abbiamo una relazione lineare tra curva
e parametro. L’algoritmo di suddivisione, da noi individuato, passa per
il calcolo della lunghezza della curva.
La lunghezza di una curva parametrica dovrebbe essere calcolata me-
diante un integrale del tipo: l =∫ 10 s(t)dt, dove s e la lunghezza dell’arco
(vedi pag. 46). Possiamo pero approssimare l’integrale con segmenti, uti-
lizzando lo stesso principio ed algoritmo della fase di disegno (de Castel-
jau): se un segmento si discosta talmente poco dalla curva da poter essere
considerato coincidente (approssimiamo la lunghezza dell’arco con la cor-
da) la sua lunghezza sara circa quella dell’arco che sottende. L’algoritmo
che abbiamo realizzato prende in ingresso:
• la curva da suddividere;
• la lunghezza del tratto in cui viene suddivisa la curva;
• la tolleranza di disegno.
La tolleranza di disegno indica il valore limite della lunghezza di un seg-
mento al di sotto del quale possiamo considerarlo un punto. L’Algoritmo
mostra lo pseudocodice che risolve il problema.
L’algoritmo restituisce un vettore contenente i valori del parametro
in cui devono essere effettuate le suddivisioni. Usando un’opportuna
funzione di suddivisione, otteniamo la poligonale costituita da tratti di
lunghezza costante.
6.2.3 Minimizzazione del numero di curve di una
linea
La ricerca di una algoritmo di riduzione nasce da almeno due necessita:
quella di interpolare i punti acquisiti durante la fase di disegno; quella di
ridurre a curva, la poligonale creata per applicare le funzioni potenziale.
62
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
Algoritmo 2: Suddivisione in tratti di lunghezza fissa
procedure Subdivideinput: t0 /*parametro che individidua il primo arco*/
t1 /*parametro che individua il secondo arco*/segmentLenght /*lunghezza del tratto in cui si desidera
suddividere la curva*/output: td /*parametro di suddivisione*/
[1] if (t1 − t0 < EPSILON) /*se i due parametri sonotalmente prossimi da non essere distinguibili*/
[2] then return[3] Calcola l’arco L0 che va t = 0 a t0[4] Calcola l’arco L1 che va t = 0 a t1[5] Calcola quanti tratti sono stati individuati e
la lunghezza totale osservata.[6] if l’arco L1 ha lunghezza minore della lunghezza
totale osservata[7] then return[8] if la differenza tra la lunghezza di arco L1 e lunghezza
totale osservata e minore di tolleranza.[9] then calcola il parametro linearizzato[10] td=parametro trovato in 9[11] return[12] else la curva e ancora troppo lunga[13] Calcola tm = t1+t0
2[14] subdivide(t0, tm, segmentLenght)[15] subdivide(tm, t1, segmentLenght)
63
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
L’algoritmo di riduzione da noi utilizzato (e mostrato in Algoritmo ),
si ispira al metodo di [11], ripreso nel lavoro di [12]. Questo algoritmo
permette di trovare una linea che interpola un insieme ordinato di punti
(a meno di una tolleranza fissata dall’utente), sfruttando un metodo che
minimizza la somma dei quadrati delle distanza tra punti e linea. In in-
gresso avremo il set di punti da interpolare e il valore di distanza minima
che desideriamo sussista tra punti e curva; in uscita l’insieme di curve
interpolanti.
Indichiamo con Pi = (xi, yi) (i = 1, . . . , n) i punti da interpolare e
fissiamo il grado del polinomio della curva parametrica da utilizzare a 3;
riconduciamo la curva di Bezier in forma di polinomio:
B(t) = b0(1− t)3 + b13t(1− t)1 + b23t2(1− t) + b3t
3 =
=
axt3 + bxt
2 + cxt + dx
ayt3 + byt
2 + cyt + dy
.(6.1)
Il problema sara d’individuare i punti di controllo della curva o, in
modo equivalente, i coefficienti del polinomio in Eq. 6.1. I coefficienti
ax,bx,cx,dx,ay,by,cy,dy sono collegati ai punti di controllo, mediante:
ax = −b0x + 3b1x − 3b2x + b3x
bx = 3b0x − 6b1x + 3b2x
cx = −3b0x + 3b1x
dx = b0x
ay = −b0y + 3b1y − 3b2y + b3y
by = 3b0y − 6b1y + 3b2y
cy = −3b0y + 3b1y
dy = b0y.
(6.2)
Per calcolare le posizioni dei punti Pi sulla curva, avremo bisogno di una
64
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
parametrizzazione iniziale che possiamo ottenere come:
ti =
0, i = 1
lenght(P1P2...Pi)lenght(P1P2...Pn)
, 1 < i ≤ n. (6.3)
Questo tipo di parametrizzazione non e pero molto buona e sara neces-
sario calcolarne una migliore in seguito.
La somma delle distanze al quadrato e data da:
S =∑n
i=1 ‖B(ti)−Pi‖2 =
=∑n
i=1(axt3i + bxt
2i + cxti + dx − xi)
2+
+∑n
i=1(ayt3i + byt
2i + cyti + dy − yi)
2
.. (6.4)
I coefficienti di Eq. 6.2 possono essere ricavati dalla Eq. 6.4 calcolando
gli zeri delle derivate rispetto ad ax, . . . , dx e ay, . . . , dy e risolvendo il
sistema cosı ottenuto:
ax∑n
i=1 t6i + bx∑n
i=1 t5i + cx∑n
i=1 t4i + dx∑n
i=1 t3i =∑n
i=1 xit3i
ax∑n
i=1 t5i + bx∑n
i=1 t4i + cx∑n
i=1 t3i + dx∑n
i=1 t2i =∑n
i=1 xit2i
ax∑n
i=1 t4i + bx∑n
i=1 t3i + cx∑n
i=1 t2i + dx∑n
i=1 t1i =∑n
i=1 xiti
ax∑n
i=1 t3i + bx∑n
i=1 t2i + cx∑n
i=1 ti + ndx =∑n
i=1 xi
(6.5)
(analogamente per gli ay, . . . , dy). Il sistema in Eq. 6.5 puo essere risolto
utilizzando il metodo delle eliminazioni di Gauss. La parametrizzazione
scelta inizialmente comporta che il risultato ottenuto e, generalmente,
poco significativo. Possiamo, allora, calcolarne una nuova utilizzando
la curva appena trovata; utilizzeremo i parametri relativi alla distanza
minima dei punti da interpolare rispetto alla curva.
Il ciclo effettuato in linea 7 di Algoritmo limita il numero di volte per
cui viene effettuata la riparametrizzazione; questo perche, dopo qualche
iterazione, non abbiamo piu un miglioramento dell’errore quadratico. Se
dopo aver riparametrizzato, non abbiamo ancora trovato un valore del-
65
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
Algoritmo 3: Interpolazione di un set di punti
procedure Interpolateinput: vectOfPoints /*vettori di punti da interpolare*/
maxError /*errore massimo ammissibile*/output: curve /*curva interpolata*/
[1] Calcola una parametrizzazione iniziale ti basata sulla lunghezza dell’arco.[2] Interpola una curva di Bezier usando il metodo ai minimi quadrati.[3] if l’interpolazione e buona (errore massimo < maxError)[4] then poni curve=curva calcolata in 2[5] return[6] else[7] for numero di ripetizioni consentito[8] Riparametrizza e ricalcola una curva ai minimi quadrati.[9] if la parametrizzazione e buona[10] then poni curve=curva calcolata in 8[11] return[12] else dividi vectOfPoints in v1, v2
[13] Interpolate(v1, maxError)[14] Interpolate(v2, maxError)
66
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
l’errore quadratico, minore della distanza minima desiderata, dobbiamo
suddividere in due il set di punti e richiamare la procedura (linea 12).
Otteniamo, cosı, due curve a cui dovremo far mantenere relazioni di con-
tinuita. Indicando con l’apice (l) la curva che interpola il primo set di
dati e con (r) quella del secondo, possiamo scrivere:
B(l)(t) =
a(l)x t3 + b(l)
x t2 + c(l)x t + d(l)
x
a(l)y t3 + b(l)
y t2 + c(l)y t + d(l)
y
, 0 ≤ t ≤ 1
B(r)(t) =
a(r)x t3 + b(r)
x t2 + c(r)x t + d(r)
x
a(r)y t3 + b(r)
y t2 + c(r)y t + d(r)
y
, 0 ≤ t ≤ 1
. (6.6)
In [12] viene proposto di mantenere una continuita di tipo G1 fissando:
B(l)(1) = B(r)(0)
αB(l)′(1) = B(r)′(0), (6.7)
con α = lenght(Pm...Pn)lenght(P1...Pm)
(m e l’indice che segnala in quale punto e stata
trovato la massima distanza dalla curva). Il nostro apporto, nel miglio-
rare tale algoritmo, consiste nell’individuare una stima di α. Invece di
utilizzare il valore tangente dell’ Eq. 6.7, ricaviamo un versore (che ci
indica la direzione della tangente in B(l) per t = 1) e ne stimiamo il
modulo; in questo modo riguadagniamo un grado di liberta da utilizzare
nelle formule ai minimi quadrati. Il sistema che proponiamo e:
ax
∑ni=1 t6i + bx
∑ni=1 t5i + αtx
∑ni=1 t4i =
∑ni=1(xi − dx)t
3i
ax∑n
i=1 t5i + bx∑n
i=1 t4i + αtx∑n
i=1 t3i =∑n
i=1(xi − dx)t2i
ax∑n
i=1 t4i + bx∑n
i=1 t3i + αtx∑n
i=1 t2i =∑n
i=1(xiti − dx)tx
(6.8)
dove con α indichiamo il modulo della tangente e con tx la componente
x del suo versore (un sistema simile puo essere utilizzato per la compo-
nente y). Particolare attenzione meriteranno i casi limite di set di punti
costituiti da 1,2 o 3 punti, in quanto il sistema risulta singolare. Il ca-
67
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
so di punto unico deve essere evitato controllando la suddivisione; per 2
punti possiamo calcolare, come interpolazione, il segmento che li ha come
estremi; per il caso di 3 punti possiamo utilizzare condizioni di curvatura.
6.2.4 Taglio e rotazione
Taglio
L’operazione effettuata dal Bender, permette di ruotare una parte di
disegno, mantenendo la continuita tra le linee della zona di taglio (come
e mostrato in Fig. 6.5). Per effettuare quest’operazione e stato necessario
individuare un algoritmo d’intersezione tra curva e retta che mostriamo
in Algoritmo
Algoritmo 4: Intersezioni con una retta
procedure Intersectinput: curve /*curva con cui calcolare le intersezioni*/
segment /*segmento da intersecare*/output: var /*valore del parametro nel punto d’intersezione*/
[1] for ogni tratto di curve ottenuto usando de Casteljau[2] Calcola l’intersezione tra tratto e segment[3] if c’e intersezione[4] then calcola il parametro esatto che compete all’intersezione[5] var=parametro calcolato in 2
L’ Algoritmo non presenta particolari difficolta d’implementazione,
prende una curva, la tolleranza di disegno, il segmento da intersecare e
ritorna i punti in cui avviene l’intersezione (o i parametri che gli com-
petono). Il passo in linea 4 richiede una spiegazione piu dettagliata.
L’indicazione di esatto e dovuta al fatto che le curve non hanno, in ge-
nere, una parametrizzazione uniforme. Questo, come gia detto, dipende
dalla relazione tra parametro e punto corrispondente che non e lineare;
68
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
il calcolo del valore esatto richiederebbe di risolvere l’equazione:
b0(1− t)3 + b13t(1− t)2 + b23t2(1− t) + b3t
3 = P, (6.9)
ed il controllo delle soluzioni. Noi abbiamo preferito utilizzare un’altro
sistema.
L’aver applicato l’algoritmo di de Casteljau comporta che di ogni,
ogni tratto, conosciamo i parametri t0, t1 che lo individuano. Se un
tratto interseca il segmento di taglio possiamo semplicemente riapplicare
l’AlgoritmoQuesta volta l’algoritmo viene fatto partire dai valori t0, t1 del
segmento e la lunghezza sara pari alla distanza tra il punto di parametro
t0 e l’intersezione. Il risultato fornito da Algoritmo sara il parametro
cercato.
Rotazione
L’algoritmo di rotazione e mostrato in Algoritmo
Algoritmo 5: Rotazione
procedure Rotateinput: vector /*vettore prima della rotazione*/
vectorRotate /*vettore ruotato*/listOfCurves /*lista di curve di raccordo*/
output: listOfCurves /*la stessa lista con le curve ruotate*/[1] Da vector e vectorRotate ricava l’angolo ed il verso di rotazione.[2] Calcola la matrice di rototraslazione.[3] Calcola il modulo della tangente.[4] for each curva in listOfCurves.[5] Applica la matrice di rototraslazione agli estremi che si trovano
nel verso di rotazione ed alle rispettive tangenti.
Il calcolo del verso di rotazione (in linea 1) ci permette di stabilire
quali sono i punti di controllo della curva di raccordo che devono ruotare.
69
6. UTILIZZO E PROGETTO DI TECNICHE DI MANIPOLAZIONE
La matrice di rototraslazione e individuata dall’angolo e dal punto rispet-
to a cui stiamo facendo la rotazione. Ci soffermiamo sulla linea 3 dove
abbiamo indicato di calcolare il modulo della tangente. Questo modu-
lo viene ricavato come funzione dell’angolo di rotazione e della distanza
dal centro di rotazione. La funzione indicata e del tipo z = k · x · y
(con k > 0 costante arbitraria), questo permette che, durante il movi-
mento, il raccordo nei punti di giunzione sia morbido e la deformazione
progressiva.
70
Capitolo 7
Il programma Fasped
7.1 Il programma Fasped
Fasped1 e l’ambiente di test che abbiamo realizzato al fine di provare gli
algoritmi trovati. Fasped e un editor vettoriale (con limitate funzionalita)
che permette di disegnare delle linee, di manipolarle, di salvare il risultato
della manipolazione su un file e di ricaricarlo in seguito.
Il linguaggio di programmazione utilizzato per la realizzazione di Fa-
sped e stato il C++. Per la realizzazione di alcune strutture dati ab-
biamo utilizzato le librerie STL[13]; la gestione della grafica ha richiesto
l’utilizzo delle librerie OpenGL[14]; nella gestione dell’interfacciamento
abbiamo utilizzato le librerie MFC2 [15] (versione per sistema operativo
Windows) e Qt3[16] (versione per sistema operativo Linux). Particolare
attenzione e stata posta per realizzare un programma: multipiattafor-
ma, indipendente da una specifico ambiente a finestre e da uno specifico
compilatore.
Per realizzare la versione di Fasped, presentata in questo lavoro, ab-
1Fasped e l’acronimo di Fabrizio spline editor.2Le MFC (Microsoft Foundation Class) sono delle librerie C++, utilizzate in
ambiente Windows per la gestione di finestre e degli eventi ad esse associati.3Le Qt sono delle librerie C++, di gestione finestre, utilizzabili su diverse
piattaforme. Sono sviluppate dalla Trolltech.
71
7. IL PROGRAMMA FASPED
biamo scritto circa 20.000 linee di codice. Il nostro programma e in grado
di leggere files di diversi formati4; permette di salvare il disegno realiz-
zato su un files; permette l’editing e la manipolazione di linea con i tool
presentati nel capitolo precedente; funziona su due diverse piattaforme.
In Fasped e stata utilizzata una programmazione orientata agli og-
getti. Ogni oggetto e rappresentato da una classe che racchiude al suo
interno le strutture dati che la caratterizzano e le funzioni (chiamate
metodi in un linguaggio ad oggetti) necessarie a manipolarle.
In questo capitolo ci prefiggiamo di fornire al lettore una guida all’uti-
lizzo del codice redatto per lo sviluppo di algoritmi di manipolazione. La
descrizione delle classi e delle funzioni riportate non sara, quindi, esausti-
va, ma approfondiremo solo quegli aspetti che riteniamo possano essere
utili per operare ed estendere quanto redatto5.
7.2 La struttura delle classi per l’interfac-
cia
Cominciamo l’analisi delle classi utilizzate per l’interfacciamento. Queste
classi servono per visualizzare un oggetto, raffigurante un disegno, in una
finestra.
7.2.1 fbGrOb
Nel nostro progetto, l’oggetto base e stato chiamato fbGrOb che e l’acro-
nimo di Graphic Object (in Fig. 7.1 il grafico delle classi derivate). Un
Graphic Object contiene al suo interno le strutture ed i metodi necessari
4I formati supportati sono tutti di tipo vettoriale e sono: fse, fasped spline editor;tzv, toonz vector. Abbiamo inoltre due formati grezzi: dat, punti per la costruzionedi una b-spline; frp, (fasped raw points) un’unica successione di punti (utilizzato peril testing di alcune routines.).
5Fasped e dotato di documentazione in formato html con la descrizione dettagliatadi ogni classe, a cui si rimanda per ulteriori approfondimenti
72
7. IL PROGRAMMA FASPED
Figura 7.1: Gerarchia delle classi originate da fbGrOb
73
7. IL PROGRAMMA FASPED
per: essere inizializzato (l’inizializzazione e necessaria prima della visua-
lizzazione per settare alcune variabili OpenGL e calcolare la “Bounding-
Box” dell’oggetto6); visualizzare un disegno in una finestra; modificare
le dimensioni; associargli un colore; descrivere alcuni particolari (come il
tipo di tratteggio delle linee); etc.7.
7.2.2 fbWindow
Un oggetto fbWindow, deriva da un oggetto di tipo em fbGrOb. Que-
sto oggetto racchiude in se le funzionalita che permettono d’interagire
con un gestore di finestre; reagisce agli eventi input (quelli provenienti
da mouse e tastiera), notificandoli all’interno dell’applicazione ed effet-
tua la conversione tra coordinate window a coordinate world (permette
l’interazione con gli oggetti del disegno, nel loro sistema di riferimento).
7.2.3 fbFasped
L’oggetto fbFasped deriva dall’oggetto fbWindow (vedi Fig. 7.2). Il fatto
di ereditare i metodi di fbWindow lo rende in grado di ricevere eventi
dall’esterno e di notificarli ai tools, inoltre, ha delle funzionalita d’inter-
facciamento che permettono: di cambiare il tool attualmente in uso; di
modificare le coordinate finestra correnti nelle azioni di zoom e di pan-
ning (vedi pag. 53); di selezionare il disegno attivo, tra quelli disponibili
in una lista. fbFasped contiene al suo interno una struttura “lista di fb-
Frame”. Ogni oggetto fbFrame e in grado di contenere ed interagire con
un disegno (vedremo piu dettagliatamente in seguito). fbFasped gestisce
il salvataggio ed il caricamento da file degli fbFrame. Queste operazio-
ni sono effettuate con le classi fbSaver e fbLoader. Per default fbFasped
visualizza e rende attivo (cioe manipolabile) l’ultimo fbFrame aperto;
6La “BoundingBox” e un rettangolo in cui l’oggetto e inscritto, per le curve diBezier coincide con la minmax box.
7Per gli altri metodi e variabili si rimanda all’header o alla documentazione html.
74
7. IL PROGRAMMA FASPED
Figura 7.2: Interazione tra l’oggetto fbFasped con fbTool e fbFrame.
questo comportamento puo essere aggirato utilizzando fbFrameManager
(vedi pag. 59).
7.3 La struttura delle classi dei tools
La manipolazione delle linee e resa possibile dall’utilizzo dei tools che so-
no stati presentati nel capitolo precedente. La classe da cui viene derivato
ogni tool e fbTool.
7.3.1 fbTool
fbTool deriva da em fbGrOb (questo perche ogni tool ha proprio curso-
re OpenGL) ed e una classe astratta (serve come modello di riferimen-
to), non potra essere istanziata direttamente (allocata della memoria per
75
7. IL PROGRAMMA FASPED
creare un oggetto) ma solo usata come “contenitore” per le classi figlie8.
fbTool gestisce l’interazione tra tool ed eventi di input (tastiera e mouse),
comunicando con fbFasped.
Facciamo un esempio. La pressione del pulsante sinistro del mouse
(notificata dal gestore di finestre) viene intercettato dal metodo fbFa-
sped::OnLMouseDown di fbFasped. Questo metodo converte le coordina-
te finestra, nel sistema di coordinate solidale con il disegno e richiama il
metodo che gestisce il click del pulsante sinistro in fbTool. Questo meto-
do, formalmente uguale a quello di fbWindow, in funzione del tool che e
stato precedentemente selezionato, gestisce l’evento. Ogni tool (presen-
tato nel capitolo precedente), e contenuto in una classe, nel seguito ne
vedremo qualcuno.
7.3.2 fbCursor
All’avvio del programma il tool di default e fbCursor il cui unico compito
e quello di visualizzare un cursore a video. A questo tool non corrisponde
nessuna azione, serve solo come riferimento per l’utente.
7.3.3 fbFrameManager
fbFrameManager visualizza una griglia in cui e possibile selezionare una
tavola di disegno da manipolare.
7.4 La struttura delle classi per la grafica
In questo paragrafo presenteremo le classi che permettono la visualizza-
zione di linee e disegni.
8Questa proprieta e chiamata polimorfismo. Il polimorfismo e definito come: “lacapacita di chiamare una molteplicita di funzioni, usando la stessa interfaccia (cosıcome fornita dalle funzioni virtuali)” (vedi [17]).
76
7. IL PROGRAMMA FASPED
7.4.1 fbFrame
Come detto in precedenza, una finestra fbFasped gestisce il disegno degli
oggetti fbFrame in essa contenuti. Un oggetto fbFrame deriva da em
fbGrOb, quindi al suo interno sono presenti i metodi necessari per il
disegno (derivati da em fbGrOb). C’e, inoltre, una lista di oggetti di
tipo fbPolyLine (che rappresentano una linea) ed i metodi per gestire la
selezione.
7.4.2 fbPolyLine
Un oggetto fbFrame e costituito da una lista di oggetti di tipo fbPolyLine.
fbPolyLine permette di gestire e visualizzare una linea che rispetti una
continuita di tipo C0, e costituito da una lista di fbCurve e gestisce la
selezione di una curva. Una fbPolyLine e in grado di restituire (dato un
vettore arbitrario di punti), la linea che li interpola (con l’algoritmo ai
minimi quadrati visto a pag.
7.4.3 fbCurve
fbCurve e una delle classi piu importanti di Fasped, rappresenta l’elemen-
to grafico di base della nostra applicazione: ogni disegno da visualizzare,
deve essere convertito in fbCurve. fbCurve (come le altre classi fino ad
ora descritte) deriva da em fbGrOb, e quindi possiede le funzioni neces-
sarie per la visualizzazione; inoltre, contiene tutti i metodi e le strutture
dati che permettono la gestione di una curva di Bezier di grado 3: dalla
selezione del parametro relativo al punto piu vicino, alla visualizzazione.
In fbCurve troviamo una struttura “vettore di punti fbPoint” (fbPoint e
una classe che gestisce un punto del piano con coordinate di tipo dou-
ble). Questo vettore contiene i punti di controllo di una curva di Bezier
in forma di polinomi di Bernstein. Sono inoltre presenti altre variabili
per la gestione della selezione e dell’algoritmo di suddivisione.
77
7. IL PROGRAMMA FASPED
Il disegno di una curva di Bezier puo essere fatto in diversi modi (diffe-
renze finite, de Casteljau), noi abbiamo utilizzato l’algoritmo di de Caste-
ljau che in fbCurve si trova implementato nel metodo fbCurve::subdivide.
I parametri utilizzati in questo metodo sono:
• quattro punti;
• due valori estremi del parametro;
• un puntatore ad un oggetto di tipo fbOperation.
fbOperation (di cui tratteremo piu estesamente in seguito) e un ogget-
to che implementa un’operazione matematica restituendo un valore. Il
metodo fbCurve::subdivide, controlla che la distanza dei punti di control-
lo intermedi, dalla congiungente gli estremi, non ecceda la tolleranza;
se e nei limiti effettua l’operazione descritta da fbOperation, altrimen-
ti divide ulteriormente la curva iterando de Casteljau. L’aver separato
l’algoritmo dalla funzione specifica permette di aggiungere nuove fun-
zioni, semplicemente realizzando la classe che le implementa. In Fasped
abbiamo:
• fbCurveMeasure: permette di misurare la lunghezza di una curva;
• fbCurveDraw: permette il disegno passando le posizione degli estre-
mi dei segmenti ad OpenGL;
• fbIntersectCircle: permette di ricavare i valori del parametro in cui
una curva interseca una circonferenza;
• fbIntersectStraightLine: permette di ricavare i valori del parametro
in cui una curva interseca una retta;
• fbCurveSubdivideForLenght: restituisce i valori del parametro in
cui una curva ha una lunghezza stabilita;
78
7. IL PROGRAMMA FASPED
• fbCurveSubdividePixelSize: restituisce i valori del parametro in cui
una curva viene suddivisa dall’algoritmo di de Casteljau;
• fbCurveSubdivideUniform: restituisce i valori del parametro neces-
sari per suddividere una curva in tratti di lunghezza fissa.
Come fbPolyLine anche in fbCurve abbiamo la possibilita di trovare
la curva che interpoli un vettore di punti dati. Questo e reso possibile in
un costruttore di fbCurve che prende come parametri:
• un vettore di punti;
• le direzioni delle tangenti nei due estremi;
• la tolleranza desiderata.
Se le direzioni delle tangenti non sono note (vettori di modulo nullo),
viene effettuata una normale ricerca ai minimi quadrati, altrimenti (ver-
sori di tangenza) si cerca di stimare il modulo del vettore in modo da
rispettare condizioni di continuita di tipo G1.
Altri metodi presenti in fbCurve permettono di conoscere, per un
fissato valore del parametro: il punto corrispondente; la curvatura; il
vettore tangente e la derivata seconda.
Il punto corrispondente ad un dato parametro e una semplice appli-
cazione del ripetuto algoritmo di de Casteljau.
Di come venga calcolata la curvatura abbiamo parlato in un para-
grafo nei capitoli precedenti (vedi pag. 44) e si puo sfruttare l’algoritmo
presentato a pag. 48. E possibile tracciare un grafico di curvatura, cam-
pionando il parametro e utilizzandone i valori per estrarre dei punti da
unire con una poligonale9.
Il calcolo del valore tangente e della derivata seconda puo essere af-
frontato in due modi: possiamo ricavare il polinomio esplicito dalla for-
9Il grafico di curvatura puo essere utile per analizzare la dipendenza dallaparametrizzazione (vedi [10]).
79
7. IL PROGRAMMA FASPED
ma di Bernstein e calcolarne il valore per il parametro che ci interessa;
possiamo applicare le proprieta dei polinomi di Bernstein. Data la no-
stra rappresentazione la seconda modalita e senz’altro piu semplice ed
efficiente. La derivata di una funzione di Bernstein e data da:
d
dtBn
i (t) = n[Bn−1i−1 (t)−Bn−1
i (t)]. (7.1)
L’equazione della tangente allora sara: b′(t) = b01(1−t)2+b112t(1−t)+
b21t2 cioe, un polinomio di Bernstein di grado 2, con punti di controllo
che sono rispettivamente b01 = 3(b1 − b0), b11 = 3(b2 − b1), b21 =
3(b3 − b2). L’equazione della derivata seconda b′′(t) = b02(1− t) + b12t
e ottenuta semplicemente ripetendo il procedimento, con punti ottenuti
come b02 = 2(b01 − b11), b12 = 2(b11 − b21).
7.5 La struttura delle classi per l’analisi
numerica
La necessita di gestire trasformazioni, calcolare radici di polinomi, ri-
solvere semplici sistemi lineari, ci ha condotto a realizzare una piccola
libreria matematica, di cui analizzeremo qualche classe.
Le classi che seguono, oltre a gestire le operazioni matematiche tra
loro esistenti, permettono la visualizzazione delle primitive che imple-
mentano.
7.5.1 fbPoint
In Fasped e stato deciso di tenere separati, in due classi diverse, i punti
ed i vettori. Da un punto di vista informatico le informazioni ed i metodi
necessari per la gestione di questi due oggetti sono simili (dobbiamo me-
morizzare le posizioni di due coordinate, fornire dei metodi per la somma
80
7. IL PROGRAMMA FASPED
la differenza il prodotto, etc.), ma l’aspetto matematico e differente (un
vettore e dato come differenza tra due punti, la somma di un punto ed
un vettore restituisce un vettore, tra punti non sono possibili operazio-
ni di somma10, etc.) vedi [10]. Un punto e stato quindi implementato
utilizzando una classe template (che rende possibili mantenere l’interfac-
cia della classe e specificare il tipo di cui si ha bisogno). Abbiamo cosı
fornito: punti con due coordinate (fbPoint2), con tre (fbPoint3) e con
quattro (fbPoint4)11. Abbiamo poi definito il tipo fbPoint come:“punto
bidimensionale con coordinate di tipo double”. In fbPoint sono disponi-
bili metodi per accedere alle coordinare e fare il prodotto con uno scalare.
Il calcolo del vettore che passa per due punti e delegato ad una funzione
esterna, cosı come sono esterne le funzioni per calcolare la distanza, fare
la combinazione baricentrica, il disegno e l’input/output.
7.5.2 fbStraightLine
fbStraightLine implementa una retta (rappresentata in forma implicita
con coefficienti a, b, c) ed ha i metodi necessari per: calcolare la retta
normale in un punto; le intersezioni (con altre rette, segmenti, o curve);
il disegno; etc..
7.5.3 fbVector
fbVector permette di effettuare tutte le operazioni su un vettore di di-
mensione arbitraria.
7.5.4 fbMatrix
fbMatrix e stato realizzato per operare con matrici di dimensione massima
di 4 × 4. Permette di calcolare il determinante e l’inversa; gestisce il
10E, pero, permessa l’operazione di combinazione baricentrica (vedi [10]).11I punti tri e quadridimensionali sono stati resi disponibili per eventuali estensioni
future.
81
7. IL PROGRAMMA FASPED
prodotto con i vettori (distinguendo un vettore riga da uno colonna).
La struttura dati utilizzata per la memorizzazione delle componenti e
compatibile con quella utilizza da OpenGL (le operazioni con fbMatrix
possono essere passate in modo coerente ad OpenGL).
7.5.5 fbOperation
In precedenza e stato detto che, per l’algoritmo di de Casteljau, abbiamo
voluto separare l’algoritmo dalla particolare operazione. Questo e reso
possibile dalla classe astratta fbOperation. fbOperation obbliga le classi
da lei derivate ad implementare un metodo che restituisce un valore di
tipo double. Le classi derivate da fbOperation, che calcolano il valore
di una particolare operazione (ad esempio un potenziale), possono, cosı,
essere facilmente sostituite.
82
Capitolo 8
Lavori futuri
Durante la presentazione del lavoro svolto per questa tesi, abbiamo sug-
gerito le possibili estensioni che potrebbero essere fatte in futuro. In
questo capitolo ci proponiamo di riassumerle e spiegare le linee guida che
potrebbero essere seguite.
8.1 Line Library
Lo strumento Arrow permette la manipolazione di una singola linea
sfruttando delle funzioni potenziali semplici (famiglia a coseno rialzato,
funzioni esponenziali). Si potrebbe pensare di utilizzare delle funzioni
con forme complesse che, invece di spostare la linea. la deformino pro-
gressivamente. Potremmo cosı riuscire a fornire una libreria di linee,
(linee arricciate, linee a dente di sega, etc.) da utilizzare per il disegno,
analogamente a quella offerta dall’utilizzo delle wavelets.
8.2 Feature del disegno
Un’altra strada da percorrere e quella di fornire strumenti associati alle
“feature” del disegno. Analogamente al Bender questi strumenti per-
mettono di effettuare manipolazioni di “oggetti grafici”. L’idea che si
83
8. LAVORI FUTURI
vorrebbe portare avanti e di associare un movimento prestabilito a par-
ticolari caratteristiche del disegno (come le cuspidi o gli archi). Volendo
realizzare uno strumento di questo genere, il primo passo sarebbe di ri-
conoscere la forma assunta dal tratto che si vuole manipolare e quindi
muoverlo, riconducendosi al modello generale.
84
Appendice A
Appendice A
A.1 Codice Sorgente
In questa appendice sono presenti i file delle dichiarazioni (header) del-
le classi utilizzate in Fasped. Il codice completo puo essere richiesto
all’autore presso l’indirizzo di posta elettronica: [email protected].
A.1.1 fbArrow//———————————————————//// FILENAME : fbArrow.h//// CLASS(ES) : fbArrow//// DESCRIPTION : Tool per la gestione delle linee senza// modificare i pti di controllo//// DATE : 12/11/2000// COPYRIGTH : (C) 2000 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————#ifndef FB ARROW H#define FB ARROW H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbTool.h
#include fbPolyLine.h
#include fbConfig.h
#include fbCurveHierarchical.h
/∗!fbArrow: Tool per la gestione delle linee senza
modificare i pti di controllo∗/
class fbArrow : public fbTool{private:
//! Valore del minimo della curva selezionatafbDouble m dTOfMinimum;
//! Iteratore alla SetLine da spostare (offset rispetto al SetLine in cui si trova)itSetLine m itSLToMove;
fbPolyLine∗ m pPLLeft;
85
A. APPENDICE A
fbPolyLine∗ m pPLLeftCopy;fbPolyLine∗ m pPLRight;fbPolyLine∗ m pPLRightCopy;fbInt
m nCurveToReduceLeft ,m nCurveToReduceRight;
public:void ReplaceCurves();void MoveCurves();void FindCurves();
fbArrow(fbFasped∗ pFasped);
virtual ∼fbArrow();
void HandleMouse(const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);
void HandleMouseMove(const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);
void Clear();
void InitGL();
void Draw();};
#endif // !defined( FB ARROW H )//———————————————————// End Of File//———————————————————
A.1.2 fbAutoSwitch//———————————————————//// FILENAME : fbAutoSwitch.h//// CLASS(ES) : fbCursor//// DESCRIPTION : La forza della rete neurale//// HISTORY : ??/??/2000//———————————————————#ifndef FB AUTOSWITCH H#define FB AUTOSWITCH H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbTool.h
class fbAutoSwitch : public fbTool{public:
// trova il quadrato opportuno dalla posizione del mouse
void InitGL();void Clear();
void HandleMouse (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDouble dPositionY);void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y);void Draw();
fbAutoSwitch(fbFasped∗);virtual ∼fbAutoSwitch();
private:
fbFloat m pCursor[4];};
#endif // !defined( FB AUTOSWITCH H )//———————————————————// End Of File//———————————————————
A.1.3 fbBender//———————————————————//// FILENAME : fbBender.h//// CLASS(ES) : fbBender//
86
A. APPENDICE A
// DESCRIPTION : Il piegatore//// HISTORY : 03/11/2000//———————————————————#ifndef FB BENDER H#define FB BENDER H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbPoint.h
#include fbTool.h
#include fbStraightLine.h
#include fbCircle.h
#include fbPolyLine.h
#include <vector>
//!fbBender :Il Piegatore/∗!
Si occupa di piegare le curve intersecate dal segmento che lo descrive∗/class fbBender : public fbTool{
//! Vettore di offset di disegnofbFloat m pCursor[6];
//! Bilancino selezionato al clickfbInt m nSelectedCircle;
//! Due bilancini per il disegnofbCircle m Anchors[2];
//! Posizione del II pto clickatofbPoint m SecondPoint[2];
//! Quante volte e’ stato fatto clickfbInt m nClickCounter;
//! Corregge la selezione non perfetta del toolfbVector m VectorOfCorrection;
//! Vettore per la direzione dell’alto rispetto al toolfbVector m Up;
//! Puntatori alle curve di raccordo sul linkPolyLine m lCurveForLink;
//! Puntatori alle curve di raccordo sul linkPolyLine m lCurveForLinkCopy;
//! Puntatori alle curve da muoverePolyLine m lCurveToMove;
//! Puntatori alle curve da muoverePolyLine m lCurveToMoveCopy;
//! iteratore alla polyline nel frameitSetLine m itToPolyLineInFrame;
//! Controlla che sia avvenuta almeno una rotazione (trascinamento del mouse)fbBool m bIsRotated;
public:
//! Trova e sposta le curve individuate dal bendervoid FindAndMoveCurves();
//! Divide la curva e aggiunge i valori modificati alle curve di Link e di MovimentofbDouble SplitCurve(fbCurve∗ pCurve,std::vector<fbDouble>& vT, PolyLine& PLine);
//! Trova le curve di raccordo e di movimentovoid FindCurves();
enum{
FB BENDER ANCHOR 1 = 0, // etichetta per il cerchietto a sxFB BENDER ANCHOR 2 = 1, // etichetta per il cerchietto a dxFB BENDER ANCHOR = 3, // etichetta per la traslazioneFB BENDER SHAPE CIRCULAR , // rimpiazza le curve con le ruotateFB BENDER SHAPE SUBDIVIDED , // rimpiazza le curve con le suddiviseFB BENDER SHAPE INTERPOLATED, // rimpiazza le curve con una versione che media tra i pti di
controllo};
//! Ritorna il bilancino selezionato al clickfbInt FindAnchor(const fbPoint& Point);
void InitGL();
87
A. APPENDICE A
void Draw();void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y);void HandleMouse (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDouble dPositionY);void Clear();fbBender(fbFasped∗);virtual ∼fbBender();
};
#endif // !defined( FB BENDER H )//———————————————————// End Of File//———————————————————
A.1.4 fbBSpline//———————————————————//// FILENAME : fbBSpline.h//// CLASS(ES) ://// DESCRIPTION ://// DATE : Sun Mar 11 2001// COPYRIGTH : (C) 2001 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————
#ifndef FB BSPLINE H#define FB BSPLINE H
#include fbConfig.h
#include fbGrOb.h
#include fbPoint.h
#include fbPolyLine.h
#include <vector>
/∗∗Classe per la gestione di una BSpline∗@author Fabrizio Morciano∗/
class fbBSpline : public fbGrOb{private:
/∗∗ Nodi della spline ∗/std::vector<fbDouble> m vKnots;/∗∗ Punti di controllo ∗/std::vector<fbPoint> m vCtrlPnts;/∗∗ Grado della BSpline ∗/fbInt m nDegree;/∗∗ Domini intervallo ∗/fbInt m nL;/∗∗ Flag di controllo dati ∗/fbBool m bIsValid;/∗∗ Vettore con le componenti della base∗/fbVector m vBasisValue;
public:fbBSpline();
fbBSpline( const fbInt nDegree ,\const fbInt nL ,\const fbDouble∗ px ,\const fbDouble∗ py ,\const fbDouble∗ pKnots );
fbBSpline( const fbInt nDegree ,\const fbInt nL ,\const fbFloat ∗ px ,\const fbFloat ∗ py ,\const fbFloat ∗ pKnots );
fbBSpline( const fbInt nDegree ,\const std::vector<fbPoint> &vPnts ,\const std::vector<fbDouble> &vKnotw );
∼fbBSpline();
/∗∗ Metodo di display ∗/void Draw();
/∗∗ Metodo di inizializzazione ∗/void InitGL();
/∗∗ Ritorna il pto il cui parametro e’ dD o infinito se errore∗/fbPoint GetPoint(const fbDouble dD);
/∗∗ Ritorna il valore della base per dD o infinito se errore∗/
88
A. APPENDICE A
fbDouble GetBasis(const fbDouble dD);
/∗∗ Ritorna il valore della base per dD o infinito se errore∗/fbDouble GetBasis(const fbDouble dD,const fbInt nL, const fbInt nDegree);
/∗ Dovrebbe ritornare la derivata parziale della Bspline∗/fbDouble GetDerivateBasis(const fbDouble dD,fbInt& nL);
/∗∗ Ritorna il valore delle basi non nulle per dD selezionando il grado da (0 a 3)∗/fbVector& GetColumnOfBasis(const fbDouble dD, fbInt& nP,const fbInt nColumn=3);
/∗∗ Ritorna l’indice dell’intervallo ∗/inline fbInt findL(const fbDouble dU){
if(dU >= 0 && dU < m vKnots[0] )return 0;
fbUInti;
for(i=1; i<m vKnots.size() ; i++){
if ( dU >= m vKnots[i-1] && dU < m vKnots[i])return i;
}
if( dU == m vKnots.back() ){
for(i=m vKnots.size() - 1 ; i > 0 ; i– –){
if ( dU != m vKnots[i])return i;
}}
return FB ERROR;}
/∗∗ Ritorna il punto di indice i o il primo ∗/fbPoint& CtrlPoint(const fbUInt i);
/∗∗ Ritorna il nodo di indice i o il primo ∗/fbDouble& GetKnot(const fbUInt i);
/∗∗ Fissa il grado ∗/inline void SetDegree(const fbInt nDegree);/∗∗ Ritorna il grado ∗/inline fbInt GetDegree() const ;
/∗∗ Fissa i numeri di intervalli base∗/inline void SetInterval(const fbInt nL);/∗∗ Ritorna il numero di intervalli base∗/inline fbInt GetInterval() const;
/∗∗ Fissa i punti di controllo di una BSpline ∗/void SetCtrlPoints ( const std::vector<fbPoint>& vCtrlPnts );
/∗∗ Ritorna i punti di controllo della BSpline∗/std::vector<fbPoint>& GetCtrlPoints ();
/∗∗ Fissa i nodi di una BSpline ∗/void SetKnots ( const std::vector<fbDouble>& vKnots );
/∗∗ Ritorna il vettore dei nodi∗/std::vector<fbDouble>& GetKnots ();
void InitBSpline();/∗∗ Metodo per prendere il punto derivato in dD ∗/fbPoint GetDerivatePoint(const fbDouble dD);
friend std::ostream& operator �(std::ostream &os, fbBSpline∗ pCurve);friend std::ostream& operator �(std::ostream &os, fbBSpline& pCurve);friend std::istream& operator �(std::istream &is, fbBSpline∗ pCurve);friend std::istream& operator �(std::istream &is, fbBSpline& pCurve);
};
/∗∗ Fissa il grado ∗/inline void fbBSpline::SetDegree(const fbInt nDegree){
this→m nDegree = nDegree;}
/∗∗ Fissa il grado ∗/inline fbInt fbBSpline::GetDegree() const{
return this→m nDegree;}
89
A. APPENDICE A
/∗∗ Fissa il grado ∗/inline void fbBSpline::SetInterval(const fbInt nL){
this→m nL = nL;}
/∗∗ Fissa il grado ∗/inline fbInt fbBSpline::GetInterval() const{
return this→m nL;}
inline std::vector<fbPoint>& fbBSpline::GetCtrlPoints (){
return this→m vCtrlPnts;}
inline std::vector<fbDouble>& fbBSpline::GetKnots (){
return this→m vKnots;}
#endif
//———————————————————// End Of File//———————————————————
A.1.5 fbCircle//———————————————————//// FILENAME : fbCircle.h//// CLASS(ES) : fbCircle//// DESCRIPTION : classe per la gestione di un cerchio//// HISTORY ://———————————————————#ifndef FB CIRCLE H#define FB CIRCLE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbGrOb.h
#include fbConfig.h
#include fbPoint.h
#include fbPolyLine.h
#include <vector>
/∗!fbCircle Classe per la gestione di un cerchio∗/
class fbCircle : public fbGrOb{private:
//!Centro del cerchiofbPoint m Center ;
//! raggiofbDouble m dRadius;
//!Passo a cui viene effettuato il disegnofbDouble m dStep;
// raggio al quadratofbDouble m dRadius 2;
//! Disegna i pti simmetricivoid drawSimmetricPoints(const fbDouble& ,const fbDouble& ,const fbDouble& ,const fbDouble& );
public:
fbCircle();
//! Costruttore: il raggio se negativo viene preso il valore assolutofbCircle(const fbPoint& Center,const fbDouble dRadius);
virtual ∼fbCircle();
/∗! 2 2 2ritorna la soluzione di x +y -r + tol se < 0 internose > 0 esterno se ==0 sulla circonferenzaN.B e’ al quadrato∗/
fbDouble metaCheckPointInCircle(const fbPoint& Point) const;
90
A. APPENDICE A
fbDouble metaCheckPointInCircle(const fbDouble x,const fbDouble y) const;
//! Fissa il raggio se negativo viene preso il valore assolutovoid SetRadius(const fbDouble& dRadius);
//! Ritorna il centroinline fbPoint GetCenter() const;
//! Fissa la posizione del centroinline void SetCenter(const fbPoint& Center);
//! Ritorna il raggioinline fbDouble GetRadius() const;
//! Fa accedere il visitorvoid AcceptVisitor(fbVisitor∗ pVisitor);
void Draw();
/∗!Effettua le inizializzazioni necessarie persettare le dimensioni in funzione della scala
∗/void InitGL();
/∗! Funzione che ritorna l’intersezione di una cerchio con una retta i valori ottenutipossono essere 0 1 o 2 e possono essere testati dalla dimensione del vettore di pti in uscita
∗/friend void fbCircleIntersectionWithStraightLine(const fbCircle& Circle,const fbStraightLine
StraightLine,std::vector<fbPoint>& vecRes);};
/∗———————————————————∗\|∗ Espansione inline per la class fbCircle ∗||∗ BEGIN ∗|\∗———————————————————∗/
inline void fbCircle::SetCenter(const fbPoint& Center){
m Center=Center;}
inline fbDouble fbCircle::GetRadius() const{
return m dRadius;}
inline fbPoint fbCircle::GetCenter() const{
return m Center;}
/∗———————————————————∗\|∗ END ∗||∗ Espansione inline per la class fbCircle ∗|\∗———————————————————∗/
/∗!Converte un cerchio in una polyline
∗/void fbCircleToPolyLine(const fbCircle& Circle,PolyLine & PLine,const fbDouble dAngle0,const fbDoubledAngle1,const fbDouble dTolerance);
#endif // !defined( FB CIRCLE H )//———————————————————// End Of File//———————————————————
A.1.6 fbClamp//———————————————————//// FILENAME : fbClamp.h//// CLASS(ES) ://// DESCRIPTION://// DATE : Wed Feb 28 2001// COPYRIGTH : (C) 2001 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————
#ifndef FBCLAMP H#define FBCLAMP H
91
A. APPENDICE A
/∗∗Classe per la gestione di valori di campo∗@author Fabrizio Morciano∗/
template <class T> class fbClamp{private :
//! Valore assegnatoT m Val;//! Valore di minimoT m Minimum;//! Valore di massimoT m Maximum;
inline T test(T Min, T Val, T Max ){
return (Val < Min) ? Min : (Val > Max) ? Max : Val;}
public :
fbClamp():m Val ( 0 ),m Minimum( 0 ),m Maximum( 0 ){}
fbClamp(T val):m Val ( val ),m Minimum( val ),m Maximum( val ){}
fbClamp(T min, T val, T max ):m Val ( val ),m Minimum( min ),m Maximum( max ){
val = test( min, val, max );}
fbClamp(const fbClamp& pToCopy){
if( &pToCopy == this )return;
m Val = pToCopy→m Val ;m Minimum = pToCopy→m Minimum ;m Maximum = pToCopy→m Maximum ;
}
∼fbClamp(){}
fbClamp& operator=(const fbClamp& pToCopy){
if( &pToCopy == this )return ∗this;
m Val = pToCopy.m Val ;m Minimum = pToCopy.m Minimum ;m Maximum = pToCopy.m Maximum ;
return ∗this;}
T& operator=(const T& val){
return ( this→m Val = test( m Minimum, val, m Maximum ) );}
T& operator∗(){
return this→m Val;}
T& operator()(){
return this→m Val;}
T& Max(){
return this→m Maximum;}
T& Min(){
return this→m Minimum;}
};
92
A. APPENDICE A
template <class T> std::ostream& operator�(std::ostream &os, fbClamp<T>& val){
return os�∗val;}
template <class T> std::ostream& operator�(std::ostream &os, fbClamp<T>∗ val){
return os�∗∗val;}
template <class T> std::istream& operator�(std::istream &is, fbClamp<T>& val){
return os�∗val;}
#endif // FBCLAMP H//———————————————————// End Of File//———————————————————
A.1.7 fbConfig//———————————————————//// FILENAME : fbConfig.h//// CLASS(ES) ://// DESCRIPTION : File di configurazione per le piattaforme//// HISTORY : 18/08/2000// 06/01/2001 inserita macro FB WIN32 LIB per problemi// d’inclusione con Matrix Template Library//———————————————————#ifndef FBCONFIG H#define FBCONFIG H
// Definizione di tipotypedef bool fbBool ;typedef char fbChar ;typedef short fbShort ;typedef int fbInt ;typedef long fbLong ;typedef float fbFloat ;typedef double fbDouble;typedef unsigned short fbUShort;typedef unsigned int fbUInt;typedef unsigned long fbULong;
const fbInt FASPED MINOR VERSION = 0;const fbInt FASPED MAJOR VERSION = 0;
// Dichiarazioni di MACRO per i controlli matematica#ifdef WIN32
#define FB NAMESPACE SUPPORT 1
#if MSC VER > 1000// disabilito i warning di template troppo lunghi#pragma warning( disable : 4786 )
#endif
# ifdef FB MFC PROJECT# include stdafx.h
# elif FB WIN32 LIB# include <windows.h># endif
#include <float.h>#include <limits.h>
const fbDouble FB DOUBLE MAX = DBL MAX;const fbDouble FB DOUBLE MIN = DBL MIN;
const fbFloat FB FLOAT MAX = FLT MAX;const fbFloat FB FLOAT MIN = FLT MIN;
const fbInt FB INT MAX = INT MAX;const fbInt FB INT MIN = INT MIN;
#elif defined( GNUC ) // else WIN32 if GNUC
#include <limits.h>#include <cstdio>#include <cstdlib>
#if defined(LINUX) // if LINUX
93
A. APPENDICE A
#include <values.h>#include <cstdio>#include <cstdlib>
const fbDouble FB DOUBLE MAX = DBL MAX;const fbDouble FB DOUBLE MIN = DBL MIN;
const fbFloat FB FLOAT MAX = FLT MAX;const fbFloat FB FLOAT MIN = FLT MIN;
#else // if ! LINUX
const fbDouble FB DOUBLE MAX = 1E 128 ;const fbDouble FB DOUBLE MIN = 1E-128 ;
const fbFloat FB FLOAT MAX = 1E 64 ;const fbFloat FB FLOAT MIN = 1E-64 ;
#endif // end LINUX
const fbInt FB INT MAX = INT MAX;const fbInt FB INT MIN = INT MIN;
#elif // If Unkown
#error Inserire il codice per le altre piattaforme
#endif // end WIN32
const fbUInt FB NULL = 0x0000;const fbInt FB ERROR =-1 ;
// I valori si trovano in fbFasped.cpp
extern const fbDouble FB PICK RAY ; //= 10.0 Raggio di pick (N.B. in pixel)extern const fbDouble FB EPSILON ; //= 1.0E-6 Tolleranza di defaultextern const fbDouble FB TOLERANCE ; //= .01extern const fbDouble FB PIXEL SIZE ; //= .01 Dimensione di un pixel (pixel / cm)extern const fbDouble FB RENDER QUALITY MAX; //= 0.125 Limiti di qualita’ di disegno (non usati)extern const fbDouble FB RENDER QUALITY MIN; //= 2.0extern const fbDouble FB MINIMUM DIVISION NUMBER; //= 15.0
// Dichiarazioni di MACRO per gli eventi del Mouseconst fbUInt FB MOUSE LEFT = 0x0001;const fbUInt FB MOUSE MIDDLE = 0x0002;const fbUInt FB MOUSE RIGHT = 0x0004;
const fbUInt FB MOUSE UP = 0x0010;const fbUInt FB MOUSE DOWN = 0x0020;
const fbUInt FB MOUSE MASK = 0x00FF;const fbUInt FB MOUSE MASK STATUS = 0x00F0;const fbUInt FB MOUSE MASK BUTTON = 0x000F;
// Dichiarazioni di MACRO per gli eventi di Keyboardconst fbUInt FB KEY UP = 0x0100;const fbUInt FB KEY DOWN = 0x0200;const fbUInt FB KEY CTRL = 0x0400;const fbUInt FB KEY ALT = 0x0800;const fbUInt FB KEY SHIFT = 0x0A00;
const fbUInt FB KEY MASK = 0x0F00;
// Qualche Macro per accelerare la scrittura di un coloreconst fbFloat FB RED [ ] = {1.0f,0.0f,0.0f,1.0f};const fbFloat FB GREEN [ ] = {0.0f,1.0f,0.0f,1.0f};const fbFloat FB BLUE [ ] = {0.0f,0.0f,1.0f,1.0f};const fbFloat FB YELLOW [ ] = {1.0f,1.0f,0.0f,1.0f};const fbFloat FB CYAN [ ] = {0.0f,1.0f,1.0f,1.0f};const fbFloat FB MAGENTA[ ] = {1.0f,0.0f,1.0f,1.0f};
const fbFloat FB WHITE [ ] = {1.0f,1.0f,1.0f,1.0f};const fbFloat FB BLACK [ ] = {0.0f,0.0f,0.0f,1.0f};const fbFloat FB GRAY1 [ ] = {0.9f,0.9f,0.9f,1.0f};const fbFloat FB GRAY2 [ ] = {0.7f,0.7f,0.7f,1.0f};const fbFloat FB GRAY3 [ ] = {0.5f,0.5f,0.5f,1.0f};const fbFloat FB GRAY4 [ ] = {0.3f,0.3f,0.3f,1.0f};const fbFloat FB GRAY5 [ ] = {0.1f,0.1f,0.1f,1.0f};
#ifdef WIN32# define FB SLASH \\\\# define FB RESOURCES res
#else
# define FB SLASH /
# define FB RESOURCES ../res
#endif // ifdef WIN32
// Dichiarazioni di MACRO per il debug
94
A. APPENDICE A
#if defined( DEBUG) || defined (DEBUG)# define FB DEBUG 1#endif // if defined( DEBUG) || defined (DEBUG)
#ifdef WIN32
# if defined(FB DEBUG)&& defined(FB MFC PROJECT)# define FB TRACE TRACE# else
# define FB TRACE(a) {fprintf(stderr,a);}# endif //if defined(FB DEBUG)&& defined(FB MFC PROJECT)
#else
# ifdef FB DEBUG# define FB TRACE printf# else
# define FB TRACE printf# endif //ifdef FB DEBUG
#endif //ifdef WIN32
#ifdef FILE# define FB STUB FILE FILE#else
# define FB STUB FILE ((const char ∗)0L)#endif // ifdef FILE
#ifdef LINE# define FB STUB LINE LINE#else
# define FB STUB LINE 0#endif // ifdef LINE
#ifdef FUNCTION# define FB STUB FUNC FUNCTION#else
# define FB STUB FUNC ((const char ∗)0L)#endif // ifdef FUNCTION
/∗FB STUB(): this is the method which prints out stubinformation. Used where there is functionality missing.
∗/
#if FB DEBUG
# define FB STUB() \do { \
(void)fprintf(stderr, STUB: functionality not yet completed); \if (FB STUB FILE) { \
(void)fprintf(stderr, at %s, FB STUB FILE); \if (FB STUB LINE > 0) (void)fprintf(stderr, :line %u, FB STUB LINE); \if (FB STUB FUNC) (void)fprintf(stderr, :[%s], FB STUB FUNC); \
} \(void)fprintf(stderr, \n); \
} while (0)
#else
# define FB STUB() do { } while (0)
#endif // if FB DEBUG
#endif //!defined( FBCONFIG H )//———————————————————//End Of File//———————————————————
A.1.8 fbCursor//———————————————————//// FILENAME : fbCursor.h//// CLASS(ES) : fbCursor//// DESCRIPTION : classe per la gestione del cursore openGL//// HISTORY : 07/09/2000//———————————————————#ifndef FB CURSOR H#define FB CURSOR H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
95
A. APPENDICE A
#include fbConfig.h
#include fbPoint.h
#include fbTool.h
#include fbStraightLine.h
#include fbCircle.h
#include <vector>#include fbPolyLine.h
class fbPolyLine;/∗!
fbCursor Classe che gestisce un cursore video di forma opportuna∗/
class fbCursor : public fbTool{
//! Pti che descrivono il toolfbDouble m pCursor[4];
public:void Clear();
fbCursor(fbFasped∗ pAppl);
virtual ∼fbCursor();
void HandleMouseMove(const fbUInt nButtonPressed,const fbDouble dXPosition,const fbDoubledYPosition);
void HandleMouse(const fbUInt nButtonPressed,const fbDouble dPositionX,const fbDouble dPositionY);
void Draw();
void InitGL();
};
#endif // !defined( FB CURSOR H )//———————————————————// End Of File//———————————————————
A.1.9 fbCurve//———————————————————//// FILENAME : fbCurve.h//// CLASS(ES) : fbCurve//// DESCRIPTION : classe per la gestione di una cubica OpenGL//// HISTORY : 09/08/2000// 13/08/2000 cambiato il tuipo di m nShowCtrlPoints GLenum -> fbLong// rinominate le funzioni To -> Of// rimosso codice gestione linea spessore// 08/09/2000 codice gestione curve multiple//———————————————————#ifndef FB CURVE H#define FB CURVE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbGrOb.h
#include fbPoint.h
#include fbConfig.h
#include fbStraightLine.h
#include <vector>#include <list>
class fbCurve;
typedef std::list<fbCurve∗> PolyLine;/∗!fbCurve: classe di gestione per un arco di spline∗/
class fbOperation;class fbRectangle;
class fbCurve : public fbGrOb{protected:
fbDouble ∗Reparametrize(const std::vector<fbPoint>& p,const fbDouble ∗v1);
void setMinMaxBox();
static fbInt s nSubdivisionCounter;
96
A. APPENDICE A
//! Retta per i vari controllistatic fbStraightLine m ManagementStraightLine;
//! Pti di controllo per la gestione della lineafbPoint ∗m pCtrlPoints;
//! Pto aggiuntivo che fissa il bottom left del minmax della curvafbPoint m MinMaxBottomLeft;
//! Variabile per il grado della curvafbInt m nOrder;
//! Variabile di gestione del tipo di oggetto da disegnare (tangente - poliedro -minmax)fbInt m nObjectType;
//! Lungezza della curvafbDouble m dLenght;
//! Distanza dell’ultimo pto testato dalla curvafbDouble m dPointDistance;
//! Valore della t che compete al pto della curva piu’ vicino al pto testatofbDouble m dTValue;
//! Valore dell’indice del vettore per cui si registra il massimo errorefbInt m nIndexOfMaxSqrError;
//! Valore del massimo errore quadratico trovatofbDouble m dMaxSqrError;
//! Valore di soglia per il calcolo delle curvefbDouble m dThreshold;
void subdivide(const fbDouble p1x,const fbDouble p1y,\const fbDouble p2x,const fbDouble p2y,\const fbDouble p3x,const fbDouble p3y,\const fbDouble p4x,const fbDouble p4y,\const fbDouble t0 ,const fbDouble t1,fbOperation∗ Op);
public:
inline fbInt GetIndexOfMaxSqrError() ;
inline fbDouble GetMaxSqrError() ;
fbDouble ComputeMaxError(const std::vector<fbPoint>& vPnts,const fbDouble∗ );
void GenerateCubic(const std::vector<fbPoint>& vPnts,fbDouble∗& dt ,const fbVector&,const fbVector&);
void GenerateCubicMinSqr(const std::vector<fbPoint>& vPnts, fbDouble∗& dt);
void GenerateCubicWithG1Condition(const std::vector<fbPoint>& vPnts,fbDouble∗& dt ,const fbVector& TangentLeft );
void GenerateCubicWithTangentCondition(const std::vector<fbPoint>& vPnts,fbDouble∗& dt ,const fbVector& TangentLeft ,\const fbVector& TangentRight );
fbDouble GetAlpha(const std::vector<fbPoint>& vLeft,const std::vector<fbPoint>& vRight);
//! Ritorna la curvatura con segno nel parametrofbDouble GetCurvature(const fbDouble dTVal);
//! Ritorna la lunghezza del piu’ piccolo segmento di poligonalefbDouble GetMinimumSegment(const fbDouble dTol = FB ERROR);
void InitGL();
//! Ritorna il rettangolo di MinMaxBoxfbRectangle GetMinMaxBox() const;
//! ritorna l’arco a dx rispetto a dValuefbCurve∗ GetLeftArc(const fbDouble dValue);
//! ritorna l’arco a dx rispetto a dValuefbCurve∗ GetRightArc(const fbDouble dValue);
//! Ritorna la lunghezza della curva con una precisione fissata (se default quella video)fbDouble GetLenght(const fbDouble dPrecision=FB ERROR);
//! Ritorna la t che compete al pto della curva piu’ vicino a PointfbDouble SelectPoint(const fbPoint& Point, const fbDouble dPickRay);
//! Fissa il tipo di oggetto da disegnare per la curva/∗!Metodo che in funzione del tipo nType (tra quelli presenti in fbCurve)
97
A. APPENDICE A
disegna oltre alla curva elementi aggiuntivi (MINMAX,CONTROL POINTS, TANGENTI,ETC)∗/inline void SetObjectType(const fbInt nType);
//! Visualizza vari oggett in funzione del tipo correntemente settatovoid DrawObjects();
//! Tipo per la selezione del tipo di oggetti da visualizzareenum{
FB CURVE P1 = 0, //! Riferimento al primo pto di controlloFB CURVE P2 = 1,FB CURVE P3 = 2,FB CURVE P4 = 3,FB CURVE NOT OBJECT , //! Nessun oggetto da visualizzareFB CURVE TAN 1 , //! Visualizza la tangente per P0 P1FB CURVE TAN 2 , //! Visualizza la tangente per P2 P3FB CURVE TAN ALL , //! Visualizza entrambe le tangentiFB CURVE CONVEX HULL, //! Visualizza l’involucro convessoFB CURVE MINMAX , //! Visualizza la minmax boxFB CURVE CTRL PNTS ALL , //! Visualizza tutti i pti di controlloFB CURVE CTRL PNTS F L , //! Visualizza il primo e l’ultimoFB CURVE CTRL PNTS AND TAN ,FB CURVE SUBDIVISION PIXEL SIZE , //! Divide la curva in pezzi di lunghezza pari alla
poligonale a videoFB CURVE SUBDIVISION UNIFORM , //! Divide la curva in pezzi pari al passo selezionatoFB CURVE SUBDIVISION FOR LENGHT , //! Divide la curva rispetto in 2 rispetto alla lunghezza
desiderataFB CURVE IS IN REGION = -1, //! Ritorna se la curva e’ interna ad una regioneFB CURVE INTERSECT REGION = -2, //! Ritorna se la curva interseca una regioneFB CURVE IS OUT REGION = -3 //! Ritorna se la curva e’ completamente esterna ad una regione
};
//! Seleziona un pto di controllo della curva/∗! Ritorna un offset o FB ERROR se il pto e’ piu’ distante di dPickRay da PointN.B. dPickRay e’ assunto essere il quadrato del valore di raggio di pickin tal modo le distanze sono controllate senza calcolare le radici∗/fbInt SelectByControlPoint(const fbPoint& Point,const fbDouble dPickRay);
fbPoint GetPoint(const fbDouble tVal) const;
//! CostruttorefbCurve();
//! Costruttore per 4 tpifbCurve(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3,const fbPoint& p4);
//! Costruttore per 4 tpi e dimensione pixelfbCurve(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3,const fbPoint& p4,const fbDouble
dPixelSize);
//! Costruttore di copiafbCurve(const fbCurve& Curve);
//! Costruttore pe interpolazione ai minimi quadratifbCurve(const std::vector<fbPoint>& vPnts,\
const fbVector& TangentLeft,\const fbVector& TangentRight,const fbDouble dTolerance);
//! Distruttorevirtual ∼fbCurve();
//! Metodo che disegna le curvevirtual void Draw();
//! Ritorna i pti di controllo della lineavoid GetCtrlPoints(fbPoint& p1,fbPoint& p2,fbPoint& p3,fbPoint& p4);
//! Ritorna i pti di controllo della curvainline fbPoint∗ GetCtrlPoints();
//! Fissa i pti di controllo della lineainline void SetCtrlPoints(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3,const fbPoint& p4);
inline void SetCtrlPoints(const fbPoint ∗p);
void SetCtrlPoints(const fbDouble p1x,const fbDouble p1y,const fbDouble p2x,const fbDouble p2y, constfbDouble p3x,const fbDouble p3y, const fbDouble p4x,const fbDouble p4y);
//! Ritorna il grado della curvainline fbInt GetOrder() const;
//! Fissa il grado della curva (se il grado viene alzato i pti sono estrapolati in modo opportuno,// se abbassato li risultato e’ indefinito solo i pti iniziali sono garantiti)void SetCurveOrder(const fbInt nVal);
//! Metodo per controllare che il pto sia nel minmax della curva a meno di FB PICK RAY
98
A. APPENDICE A
fbBool CheckPointInMinMax(const fbPoint &Point,const fbDouble RayOfPick) const;
//! Metodo per controllare che il pto sia nel convex hull della curva a meno di FB PICK RAYfbBool CheckPointInConvexHull(const fbPoint& Point,const fbDouble RayOfPick);
//! Metodo di copiafbCurve& operator=(const fbCurve&);
void AcceptVisitor(fbVisitor ∗ pVisitor);/∗!Metodo che calcola i valori di t che delimitano le parti strettamente convessedi una curva( cubica o quadrica ) i valori estratti sono tutti quelli validi in \f$\forall t \in [0,1]\f$Viene utilizzata la condizione che una curva descritta da \f$x(t),y(t)\f$e’ monotona rispetto agli assi se risulta che \f$x∧{’}(t)\cdot y∧{’}(t)>0\f$\f$ \forall t \in [\hat{t} 0,\hat{t} 1] \f$ in tale intervallo la curva puo’ essereconsiderata funzione strettamente monotona sia come \f$ x=x(t)\f$ sia come \f$ y=y(t) \f$.In un sotto intervallo \f$[\hat{t} 0,\hat{t} 1]\f$ di \f$[t 0,t 1]\f$ la curva e’ strettamenteconvessa se \f$ y∧{’}(t) \cdot x∧{”}(t) -y∧{”}(t) \cdot x∧{’}(t) > 0 (<0)\f$\f$\forall t \in [\hat{t} 0,\hat{t} 1]\f$ per la condizione di non annullamento della derivata seconda.Allora le radici delle 3 equazioni\f$x∧{’}(t)=0 \f$,\f$ y∧{’}(t)=0\f$, \f$y∧{’}(t) \cdot x∧{”}(t) -y∧{”}(t) \cdot x∧{’}(t) = 0 \f$ cherispettano il vincolo \f$\forall t \in [0,1]\f$ sono tutti i sotto intevalli in cui la curva e’ monotonae convessa.∗/void SplitInConvexInterval(std::vector<fbDouble>& vpTValues);
/∗∗ Calcola i coefficienti del polinomio di gradoopportuno equivalente a quello rappresentatoin polinomi di Bezier ∗/void BezierToPoly(std::vector<fbPoint>& vdCoeff) const ;
void BezierToPoly(std::vector<fbDouble>& vdCoeffX,std::vector<fbDouble>& vdCoeffY) const ;
fbPoint GetAcceleration(const fbDouble tVal);
fbPoint GetSpeed(const fbDouble tVal);
fbPoint GetTangent(const fbDouble tVal);
/∗!Calcola i valori del parametro in cui la curva deve essere suddivisasecondo il metodo stabilito in nTypeOfSubdivision∗/void GetValuesForSubdivision(std::vector<fbDouble>& vVectorOfValue,const fbInt
nTypeOfSubdivision,const fbDouble dInit,const fbDouble dTol = FB ERROR);
void IntersectionWithStraightLine (const fbStraightLine & StraightLine , std::vector<fbDouble>&vTOfIntersection);
void IntersectionWithSegment (const fbSegment& Segm , std::vector<fbDouble>& vTOfIntersection);
fbInt IntersectionWithCircle (const fbCircle & Circle, std::vector<fbDouble>& vTOfIntersection);
fbPoint& operator [ ](const fbInt nCtrlPnt);
friend std::ostream& operator �(std::ostream &os, fbCurve ∗pCurve);friend std::ostream& operator �(std::ostream &os, fbCurve &pCurve);friend std::istream& operator �(std::istream &is, fbCurve ∗pCurve);friend std::istream& operator �(std::istream &is, fbCurve &pCurve);
private:
/∗!Metodo privato per inizializzare la procedura di suddivisione
∗/inline void subdivideInit(const fbDouble dTol = FB ERROR);
fbDouble m dVideoTolerance;};
/∗———————————————————∗\|∗ Espansione inline per la class fbCurve ∗||∗ BEGIN ∗|\∗———————————————————∗/
inline void fbCurve::SetObjectType(const fbInt nType){
m nObjectType = nType;}
inline fbPoint∗ fbCurve::GetCtrlPoints(){
SetInitGL(false);return &m pCtrlPoints[0];
}
inline void fbCurve::SetCtrlPoints(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3,const fbPoint& p4){
SetCtrlPoints(p1.X,p1.Y,p2.X,p2.Y,p3.X,p3.Y,p4.X,p4.Y);}
inline void fbCurve::SetCtrlPoints(const fbPoint ∗p)
99
A. APPENDICE A
{SetCtrlPoints(p[0].X,p[0].Y,p[1].X,p[1].Y,p[2].X,p[2].Y,p[3].X,p[3].Y);
}inline fbInt fbCurve::GetOrder() const{
return m nOrder ;}
inline void fbCurve::subdivideInit(const fbDouble dTol){
if(dTol == FB ERROR)m dVideoTolerance = GetTolerance() ∗ 0.5;
elsem dVideoTolerance = dTol;
s nSubdivisionCounter = 0;
}
inline fbInt fbCurve::GetIndexOfMaxSqrError(){
return m nIndexOfMaxSqrError;}
inline fbDouble fbCurve::GetMaxSqrError(){
return m dMaxSqrError;}/∗———————————————————∗\|∗ END ∗||∗ Espansione inline per la class fbCurve ∗|\∗———————————————————∗/
/∗!Restituisce un vettore con i valori del parametro in cui la curvae approssimabile ad una poligonale spezzata
FbErase:E’ orribile deve essere eliminata e rimpiazzata da un metodo
∗///void fbCurveRetrieveSubdivisionForTolerance(const fbCurve∗ pCurve,const fbDoubledTolerance,std::vector<fbDouble>& vInterval);
/∗!Confronta 2 curve e vede la posizione per cui differiscono per piu di tolviene utilizzata per il confronto la curva che maggior numero di suddivisioniFbErase:E’ orribile deve essere eliminata e rimpiazzata da un metodo
fbDouble fbCurveToCurveDistance(fbCurve∗ pCurve 1,fbCurve∗ pCurve 2,const fbDouble dTol);∗/
/∗! Raccorda la continuita’ fra 2 curve fissandola a nValFIXMEvoid fbCurveFixContinuity(fbCurve∗ pCurve left,fbCurve∗ pCurve right,const fbInt nVal,const fbDouble dTolerance);∗/
/∗! Cerca di ricondurre due archi in uno soloReturn:NULL se non e’ possibilenew fbCurve puntatore con la nuova curva∗/fbCurve∗ fbCurveTwoToOne( fbCurve∗ pCurve left,fbCurve∗ pCurve right,const fbDouble dTolerance);
/∗!Ritorna il valore di continuita’ mantenuto dalle due curveritorna FB ERROR se non c’e’ continuita’ o una delle 2 curve e NULLun valore tra 0 e 2∗/fbInt fbCurveGetContinuity(fbCurve∗ pCurve left,fbCurve∗ pCurve right,const fbDouble dTolerance);
/∗!Calcola le intersezioni tra 2 curve∗/fbBool fbCurveIntersectionWithCurve(fbCurve& pCurve,const fbCurve& Curve,const fbDoubledTolerance,std::vector<fbDouble>& vTOfIntersection);
#endif // !defined( FB CURVE H )//———————————————————// End Of File//———————————————————
A.1.10 fbDrawingPin//———————————————————
100
A. APPENDICE A
//// FILENAME : fbDrawingPin.h//// CLASS(ES) : fbDrawingPin//// DESCRIPTION : Tool per la gestione del blocco e suddivisione di una curva//// HISTORY : 29/09/2000//———————————————————#ifndef FB DRAWING PIN H#define FB DRAWING PIN H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbTool.h
#include <list>#include fbPoint.h
/∗!fbDrawingPin: classe Tool che si occupa di gestire gli eventi del tool puntinaNel pto in cui viene inserita una puntina divide la curva in 2 partie fissa l’attivita’ del pto in modo tale da venir riconosciuto comevincolo dagli altri tool (quelli di spostamento)
∗/class fbDrawingPin : public fbTool{private:
//! Punti che descrivono il disegnofbFloat m pDrawingPin[6];
public:void Clear();
fbDrawingPin(fbFasped∗);virtual ∼fbDrawingPin();
//! Metodo di inizializzazionevoid InitGL();
void Draw();
void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDoubledPositionY);
void HandleMouse (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDouble dPositionY);};
#endif // !defined( FB DRAWING PIN H )//———————————————————// End Of File//———————————————————
A.1.11 fbEllipse//———————————————————//// FILENAME : fbEllipse.h//// CLASS(ES) : fbEllipse//// DESCRIPTION : classe per la gestione di un’ellisse//// HISTORY : 14/11/2000//———————————————————#ifndef FB ELLIPSE H#define FB ELLIPSE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbGrOb.h
#include fbPoint.h
#include fbConfig.h
#include fbStraightLine.h
class fbRectangle;
#include <vector>/∗!
fbEllipse : classe per la gestione di un’ellisseFIXME manca la gestione per mezzo dei fuochi
∗/class fbEllipse :public fbGrOb{
//! Baricentro
101
A. APPENDICE A
fbPoint m Barycenter;
//! Vertici dell’ellissefbDouble m da;
//! Vertici dell’ellissefbDouble m db;
//! Tolleranza al quadratofbDouble m dSqrTolerance;
//! Disegna segmenti nei 4 quadrantivoid drawSimmetricPoints(const fbDouble x, const fbDouble y,const fbDouble x1, const fbDouble y1);
//! Si occupa di fare la suddivisione ed il disegnovoid subdivideAndDraw(const fbDouble dAngle0,const fbDouble dAngle1);
public:
fbEllipse();fbEllipse(const fbDouble a,const fbDouble b);fbEllipse(const fbDouble a,const fbDouble b,const fbPoint Barycenter);
virtual ∼fbEllipse();
//! Massimo grado di annidamento nella suddivisionestatic fbDouble s MaxDepth;
//! Fissa i valori dei vertici per l’ellisseinline void SetVertex(const fbDouble a,const fbDouble b);
//! Ritorna i valori correntir per i vertici dell’ellisseinline void GetVertex(fbDouble& a,fbDouble &b) const;
//! Fissa la posizione del baricentroinline void SetBarycenter(const fbPoint& p);
//! Ritorna la posizione del baricentroinline fbPoint GetBarycenter() const;
void Draw();void InitGL();
//! funzione che si occupa della divisione ricorsivafriend void subdivideEllipse(const fbEllipse& Ellipse,const fbDouble dAngle0,const fbDouble
dAngle1,void(∗MyFunc)(fbPoint&,fbPoint&,void∗),void∗ pUserData);};
inline void fbEllipse::SetVertex(const fbDouble a,const fbDouble b){
m da = a; m db = b;}
inline void fbEllipse::GetVertex(fbDouble& a,fbDouble &b) const{
a= m da; b=m db;}
inline void fbEllipse::SetBarycenter(const fbPoint& p){
m Barycenter = p;}
inline fbPoint fbEllipse::GetBarycenter() const{
return m Barycenter;}
/∗!Converte un’ ellisse in una polylineFIXME: E’ tremendamente ridondante
∗/void fbEllipseToPolyLine(const fbEllipse& Ellipse,PolyLine& PLine,const fbDouble dAngleFirst = 0,constfbDouble dAngleLast = FB 2 PI);
/∗!Converte un’ ellisse in una polylineFIXME: E’ tremendamente ridondante
∗/void fbEllipseToPolyLine(const fbRectangle& BBoxOfEllipse,PolyLine& PLine,const fbDouble dAngleFirst =0,const fbDouble dAngleLast = FB 2 PI);#endif // !defined( FB ELLIPSE H )//———————————————————// End Of File//———————————————————
A.1.12 fbException//———————————————————
102
A. APPENDICE A
//// FILENAME : fbException.h//// CLASS(ES) : fbException//// DESCRIPTION : Classe per la gestione delle eccezioni//// HISTORY : 23/08/2000//———————————————————#ifndef FB EXCEPTION H#define FB EXCEPTION H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000#include <string>
/∗!fbException Classe base per la gestione delle eccezioni
∗/class fbException{protected:
//! Stringa di messaggiostd::string m ExceptionMessage;
public:
fbException();fbException(const std::string sMessageError);virtual ∼fbException();
//! Metodo per mostrare l’eccezionevoid ShowException();
//! Metodo che contiene l’eventuale stringa informativa aggiuntivavirtual void ExceptionPrint() =0;
};
/∗!fbRangeException Classe per la gestione di eccezione di range
∗/class fbRangeException : public fbException{public:
fbRangeException();fbRangeException(const std::string&);virtual ∼fbRangeException();
void ExceptionPrint();};
#endif // !defined( FB EXCEPTION H )//———————————————————// End Of File//———————————————————
A.1.13 fbFasped//———————————————————//// FILENAME : fbFasped.h//// CLASS(ES) : fbFasped//// DESCRIPTION ://// HISTORY : ??/??/2000//———————————————————#ifndef FB FASPED H#define FB FASPED H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbPoint.h
#include fbWindow.h
#include fbConfig.h
#include fbClamp.h
#include <list>
class fbFrame;
typedef std::list<fbFrame∗> SetFrame;typedef SetFrame::iterator itSetFrame;typedef SetFrame::const iterator citSetFrame;
103
A. APPENDICE A
class fbFasped : public fbWindow{
//! Lista di puntatori a FrameSetFrame m lpFrame;
//! Frame CorrentefbFrame∗ m pCurrentFrame;
//! Pagina di riferimento A4fbRectangle m A4Page;
//! Raggio di Pick in coordinate mondofbDouble m dPickRay;
//! Lunghezza di azione per gli algoritmi che non dipendono dalla parametrizzazionefbDouble m dLenghtForAction;
//! Parametro di controllo qualita’ di riduzionefbClamp<fbDouble> m dReductionQuality;
//! Numero che forza la divisione per curve troppo lunghefbInt m nNumberOfMinimumDivision;
//! Flag per l’algoritmo di riduzionefbBool m bReductionFlag;
public:
fbFasped();
virtual ∼fbFasped();
void InitGL();
/∗!La riduzione deve essere attiva o no
∗/inline void SetReductionFlag(const fbBool);
/∗!Stato del flag di riduzione
∗/inline fbBool GetReductionFlag() const;
/∗!Ritorna il valore di qualita’ di divisione
∗/inline fbDouble GetReductionQuality();
/∗!Fissa il valore di qualita’ di divisione
∗/inline void SetReductionQuality(const fbDouble dDivisions);
/∗!Ritorna la lunghezza su cui propagare lo spostamento per gli algoritmiindipendenti dalla parametrizzazione.
∗/inline fbDouble GetLenghtForAction() const;
/∗!Ritorna la lunghezza su cui propagare lo spostamento per gli algoritmiindipendenti dalla parametrizzazione.
∗/inline void SetLenghtForAction(const fbDouble dLofA);
/∗!Ritorna il raggio di pick per i tools di selezione
∗/inline fbDouble GetPickRay() const;
/∗!Fissa il frame corrente per la visualizzazione
∗/void SetCurrentFrame(fbFrame∗ pFrame);
/∗!Ritorna la lista dei frame caricati
∗/inline SetFrame& GetSetFrame();
/∗!Carica un frame nell’applicazione.∗/
void AddFrame(fbFrame∗ pFrame);
/∗!Ritorna un puntatore al frame corrente attivo∗/
104
A. APPENDICE A
inline fbFrame∗ GetFrame() const;
//! Sostituisce pFrameToChange con pNewFrame altimenti aggiunge alla finevoid ChangeFrame(fbFrame ∗pFrameToChange,fbFrame∗ pNewFrame);
/∗!controlla se il frame pFrame deve essere aggiunto o sostituito nellalista dei frame correnti
∗/void AddOrChange(fbFrame∗ pFrame);
/∗!La lista dei frame ha l’ultimo frame vuoto o no(decide se rimpiazzare un frame vuoto o aggiungerne)FBErase ( si puo’ utilizzare GetSetFrame)
∗/fbBool IsEmpty() ;
/∗!Elimina tutti i frame dall’ applicazione
∗/void ClearAll();
virtual void OnLMouseMove (const fbInt,const fbInt);virtual void OnLMouseDown (const fbInt,const fbInt);virtual void OnLMouseUp (const fbInt,const fbInt);virtual void OnRMouseMove (const fbInt,const fbInt);virtual void OnRMouseDown (const fbInt,const fbInt);virtual void OnRMouseUp (const fbInt,const fbInt);virtual void OnMouseMove (const fbInt,const fbInt);
void OnResize (const fbInt nW,const fbInt nH);void Draw ();void UpdateGLProjection(const fbDouble& CenterOfWindowX,const fbDouble& CenterOfWindowY);
friend std::ostream& operator�(std::ostream& os,fbFasped∗ pFasped);friend std::istream& operator�(std::istream& is,fbFasped∗ pFasped);
};
/∗———————————————————∗\|∗ Espansione inline per la class fbFasped ∗||∗ BEGIN ∗|\∗———————————————————∗/
inline void fbFasped::SetLenghtForAction(const fbDouble dLofA){
this→m dLenghtForAction = dLofA;}
inline fbDouble fbFasped::GetLenghtForAction() const{
return this→m dLenghtForAction;}
/∗!Ritorna il valore di qualita’ di divisione
∗/inline fbDouble fbFasped::GetReductionQuality(){
return this→m dReductionQuality();}
/∗!Fissa il valore di qualita’ di divisione
∗/inline void fbFasped::SetReductionQuality(const fbDouble dQuality){
this→m dReductionQuality = dQuality;}
/∗!Stato del flag di riduzione
∗/inline fbBool fbFasped::GetReductionFlag() const{
return m bReductionFlag;}/∗!
∗/inline void fbFasped::SetReductionFlag(const fbBool bVal){
m bReductionFlag = bVal;}
/∗!Ritorna il raggio di Pick
∗/inline fbDouble fbFasped::GetPickRay() const
105
A. APPENDICE A
{return m dPickRay;
}
/∗!Ritorna un riferimento alla lista dei frames a video
∗/inline SetFrame& fbFasped::GetSetFrame(){
return m lpFrame;}
/∗!Ritorna un puntatore al frame corrente attivo
o a quello selezionato∗/
inline fbFrame∗ fbFasped::GetFrame() const{
return m pCurrentFrame;}
/∗———————————————————∗\|∗ END ∗||∗ Espansione inline per la class fbFasped ∗|\∗———————————————————∗/
#endif // !defined( FB FASPED H )//———————————————————// End Of File//———————————————————
A.1.14 fbFrameManager//———————————————————//// FILENAME : fbFrameManager.h//// CLASS(ES) : fbFrameManager//// DESCRIPTION : Classe per la gestione di set di frame//// HISTORY : 28/10/2000//———————————————————#ifndef FB FRAME MANAGER H#define FB FRAME MANAGER H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbGrOb.h
class fbFrameManager : public fbTool{
fbInt m pPattern[7][5];fbFloat m pCursor[4];
// fbInt m nFrameNumber;public:
void ResetLattice();fbFrameManager(fbFasped∗);virtual ∼fbFrameManager();
void FindQuad(const fbDouble x,const fbDouble y,int &i1, int &j1);void DrawFullQuad(fbDouble x,fbDouble y);void InitGL();void Draw();void Clear();void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y);void HandleMouse (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y);
};
#endif // !defined( FB FRAME MANAGER H )//———————————————————// End Of File//———————————————————
A.1.15 fbFrame//———————————————————//// FILENAME : fbFrame.h//
106
A. APPENDICE A
// CLASS(ES) : fbFrame//// DESCRIPTION : classe di gestione di una sessione di disegno//// HISTORY : 26/07/2000// 29/09/2000 cambiata struttura di visualizzazione vector<list<fbCurve∗> >// un vettore di liste//———————————————————#ifndef FB FRAME H#define FB FRAME H
#if MSC VER > 1000#pragma once
//disabilito il warning di campi nome troppo lunghi nei template#endif // MSC VER > 1000
#include fbGrOb.h
#include fbCurve.h
#include <iostream>#include <vector>#include <list>#include fbConfig.h
#include fbPolyLine.h
//! Iteratori per la gestione di un insieme di lineetypedef std::list<fbPolyLine∗> SetLine;typedef SetLine::iterator itSetLine;typedef SetLine::reverse iterator ritSetLine;
//! Funzione per pulire una setlinevoid fbSetLineClear(SetLine& SLine);
//! Funzione per pulire una setlinevoid fbSetLineClear(SetLine∗ SLine);
//———————————————————
/∗!template SetLineBackInsertIterator permette di copiare i contenuti dei frame
∗/template <class Container> class SetLineBackInsertIterator{
Container& container;public:
explicit SetLineBackInsertIterator(Container& Line):container(Line)
{};
SetLineBackInsertIterator& operator=(const fbPolyLine∗ value ){
container.push back(new fbPolyLine(∗value));return (∗this);
};
SetLineBackInsertIterator& operator∗() {return (∗this); }SetLineBackInsertIterator& operator++() {return (∗this); }SetLineBackInsertIterator operator++(int) {return (∗this); }
};
//———————————————————
/∗!fbFrame : Classe che descrive una scena composta da una lista
di linee∗/
class fbFrame :public fbGrOb{protected:
private:
/// Fissa a runtime la qualita’ videofbDouble m dRenderQuality;
//! Database delle curve (lista di liste di cubiche (m l(ist)l(ist)fbCurve)SetLine m llfbCurve;
public:
fbFrame();
virtual ∼fbFrame();
fbFrame(const fbFrame& pFrame);
//! Fissa il tipo dell’ultima curva (FB POLYLINE OPENED / FB POLYLINE CLOSED )/∗!
Fissa il tipo dell’ultima curva-# FB POLYLINE OPENEDPer default le curve sono aperte
107
A. APPENDICE A
-# FB POLYLINE CLOSEDL’ultimo pto coincide con il primo
∗/void SetLineType(const fbInt nType);
//! Metodo di disegnovoid Draw();
//Aggiunge una curve alla listavoid AddCurve(fbCurve∗ pCurve);
//! Metodo che ritorna la polyline che contiene la curva altrimenti end()void Select(const fbCurve ∗pCurve,itSetLine& itSL,itPolyLine& itPL);
//! Seleziona la curva con il pto di controllo piu’ vicino al pto Point a meno di dPickRay/∗!
Ritorna l’iteratore che indirizza la polyline e di quella polyline l’iteratoreche indirizza la curva che soddisfa il minimoPer valore di ritorno un valore tra FB CURVE P1-4 o FB ERROR se errore
∗/fbInt SelectCurveByCtrlPoint(const fbPoint& pPoint,const fbDouble dPickRay,itSetLine& itSL,itPolyLine&
itPL);
//! Seleziona la curva con il pto piu’ vicino al pto Point a meno di dPickRay/∗!
Ritorna l’iteratore che indirizza la polyline e di quella polyline l’iteratoreche indirizza la curva che soddisfa il minimoPer valore di ritorno un valore tra 0-1 o FB ERROR se errore
∗/fbDouble SelectCurveByPoint(const fbPoint& pPoint,const fbDouble dPickRay,itSetLine& itSL,itPolyLine&
itPL);
//! Aggiunge una nuova linea alla listavoid AddPolyLine(fbPolyLine∗ Line);
//! Elimina tutte le curve della listavoid DeleteAllCurves();
/// Metodo per la gestione del visitatorevoid AcceptVisitor(fbVisitor∗);
//! Riferimento alla lista contenutainline SetLine& GetSetLine();
//! Funzione per la riduzione delle linee a videovoid ReduceLines(void (∗RedFunc)(PolyLine&,const fbDouble) = 0 );
//! InizializzazionefbFrame& operator =(const fbFrame &Frame);
friend std::ostream& operator�(std::ostream &os, fbFrame∗ pFrame);friend std::ostream& operator�(std::ostream &os, fbFrame& pFrame);friend std::istream& operator�(std::istream &is, fbFrame ∗pFrame);friend std::istream& operator�(std::istream &is, fbFrame &pFrame);
};
/∗——————————————————-∗\|∗ Espansione inline per la classe fbFrame ∗||∗ BEGIN ∗|\∗——————————————————-∗/
inline SetLine& fbFrame::GetSetLine(){
return m llfbCurve;}
/∗——————————————————-∗\|∗ END ∗||∗ Espansione inline per la classe fbFrame ∗|\∗——————————————————-∗/
#endif // !defined( FB FRAME H )//———————————————————// End Of File//———————————————————
A.1.16 fbFunction//———————————————————//// FILENAME : fbFunction.h//// CLASS(ES) : fbFunction//// DESCRIPTION : classe di gestione di una funzione//// HISTORY : 09/12/2000
108
A. APPENDICE A
//———————————————————#ifndef FB FUNCTION H#define FB FUNCTION H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbMath.h
#include fbConfig.h
#include <functional>#include <cmath>
template <class T> class fbFactors{
// Vettore di fattori su cui operarestd::vector<T> m vFactors;
public:
fbFactors(){}
fbFactors(T Factor){
m vFactors.push back(Factor);}
fbFactors(std::vector<T> vFactor){
copy(vFactor.begin(),vFactor.end(),std::back inserter(m vFactors));}
fbFactors(const fbFactors& Factors){
std::copy(Factors.m vFactors.begin(),Factors.m vFactors.end(),std::back inserter(this→m vFactors));}
fbFactors& operator=(const fbFactors<T>& vFactor){
if(&vFactor == this)return ∗this;
std::copy(vFactor.m vFactors.begin(),vFactor.m vFactors.end(),std::back inserter(m vFactors));
return ∗this;}
std::vector<T>& GetFactors(){
return m vFactors;}
};
template <class T> std::ostream& operator�(std::ostream& os, fbFactors<T>& Factors){
for(fbUInt i = 0 ;i<Factors.GetFactors().size();i++)os�Factors.GetFactors()[i]�’ ’;
return os;
}
template <class T> std::ostream& operator�(std::ostream& os, fbFactors<T>∗ Factors){
for(fbUInt i = 0 ;i<Factors→GetFactors().size();i++)os�Factors→GetFactors()[i]�’ ’;
return os;}
template <class T> class fbFunction{public:
fbFunction(){};
virtual ∼fbFunction(){};
virtual T operator()( const T& Factor) {return 0;}
virtual T operator()( const T& Factor1,const T& Factor2) {return 0;}
virtual T operator()( fbFactors<T> & Factors) {return 0;}
109
A. APPENDICE A
};
typedef fbFactors<fbDouble> fbFactorsDouble;typedef fbFactors<fbFloat> fbFactorsFloat;typedef fbFactors<fbInt> fbFactorsInt;
typedef fbFunction<fbDouble> fbFunctionDouble;typedef fbFunction<fbDouble> fbFunctionFloat;typedef fbFunction<fbDouble> fbFunctionInt;
template <class T> class fbSin :public fbFunction<T>{
public:
T operator()( const T& Factor ){
return sin(Factor);}
};
/∗!fbPolynomious: Classe per la gestione di un polinomio i valori sono memorizzati in ordinedecrescente con il grado:an x∧n + ...+ a0v[0] = an....v[n] = a0
∗/class fbPolynomious :public fbFunction<fbDouble>{
std::vector<fbDouble> m PolyCoeff;
public:
fbPolynomious(){}
fbPolynomious(const std::vector<fbDouble>& PolyCoeff){
std::copy(PolyCoeff.begin(),PolyCoeff.end(),std::back inserter(m PolyCoeff));}
fbPolynomious(const fbDouble p1,const fbDouble p0){
m PolyCoeff.push back(p1);m PolyCoeff.push back(p0);
}
fbPolynomious(const fbPolynomious& Poly){
std::copy(Poly.m PolyCoeff.begin(),Poly.m PolyCoeff.end(),std::back inserter(m PolyCoeff));}
virtual ∼fbPolynomious(){
m PolyCoeff.clear();}
inline std::vector<fbDouble>& GetCoeff();fbPolynomious& operator=(const fbPolynomious& Poly);
fbPolynomious operator+(const fbPolynomious& Poly);
fbDouble operator()( fbFactorsDouble& Factors) ;
fbPolynomious operator∗(const fbPolynomious& Poly) const;fbPolynomious operator∗(const fbDouble dVal) const;
};
fbPolynomious operator∗(const fbDouble dVal,const fbPolynomious& Poly);std::ostream& operator�(std::ostream& os, fbPolynomious& Poly);std::ostream& operator�(std::ostream& os, fbPolynomious∗ Poly);
inline std::vector<fbDouble>& fbPolynomious ::GetCoeff(){
return m PolyCoeff;}
#endif // !defined( FB FUNCTION H )//———————————————————// End Of File//———————————————————
110
A. APPENDICE A
A.1.17 fbGeometric//———————————————————//// FILENAME : fbGeometric.h//// CLASS(ES) : fbGeometric//// DESCRIPTION : Wrapper per tutti gli oggetti geometrici//// HISTORY : 28/10/2000//———————————————————#include <fbException.h>#include <fbConfig.h>#include <fbMath.h>#include <fbPoint.h>#include <fbPolyLine.h>#include <fbCurve.h>#include <fbStraightLine.h>#include <fbRectangle.h>#include <fbCircle.h>#include <fbTriangle.h>#include <fbUtil.h>
A.1.18 fbGeoObj//———————————————————//// FILENAME : fbGeoObj.h//// CLASS(ES) : fbGeoObj//// DESCRIPTION : Tool per il disegno di oggetti// geometrici (cerchi,ellissi,rettangoli,etc..)//// HISTORY : 09/11/2000//———————————————————#ifndef FB GEOOBJ H#define FB GEOOBJ H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbTool.h
#include fbPoint.h
#include fbFrame.h
#include <list>#include fbConfig.h
#include fbPolyLine.h
#include fbRectangle.h
/∗!fbGeoObj: fbGeo(metric)Obj(ect)Tool per il disegno di oggetti geometrici (cerchi,ellissi,rettangoli,etc..)
∗/class fbGeoObj : public fbTool{private:
//! Oggetto a videoPolyLine m ComplexObject;
//! Variabile che contiene il tipo di oggetto graficofbInt m nGeoObj;
//! Bounding Box per gli oggetti da rappresentarefbRectangle m BBox;
public:inline fbInt GetGeoObj() const;
enum{
FB GEO OBJ RECTANGLE,FB GEO OBJ ELLIPSE,FB GEO OBJ CIRCLE,FB GEO OBJ LINE
};
fbGeoObj(fbFasped∗);fbGeoObj(fbFasped∗,fbInt);
virtual ∼fbGeoObj();
void InitGL();void Draw();void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y);
111
A. APPENDICE A
void HandleMouse (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y);void Clear();
//! Fissa il tipo di oggtto che il tool deve rappresentarevoid SetGeoObj(const fbInt nTypeOfGeoObj);
};
inline fbInt fbGeoObj::GetGeoObj() const{
return m nGeoObj;}
#endif // !defined( FB GEOOBJ H )//———————————————————// End Of File//———————————————————
A.1.19 fbGrOb//———————————————————//// FILENAME : fbGrOb.h//// CLASS(ES) : fbGrOb//// DESCRIPTION : f(a)b(rizio)Gr(aphic)Ob(ject)// classe di gestione di un generico oggetto grafico//// HISTORY : 26/07/2000// 11/08/2000 Cambiate le dimensioni da fbInt a fbDouble per poterle// usare anche negli oggetti curva e pto// Aggiunto metodo per l’accesso mediante visitor// 13/08/2000 Aggiunto metodo per controllo colore// 27/09/2000 Cambiato Draw=0 con Draw(const fbRectangle&) per tener conto// del disegno fuori dalla BBox// 14/10/2000 Rimosso Draw(cons fbRectangle&) gli aggiornamenti// sono eseguiti al resize// 15/11/2000 Aggiornate le funzioni inline// 23/05/2001 Rinominati :// m bInit -> m bInitGL// Init() -> InitGL()// GetInit() -> GetInitGL()// SetInit() -> SetInitGL()//———————————————————#ifndef FB GROB H#define FB GROB H
#include fbConfig.h
#include fbVisitor.h
#include <iostream>
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
//! fbStipple descrive la struttura di una linea tratteggiata (vedi OpenGL glLineStipple)struct fbStipple{
fbInt m nFactor; //! Fattore di stipple (piu’ e’ grande piu’ il tratteggio e’ largo)fbUShort m nPattern; //! Maschera descrittiva
fbStipple():m nFactor (1),m nPattern (0xAAAA)
{}
fbStipple(const fbInt nFact,const fbUShort nPatt):m nFactor (nFact),m nPattern (nPatt)
{}};
/∗!fbGrOb classe base di ogni oggetto grafico
∗/class fbGrOb{
//! Flag di visibilita’ per l’oggetto graficofbBool m bVisibility;
//! Flag di disponibilita’ dell’oggetto (se e’ attivo fa qualcosa altrimenti no)fbBool m bActivity;
//! Flag di inizializzazione contesto OpenGLfbBool m bInitGL;
//! Colore dell’oggetto attivo
112
A. APPENDICE A
fbFloat m pdColor[4];
//! Colore dell’oggetto non attivofbFloat m pdColorNoActive[4];
//! Larghezza dell’oggetto graficofbDouble m dWidth;
//! Altezza dell’oggetto graficofbDouble m dHeight;
//! Tolleranza del disegno/∗!
Fissa la tolleranza di disegno.N.B.
(Questo valore non e’ comune ma specifico per ogni oggetto puo’essere variato in funzione delle necessita’ dell’oggetto)
∗/fbDouble m dTolerance;
//! GLenum per il disegnofbLong m nDrawType;
//! Stile della lineafbStipple m nStipplePattern;
//! Puntatore a funzione di callback pre drawvoid (∗m pPreDrawFunction) (fbGrOb∗);
//! Puntatore a funzione di callback post drawvoid (∗m pPostDrawFunction) (fbGrOb∗);
public:
fbGrOb();fbGrOb(const fbDouble,const fbDouble);fbGrOb(const fbGrOb&);
virtual ∼fbGrOb();
enum{
FB COLOR ACTIVE ,//! Macro per settare il colore attivoFB COLOR NOT ACTIVE //! Macro per settare il colore non attivo
};
//! Fissa il valore della tolleranza del disegnoinline void SetTolerance(const fbDouble dVal);
//! Ritorna il valore della tolleranza del disegnoinline fbDouble GetTolerance() const;
//! Fissa GLenum per il disegno correnteinline void SetDrawType(const fbLong nVal);
//! Ritorna GLenum per il disegno correnteinline fbLong GetDrawType() const;
//! Linka una funzione alla callback di predisegno/∗!
Questa funzione sovrascrive le proprieta’ precedentementefissate nella Init tenetene conto nel suo utilizzo
∗/inline void SetPreDrawFunction(void (∗func)(fbGrOb∗));
//! Linka una funzione alla callback di postdisegnoinline void SetPostDrawFunction(void (∗func)(fbGrOb∗));
//! Inizializzazionevirtual fbGrOb& operator=(const fbGrOb &GraphiObject);
//! Metodo di inizializzazionevirtual void InitGL();
//! Metodo di gestione disegnovirtual void Draw()=0;
//! Wrapper per il metodo Drawvoid Display();
//! Fissa il flag di visibilita’inline void SetVisibility(const fbBool bVal=true);
//! Ritorna il flag di visibilita’inline fbBool GetVisibility() const;
//! Fissa il flag di attivita’inline void SetActivity(const fbBool bVal=true);
113
A. APPENDICE A
//! Ritorna il flag di attivita’inline fbBool GetActivity() const;
//! Fissa il flag di inizializzazione del contesto OpenGLinline void SetInitGL(const fbBool bVal);
//! Ritorna il flag di inizializzazione del contesto OpenGLinline fbBool GetInitGL() const;
//! Fissa le dimensioni dell’oggettoinline void SetSize(const fbDouble dW, const fbDouble dH);
//! Ritorna le dimensioni dell’oggettoinline void GetSize(fbDouble & dW, fbDouble& dH) const;
//! Ritorna la largezza dell’oggettoinline fbDouble H() const;
//! Fissa l’altezza dell’oggettoinline void H( const fbDouble dVal);
//! Ritorna l’altezza dell’oggettoinline fbDouble W() const;
//! Fissa la largezza dell’oggettoinline void W( const fbDouble dVal);
//! Ritorna lo stile della lineainline fbStipple GetStipplePattern();
//! Fissa lo stile della lineainline void SetStipplePattern(const fbStipple nStipple);
//! Ritorna il colore dell’oggettovoid GetColor(fbFloat& dR,fbFloat& dG,fbFloat& dB,fbFloat& dA,fbInt
nActive=FB COLOR ACTIVE) const;const fbFloat∗ GetColor(fbInt nActive = FB COLOR ACTIVE) const;
inline void GetColorActive (fbFloat& dR,fbFloat& dG,fbFloat& dB,fbFloat& dA) const ;inline const fbFloat∗ GetColorActive () const { return &m pdColor[0];}inline void GetColorNoActive(fbFloat& dR,fbFloat& dG,fbFloat& dB,fbFloat& dA) const;inline const fbFloat∗ GetColorNoActive() const { return &m pdColorNoActive[0];}
//Fissa il colore dell’oggettovoid SetColor (fbFloat dR,fbFloat dG,fbFloat dB,fbFloat dA,fbInt nActive=FB COLOR ACTIVE);void SetColor (const fbFloat∗ dVectorColor4D,fbInt nActive=FB COLOR ACTIVE);
inline void SetColorActive (fbFloat dR,fbFloat dG,fbFloat dB,fbFloat dA) ;inline void SetColorActive (const fbFloat∗ dVectorColor4D);inline void SetColorNoActive(fbFloat dR,fbFloat dG,fbFloat dB,fbFloat dA);inline void SetColorNoActive(const fbFloat∗ dVectorColor4D);
//! Metodo visitatore per l’accesso ai sottocampivirtual void AcceptVisitor(fbVisitor∗);
friend std::ostream& operator�(std::ostream &os, fbGrOb∗ pObj);friend std::ostream& operator�(std::ostream &os, fbGrOb& pObj);
};
/∗—————————————-∗\|∗ Espansione inline per la class fbGrOb ∗||∗ BEGIN ∗|\∗—————————————-∗/
inline void fbGrOb::SetTolerance(const fbDouble dVal){
m dTolerance = dVal;}
inline fbDouble fbGrOb::GetTolerance() const{
return m dTolerance ;}
inline void fbGrOb::SetDrawType(const fbLong nVal){
m nDrawType = nVal;}
inline fbLong fbGrOb::GetDrawType() const{
return m nDrawType ;}
inline void fbGrOb::SetPreDrawFunction(void (∗func)(fbGrOb∗)){
m pPreDrawFunction=func;}
inline void fbGrOb::SetPostDrawFunction(void (∗func)(fbGrOb∗)){
m pPostDrawFunction=func;}
114
A. APPENDICE A
inline void fbGrOb::SetVisibility(const fbBool bVal){
m bVisibility=bVal;}
inline fbBool fbGrOb::GetVisibility() const{
return m bVisibility;}
inline void fbGrOb::SetActivity(const fbBool bVal){
m bActivity=bVal;}inline fbBool fbGrOb::GetActivity() const{
return m bActivity;}
inline void fbGrOb::SetInitGL(const fbBool bVal){
m bInitGL = bVal;}
inline fbBool fbGrOb::GetInitGL() const{
return m bInitGL;}
inline void fbGrOb::SetSize(const fbDouble dW, const fbDouble dH){
m dWidth = dW; m dHeight= dH;}
inline void fbGrOb::GetSize(fbDouble & dW, fbDouble& dH) const{
dW = m dWidth ; dH = m dHeight ;}
inline fbDouble fbGrOb::H() const{
return m dHeight;}
inline void fbGrOb::H( const fbDouble dVal){
m dHeight=dVal;}
inline fbDouble fbGrOb::W() const{
return m dWidth;}
inline void fbGrOb::W( const fbDouble dVal){
m dWidth =dVal;}
inline void fbGrOb::GetColorActive (fbFloat& dR,fbFloat& dG,fbFloat& dB,fbFloat& dA) const{
dR= m pdColor[0]; dG= m pdColor[1];dB= m pdColor[2]; dA= m pdColor[3];}
inline void fbGrOb::GetColorNoActive(fbFloat& dR,fbFloat& dG,fbFloat& dB,fbFloat& dA) const{
dR= m pdColorNoActive[0]; dG= m pdColorNoActive[1];dB= m pdColorNoActive[2]; dA=m pdColorNoActive[3];}
inline void fbGrOb::SetColorActive (fbFloat dR,fbFloat dG,fbFloat dB,fbFloat dA){
m pdColor[0]=dR; m pdColor[1]=dG; m pdColor[2]=dB; m pdColor[3]=dA;
}
inline void fbGrOb::SetColorActive (const fbFloat∗ dVectorColor4D){
m pdColor[0]=dVectorColor4D[0]; m pdColor[1]=dVectorColor4D[1];m pdColor[2]=dVectorColor4D[2]; m pdColor[3]=dVectorColor4D[3];
}
inline void fbGrOb::SetColorNoActive(fbFloat dR,fbFloat dG,fbFloat dB,fbFloat dA){
m pdColorNoActive[0]=dR; m pdColorNoActive[1]=dG;m pdColorNoActive[2]=dB; m pdColorNoActive[3]=dA;
}
115
A. APPENDICE A
inline void fbGrOb::SetColorNoActive (const fbFloat∗ dVectorColor4D){
m pdColorNoActive[0]=dVectorColor4D[0]; m pdColorNoActive[1]=dVectorColor4D[1];m pdColorNoActive[2]=dVectorColor4D[2]; m pdColorNoActive[3]=dVectorColor4D[3];
}
inline fbStipple fbGrOb::GetStipplePattern(){
return m nStipplePattern;}
void fbGrOb::SetStipplePattern(const fbStipple nStipple){
m nStipplePattern.m nFactor = nStipple.m nFactor;m nStipplePattern.m nPattern= nStipple.m nPattern;
}
/∗—————————————-∗\|∗ END ∗||∗ Espansione inline per la class fbGrOb ∗|\∗—————————————-∗/
//! Funzione per il disegno di un oggettoinline fbGrOb∗ fbGrObDraw(fbGrOb∗ pObj){
pObj→Display();return pObj;
}
//! Funzione per la distribuzione di visitorinline fbGrOb∗ fbGrObVisit(fbGrOb∗ pObj,fbVisitor∗ pVisitor){
pObj→AcceptVisitor(pVisitor);return pObj;
}
//! Funzione di preDraw per l’abilitazione dello stipplevoid fbDrawEnableStipple (fbGrOb∗);
//! Funzione di postDraw per la disabilitazione dello stipplevoid fbDrawDisableStipple(fbGrOb∗);
#endif // !defined( FB GROB H )//———————————————————// End Of File//———————————————————
A.1.20 fbLoader//———————————————————//// FILENAME : fbLoader.h//// CLASS(ES) ://// DESCRIPTION ://// DATE : Wed Oct 4 2000// COPYRIGTH : (C) 2000 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————#ifndef FB LOADER H#define FB LOADER H
#include fbWindow.h
#include fbConfig.h
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
class fbLoader{
fbBool LoadFSE(const fbChar∗ pFileName);fbBool LoadTZV(const fbChar∗ pFileName);fbBool LoadDAT(const fbChar∗ pFileName);fbBool LoadFRP(const fbChar ∗pFileName);
#ifdef SVG ENABLEDfbBool LoadSVG(const fbChar ∗pFileName);
#endif
public:fbBool LoadFile(const fbChar∗ pFileName);fbLoader(fbFasped∗);virtual ∼fbLoader();/∗∗ Metodo per centrare il disegno in finestra ∗/void MaximizeAndCenter(const fbPoint& tL,const fbPoint& bR);
116
A. APPENDICE A
private:fbFasped∗ m pApplicationReference;
};
#endif // !defined( FB LOADER H )//———————————————————// End Of File//———————————————————
A.1.21 fbMath//———————————————————//// FILENAME : fbMath.h//// CLASS(ES) : fbMatrix.fbVector,fbVector2D//// DESCRIPTION : f(a)b(rizio)Math// qualche funzione per la gestione matematica//// HISTORY : 16/08/2000// 08/09/2000 aggiunto vettore 2D ma la politica di// gestione ancora non l’ho fissata// 15/05/2000 aggiunto codice gestione angoli//———————————————————#ifndef FBMATH H#define FBMATH H
#include <vector>#include <iostream>#include <algorithm>#include <cmath>#include fbException.h
#include fbConfig.h
#include fbPoint.h
/∗!Libreria matematica
∗/
// Qualche costante sempre comoda
//! P grecoconst fbDouble FB PI = 3.1415926535897932384626433832795;
//! P greco ∗2const fbDouble FB 2 PI = 6.283185307179586476925286766559;
//! P greco /2const fbDouble FB PI 2 = 1.5707963267948966192313216916398;
//! P greco /3const fbDouble FB PI 3 = 1.0471975511965977461542144610932;
//! P greco /4const fbDouble FB PI 4 = 0.78539816339744830961566084581988;
//! 180 / P grecoconst fbDouble FB RAD ANGLE = 57.295779513082320876798154814105;
//! P greco /180const fbDouble FB ANGLE RAD = 1.74532925199432957692369076848e-2;
//! econst fbDouble FB E = 2.71828182846;
//! 1/3const fbDouble FB 1 3 = 0.33333333333333333333333333333333;
//! 2/3const fbDouble FB 2 3 = 0.66666666666666666666666666666667;
//! Formule di conversione angoli radianti e viceversainline fbDouble fbRadToAngle(const fbDouble& dVal){
return FB RAD ANGLE∗dVal;}
inline fbDouble fbAngleToRad(const fbDouble& dVal){
return FB ANGLE RAD∗dVal;}
//! Valore assolutotemplate <class T> inline T fbAbs(T val){ return(val > 0 ? val : -val);}
//! Tipo di range:enum fbRangeType{
117
A. APPENDICE A
fbRangeAllIncluded,fbRangeLeftIncluded,fbRangeRightIncluded,fbRangeNotIncluded
};
template <class T> inline T fbMin(T fv,T sv ){
return fv < sv ? fv : sv ;}
template <class T> inline T fbMax(T fv,T sv ){
return fv > sv ? fv : sv ;}
//! Check nel range/∗!
Il caso particolare in cui minVal e maxVal coincidono e’ risoltoverificando l’uguaglianza tra (minVal=maxVal) e testVal se e’ verificataritorna true
∗/template <class T> inline fbBool fbRange(const T minVal,\
const T testVal,\const T maxVal,\const fbRangeType RangeType=fbRangeAllIncluded)
{T tempMin=minVal,
tempMax=maxVal ;
// se gli ho passato i valori in ordine errato aggiustaif( tempMin>tempMax)
swap(tempMin,tempMax);
if(tempMin == tempMax){
switch(RangeType){case fbRangeAllIncluded:case fbRangeLeftIncluded:case fbRangeRightIncluded:
if(tempMin==testVal)return true;
break;case fbRangeNotIncluded: //sempre errato
return false;break;
}return false;
}
switch(RangeType){case fbRangeAllIncluded:
if(tempMin<=testVal && testVal<=tempMax)return true;
break;case fbRangeLeftIncluded:
if( tempMin<=testVal && testVal<tempMax)return true;
break;
case fbRangeRightIncluded:if( tempMin<testVal && testVal<=tempMax)
return true;break;
case fbRangeNotIncluded:if( tempMin<testVal && testVal<tempMax)
return true;break;
}
return false;}
//! Restituisce il segno (1 -> +; -1 -> - ;0 -> 0)template <class T> inline T fbSign(const T& number){
if(number> (T)0)return 1;
if(number == (T)0)return 0;
return -1;}
//! Calcola la potenza intera di un numero realeinline fbDouble fbPowInt (const fbDouble Base, const fbInt exp)
118
A. APPENDICE A
{if (exp == 0)
return 1.0;
fbDouble outVal = Base;
for (int i = 1; i < exp; i++)outVal ∗ = Base;
return outVal;}
//! Quadrato di valtemplate <class T> inline T fbSqr(const T val){
return val ∗ val;}
//! FattorialefbDouble fbFact(fbDouble nVal);
//! Binomio di NewtonfbDouble fbNewtonBinomy(fbInt n,fbInt i);
//! Bernstein EvaluatorfbDouble Bernstein(fbDouble t,fbInt n,fbInt i);
/∗!Riporta il polinomio di 3◦ grado in forma di bezier.I Pti sono ordinati da P0 a Pn-1SIDE EFFECTS: solo il caso di grado 3 e’ supportato
∗/template <class T1,class T2> void fbPolyToBezier(const T1& Poly, T2& Bezier){
switch(Poly.size()){case 4:
Bezier.resize(4);Bezier[0] = Poly[3];Bezier[1] = (Poly[2]∗FB 1 3 + Bezier[0] );Bezier[2] = (Poly[1]∗FB 1 3 - Bezier[0] + Bezier[1] ∗ 2.0 );Bezier[3] = Poly[0] + Bezier[0] - Bezier[1] ∗ 3.0 + Bezier[2] ∗ 3.0;break;
case 3:Bezier.resize(3);Bezier[0] = Poly[2];Bezier[1] = Poly[1]∗0.5 + Bezier[0];Bezier[2] = Poly[0] - Bezier[0] + 2.0 ∗ Bezier[1];break;
}}
/∗!Calcola le soluzione di un polinomio di grado 2N.B. Testato e controllato
∗/template <class T> fbInt fbPoly2DegreeFindRoots(const T& a, const T& b, const T& c , T& sol 1, T& sol 2){
if ( a == 0.0 ){
if( b == 0.0 )return 0;
sol 1 = sol 2 = - c / b;return 1;
}
TDelta = b∗b - 4.0 ∗ a ∗ c;
if( Delta < 0.0 )return 0;
if( Delta == 0.0 ){
sol 1 = sol 2 = - b / 2.0 ∗ a;return 1;
}
// modificato secondo Numerical RecipesT
q = -0.5 ∗ ( b + fbSign( b ) ∗ sqrt(Delta));
if( b != 0){
sol 1 = q / a;sol 2 = c / q ;
}else{sol 2 = sqrt( - c / a );sol 1 = - sol 2;
119
A. APPENDICE A
}return 2;
}
/∗! Calcola le radici di una equazione di grado n ( per ora n<=2 Ha Ha)viene ipotizzato che i coefficienti siano passati in ordinedescrescente con il grado del polinomio:\f$ a nt∧n + a {n-1}t∧{n-1} + a {n-2}t∧{n-2} + ... + a 0 =0 \f$= v[0] , v[1],...,v[n]
∗/template <class T> void fbPolyFindRoots(const std::vector<T>& pdCoeff,std::vector<T>& vdRes){
std::vector<T>tempVector;
copy(pdCoeff.begin(),pdCoeff.end(),back inserter(tempVector));//insert iterator<std::vector<T> >(tempVector,tempVector.end()));
// per sicurezza faccio pulizievdRes.clear();
//se il polinomio e’ di gradio superiore a 4 o vuotoif(tempVector.size() >5 || tempVector.empty())
throw fbRangeException(string( FILE )+string( fbPolyFindRoots(const std::vector<T>& ...): Bad poly
size));
if(tempVector.size() == 2){
if(tempVector[0] == 0 )throw fbRangeException(string( FILE )+string( fbPolyFindRoots(const std::vector<T>& ...):
Constant value only in poly));else{
vdRes.push back(-tempVector[1]/tempVector[0]);return;
}}
if(tempVector.size() == 3){
TdSol 1,dSol 2;
fbIntnNumberOfSol = fbPoly2DegreeFindRoots(tempVector[0], tempVector[1], tempVector[2], dSol 1,
dSol 2);
if(nNumberOfSol == 0)throw fbRangeException(string( FILE )+string( fbPolyFindRoots(const std::vector<T>& ...):
Roots are complex));
if(nNumberOfSol == 1)vdRes.push back( dSol 1 );
if(nNumberOfSol == 2){
vdRes.push back( dSol 1 );vdRes.push back( dSol 2 );
}
}
if(tempVector.size() == 4){
if(tempVector[0] == 0 ) // e’ il caso di secondo grado{
// faccio risalire i valoritempVector[0] = tempVector[1];tempVector[1] = tempVector[2];tempVector[2] = tempVector[3];tempVector.pop back(); // elimino l’ultimofbPolyFindRoots<T>(tempVector,vdRes);
}else{
// normalizzo per utilizzare le formule di Cardano (N.B. a e’ != 0.0)for(fbUInt i=0;i<4;i++)
tempVector[i]/=tempVector[0];
/∗Q = (a∗a - 3∗b) /9
R = (2∗a∗a∗a -9∗a∗b+27∗c)/54
con a = tempVector[1]b = tempVector[2]c = tempVector[3]
∗/T
120
A. APPENDICE A
a2 = tempVector[1]∗tempVector[1],Q = (a2 - 3.0∗ tempVector[2])/9.0,R = (2.0 ∗ a2 ∗tempVector[1] -9.0 ∗tempVector[1]∗tempVector[2] +27.0∗tempVector[3])/54.0,Q3 = Q∗Q∗Q,R2 = R∗R;
if(R2<Q3){
Ttheta = acos(R/sqrt(Q3)) / 3.0,coeff 1 = -2.0∗sqrt(Q),coeff 2 = -tempVector[1]/3.0;
vdRes.push back(coeff 1∗cos(theta)+coeff 2);vdRes.push back(coeff 1∗cos(theta+2.0∗FB PI 3)+coeff 2);vdRes.push back(coeff 1∗cos(theta-2.0∗FB PI 3)+coeff 2);
}else{
/∗T
A = - pow([ R + sqrt(R2 - Q3)],3.0);
if(A!= 0.0)B = Q / A:
elseB =0.0;
vdRes.push back(coeff 1∗cos(theta)+coeff 2);∗/throw fbRangeException(string( FILE )+string( fbPolyFindRoots(const std::vector<T>&...):
Roots are complex));}
}}
}
/∗! Calcola la derivata di un polinomioviene ipotizzato che i coefficienti siano passati in ordinedescrescente con il grado del polinomio:\f$ a nt∧n + a {n-1}t∧{n-1} + a {n-2}t∧{n-2} + ... + a 0 =0 \f$ritorna\f$ a n\cdot n\cdot t∧{n-1} + a {n-1}\cdot (n-1)\cdot t∧{n-2} + ... + a 1 =0 \f$
∗/template <class T> void fbPolyDerive(const std::vector<T>& pdCoeff,std::vector<T>& vdRes){
std::vector<T> tempVector;copy(pdCoeff.begin(),pdCoeff.end(),back inserter(tempVector));
//insert iterator<std::vector<T> >(tempVector,tempVector.end()));
// per sicurezza faccio pulizievdRes.clear();fbUInt degree=tempVector.size() - 1 ;for(fbUInt i=0;i<tempVector.size()-1;i++)
vdRes.push back((degree - i )∗tempVector[i]);}
/∗! Valuta un polinomio per un dato valoreviene ipotizzato che i coefficienti siano passati in ordinedescrescente con il grado del polinomio:\f$ a nt∧n + a {n-1}t∧{n-1} + a {n-2}t∧{n-2} + ... + a 0 =0 \f$
∗/template <class T > T fbPolyEval(const std::vector<T>& pdCoeff,const T dVal){
fbUIntpolyOrder = pdCoeff.size();
if(polyOrder < 0 )throw fbRangeException(string( FILE )+string( fbPolyEval(const std::vector<fbDouble>& pdCoeff,const
fbDouble dVal): Ply is too little));
if(polyOrder ==0 )return 0;
if(polyOrder ==1 )return pdCoeff[0];
TexitVal=pdCoeff[0] ;
for(fbUInt i=0;i<polyOrder-1;i++)exitVal = exitVal ∗ dVal + pdCoeff[i+1];
return exitVal;}
/∗!Prodotto fra due polinomi
∗/template <class T> void fbPolyMultPoly(const std::vector<T>& FirstPoly,const std::vector<T>&
121
A. APPENDICE A
SecondPoly,std::vector<T>& ResPoly){
std::vector<T> tempVector;
if ( FirstPoly.size() == 0 || SecondPoly.size() == 0) //se i polinomi non sono validi ritornoreturn;
fbBoolbIsFirstGreaterSecond = false; // fisso un ordinamento
if ( FirstPoly.size() > SecondPoly.size() )bIsFirstGreaterSecond = true;
const std::vector<T> // e faccio due alias per il maggiore ed il minore&tempMaxPoly = bIsFirstGreaterSecond ? FirstPoly : SecondPoly,&tempMinPoly = bIsFirstGreaterSecond ? SecondPoly: FirstPoly ;
// lode ad STL by SGI doctempVector.resize(tempMaxPoly.size()); //alloco spazio per il polinomio piu’ grande
fbUIntnOffset =0; //offset per il polinomio risultato
for(fbUInt i=0;i<tempMinPoly.size();i++) // per tutti i coeff del polinomio di grado minore{
if(i<tempMinPoly.size() - 1)tempVector.push back(0); // aggiungo l’ultimo elemento
if(tempMinPoly[i]!=0.0) // salto le moltiplicazioni nullefor(fbUInt j=nOffset;j<tempMaxPoly.size() + nOffset;j++) // e per tutto il polinomio di grado
maggioretempVector[j] += tempMinPoly[i] ∗ tempMaxPoly[j - nOffset]; // moltiplico i coeff.
nOffset++; // sposto la finestra}
ResPoly.clear(); // pulisco il vettore del risultato
// e copio il risultatocopy(tempVector.begin(),tempVector.end(),back inserter(ResPoly));
}
/∗!Somma fra due polinomi
∗/template <class T> void fbPolyAddPoly(const std::vector<T>& FirstPoly,const std::vector<T>&SecondPoly,std::vector<T>& ResPoly){
std::vector<T> tempVector;
if ( FirstPoly.size() == 0 || SecondPoly.size() == 0) //se i polinomi non sono validi ritorno{
if(FirstPoly.size() == 0){
if(SecondPoly.size() != 0)copy(SecondPoly.begin(),SecondPoly.end(),back inserter(ResPoly));
}else
copy(FirstPoly.begin(),FirstPoly.end(),back inserter(ResPoly));
return;}
fbBoolbIsFirstGreaterSecond = false; // fisso un ordinamento
if ( FirstPoly.size() > SecondPoly.size() )bIsFirstGreaterSecond = true;
const std::vector<T> // e faccio due alias per il maggiore ed il minore&tempMaxPoly = bIsFirstGreaterSecond ? FirstPoly : SecondPoly,&tempMinPoly = bIsFirstGreaterSecond ? SecondPoly: FirstPoly ;
tempVector.resize(tempMaxPoly.size(),0);for(fbUInt i=tempMaxPoly.size()-1;i>0;i– –) // per tutti i coeff del polinomio di grado minore
tempVector[i] = tempMaxPoly[i] + (i<tempMinPoly.size() ? tempMinPoly[i] : 0);
tempVector[0] = tempMaxPoly[0] + tempMinPoly[0];
ResPoly.clear(); // pulisco il vettore del risultato
// e copio il risultatocopy(tempVector.begin(),tempVector.end(),back inserter(ResPoly));
}
/∗! Data una matrice di tipo C/C++ di dimensione nRow x nColmemorizzata per righe restituisce l’indiceche compete al vettore equivalente
∗/inline fbInt fbMatrixToVector(const fbInt i,const fbInt j,const fbInt nCol){
122
A. APPENDICE A
return (i∗nCol+j);}
/∗! Dato un vettore di tipo C/C++ di dimensione nRow x nColritorna dalla posizione nPos gli indici dellacorrispondente matrice memorizzata per righe
∗/inline void fbVectorToMatrix(fbInt& i,fbInt& j,const fbInt nPos ,const fbInt nCol){
j = nPos % nCol;
i = (fbInt)(nPos - j)/nCol;}
const fbInt FB L1 = 4;const fbInt FB L2 = 3;const fbInt FB L3 = 2;const fbInt FB L4 = 1;
const fbInt FB R1 = -1;const fbInt FB R2 = -2;const fbInt FB R3 = -3;const fbInt FB R4 = -4;
/∗! Funzione che calcola con l’algoritmo di deCasteljau pti dello spazio \f$ E∧2 \f$per le costanti che richiamano i pti e’ stata utilizzata la stessa convenzionede Computer Graphics Authors: Foley VanDam (Pag 508)
∗/fbPoint DeCasteljau( const fbInt degree, //! Grado
const fbDouble t, //! Parametroconst fbPoint∗ p, //! Pti di controlloconst fbInt nMiddlePoint); //! Per estrarre i pti di controllo intermedi 1<degree+1
/∗!fbPoint fbCurveSubCtrlPnt(const fbPoint∗ p,const fbInt nVal, const fbDouble dTval):
funzione che calcola i sotto punti di controllo di un arco di Bezier di 3 grado
const fbPoint∗ p -> vettore di pti di controlloconst fbDouble dTval -> valore del parametroconst fbInt nVal -> pto da calcolare
∗/fbPoint fbCurveSubCtrlPnt(const fbPoint∗ p,const fbDouble dTval,const fbInt nVal);
//! Funzione che calcola il determinante di una matrice 2x2// Calcola il determinante di una matrice 2x2 [[a c]// [b d]]
inline fbDouble det2x2(const fbDouble a,const fbDouble b,const fbDouble c,const fbDouble d){
return (a∗d-b∗c);}
//! Funzione che calcola il determinante di una matrice 3x3// Calcola il determinante di una matrice 2x2 [[a d g]// [b e h]// [c f i]]inline fbDouble det3x3( const fbDouble a,const fbDouble b,const fbDouble c,\
const fbDouble d,const fbDouble e,const fbDouble f,\const fbDouble g,const fbDouble h,const fbDouble i)
{return (a∗det2x2(e,f,h,i)-d∗det2x2(b,c,h,i)+g∗det2x2(b,c,e,f));
}
//! Per le matrici utilizzi la stessa convenzione di OpenGL//! e le memorizzo per colonneclass fbVector;
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗\|∗ fbMatrix ∗|\∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
/∗!fbMatrix classe per la gestione di matrici (esclusivamente 4x4)i principali operatori sono previsti la convenzione utilizzata perla memorizzazione rispecchia quella OpenGL (e’ possibile passaregli elementi di una matrice a glMultMatrix()
∗/class fbMatrix{private:
//! Vettore con le variabilifbDouble m pdValue[16];
//! Vettore per il ritorno della colonna
123
A. APPENDICE A
// l’ho reso fisso per un miglior controllo di memoriafbDouble m pdTempColumn[4];
//! Numero di righefbInt m nRow;
//! Numero di ColonnefbInt m nCol;
//! Metodo che calcola il prodotto tra una riga ed una colonnainline fbDouble dRowColProduct(fbDouble ∗pRow,fbDouble ∗pCol);
public:
fbMatrix();
fbMatrix(const fbMatrix&);
virtual ∼fbMatrix();
//! Prodotto fra matricifbMatrix operator∗(fbMatrix&);
//! Prodotto fra matrice e vettore colonnafbVector operator∗(fbVector&);
//! Assegnazione fra matricifbMatrix& operator=(const fbMatrix&);
//! Fissa la dimensione della matrice <= 4x4inline void SetSize(const fbInt nRow,const fbInt nCol);
//! Ritorna la dimensione della matrice <= 4x4inline void GetSize(fbInt &nRow,fbInt& nCol);
//! Ritorna il valore nella posizione nRow nCol (0<=nRow,nCol<=3 )inline fbDouble& GetValue(const fbInt nRow,const fbInt nCol);
//! Fissa il valore nella posizione nRow nCol (0<=nRow,nCol<=3 )inline void SetValue(const fbInt r,const fbInt c,const fbDouble dVal);
//! Copia pElements come elementi della matricevoid SetValues(const fbDouble∗ pElements);
//! Ritorna la colonna nella posizione nCol (0<=nCol<=3 )inline fbDouble∗ GetColumn(const fbInt nCol);
//! Ritorna la riga nella posizione nRow (0<=nCol<=3 )inline fbDouble∗ GetRow(const fbInt nRow);
//! Traspone la matricevoid transpose();
//! Calcola il determinante della matricefbDouble det();
//! Calcola l’inversa della matricefbMatrix& inv();
//! Metodo per costruire una matrice di Vandermonde con il vettore colonnafbMatrix& Vandermonde(const fbDouble& a,const fbDouble& b,const fbDouble& c,const fbDouble& d);
//! Stampa a console della matricevoid Print();
friend std::ostream& operator�(std::ostream&,fbMatrix&);friend std::ostream& operator�(std::ostream&,fbMatrix∗);
};
/∗———————————————————∗\|∗ Espansione inline per la class fbMatrix ∗||∗ BEGIN ∗|\∗———————————————————∗/
inline fbDouble fbMatrix::dRowColProduct(fbDouble ∗pRow,fbDouble ∗pCol){
return ((pRow[0]∗pCol[0])+(pRow[1]∗pCol[1])+(pRow[2]∗pCol[2])+(pRow[3]∗pCol[3]));}
//! Fissa la dimensione della matrice <= 4x4inline void fbMatrix::SetSize(const fbInt nRow,const fbInt nCol){
m nRow=nRow;m nCol=nCol;
}
//! Ritorna la dimensione della matrice <= 4x4inline void fbMatrix::GetSize(fbInt &nRow,fbInt& nCol){
nCol = m nCol;nRow = m nRow;
124
A. APPENDICE A
}
//! Ritorna il valore nella posizione nRow nCol (0<=nRow,nCol<=3 )inline fbDouble& fbMatrix::GetValue(const fbInt nRow,const fbInt nCol){
return m pdValue[nRow∗4+nCol];}
//! Fissa il valore nella posizione nRow nCol (0<=nRow,nCol<=3 )inline void fbMatrix::SetValue(const fbInt r,const fbInt c,const fbDouble dVal){
m pdValue[r∗4+c]=dVal;}
//! Ritorna la colonna nella posizione nCol (0<=nCol<=3 )inline fbDouble∗ fbMatrix::GetColumn(const fbInt nCol){
m pdTempColumn[0] = GetValue(0,nCol); m pdTempColumn[1] = GetValue(1,nCol);m pdTempColumn[2] = GetValue(2,nCol); m pdTempColumn[3] = GetValue(3,nCol);
return &m pdTempColumn[0];}
//! Ritorna la riga nella posizione nRow (0<=nCol<=3 )inline fbDouble∗ fbMatrix::GetRow(const fbInt nRow){
return &m pdValue[nRow∗m nRow] ;}/∗———————————————————∗\|∗ END ∗||∗ Espansione inline per la class fbMatrix ∗|\∗———————————————————∗/
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗\|∗ fbVector ∗|\∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
class fbVector{
public:enum fbVectorType{
FB VECTOR COLUMN,FB VECTOR ROW
};
// Costruttore di default un vettore e’ colonna e di 4 elementifbVector();
//! Costuttore (tipo vettore(riga-colonna),dimensione)fbVector(const fbVectorType,const fbInt);
//! Costruttore di copiefbVector(const fbVector&);
//! Costruttore per vettore colonna a 2 dimensionifbVector(const fbDouble x,const fbDouble y);
//! Costruttore per vettore colonna a 3 dimensionifbVector(const fbDouble x,const fbDouble y,const fbDouble z);
//! Costruttore per vettore colonna a 4 dimensionifbVector(const fbDouble x,const fbDouble y,const fbDouble z,const fbDouble w);
//! Costruttore che calcola il vettore (colonna) da 2 pti bidimensionali (p2-p1)fbVector(const fbPoint& p1,const fbPoint& p2);
//! Distruttorevirtual ∼fbVector();
//! Ritorna la dimensioneinline fbInt GetSize() const;
//! Ritorna l’orientamento (riga,colonna)inline fbInt GetType() const;
//! Fissa il valore in nPos a dVal (se errore rilancia fbRangeException)void SetElement(const fbInt nPos,const fbDouble dVal);
/∗!Fissa gli elementi del vettore in funzione delle dimensioni del vettore passato (per default 4)i valori e la dimensione precedente vengono cancellate.
∗/void SetElements(const fbDouble∗,const fbInt nSize=4);
/∗! Fissa il vettore a 2 dimensioni con valori v[0] = el1 e v[1] = el2i valori e la dimensione precedente vengono cancellate.
∗/void SetElements(const fbDouble& el1,const fbDouble& el2);
125
A. APPENDICE A
/∗! Fissa il vettore a 3 dimensioni con valori v[0] = el1 , v[1] = el2 e v[2] = el3i valori e la dimensione precedente vengono cancellate.
∗/void SetElements(const fbDouble& el1,const fbDouble& el2,const fbDouble& el3);
/∗! Fissa il vettore a 3 dimensioni con valori v[0] = el1 , v[1] = el2 , v[2] = el3 e v[3] = el4i valori e la dimensione precedente vengono cancellate.
∗/void SetElements(const fbDouble& el1,const fbDouble& el2,const fbDouble& el3,const fbDouble& el4);
//! Ritorna l’elemento in posizione nPos (se errore rilancia fbRangeException)fbDouble GetElement(const fbInt nPos) const;
//! Modulo del vettoreconst fbDouble Abs() const;
//! Normalizza il vettorefbVector& Normalize();
//! InizializzazionefbVector& operator= (const fbVector &);
//! Prodotto scalare fra due vettori (se errore rilancia fbRangeException)fbDouble operator∗ (const fbVector& vect);
//! Prodotto con uno scalarefbVector operator∗ (const fbDouble dVal);
//! Prodotto con uno scalarefbVector& operator∗ = (const fbDouble dVal);
/∗!Prodotto vettoriale (il vettore in uscita e’ dimensionato in modo da essere coerentema non gestisce casi maggiori di vettori a 3 dimensioni)
∗/fbVector operator∧ (const fbVector &vect);
//! Meno unario (ruota il vettore di 180 gradi)inline fbVector& operator- ();
//! Restituisce l’elemento in posizione nPos (se errore rilancia fbRangeException)fbDouble& operator[ ] (const fbInt nPos);
//! Stampa a videofriend std::ostream& operator�(std::ostream&,fbVector&);friend std::ostream& operator�(std::ostream&,fbVector∗);
private://! Elementi del vettorefbDouble ∗m pdEls;
//! Dimensioni del vettorefbInt m nSize;
//! Tipo del vettore e dimensionefbVectorType m nType;
};
/∗———————————————————∗\|∗ Espansione inline per la class fbVector ∗||∗ BEGIN ∗|\∗———————————————————∗/
inline fbVector& fbVector::operator-(){
for(fbInt i = 0;i < m nSize; i++)m pdEls[i] = -m pdEls[i];
return ∗this;}
inline fbInt fbVector::GetSize() const{
return m nSize;}
inline fbInt fbVector::GetType() const{
return m nType;}
/∗———————————————————∗\|∗ END ∗||∗ Espansione inline per la class fbVector ∗|\∗———————————————————∗/
/∗———————————————————∗\|∗ fbVector VS fbPoint ∗|
126
A. APPENDICE A
|∗ Funzioni comuni per la gestione ∗||∗ di vettori e pti. ∗|\∗———————————————————∗/
//! Prodotto per uno scalarefbVector operator∗ (const fbDouble dVal,const fbVector& v); // d ∗ v = v
//! Ritorna un pto dalla somma di un pto con un vettoretemplate <class T> inline fbPoint2<T> operator+ (const fbPoint2<T>& p,const fbVector& v) // p + v = p{
return fbPoint2<T>(p.X + v.GetElement(0),p.Y + v.GetElement(1));}
template <class T> inline fbPoint3<T> operator+ (const fbPoint3<T>& p,const fbVector& v) // p + v = p{
return fbPoint3<T>(p.X + v.GetElement(0),p.Y + v.GetElement(1),p.Z + v.GetElement(2));}
template <class T> inline fbPoint4<T> operator+ (const fbPoint4<T>& p,const fbVector& v) // p + v = p{
return fbPoint4<T>(p.X + v.GetElement(0),p.Y + v.GetElement(1),p.Z + v.GetElement(2),p.W +v.GetElement(3));}
//! Ritorna un pto dalla somma di un pto con un vettoreinline fbPoint operator+ (const fbVector& v,const fbPoint& p) // v + p = p{
return fbPoint(p.X + v.GetElement(0),p.Y + v.GetElement(1));}template <class T> inline fbPoint2<T> operator+ (const fbVector& v,const fbPoint2<T>& p) // v + p = p{
return fbPoint2<T>(p.X + v.GetElement(0),p.Y + v.GetElement(1));}template <class T> inline fbPoint3<T> operator+ (const fbVector& v,const fbPoint3<T>& p) // v + p = p{
return fbPoint3<T>(p.X + v.GetElement(0),p.Y + v.GetElement(1),p.Z + v.GetElement(3));}template <class T> inline fbPoint4<T> operator+ (const fbVector& v,const fbPoint4<T>& p) // v + p = p{
return fbPoint4<T>(p.X + v.GetElement(0),p.Y + v.GetElement(1),p.Z + v.GetElement(2),p.W +v.GetElement(3));}
//! Ritorna un pto dalla somma di un pto con un vettoreinline fbPoint operator+ (const fbPoint∗ p,const fbVector& v) // p + v = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1));}template <class T> inline fbPoint2<T> operator+ (const fbPoint2<T>∗ p,const fbVector& v) // p + v = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1));}template <class T> inline fbPoint3<T> operator+ (const fbPoint3<T>∗ p,const fbVector& v) // p + v = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1),p→Z + v.GetElement(2));}template <class T> inline fbPoint4<T> operator+ (const fbPoint4<T>∗ p,const fbVector& v) // p + v = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1),p→Z + v.GetElement(2),p→W +v.GetElement(3));}
//! Ritorna un pto dalla somma di un pto con un vettoreinline fbPoint operator+ (const fbVector& v,const fbPoint∗ p) // v + p = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1));}template <class T> inline fbPoint2<T> operator+ (const fbVector& v,const fbPoint2<T>∗ p) // v + p = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1));}template <class T> inline fbPoint3<T> operator+ (const fbVector& v,const fbPoint3<T>∗ p) // v + p = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1),p→Z + v.GetElement(2));}template <class T> inline fbPoint4<T> operator+ (const fbVector& v,const fbPoint4<T>∗ p) // v + p = p{
return fbPoint(p→X + v.GetElement(0),p→Y + v.GetElement(1),p→Z + v.GetElement(2),p→W +v.GetElement(3));}
//! Ritorna un vettore come somma di 2 vettoriinline fbVector operator+ (const fbVector& a,const fbVector& b) // p + v = p{
return fbVector ( a.GetElement(0)+b.GetElement(0),a.GetElement(1)+b.GetElement(1));}
//! Ritorna un vettore dalla differenza tra 2 pti (p1-p2)inline fbVector operator- (const fbPoint& p1,const fbPoint& p2) // p - p = v{
return fbVector(p2,p1);
127
A. APPENDICE A
}
template <class T> inline fbVector operator- (const fbPoint2<T>& p1,const fbPoint2<T>& p2) // p - p = v{
return fbVector(p2,p1);}
template <class T> inline fbVector operator- (const fbPoint3<T>& p1,const fbPoint3<T>& p2) // p - p = v{
fbVector p(fbVector::FB VECTOR COLUMN,3);
p[0] = p2.X - p1.X;p[1] = p2.Y - p1.Y;p[2] = p2.Z - p1.Z;
return p;}
template <class T> inline fbVector operator- (const fbPoint4<T>& p1,const fbPoint4<T>& p2) // p - p = v{
fbVector p(fbVector::FB VECTOR COLUMN,4);
p[0] = p2.X - p1.X;p[1] = p2.Y - p1.Y;p[2] = p2.Z - p1.Z;p[3] = p2.W - p1.W;
return p;}
//! Ritorna un vettore dalla differenza tra 2 vettori (v2-v1)inline fbVector operator- (const fbVector& v1,const fbVector& v2) // v - v = v{
fbIntnMin = fbMin(v1.GetSize(),v2.GetSize());
fbVector p (fbVector::FB VECTOR COLUMN,3);
for(fbInt i = 0; i< nMin;i++)p[i] = v2.GetElement(i)-v1.GetElement(i);
return p;}
//! Ritorna un pto applicato al vettore ruotato di 180 gradi
template <class T> inline T operator- (const T& p,const fbVector& v) // p - v = p{
fbVector v1(v);return (p + (-v1));
}
//! Ritorna un pto applicato al vettore ruotato di 180 gradi
template <class T> inline T operator- (const fbVector& v,const T& p) // v - p = p{
fbVector v1(v);return (-T(p) + v1);
}
//! Rotazione di 90 gradiinline fbVector fbVectorRotate90(const fbVector& vToRotate){
return fbVector(-vToRotate.GetElement(1),vToRotate.GetElement(0));}
//! Rotazione di -90 gradiinline fbVector fbVectorRotateMinus90(const fbVector& vToRotate){
return fbVector(vToRotate.GetElement(1),-vToRotate.GetElement(0));}
#endif // FBMATH H//———————————————————// End Of File//———————————————————
A.1.22 fbOperation//———————————————————//// FILENAME : fbOperation.h//// CLASS(ES) ://// DESCRIPTION : Classe per la gestione degli algoritmi
128
A. APPENDICE A
// di suddivisioni di fbCurve//// HISTORY ://———————————————————#ifndef FB OPERATION H#define FB OPERATION H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbPoint.h
#include fbStraightLine.h
#include fbCircle.h
#include fbFunction.h
class fbOperation :public fbFunctionDouble{public:
fbOperation();virtual ∼fbOperation();virtual fbDouble operator ()(){
return 0;};virtual fbDouble operator ()(const fbDouble ,const fbDouble ,const fbDouble ,const fbDouble ,const
fbDouble ,const fbDouble ){
return 0;};
};
/∗!Classe per disegnare nell’algoritmo di suddivisione
∗/class fbCurveDraw :public fbOperation{public:
fbCurveDraw();virtual ∼fbCurveDraw();fbDouble operator ()( const fbDouble px1,const fbDouble py1,\
const fbDouble px2,const fbDouble py2,const fbDouble t0,const fbDouble t1);
};
/∗!Classe per effettuare le misurazioniil valore deve essere calcolato esternamente qua ci occupiamo di memorizzaregli incrementi successivi
∗/class fbCurveMeasure: public fbOperation{
fbDouble m dCurveLenght;
public:
fbCurveMeasure():m dCurveLenght(0.0)
{}
virtual ∼fbCurveMeasure(){}
inline fbDouble operator ()( const fbDouble px1,const fbDouble py1,\const fbDouble px2,const fbDouble py2,const fbDouble t0,const fbDouble t1)
{return m dCurveLenght+=fbPointsDistance(px1,py1,px2,py2);
};
inline fbDouble operator ()(){
return m dCurveLenght;};
};
class fbCurve;
/∗!Classe per effettuare la parametrizzazione uniforme
∗/template <class Container > class fbCurveSubdivideUniform: public fbOperation{
//! Tolleranza richista dall’algoritmofbDouble m dTolerance;
129
A. APPENDICE A
//! Passo richiestofbDouble m dStep;
//! Flag per il controllo dell’inserimento dell’ ultimo valorefbInt m nLastInterval;
//! Numero di intervalli previstifbInt m nIntervals;
//! Vettore dove salvare il parametroContainer& m Container;
//! Curva di cui devo effettuare la suddivisionefbCurve∗ m pCurve;
public:
//! CostruttorefbCurveSubdivideUniform(Container& Cont,const fbDouble dStep,const fbDouble dTolerance,fbCurve∗
pCurve):m Container (Cont ),m pCurve (pCurve)
{m dStep = fbAbs(dStep);m dTolerance = fbAbs(dTolerance);
}
virtual ∼fbCurveSubdivideUniform(){}
fbDouble operator ()(){
FB TRACE(Non capisco questo controllo: riguardalo\n);if( m dStep < m dTolerance )
return FB ERROR;
fbDoubledRatio = m pCurve→GetLenght();
if(dRatio <= m dStep)return 0;
dRatio /= m dStep;
// numero di intervalli da calcolarem nIntervals = (fbInt)floor(dRatio);
/∗FBErase :controllo inutile
// se c’e’ qualcosa di sbagliato ritornoif ( m nIntervals <= 0)
return FB ERROR;else∗/{
// In linea di principio non posso sapere// se l’ultimo valore deve essere inserito o meno, dipende dal// resto di questa divisione se e’ prossimo a zero allora// non lo inserisco altrimento si’ (il passo di divisione e’// fissato a piacere e quindi potrebbe non essere un multiplo// della lunghezza della linea)
if(fbAbs(dRatio - m nIntervals) < m dTolerance)m nLastInterval = 1;
elsem nLastInterval = 0;
FB TRACE(Posso migliorare questo codice calcolando prima la curva [0,t1]\n);FB TRACE(poi, da quella, calcolare [t0,t1]. Alla fine faccio \n);FB TRACE(la differenza tra la lunghezza di L1 e quella di L0\n);FB TRACE(in questo modo risparmio cicli di calcolo della lunghezza nelle fasi finali.\n);
// inizio la ricercarecursion(0.0,1.0);
}return m Container.size();
}
private:
/∗!Calcola in modo ricorsivo i parametri necessari per unaparametrizzazione uniforme
∗/void recursion(const fbDouble t0,const fbDouble t1){
if(t1-t0 < FB EPSILON)return;
// Calcolo le curve individuate dai due parametri correnti...fbCurve∗
130
A. APPENDICE A
pCurveLeft 0 = m pCurve→GetLeftArc(t0);fbCurve∗
pCurveLeft 1 = m pCurve→GetLeftArc(t1);
// ... e la loro lunghezzafbDouble
dL0 = pCurveLeft 0→GetLenght(),dL1 = pCurveLeft 1→GetLenght();
// calcolo il valore della lunghezza di parametrizzazionefbDouble
dCurrentStepLenght = (m Container.size() + 1)∗m dStep;
// se la lunghezza massima per questo intervallo e’ minore di quella// prevista per la parametrizzazione ritornoif(dL1 < dCurrentStepLenght){
delete pCurveLeft 0;delete pCurveLeft 1;return;
}
// se la lunghezza massima e’ minore di m dTolerance della lunghezza// di parametrizzazioneif ( dL1 - dCurrentStepLenght < m dTolerance){
delete pCurveLeft 0;delete pCurveLeft 1;
// aggiungo il valore del parametro corrente proporzionale alla lunghezza che// gli competeif( m Container.size () < (unsigned)(m nIntervals-m nLastInterval) ) // test di ultimo intervallo
(vedi sopra)m Container.push back( (t1 - t0) / (dL1 - dL0) ∗ (dCurrentStepLenght - dL0 ) + t0 );
}else // divisioni successive{
delete pCurveLeft 0;delete pCurveLeft 1;
fbDoubletm = (t1 + t0) ∗0.5;
recursion(t0,tm);recursion(tm,t1);
}}
};
/∗!Classe per effettuare la parametrizzazione PixelBased
∗/template <class Container > class fbCurveSubdividePixelSize: public fbOperation{
//! Vettore dove salvare il parametroContainer& m Container;
public:
//! CostruttorefbCurveSubdividePixelSize(Container& Cont)
:m Container (Cont ){}
virtual ∼fbCurveSubdividePixelSize(){}
fbDouble operator ()( const fbDouble px1,const fbDouble py1,const fbDouble px2,const fbDouble py2,const fbDouble t0 ,const fbDouble t1)
{if(t1 != 1.0)
m Container.push back(t1);
return m Container.size();}
};
/∗!Classe per calcolore il parametro che spetta ad una data lunghezzaAggiunto posizioni iniziali del parametro per accelerare la ricerca.
∗/class fbCurveSubdivideForLenght: public fbOperation{
//! Curva di cui devo effettuare la suddivisionefbCurve∗ m pCurve;
//! Tolleranza richista dall’algoritmofbDouble m dTolerance;
131
A. APPENDICE A
//! Passo richiestofbDouble m dStep;
//!Valore trovatofbDouble m dParameterValue;
//!Valori del parametro inizialifbDouble m dT0,m dT1;
public:
//! CostruttorefbCurveSubdivideForLenght( const fbDouble dStep,
const fbDouble dTolerance,fbCurve ∗pCurve,const fbDouble dT0=0.0,const fbDouble dT1=1.0);
virtual ∼fbCurveSubdivideForLenght();
fbDouble operator ()();
private:void recursion(const fbDouble t0,const fbDouble t1);
};
/∗!Classe per la gestione delle intersezioni con le rette∗/
template <class Container > class fbIntersectStraightLine : public fbOperation{
//! Container per i risultatiContainer &C;//! TolleranzafbDouble m dTolerance;//! Pti della rettafbPoint m p1,m p2;//! Riferimento alla curvafbCurve ∗m pCurve;
public:fbIntersectStraightLine(Container& Cont,const fbStraightLine& StraightLine,const fbDouble dTol, fbCurve∗
pCurve):C ( Cont ),m dTolerance ( dTol ),m pCurve ( pCurve )
{m p1 = StraightLine.GetPoint(0);m p2 = StraightLine.GetPoint(1);
}
virtual ∼fbIntersectStraightLine(){}
/∗!Non dovrebbero essere necessari controlli di consistenza dati(pto p1 == p2) perche’ sono svolti dalla routine di ricorsionedella fbCurve∗/
fbDouble operator ()( const fbDouble px1,const fbDouble py1,const fbDouble px2,const fbDouble py2,const fbDouble t0 ,const fbDouble t1)
{static fbDouble
beta,x,y;static fbCurve
∗pTempCurve 1;
static fbOperation∗pOperation;
switch(fbSegmentIntersection(px1,py1,px2,py2,\m p1.X,m p1.Y,\m p2.X,m p2.Y,x,y))
{case fbSegment::FB SEGMENT DO INTERSECT:
FB STUB();FB TRACE(Potenziale baco di divisione per 0\n);beta = fbPointsDistance( px1, py1, x , y ) / fbPointsDistance(px1, py1, px2, py2);beta = t0 + (t1-t0)∗beta;if ( fbPointsDistance( m pCurve→GetPoint(beta) , fbPoint( x, y ) ) < m dTolerance){
C.push back(beta);return C.size();
}else{
pTempCurve 1 = m pCurve→GetLeftArc( t0 );
132
A. APPENDICE A
pOperation = new fbCurveSubdivideForLenght( pTempCurve 1→GetLenght()+fbPointsDistance( fbPoint(px1,py1),\
fbPoint( x, y ) ),m dTolerance,m pCurve,t0,t1);
if((∗pOperation )() != FB ERROR )C.push back((∗pOperation )());
delete pOperation;delete pTempCurve 1;return C.size();
}
break;case fbSegment::FB SEGMENT COLLINEAR:
C.push back(t0 + (t1-t0)∗0.5);return C.size();break;
}
return C.size();}
};
/∗!Classe per la gestione delle intersezioni con le rette∗/
template <class Container > class fbIntersectCircle : public fbOperation{
//! Container per i risultatiContainer &C;//! TolleranzafbDouble m dTolerance;//! Pti del cerchiofbPoint m Center;//! Pti del cerchiofbDouble m dMetaRadius;
//! Riferimento alla curvafbCurve ∗m pCurve;
public:fbIntersectCircle(Container& Cont,const fbCircle& Circle,const fbDouble dTol, fbCurve∗ pCurve)
:C ( Cont ),m dTolerance ( dTol ),m pCurve ( pCurve )
{m Center = Circle.GetCenter();m dMetaRadius = Circle.GetRadius();m dMetaRadius ∗ = m dMetaRadius;
}
virtual ∼fbIntersectCircle(){}
/∗!Non dovrebbero essere necessari controlli di consistenza dati(pto p1 == p2) perche’ sono svolti dalla routine di ricorsionedella fbCurve∗/
fbDouble operator ()( const fbDouble px1,const fbDouble py1,const fbDouble px2,const fbDouble py2,const fbDouble t0 ,const fbDouble t1)
{static fbDouble
dMetaDistance1,dMetaDistance2;
dMetaDistance1 = fbAbs( px1 - m Center.X ) + fbAbs( py1 - m Center.Y ) ;dMetaDistance2 = fbAbs( px2 - m Center.X ) + fbAbs( py2 - m Center.Y ) ;
fbIntnNumberOfSolution = 0;
fbDoubledx = px2 - px1,dy = py2 - py1,a = dy,b = -dx,c = (py1 - m Center.Y) ∗ dx - (px1 - m Center.X) ∗ dy;
if( (dMetaDistance1 <= m dMetaRadius && m dMetaRadius <= dMetaDistance2 ) ||(dMetaDistance2 <= m dMetaRadius && m dMetaRadius <= dMetaDistance1 ) ||c∗c / (a∗a + b∗b ) <= m dMetaRadius )
{// se sono interno ha senso cercare intersezione// traslo la retta rispetto al centro e risolvo il polinomio
133
A. APPENDICE A
static fbDoubledSol 1,dSol 2;
static fbPointintersection;
if (a != 0.0 ){
fbDoublea1 = a ∗ a + b ∗ b,b1 = 2.0 ∗ b ∗ c,c1 = c ∗ c - m dMetaRadius ∗ a ∗ a ;
nNumberOfSolution = fbPoly2DegreeFindRoots(a1, b1, c1, dSol 1, dSol 2);
if( nNumberOfSolution == 0 )return C.size();
}else{
if(b != 0){
dSol 1 = sqrt(m dMetaRadius - (c∗c / b∗b));dSol 2 = -dSol 1;nNumberOfSolution = -1;
}else{
// sono paralleli e scelgo una via di mezzointersection = fbPointMiddle(fbPoint(px1,py1),fbPoint(px2,py2));nNumberOfSolution = 11;
}}
static fbCurve∗pTempCurve 1;
static fbOperation∗pOperation;
switch( nNumberOfSolution ){case 1: // due soluzioni reali e coincidenti
intersection.Y = dSol 1;intersection.X = -(b ∗ intersection.Y + c ) / a;
intersection.X += m Center.X;intersection.Y += m Center.Y;
if( ( py1 < intersection.Y && intersection.Y < py2 ) ||( py1 > intersection.Y && intersection.Y > py2 ) )
{pTempCurve 1 = m pCurve→GetLeftArc( t0 );
pOperation = new fbCurveSubdivideForLenght( pTempCurve 1→GetLenght()+fbPointsDistance( fbPoint(px1,py1), intersection ), m dTolerance, m pCurve);
if((∗pOperation )() != FB ERROR )C.push back((∗pOperation )());
delete pOperation;delete pTempCurve 1;
}break;
case -1:// soluzione lungo y
intersection.X = dSol 1;intersection.Y = -(a ∗ intersection.X + c ) / b;
intersection.X += m Center.X;intersection.Y += m Center.Y;
if( ( px1 < intersection.X && intersection.X < px2 ) ||( px1 > intersection.X && intersection.X > px2 ) )
{pTempCurve 1 = m pCurve→GetLeftArc( t0 );
pOperation = new fbCurveSubdivideForLenght( pTempCurve 1→GetLenght()+fbPointsDistance( fbPoint(px1,py1), intersection ), m dTolerance, m pCurve);
if((∗pOperation )() != FB ERROR )C.push back((∗pOperation )());
delete pOperation;delete pTempCurve 1;
}
intersection.X = dSol 2;intersection.Y = -(a ∗ intersection.X + c ) / b;
intersection.X += m Center.X;
134
A. APPENDICE A
intersection.Y += m Center.Y;if( ( px1 < intersection.X && intersection.X < px2 ) ||
( px1 > intersection.X && intersection.X > px2 ) ){
pTempCurve 1 = m pCurve→GetLeftArc( t0 );
pOperation = new fbCurveSubdivideForLenght( pTempCurve 1→GetLenght()+fbPointsDistance( fbPoint(px1,py1), intersection ), m dTolerance, m pCurve);
if((∗pOperation )() != FB ERROR )C.push back((∗pOperation )());
delete pOperation;delete pTempCurve 1;
}break;
case 2:// due soluzioni reali e distinte
intersection.Y = dSol 1;intersection.X = -(b ∗ intersection.Y + c ) / a;
intersection.X += m Center.X;intersection.Y += m Center.Y;
if( ( py1 < intersection.Y && intersection.Y < py2 ) ||( py1 > intersection.Y && intersection.Y > py2 ) )
{pTempCurve 1 = m pCurve→GetLeftArc( t0 );
pOperation = new fbCurveSubdivideForLenght( pTempCurve 1→GetLenght()+fbPointsDistance( fbPoint(px1,py1), intersection ), m dTolerance, m pCurve);
if((∗pOperation )() != FB ERROR )C.push back((∗pOperation )());
delete pOperation;delete pTempCurve 1;
}
intersection.Y = dSol 2;intersection.X = -(b ∗ intersection.Y + c ) / a;
intersection.X += m Center.X;intersection.Y += m Center.Y;
if( ( py1 < intersection.Y && intersection.Y < py2 ) ||( py1 > intersection.Y && intersection.Y > py2 ) )
{pTempCurve 1 = m pCurve→GetLeftArc( t0 );
pOperation = new fbCurveSubdivideForLenght( pTempCurve 1→GetLenght()+fbPointsDistance( fbPoint(px1,py1), intersection ), m dTolerance, m pCurve);
if((∗pOperation )() != FB ERROR )C.push back((∗pOperation )());
delete pOperation;delete pTempCurve 1;
}break;
case 11:// paralleleFB TRACE(Rette parallele\n);
pTempCurve 1 = m pCurve→GetLeftArc( t0 );
pOperation = new fbCurveSubdivideForLenght( pTempCurve 1→GetLenght()+fbPointsDistance( fbPoint(px1,py1), intersection ), m dTolerance, m pCurve);
if((∗pOperation )() != FB ERROR )C.push back((∗pOperation )());
delete pOperation;delete pTempCurve 1;
break;}
}return C.size();
}};
/∗!Calcola l’intervallo di lunghezza minimaN.B. il risultato e’ ottenuto dalla chiamata a funzione :res = (∗operation)() ;
∗/class fbCurveMinimumSegment : public fbOperation{
private:
135
A. APPENDICE A
//! Valore del minimofbDouble m dMinimumSegment;
//! Valori del parametro per cui si ha il minimofbDouble
m dT0,m dT1;
public:
fbCurveMinimumSegment();
virtual ∼fbCurveMinimumSegment();
/∗!Funzione usata nella suddivisione ricorsiva
∗/fbDouble operator ()( const fbDouble px1,const fbDouble py1,
const fbDouble px2,const fbDouble py2,const fbDouble t0 ,const fbDouble t1);
/∗!Ritorna il valore del minimo∗/
fbDouble operator ()();
/∗!Ritorna i valori del parametro per cui si consegue il minimo.∗/
void GetValuesOfParameters(fbDouble& dt0,fbDouble& dt1) const;
};
/∗!Calcola l’intervallo di lunghezza massimaN.B. il risultato e’ ottenuto dalla chiamata a funzione :res = (∗operation)() ;
∗/class fbCurveMaximumSegment : public fbOperation{private:
//! Valore del minimofbDouble m dMaximumSegment;
//! Valori del parametro per cui si ha il minimofbDouble
m dT0,m dT1;
public:
fbCurveMaximumSegment ();
virtual ∼fbCurveMaximumSegment ();
/∗!Funzione usata nella suddivisione ricorsiva
∗/fbDouble operator ()( const fbDouble px1,const fbDouble py1,
const fbDouble px2,const fbDouble py2,const fbDouble t0 ,const fbDouble t1);
/∗!Ritorna il valore del minimo∗/
fbDouble operator ()();
/∗!Ritorna i valori del parametro per cui si consegue il minimo.∗/
void GetValuesOfParameters(fbDouble& dt0,fbDouble& dt1) const;
};
/∗!Calcola la curva di grado minore
∗/void fbCurveReduction( fbCurve∗ pCurve, std::vector<fbPoint>& vPnts, fbDouble& dLeft,fbDouble& dRight);
template <class Container > class fbCurveDegreeReduction : public fbOperation{private:
136
A. APPENDICE A
//! Container per i risultatiContainer &C;
//! TolleranzafbDouble m dTolerance;
//! Riferimento alla curvafbCurve ∗m pCurve;
std::vector<fbPoint> m pTempVect;
fbDoubledMaxLeftError,dMaxRightError;
public:fbCurveDegreeReduction(Container& Cont,fbDouble dTol, fbCurve∗ pCurve)
:C ( Cont ),m dTolerance ( dTol ),m pCurve ( pCurve )
{};
virtual ∼fbCurveDegreeReduction(){};
/∗!Funzione usata nella suddivisione ricorsiva
∗/fbDouble operator ()( const fbDouble px1,const fbDouble py1,
const fbDouble px2,const fbDouble py2,const fbDouble t0 ,const fbDouble t1)
{if(t1-t0 < FB EPSILON)
return;
m pTempVect.clear();
fbCurveReduction(m pCurve, m pTempVect, dMaxLeftError,dMaxRightError);
if( dMaxLeftError <= m dTolerance && dMaxRightError <=m dTolerance ){
C.push back( (t0+t1)∗0.5 );return;
}};
/∗!Ritorna il valore del minimo∗/
fbDouble operator ()(){};
};
#endif // !defined( FB OPERATION H )//———————————————————// End Of File//———————————————————
A.1.23 fbPan//———————————————————//// FILENAME : fbPan.h//// CLASS(ES) : fbPan//// DESCRIPTION ://// HISTORY ://———————————————————#ifndef FB PAN H#define FB PAN H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbTool.h
#include fbPoint.h
#include fbFrame.h
#include <list>class fbFasped;class fbFrame;
//! fbPan Tool di gestione della traslazione/∗!
fbPan gestisce la traslazione del contesto grafico per fare
137
A. APPENDICE A
panoramica∗/class fbPan : public fbTool{
//! Stato del bottone (regola il disegno)fbBool
m bIsPressed;
//! Pti del disegnofbDouble
m pCursor[4];
//! Riferimento all’ ultima posizione validafbPoint
m OldPosition;public:
void Clear();
fbPan(fbFasped∗ pWin);
virtual ∼fbPan();
//! Gestione generica del movimento mousevoid HandleMouseMove (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y) ;
//! Gestione generica della pressione di un bottono mousevoid HandleMouse (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y) ;
void InitGL();void Draw();
};
#endif // !defined( FB PAN H )//———————————————————// End Of File//———————————————————
A.1.24 fbPencil//———————————————————//// FILENAME:fbPencil.h//// CLASS(ES)://// DESCRIPTION://// DATE : Fri Oct 6 2000// COPYRIGTH: (C) 2000 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————
#ifndef FB PENCIL H#define FB PENCIL H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbTool.h
#include fbStraightLine.h
#include fbCurve.h
#include fbCircle.h
#include <vector>
//! fbPencil/∗!
fbPencil : Classe che gestisce il disegno inserendo linee nel frame∗/class fbPencil : public fbTool{private:
//! Vettore di pti di descrizione per rappresentare un cursorefbDouble m pCursor[4];
//! Vettore contenente i pti acquisiti dal mouse movestd::vector<fbPoint> m vPointsSampled;
public:void Clear();fbPencil(fbFasped∗);virtual ∼fbPencil();
void HandleMouse (const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);void HandleMouseMove(const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);
//! Inizializzazione
138
A. APPENDICE A
/∗!Inizializza le componenti openGL del tool∗/
void InitGL();
void Draw();
};
#endif // !defined( FB PENCIL H )//———————————————————// End Of File//———————————————————
A.1.25 fbPen//———————————————————//// FILENAME:fbPen.h//// CLASS(ES)://// DESCRIPTION://// DATE : Fri Oct 6 2000// COPYRIGTH: (C) 2000 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————
#ifndef FB PEN H#define FB PEN H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbTool.h
#include fbStraightLine.h
#include fbCurve.h
#include fbCircle.h
#include <vector>
//! fbPen/∗!
fbPen : Classe che gestisce il disegno inserendo linee nel frame∗/class fbPen : public fbTool{private:
//! Flag per stabilire se sono alla prima selezionefbBool m bFirstPoint;
//! Vettore di pti di descrizione per rappresentare un cursorefbDouble m pCursor[4];
//! Segmenti per rappresentare le tangentifbSegment m PrevTangent, m NextTangent;
//! Curva fantasma per la manipolazione temporaneafbCurve m GhostCurve;
//! Cerchi di visualizzazione estremi (bilancini) e di chiusura lineafbCircle m Circle[3];
//! Pti di backup della posizione appena abbandonatafbPoint m pPrevPositions[2];
public:void Clear();fbPen(fbFasped∗);virtual ∼fbPen();
void HandleMouse (const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);void HandleMouseMove(const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);
//! Inizializzazione/∗!
Inizializza le componenti openGL del tool∗/
void InitGL();
void Draw();
};
#endif // !defined( FB PEN H )
139
A. APPENDICE A
//———————————————————// End Of File//———————————————————
A.1.26 fbPointManipulator//———————————————————//// FILENAME : fbPointManipulator.h//// CLASS(ES) :// fbPointManipulator// ListOfFFDInfoBackInsertIterator//// DESCRIPTION : Classe per la gestione del tool di manipolazione a forma di pto//// HISTORY : 14/09/2000//———————————————————#ifndef FB POINT MANIPULATOR H#define FB POINT MANIPULATOR H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbPoint.h
#include fbTool.h
#include fbConfig.h
#include fbCircle.h
#include fbFrame.h
#include fbPolyLine.h
#include fbPotential.h
#include <list>#include <vector>
/∗!fbPointManipulator : Tool che sfrutta gli algoritmi di FreeFornDeformation perfare una qualche manipolazione sul frame.
∗/class fbPointManipulator : public fbTool{
/∗!fbCurveInfoFFD per la freeform deformation carica la curva che ha intersezioni
con la regione di potenziale e i sottoarchi in cui e’ stata suddivisa∗/
struct fbCurveInfoFFD{
fbCurve∗ m pCurve;PolyLine m lCurves;fbCurveInfoFFD(fbCurve∗ pCurve,PolyLine& Curves)
:m pCurve(pCurve){
std::copy(Curves.begin(),Curves.end(),PolyLineBackInsertIterator<PolyLine>(m lCurves));}
};
//! Cerchietto a videofbCircle m Circle;
//! Copia del cerchietto a video per i calcoli successivifbCircle m CopyOfCircle;
//! Booleano per stabilire se deve essere variato la dimensione del cerhiofbBool m bMoveCircleRadius;
//! Booleano per stabilire se deve essere mosso il toolfbBool m bMoveTool;
//! Pto per il posizionamento preciso in presenza di selezione non perfettafbPoint m LocalOffset;
//! Flag per l’inseguimento del mousefbBool m bFollowMouse;
//! Lista di curve che sono interessate dall’intersezione con il manipolatorestd::list< fbCurveInfoFFD> m lSelectedCurves;
//! Copia della lista di curve che sono interessate dall’intersezione con il manipolatorestd::list< fbCurveInfoFFD> m lCopyOfSelectedCurves;
//! Flag per permettere la traslazione invece della funzione potenzialefbBool m bIsTranslable;
//! Funzione potenziale per il toolfbPotential∗ m PotentialFunction;
public:
140
A. APPENDICE A
void SubdivideCurves();
enum{
FB PNT MAN CURVES MATCH = 0,// indica dove e quando fare la suddivisioneFB PNT MAN SPLIT LEFT = 1,FB PNT MAN SPLIT RIGHT = 2
};
fbPointManipulator(fbFasped∗ pAppl);
virtual ∼fbPointManipulator();
// Gestione generica del movimento mousevirtual void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDouble
dPositionY) ;
// Gestione generica della pressione di un bottono mousevirtual void HandleMouse (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDouble
dPositionY) ;
void Draw();void InitGL();void Clear();
//! Sostiuisce le curve che sono state variatevoid ReplaceCurves();
//! Pulisce le strutture che ono servonovoid ClearLists();
//! Calcola le intersezioni con il toolvoid FindIntersection();
//! Classe per la gestione degli insert iteratorfriend class ListOfFFDInfoBackInsertIterator;
};
//———————————————————
/∗!ListOfFFDInfoBackInsertIterator si occupa della copia della lista delle curve selezionate
∗/class ListOfFFDInfoBackInsertIterator{
std::list<fbPointManipulator:: fbCurveInfoFFD>& container;public:
explicit ListOfFFDInfoBackInsertIterator(std::list<fbPointManipulator:: fbCurveInfoFFD>& Line):container(Line)
{};
ListOfFFDInfoBackInsertIterator& operator=(fbPointManipulator:: fbCurveInfoFFD& value ){
container.push back(fbPointManipulator:: fbCurveInfoFFD(newfbCurve(∗value.m pCurve),value.m lCurves));
return (∗this);};
ListOfFFDInfoBackInsertIterator& operator∗() {return (∗this); }ListOfFFDInfoBackInsertIterator& operator++() {return (∗this); }ListOfFFDInfoBackInsertIterator operator++(int) {return (∗this); }
};
#endif // !defined( FB POINT MANIPULATOR H )//———————————————————// End Of File//———————————————————
A.1.27 fbPoint//———————————————————//// FILENAME : fbPoint.h//// CLASS(ES) : fbPoint//// DESCRIPTION : Classe per la gestione di un pto//// HISTORY : 10/11/2000//———————————————————#ifndef FB POINT H#define FB POINT H
#include <cmath>#include <iostream>#include fbConfig.h
/∗!
141
A. APPENDICE A
fbPoint2 classe per la gestione di un pto bidimensionale∗/
template <class T> class fbPoint2{
public:T X,Y;
fbPoint2() :X(0) ,Y(0) {};fbPoint2(const fbPoint2& p) :X(p.X) ,Y(p.Y) {};fbPoint2(const T x,const T y) :X(x) ,Y(y) {};
virtual ∼fbPoint2(){};
fbBool operator==(const fbPoint2& p){
if(this→X == p.X && this→Y == p.Y)return true;
return false;}
#if FASPED MAJOR VERSION < 1//! Per compatibilita’inline void SetPosition(const T x,const T y){
X = x; Y= y;}
#endif
fbPoint2& operator∗ =(const T dVal){
X ∗ = dVal;Y ∗ = dVal;
return ∗this;}
fbPoint2 operator∗(const T dVal) const{
fbPoint2outVal;
outVal.X ∗ = dVal;outVal.Y ∗ = dVal;
return outVal;}
fbPoint2<T>& operator-(){
X = -X;Y = -Y;
return ∗this;}
};
/∗!fbPoint3 classe per la gestione di un pto trimidensionale / o bidimensionale omogeneo
∗/template <class T> class fbPoint3{
public:T X,Y,Z;fbPoint3() :X(0) ,Y(0) ,Z(0) {};fbPoint3(const fbPoint3& p) :X(p.X) ,Y(p.Y) ,Z(p.Z) {};fbPoint3(const T x,const T y,const T z) :X(x) ,Y(y) ,Z(z) {};virtual ∼fbPoint3(){};
fbBool operator==(const fbPoint3& p){
if(this→X == p.X && this→Y == p.Y && this→Z == p.Z )return true;
return false;}
fbPoint3& operator∗ =(const T dVal){
X ∗ = dVal;Y ∗ = dVal;Z ∗ = dVal;
return ∗this;}
fbPoint3 operator∗(const T dVal)
142
A. APPENDICE A
{fbPoint3
outVal;outVal.X ∗ = dVal;outVal.Y ∗ = dVal;outVal.Z ∗ = dVal;
return outVal;}
fbPoint3& operator-(){
X = -X;Y = -Y;Z = -Z;
return ∗this;}
};
/∗!fbPoint4 classe per la gestione di un pto quadridensionale / o tridimensionale omogeneo
∗/template <class T> class fbPoint4{
public:T X,Y,Z,W;
fbPoint4() :X(0) ,Y(0) ,Z(0) ,W(0) {};fbPoint4(const fbPoint3<T>& p) :X(p.X) ,Y(p.Y) ,Z(p.Z) ,W(p.W) {};fbPoint4(const T x,const T y,const T z,const T w) :X(x) ,Y(y) ,Z(z) ,W(w) {};virtual ∼fbPoint4(){};
fbBool operator==(const fbPoint4& p){
if(this→X == p.X && this→Y == p.Y && this→Z == p.Z && this→W == p.W)return true;
return false;}
fbPoint4& operator∗ =(const T dVal){
X ∗ = dVal;Y ∗ = dVal;Z ∗ = dVal;W ∗ = dVal;
return ∗this;}
fbPoint4 operator∗(const T dVal){
fbPoint4outVal;
outVal.X ∗ = dVal;outVal.Y ∗ = dVal;outVal.Z ∗ = dVal;outVal.W ∗ = dVal;
return outVal;}
fbPoint4& operator-(){
X = -X;Y = -Y;Z = -Z;W = -W;
return ∗this;}
};
//! Typedef per compatibilita’typedef fbPoint2<fbDouble> fbPoint;
//! Pto medio/∗inline fbPoint fbPointMiddle(const fbPoint& p1,const fbPoint& p2){
return fbPoint( (p1.X + p2.X)∗0.5 , (p1.Y+ p2.Y)∗0.5 );}∗/
template <class T> inline fbPoint2<T> fbPointMiddle(const fbPoint2<T>& p1,const fbPoint2<T>& p2){
143
A. APPENDICE A
return fbPoint2<T>( (p1.X + p2.X)∗0.5 , (p1.Y+ p2.Y)∗0.5 );}
template <class T> inline fbPoint3<T> fbPointMiddle(const fbPoint3<T>& p1,const fbPoint3<T>& p2){
return fbPoint3<T>( (p1.X + p2.X)∗0.5 , (p1.Y+ p2.Y)∗0.5 ,(p1.Z + p2.Z)∗0.5);}
template <class T> inline fbPoint4<T> fbPointMiddle(const fbPoint4<T>& p1,const fbPoint4<T>& p2){
return fbPoint4<T>( (p1.X + p2.X)∗0.5 , (p1.Y+ p2.Y)∗0.5 ,(p1.Z + p2.Z)∗0.5,(p1.W + p2.W)∗0.5);}
//! Distanza fra 2 ptitemplate <class T> inline T fbPointsDistance(const fbPoint2<T>& p1,const fbPoint2<T>& p2){
return sqrt( fbSqr(p2.X - p1.X) + fbSqr(p2.Y - p1.Y));}
template <class T> inline T fbPointsDistance(const fbPoint3<T>& p1,const fbPoint3<T>& p2){
return sqrt( fbSqr(p2.X - p1.X) + fbSqr(p2.Y - p1.Y) + fbSqr(p2.Z - p1.Z));}
template <class T> inline T fbPointsDistance(const fbPoint4<T>& p1,const fbPoint4<T>& p2){
return sqrt( fbSqr(p2.X - p1.X) + fbSqr(p2.Y - p1.Y) + fbSqr(p2.Z - p1.Z) + fbSqr(p2.W - p1.W));}
//! Distanza fra 2 ptitemplate <class T> inline T fbPointsDistance(const T x1,const T y1,const T x2,const T y2){
return sqrt( fbSqr(x2-x1) + fbSqr(y2-y1));}
template <class T> inline T fbPointsDistance(const T x1,const T y1,const T z1,const T x2,const T y2,const T z2){
return sqrt( fbSqr(x2-x1) + fbSqr(y2-y1) + fbSqr(z2-z1));}
//! Test di collinearita’ (p2-p1)∧(p3-p1) (ritorna true false e non tiene conto del segno)fbBool fbPointsAreCollinear(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3,const fbDoubledTolerance);
//! Test di collinearita’ (p2-p1)∧(p3-p1) (ritorna il valore di altezza (v1∧v2)[2] e tiene conto del segno)fbDouble fbPointsAreCollinear(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3);
/∗!Ritorna le coordinate baricentriche di 3 ptiSe i pti non sono collineari ritorna falsese c’e’ un errore a = b =FB DOUBLE MAX
∗/fbBool fbPointsGetBaycentricCoordinates(fbDouble& dAlpha,fbDouble& dBeta,const fbPoint& p1,constfbPoint& p2,const fbPoint& p3,const fbDouble dTolerance);
/∗!Calcola la coordinata baricentrica fra 2 pti fissati i valori alpha e beta
∗/inline fbPoint fbPointsSetBaycentricCoordinates(const fbDouble dAlpha,const fbDouble dBeta,const fbPoint&p1,const fbPoint& p2){
fbPoint outPoint;
outPoint.X = p1.X ∗ dAlpha + p2.X ∗ dBeta;outPoint.Y = p1.Y ∗ dAlpha + p2.Y ∗ dBeta;
return outPoint;}
const fbUInt FB POINT CROSS = 0;const fbUInt FB POINT CIRCLE = 1;const fbUInt FB POINT CIRCLE FULL = 2;const fbUInt FB POINT QUAD = 3;
void fbPointDraw(const fbPoint& p,\const fbDouble dSizeInPixel,\const fbDouble dTol,\const fbInt nType=FB POINT CIRCLE);
template <class T> std::ostream& operator�(std::ostream& os,const fbPoint2<T>& p){
os�p.X�’ ’�p.Y�endl;return os;
}
template <class T> std::ostream& operator�(std::ostream& os,const fbPoint2<T>∗ p){
os�p→X�’ ’�p→Y�endl;return os;
144
A. APPENDICE A
}
template <class T> std::ostream& operator�(std::ostream& os,const fbPoint3<T>& p){
os�p.X�’ ’�p.Y�’ ’�p.Z�endl;return os;
}
template <class T> std::ostream& operator�(std::ostream& os,const fbPoint3<T>∗ p){
os�p→X�’ ’�p→Y�’ ’�p→Z�endl;return os;
}
template <class T> std::ostream& operator�(std::ostream& os,const fbPoint4<T>& p){
os�p.X�’ ’�p.Y�’ ’�p.Z�’ ’�p.W�endl;return os;
}
template <class T> std::ostream& operator�(std::ostream& os,const fbPoint4<T>∗ p){
os�p→X�’ ’�p→Y�’ ’�p→Z�’ ’�p→W�endl;return os;
}
template <class T> std::istream& operator�(std::istream& is,fbPoint2<T>& p){
is�p.X�p.Y;return is;
}
template <class T> std::istream& operator�(std::istream& is,fbPoint2<T>∗ p){
is�p→X�p→Y;return is;
}
template <class T> std::istream& operator�(std::istream& is,fbPoint3<T>& p){
is�p.X�p.Y�p.Z;return is;
}
template <class T> std::istream& operator�(std::istream& is,fbPoint3<T>∗ p){
is�p→X�p→Y�p→Z;return is;
}
template <class T> std::istream& operator�(std::istream& is,fbPoint4<T>& p){
is�p.X�p.Y�p.Z�p.W;return is;
}
template <class T> std::istream& operator�(std::istream& is,fbPoint4<T>∗ p){
is�p→X�p→Y�p→Z�p→W;return is;
}
template <class T> inline fbPoint2<T> operator∗(const T dVal, const fbPoint2<T>& p){
return p.operator∗(dVal);}
template <class T> inline fbPoint3<T> operator∗(const T dVal, const fbPoint3<T>& p){
return p.operator∗(dVal);}
template <class T> inline fbPoint4<T> operator∗(const T dVal, const fbPoint4<T>& p){
return p.operator∗(dVal);}#endif // FB POINT H//———————————————————// End OF File//———————————————————
A.1.28 fbPolyLine//———————————————————//// FILENAME : fbPolyLine.h//// CLASS(ES) :
145
A. APPENDICE A
// fbPolyLine// fbFindCurveInPolyline// PolyLineBackInsertIterator//// DESCRIPTION : Classe per la gestione di una polyline//// HISTORY ://———————————————————#ifndef FB POLYLINE H#define FB POLYLINE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbGrOb.h
#include fbCurve.h
#include fbPoint.h
#include <list>#include <functional>
//———————————————————
//! Iteratori per la struttura list<fbCurve∗>typedef std::list<fbCurve∗> PolyLine;typedef PolyLine::iterator itPolyLine;typedef PolyLine::reverse iterator ritPolyLine;typedef PolyLine::const iterator citPolyLine;typedef PolyLine::const reverse iterator critPolyLine;
//! Funzione di Pulizia per una PolyLinevoid fbPolyLineClear(PolyLine& PLine);void fbPolyLineClear(PolyLine∗ PLine);
/∗!PolyLineBackInsertIterator Classe inseritrice copia una polyline copiando
nella nuova polyline tutte le curve presenti∗/
template <class Container> class PolyLineBackInsertIterator{
Container& container;public:
explicit PolyLineBackInsertIterator(Container& Line):container(Line)
{};
PolyLineBackInsertIterator& operator=(const fbCurve∗ value ){
container.push back(new fbCurve(∗value));return (∗this);
};
PolyLineBackInsertIterator& operator∗() {return (∗this); }PolyLineBackInsertIterator& operator++() {return (∗this); }PolyLineBackInsertIterator operator++(int) {return (∗this); }
};
//———————————————————
/∗!fbFindCurveInPolyline : Classe per la ricerca nella polyline
∗/class fbFindCurveInPolyline:public std::unary function<fbCurve∗,fbBool>{
fbPoint m PointToCheck;fbDouble m dPickRay;fbInt m nCheckArea;
public:enum{
FB FIND IN CONVEX HULL,FB FIND IN MIN MAX
};
explicit fbFindCurveInPolyline(const fbPoint& Point,const fbDouble dPickRay,const fbIntnCheckArea=FB FIND IN CONVEX HULL)
:m PointToCheck (Point),m dPickRay (dPickRay),m nCheckArea (nCheckArea)
{};
bool operator() (fbCurve∗ a) const{
if(!a→GetVisibility())return false;
if(m nCheckArea == FB FIND IN CONVEX HULL)if(a→CheckPointInConvexHull(m PointToCheck,m dPickRay))
return true;
146
A. APPENDICE A
if(m nCheckArea == FB FIND IN MIN MAX)if(a→CheckPointInMinMax(m PointToCheck,m dPickRay))
return true;
return false;};
};
//———————————————————
/∗!fbPolyLine : Classe per la gestione di una linea di curve∗/
class fbPolyLine : public fbGrOb{
//! Lista di curvePolyLine m lCurve;
//! t della curva selezionatafbDouble m dT;
//! t della curva selezionatafbDouble m dDistance;
//! tipo di curvafbInt m nPolylineType;
public:
//! Ritorna la lista delle polyline che sono interne al cerchiofbInt IntersectionWithCircle(fbCircle& Circle, std::list<fbPolyLine∗>& lPL);
//! Aggiunge un oggetto fbPolyLine alla linea correntevoid AddPolyLine(fbPolyLine∗ pPL);
//! Aggiunge un oggetto fbPolyLine alla linea correntevoid AddPolyLine(PolyLine∗ pPL);
//! inizializza prima del disegnovoid InitGL();
//! Valore del bottom left della minmaxboxfbPoint m MinMaxBottomLeft;
//! calcola la minmaxBoxvoid setMinMaxBox();
//! Ritorna un pto della polyline per valore dT del parametrofbPoint GetPoint(const fbDouble dT);
//! Metodo che restituisce true se la curva e’ nella polylineitPolyLine Select(const fbCurve∗ pCurve);
//!Ritorna un handler alla struttura delle lineePolyLine& GetPolyLine();
void AcceptVisitor(fbVisitor∗ pVisitor);
//! Riferimento alla curva che segue pCurve o NULL se l’ultima (se pCurve == 0 ritorna l’ultima)fbCurve∗ GetNext(fbCurve∗ pCurve) const;
//! Riferimento alla curva che precede pCurve o NULL se la prima (se pCurve == 0 ritorna la prima)fbCurve∗ GetPrev(fbCurve∗ pCurve) const;
//! Seleziona una curva con pto di controllo vicino a Point/∗! Seleziona la curva pCurveSelected il cui pto di controllo nPointOffset
sia a meno di dPickRay da PointN.B. dPickRay e’ assunto essere il quadrato del valore di raggio di pick
in tal modo le distanze sono controllate senza calcolare le radici∗/void SelectByControlPoint(const fbPoint& Point,const fbDouble dPickRay,fbInt&
nControlPoint,itPolyLine& itPLine);
/∗! Tipo unumerato per indicare il tipo∗ -Tipo di curve∗ -# FB POLYLINE CLOSED∗ l’ultima curva ha l’ultimo pto in comune con la prima∗ -# FB POLYLINE OPENED∗ l’ultima curva non ha collegamenti con la prima∗/enum{
FB POLYLINE CLOSED ,FB POLYLINE OPENED
};
fbPolyLine();
//!
147
A. APPENDICE A
fbPolyLine(const PolyLine& Line);
//!
//!fbPolyLine(const std::vector<fbPoint>& vPnts, const fbVector& ,const fbVector& ,const fbDouble
dTolerance);
virtual ∼fbPolyLine();
//! Costruttore di copiafbPolyLine(const fbPolyLine& Line);
//! Operatore di assegnazionefbPolyLine& operator=(const fbPolyLine& Line);
//! Seleziona una curvavoid Select(const fbPoint& Point,const fbDouble dPickRay,fbDouble& dTValue,fbCurve∗ &pOutCurve);
//! Seleziona la curva della polyline piu’ vicina a Pointvoid SelectByPoint(const fbPoint& Point,const fbDouble dPickRay,fbDouble& dTValue,itPolyLine&
itPLine);
//! Ritorna il tipo di curva (chiusa o aperta)fbInt GetType() const { return m nPolylineType;}
//! Ritorna il tipo di curva (chiusa o aperta)void SetType(const fbInt nType){
if(nType == FB POLYLINE CLOSED )m nPolylineType = FB POLYLINE CLOSED;
if(nType == FB POLYLINE OPENED )m nPolylineType = FB POLYLINE OPENED;
};
//! Disegna la polylinevoid Draw();
//! Elimina una curvavoid DeleteCurve(fbCurve∗ pCurve);
//! Aggiunge una curvainline void AddCurve(fbCurve∗ pCurve);
//! T della ultima curva selezionata -1 altrimentiinline fbDouble GetT() const;
//! Distanza del pto dall’ultima curva selezionata -1 altrimentiinline fbDouble GetDistance() const;
//! Elimina tutte le curve della listavoid DeleteAllCurves();
friend std::ostream& operator �(std::ostream &os, fbPolyLine ∗pPLine);friend std::ostream& operator �(std::ostream &os, fbPolyLine &pPLine);friend std::istream& operator �(std::istream &is, fbPolyLine ∗pPLine);friend std::istream& operator �(std::istream &is, fbPolyLine &pPLine);
};
/∗——————————————————-∗\|∗ Espansione inline per la classe fbPolyLine ∗||∗ BEGIN ∗|\∗——————————————————-∗/
inline void fbPolyLine::AddCurve(fbCurve∗ pCurve){
SetInitGL(false);m lCurve.push back(pCurve);
}
inline fbDouble fbPolyLine::GetT() const{
return m dT;}
inline fbDouble fbPolyLine::GetDistance() const{
return m dDistance;}
/∗——————————————————-∗\|∗ END ∗||∗ Espansione inline per la classe fbPolyLine ∗|\∗——————————————————-∗/
//———————————————————
148
A. APPENDICE A
/∗!Restituisce la curva precedente pCurve nella polyline o null
∗/fbCurve∗ fbPolyLineGetPrev(const PolyLine& PLine,fbCurve ∗pCurve);
/∗!Restituisce la curva seguente pCurve nella polyline o null
∗/fbCurve∗ fbPolyLineGetNext(const PolyLine& PLine,fbCurve ∗pCurve);
/∗!Converte una curva (a meno di tolleranza) nella rispettiva polyline
∗/void fbPolyLineConvertCurveToPolyLine(fbCurve∗ pCurve,const fbDouble dTol,PolyLine& PLine);
/∗!Converte una curva nella rispettiva polyline utilizzando i valori presenti in vIn
∗/void fbPolyLineConvertCurveToPolyLine(fbCurve∗ pCurve,const std::vector<fbDouble>& vIn,PolyLine& PLine);
// restituisce il pto della polyline che compete al valore di t selezionatofbPoint fbPolyLineGetPoint(const PolyLine& PLine, const fbDouble dT);
// restituisce l’iteratore della polyline che compete al valore di t selzionatoitPolyLine fbPolyLineTPositionToArcIterator(PolyLine& PLine,const fbDouble t);
//! Cerca di diminuire il numero di archi presenti nella polylinevoid fbPolyLineReduction(PolyLine& PLine,const fbDouble dTolerance);
#endif // !defined( FB POLYLINE H )
//———————————————————// End Of File//———————————————————
A.1.29 fbPotential//———————————————————//// FILENAME : fbPotential.h//// CLASS(ES) : fbPotential//// DESCRIPTION : Classe per la gestione della funzione potenziale da applicare// al tool manipulator//// HISTORY ://———————————————————#ifndef FB POTENTIAL H#define FB POTENTIAL H
#include fbConfig.h
#include fbFunction.h
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
/∗!fbPotential : classe astratta per la descrizione di una funzione potenziale
∗/class fbPotential : public fbFunction<fbDouble>{public:
fbPotential();virtual ∼fbPotential();virtual fbDouble GetPotential(const fbPoint& PointToTest) = 0;
};
/∗!fbPotentialConstant : classe per la gestione di un potenziale costante
∗/class fbPotentialConstant: public fbPotential{public:
fbPotentialConstant() {};virtual ∼fbPotentialConstant() {};
fbDouble GetPotential(const fbPoint& PointToTest){
return 1.0;}
fbDouble operator()(fbFactorsDouble& Factors){
return 1.0;}
149
A. APPENDICE A
};
/∗!fbPotentialSpherical : classe per la gestione del potenziale nel caso sferico
∗/class fbPotentialSpherical : public fbPotential{private:
fbDouble m dMetaSphereRadius;fbPoint m SphereCenter;
public:
fbPotentialSpherical(const fbDouble dRadiusForTest,const fbPoint SphereCenter);
virtual ∼fbPotentialSpherical(){};
fbDouble GetPotential(const fbPoint& PointToTest);
fbDouble operator()(fbFactorsDouble& Factors){
FB STUB();return 1.0;
}
};
#endif // !defined( FB POTENTIAL H )//———————————————————// End Of File//———————————————————
A.1.30 fbRectangle//———————————————————//// FILENAME : fbRectangle.h//// CLASS(ES) : fbRectangle//// DESCRIPTION : Classe per disegno di un rettangolo//// HISTORY : 23/08/2000//———————————————————
#ifndef FB RECTANGLE H#define FB RECTANGLE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbGrOb.h
#include fbPoint.h
#include fbConfig.h
#include fbPolyLine.h
class fbRectangle : public fbGrOb{protected:
fbPoint m BottomLeft;public:
fbPoint GetVertex(const fbInt nVertex) const;enum{
FB RECTANGLE V1 = 0,// bottom left |FB RECTANGLE V2 = 1,// anticlockwiseFB RECTANGLE V3 = 2,// up rightFB RECTANGLE V4 = 3
};void SetBarycentre(const fbPoint& Barycentre);fbPoint GetBarycentre() const;fbRectangle IntersectionWithRectangle(const fbRectangle&);fbBool CheckPointInRectangle(const fbPoint&) const;
const static fbInt fbRectangleMatch ;const static fbInt fbRectangleOverlap ;const static fbInt fbRectangleDisjoint ;
void Draw();fbRectangle();fbRectangle(const fbDouble dWidth, const fbDouble dHeight);fbRectangle(const fbPoint&,const fbDouble dWidth, const fbDouble dHeight);fbRectangle(const fbPoint&,const fbPoint&);virtual ∼fbRectangle();
150
A. APPENDICE A
/// Fissa il bottom left del rettangolo (le dimensioni sono fissate da SetSize)void SetBottomLeft(const fbPoint&);
/// Ritorna il bottom left del rettangolo (le dimensioni sono fissate da SetSize)fbPoint GetBottomLeft() const {return m BottomLeft;};
/// Funzione che controlla se 2 rettangoli si contengonofriend fbBool fbRectangleCheckOverlap(const fbRectangle&a,const fbRectangle&b);
};
void fbRectangleToPolyline(const fbRectangle& Rect,PolyLine& PLine);
#endif // !defined( FB RECTANGLE H )//———————————————————// End Of File//———————————————————
A.1.31 fbSaver//———————————————————//// FILENAME : fbSaver.h//// CLASS(ES) : fbSaver// DESCRIPTION : Classe per il salvataggio delle informazioni//// HISTORY ://———————————————————#ifndef FBSAVER H#define FBSAVER H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbConfig.h
#include fbRectangle.h
#include fbFrame.h
#include fbException.h
#include fbPoint.h
#include fbFasped.h
#include fbPolyLine.h
#include fbCurve.h
class fbSaver{public:
fbBool SaveFile(const fbChar∗ pFileName);fbSaver(fbFasped ∗);virtual ∼fbSaver();
private:fbBool SaveFSE(const fbChar ∗ pFileName);fbFasped ∗m pApplicationReference;
};
fbBool fbSaveFRP(const fbChar ∗pFileName, const std::vector<fbPoint>& vPnts );#endif // !defined( FBSAVER H )//———————————————————// End Of File//———————————————————
A.1.32 fbSelector//———————————————————//// FILENAME : fbSelector.h//// CLASS(ES) : fbSelector//// DESCRIPTION ://// HISTORY ://———————————————————#ifndef FB SELECTOR H#define FB SELECTOR H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbTool.h
#include fbRectangle.h
#include fbPolyLine.h
class fbSelector : public fbTool
151
A. APPENDICE A
{public:
void Draw();void HandleMouse (const fbUInt nButtonPressed, const fbDouble x, const fbDouble y);void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble y, const fbDouble x);void Clear();void InitGL();fbSelector(fbFasped∗ pAppl);virtual ∼fbSelector();
private:PolyLine m PLineFound;fbRectangle m BBox;
};
#endif // !defined( FB SELECTOR H )//———————————————————// End Of File//———————————————————
A.1.33 fbStraightLine//———————————————————//// FILENAME : fbStraightLine.h//// CLASS(ES) :// fbStraightLine// fbSegment// DESCRIPTION : Classi per la gestione di rette e segmenti//// HISTORY : 23/08/2000//———————————————————#ifndef FB SLINE H#define FB SLINE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbGrOb.h
#include fbPoint.h
#include fbConfig.h
#include fbMath.h
#include <vector>
/∗!fbStraightLine Classe per la gestione di una retta∗/
class fbStraightLine : public fbGrOb{protected:
//! Pti della retta fissano anche il verso di percorrenza vanno da//! m Point[0] -> m Point[1]fbPoint m Point[2];
//! Coeff dell’ equazione normalizzatafbDouble m da,m db,m dc;
//! Ordine dei pti/∗! Flag che comunica se l’ordine dei pti e’ cambiato rispetto a quesso specificato
-# true ordine 0 1-# false ordine 1 0
∗/fbBool m bOrder;
//! Radice quadrata de \f$ sqrt(a∧2+b∧2)\f$ utile nella misura della distanzafbDouble m dSqrtOfa2plusb2;
public:fbVector GetNormalVector() const;
/∗∗ Retta delle ascissse ∗/static fbStraightLine XAxis;/∗∗ Retta delle ascissse ∗/static fbStraightLine YAxis;
//! Enumeratore per recuperare i pti della retta \f$ (y-y 0)/(y 1-y 0) = (x-x 0)/(x 1-x 0) \f$enum{
FB SLINE P1 = 0,FB SLINE P2 = 1
};
fbStraightLine();fbStraightLine(const fbPoint&,const fbPoint&);virtual ∼fbStraightLine();
152
A. APPENDICE A
//! Ritorna la distanza del ptovirtual fbDouble DistanceFromPoint(const fbPoint&) const;
//! Ritorna la distanza del ptovirtual fbDouble DistanceFromPoint(const fbDouble px,const fbDouble py) const;
/∗! Calcola la retta normale alla corrente passante per il ptoscelto
∗/fbStraightLine NormalStraightLineInPoint(const fbPoint&) const;
void Draw();
//! Ritorna il coeff angolare della retta o FB DOUBLE MAX se m db = 0fbDouble GetAngle() const;
//! Restituisce unO dei pti passati per fissare la retta (0 -> il primo , 1 -> il secondo)fbPoint GetPoint(const fbInt nVal) const;
//! Restituisce i pti passati per fissare la rettavoid GetPoints(fbPoint& p1,fbPoint& p2 ) const;
//! Ritorna i coeff della rettavoid GetCoeff(fbDouble& a,fbDouble& b,fbDouble& c) const;
//! Fissa i coeff della retta e ricalcola i pti di descrizione della rettavoid SetCoeff(const fbDouble,const fbDouble,const fbDouble);
//! Fissa un pto di descrizione della retta (0 -> il primo, 1 -> il secondo)void SetPoint(const fbPoint&,const fbInt nVal);
//! Fissa i pti per la rettavoid SetPoints(const fbPoint&,const fbPoint&);
//! Fissa i pti per la rettavoid SetPoints(const fbDouble p1x,const fbDouble p1y,const fbDouble p2x,const fbDouble p2y);
void GetBarycentricCoords(const fbPoint& point,fbDouble& a,fbDouble& b) const;
//! Calcola il pto d’intesezione fra 2 rettefriend fbPoint fbStraightLineIntersection(const fbStraightLine& first,const fbStraightLine& second);friend void fbCircleIntersectionWithStraightLine(const fbCircle& Circle,const fbStraightLine
StraightLine,std::vector<fbPoint>& vecRes);};
/∗!fbSegment Classe per la gestione di un segmento∗/
class fbSegment : public fbStraightLine{public:
enum{
FB SEGMENT DONT INTERSECT = 0,FB SEGMENT DO INTERSECT = 1,FB SEGMENT COLLINEAR = 2
};fbSegment(const fbStraightLine&);fbDouble GetLenght() const;
fbSegment(){};fbSegment(const fbPoint&,const fbPoint&);virtual ∼fbSegment();void Draw();
//! Ritorna la distanza del pto con il check del rangefbDouble DistanceFromPoint(const fbPoint &p) const;
//! Controlla che il pto appartenga al segmentofbBool ChekPointInSegment(const fbPoint&) const;
/∗! Ritorna il tpo d’intersezione fra 2 segmenti o un pto non valido(MAX DOUBLE)se l’intersezione e’ vuota
∗/
friend fbPoint fbSegmentIntersection(fbSegment& first,fbSegment& second);};
fbInt fbSegmentIntersection(const fbDouble p1x,const fbDouble p1y,const fbDouble p2x,const fbDouble p2y,\const fbDouble p3x,const fbDouble p3y,const fbDouble p4x,const fbDouble
p4y,fbDouble& p,fbDouble& py);
inline fbInt fbSegmentIntersection(const fbPoint& P1,const fbPoint& P2,const fbPoint& P3,const fbPoint&P4,fbPoint& p){
return fbSegmentIntersection(P1.X,P1.Y,P2.X,P2.Y,P3.X,P3.Y,P4.X,P4.Y,p.X,p.Y);}
#endif // !defined( FB SLINE H )//———————————————————
153
A. APPENDICE A
//End Of File//———————————————————
A.1.34 fbSubSelector//———————————————————//// FILENAME : fbSubSelector.h//// CLASS(ES) : fbSubSelector//// DESCRIPTION : Tool per la gestione delle linee mediante i pti di controllo//// DATE : 11/11/2000// COPYRIGTH : (C) 2000 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————#ifndef FB SUBSELECTOR H#define FB SUBSELECTOR H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbTool.h
#include fbConfig.h
//! fbSubSelector: Gestisce i pti di controllo delle curve/∗!
Permette la manipolazione dei pti di controllo delle curve∗/
class fbSubSelector : public fbTool{private:
//! Puntatore alla curva individuata al pickfbCurve∗ m pCurveToModifyLeft;
//!Copia per le modifiche a videofbCurve∗ m pCopyOfCurveToModifyLeft;
//! Puntatore alla curva individuata al pickfbCurve∗ m pCurveToModifyRight;
//!Copia per le modifiche a videofbCurve∗ m pCopyOfCurveToModifyRight;
//! Pto selzionato tra gli Anchor PointsfbInt m nPnt;
//! Flag di collinearita’fbBool m bIsCollinear;
public:
//! Trova il tool piu’ vicino al ptofbInt FindAnchor(const fbPoint& Point);
fbSubSelector(fbFasped∗);
virtual ∼fbSubSelector();
void Draw();void InitGL();void Clear();void HandleMouseMove(const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);void HandleMouse(const fbUInt nButtonPressedAndStatus,const fbDouble x,const fbDouble y);
private:struct fbPointWithStatus{
fbPoint m Pnt;fbBool m bIsValid;
fbPointWithStatus():m Pnt (fbPoint(FB DOUBLE MAX,FB DOUBLE MAX)),m bIsValid (false){}
fbPointWithStatus(fbPoint Pnt, fbBool bIsValid):m Pnt (Pnt),m bIsValid (bIsValid){}
};
//! Anchor point a videofbPointWithStatus m pAnchorPoints[3];
};
154
A. APPENDICE A
#endif // !defined( FB SUBSELECTOR H )//———————————————————// End Of File//———————————————————
A.1.35 fbTool//———————————————————//// FILENAME : fbTool.h//// CLASS(ES) : fbTool//// DESCRIPTION : Classe per la gestione dei tool di disegno//// HISTORY :// Presa la decisione di lavorare anche con i tools in coordinate// mondo la trasformazione di coordinate avviene ad un livello// superiore (dalla finestra che gestisce il tool e che si preoccupa// di collocarlo)// 29/09/2000://———————————————————#ifndef FBTOOL H#define FBTOOL H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbGrOb.h
#include fbFasped.h
#include fbPoint.h
#include <list>
typedef std::list<fbTool∗> ToolList;typedef ToolList::iterator itToolList;typedef ToolList::const iterator citToolList;typedef ToolList::reverse iterator ritToolList;typedef ToolList::const reverse iterator critToolList;class fbFasped;
enum fbToolType{
FB TOOL UNKNOWN =-1,FB TOOL ARROW = 0,FB TOOL AUTO SWITCH = 1,FB TOOL BENDER = 2,FB TOOL CURSOR = 3,FB TOOL DRAWING PIN = 4,FB TOOL FRAME MANAGER = 5,FB TOOL GEO OBJ = 6,FB TOOL PAN = 7,FB TOOL PENCIL = 8,FB TOOL POINT MANIPULATOR = 9,FB TOOL SELECTOR =10,FB TOOL SUB SELECTOR =11,
};
//! fbTool: Classe per la gestione dei tool/∗!fbTool classe astratta per la gestione di un tool di disegnointeragisce con il riferimento al frame passatogli nel costruttore∗/class fbTool : public fbGrOb{protected:
//! Flag di selezione per modalita’ senza texturesbool m bIsTexturized;
//! Offset (punto di riferimento per il calcolo del vettore spostamento)fbPoint m Offset;
//! Punto corrente del toolfbPoint m BottomLeft;
//! Riferimento all’applicazionefbFasped∗ m pFasped;
//! Tipo di toolfbToolType m ToolType;
//! Lista di tool che rimangono a videostatic ToolList s lPermanentTool;
//! Riferimenti alle texturesfbUInt m pTextures[2];
//! Variabile per il conteggio del numero di clickstatic fbInt s nClickCounter;
155
A. APPENDICE A
public:
fbTool(fbFasped∗);
virtual ∼fbTool();
//! Ritorna il puntatore all’applicazione correntemente attivainline fbFasped∗ GetFasped();
//! Ritorna il frame attivo nelll finestra correnteinline fbFrame∗ GetFrame();
//! Ritorna la lista delinline ToolList& GetToolList();
//! Deve gestire il messaggio di clear che arriva da fbFaspedvirtual void Clear() =0;
//! Distribuisce il visitorvoid AcceptVisitor(fbVisitor∗ pVisitor);
//! Ritorna lo stato del flag di TexturefbBool GetTextureStatus() const;
//! Setta lo stato del flag di Texturevoid SetTextureStatus(const fbBool );
//! Funzione per la cancellazione del tool e della lista a videostatic void fbDeleteTool();
//! Gestione generica del movimento mousevirtual void HandleMouseMove (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDouble
dPositionY) ;
//! Gestione generica della pressione di un bottono mousevirtual void HandleMouse (const fbUInt nButtonPressed, const fbDouble dPositionX, const fbDouble
dPositionY) ;
//! Metodo per la sostituzione del tool a run timestatic void fbChangeTool(fbTool∗ newTool);
//! Metodo di accesso al toolstatic fbTool∗ fbGetTool(fbFasped∗ pApplication);
//! Metodo di Disegno dei Toolsstatic void fbDrawTool();
//! Metodo che restituisce il tipo di toolinline fbToolType GetToolType();
//! Metodo per prendere il pto bottom left del toolinline fbPoint& GetBottomLeft();
};
/∗——————————————————-∗\|∗ Espansione funzioni inline per la classe fbTool ∗||∗ BEGIN ∗|\∗——————————————————-∗/inline fbToolType fbTool::GetToolType(){
return m ToolType;}
inline fbFasped∗ fbTool::GetFasped(){
return m pFasped;}
inline fbFrame∗ fbTool::GetFrame(){
return (m pFasped == 0 ? 0 : m pFasped→GetFrame());}
inline ToolList& fbTool::GetToolList(){
return s lPermanentTool;}
inline fbPoint& fbTool::GetBottomLeft(){
return m BottomLeft;}/∗——————————————————-∗\|∗ END ∗||∗ Espansione funzioni inline per la classe fbTool ∗|\∗——————————————————-∗/#endif // !defined( FBTOOL H )//———————————————————// End Of File//———————————————————
156
A. APPENDICE A
A.1.36 fbTriangle//———————————————————//// FILENAME : fbTriangle.h//// CLASS(ES) : fbTriangle//// DESCRIPTION : Classe per disegno di un triangolo//// HISTORY : 08/09/2000//———————————————————#ifndef FB TRIANGLE H#define FB TRIANGLE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbGrOb.h
#include fbPoint.h
#include fbConfig.h
#include fbException.h
class fbTriangle : public fbGrOb{
/// Vertici del triangolofbPoint m Vertex[3];
public:fbPoint GetBarycentre() const;fbPoint GetVertex(const fbInt);void SetVertex(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3);fbTriangle();fbTriangle(const fbPoint∗p);fbTriangle(const fbPoint& p1,const fbPoint& p2,const fbPoint& p3);fbTriangle(const fbTriangle& Triangles);
virtual ∼fbTriangle();
/// Fissa i vertici del triangolo indicizzando con nVertexNumbervoid SetVertex(const fbInt nVertexNumber,const fbPoint& Vertex);void SetVertex(const fbPoint∗ pVertex);
/// Controlla l’appartenenza di un pto ad un triangolofbBool CheckPointInTriangle(const fbPoint&);
void Draw();private:
fbDouble area(const fbPoint&a,const fbPoint& b,const fbPoint& c);fbBool checkPointInSide(const fbInt& first,const fbInt& second,const fbInt& third,const fbPoint&);
};
//! Calcola un triangolo allargato di radius (per i test)fbTriangle fbTriangleSetEnlarged(const fbPoint &p1, const fbPoint &p2, const fbPoint &p3, const fbDouble&radiusOfPick) throw (fbRangeException);
#endif // !defined( FB TRIANGLE H )//———————————————————// End Of File//———————————————————
A.1.37 fbUtil//———————————————————//// FILENAME:fbUtil.h//// CLASS(ES)://// DESCRIPTION://// DATE : Fri Oct 6 2000// COPYRIGTH: (C) 2000 by Fabrizio Morciano// E MAIL : [email protected]////———————————————————#ifndef FB UTILITY#define FB UTILITY
#include fbPoint.h
#include fbCurve.h
#include fbPolyLine.h
#include <vector>
/∗! LoadTexture : Carica la texture (.rgb) del fine fnfalse->la texture e’ caricata nel vettore vect in posizione t numtrue ->errore
∗/bool LoadTexture(const char ∗fn,unsigned int∗ vect,int t num);
157
A. APPENDICE A
//! Testa il puntatore lo elimina e alloca nuova memoriatemplate <class T> inline T∗ fbMemoryTestClearAndAlloc(T∗ &pPointerToEraseAndAlloc){
if(pPointerToEraseAndAlloc)delete pPointerToEraseAndAlloc;
return (pPointerToEraseAndAlloc = new T);}
//! Testa il puntatore lo elimina e resetta il valoretemplate <class T> inline T∗ fbMemoryTestClearAndReset(T∗ &pPointerToEraseAndReset){
if(pPointerToEraseAndReset)delete pPointerToEraseAndReset;
return (pPointerToEraseAndReset = 0);}
//! Testa il puntatore se valido ne esegue il metodotemplate <class T> inline void fbMemoryTestAndDoMethod(T∗ pPointer,void (T::∗Method)()){
if(pPointer)(pPointer→ ∗Method)();
}
//! Testa il puntatore se valido esegue una funzionetemplate <class T,class Function> inline void fbMemoryTestAndDoFunction(T∗ pPointer,Function Method){
if(pPointer)Method(pPointer);
}
//! Testa il container e pulisce i campi puntatoretemplate <class Container> void fbMemoryTestAndClearContainer( Container& container){
if(container.empty()){
typename Container::iterator tempIt;for(tempIt = container.begin();tempIt != container.end() ;tempIt++)
delete ∗tempIt;}container.clear();
}
//! Mostra il contenuto di un contenitore/∗
In Win32 puo non funzionare per strutturecomplesse in cui e presente una classe conoutput ridefinito (liste di fbPoint ad esempio)
∗/template <class T> void fbContainerPrint(const T& C){#ifdef FB MFC PROJECT
FB TRACE(Container size: %d\n,C.size());#else
std::cout�Container size: �C.size()�std::endl;#endif
typename T::const iterator citC;fbUInt i=0;for(citC = C.begin();citC != C.end(); citC++){
#ifdef FB MFC PROJECTFB TRACE([%d] %f\n,i++,∗citC);
#else
std::cout�’[’�i++�] �∗citC;#endif
}}
/∗!Suddivide un container in due parti uguali
∗/template <class T> void fbContainerSplit(const T& vIn, T& vOut 1, T& vOut 2){
typename T::const iteratorcitC = vIn.begin();
fbIntnMiddle = (fbInt)((fbFloat)vIn.size() ∗ 0.5);
advance (citC, nMiddle);
copy( vIn.begin(), citC , back inserter(vOut 1) );
copy( citC , vIn.end(), back inserter(vOut 2) );}
/∗!Suddivide un container in due parti in funzione dell’indice nIndex
∗/
158
A. APPENDICE A
template <class T> void fbContainerSplit(const T& vIn, const fbInt nIndex, T& vOut 1, T& vOut 2){
typename T::const iteratorcitC = vIn.begin();
advance (citC, nIndex);
copy( vIn.begin(), citC , back inserter(vOut 1) );
copy( citC , vIn.end(), back inserter(vOut 2) );}
/∗! Calcola il polinomio di quinto grado che risolve il problemadi trovare il valore del parametro tx di pCurve piu’ vicino
a PointToCheckBibl.
Graphics Geems Vol I pag 607Solving the nearest-point-on-curve problemAuthor(s) Philip J.Schneider
∗/std::vector<fbPoint> fbConvertToBezierForm(const fbPoint& PointToCheck,fbCurve∗ pCurve);
/∗!Risolve le radici del polinomio di quinto grado generato da fbConvertToBezierFormper la soluzione del problema del pto piu’ vicino ad una cubica
∗/fbInt PolyFifthFindRoots (const std::vector<fbPoint>& w,std::vector<fbDouble>& t, fbInt nDepth);
void fbBSplineComputeControlPoints( const std::vector<fbPoint>& vIn ,\std::vector<fbPoint>& vOut ,\std::vector<fbDouble>& vKnots );
void fbBSplineConvertInCurves(const std::vector<fbPoint>& vBSpline,\const std::vector<fbDouble>& vKnots ,\
std::vector<fbPoint>& vCtrlPntsOfCurves);
fbInt fbSplitPointsForCurvature(const std::vector<fbPoint>& vPnts,\std::vector< std::vector<fbPoint>∗ >& vOut);
/∗Calcola il valore di Alpha da utilizzare nella suddivisione
∗/fbDouble GetAlpha(const std::vector<fbPoint>& vLeft,const std::vector<fbPoint>& vRight);
/∗Filtraggio dei punti troppo vicini
∗/void fbFilterPoints(const std::vector<fbPoint>& vPnts,std::vector<fbPoint>& vPntsFiltered, const fbDoubledThreshold);
#endif
//———————————————————// End Of File//———————————————————
A.1.38 fbVisitAndChangeColor//———————————————————//// FILENAME : fbVisitAndChangeCOLOR.h//// CLASS(ES) : fbVisitAndChangeCOLOR//// DESCRIPTION : classe visitatrice che si preoccupa di cambiare// il colore//// HISTORY : 11/08/2000//———————————————————#ifndef FB VISIT AND CHANGE COLOR H#define FB VISIT AND CHANGE COLOR H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbVisitor.h
#include fbConfig.h
/∗!fbVisitAndChangeColor Classe visistatrice per la gestione del colorein un oggetto.
∗/class fbVisitAndChangeColor:public fbVisitor{
fbFloat m dR; //! Valore di RossofbFloat m dG; //! Valore di VerdefbFloat m dB; //! Valore di BlufbFloat m dA; //! Valore AlphafbInt m nTypes; //! A quale stato appartengono i colori
159
A. APPENDICE A
public:
/∗!Costruttore
∗/fbVisitAndChangeColor(fbFloat r,fbFloat g,fbFloat b,fbFloat a,fbInt nType=fbGrOb::FB COLOR ACTIVE)
:m dR(r),m dG(g),m dB(b),m dA(a),m nTypes(nType)
{};
/∗!Costruttore∗/
fbVisitAndChangeColor(const fbFloat∗ color,fbInt nType=fbGrOb::FB COLOR ACTIVE):m dR(color[0]),m dG(color[1]),m dB(color[2]),m dA(color[3]),m nTypes(nType)
{}
/∗!Permette di cambiare il colore allo stato∗/
void SetColor(const fbFloat∗ color,fbInt nType){
m dR =color[0]; m dG=color[1];m dB= color[2]; m dA=color[3];m nTypes = nType;
}
void ActionInGrOb(fbGrOb∗ pObj){
pObj→SetColor(m dR,m dG,m dB,m dA,m nTypes);}
};
#endif // !defined( FB VISIT AND CHANGE COLOR H )//———————————————————// End Of File//———————————————————
A.1.39 fbVisitAndChangeDesignAspect//———————————————————//// FILENAME : fbVisitAndChangeDesignAspect.h//// CLASS(ES) : fbVisitAndChangeDesignAspect//// DESCRIPTION : classe visitatrice che si preoccupa di cambiare// l’aspetto di visualizzazione degli oggetti// (aggiunge la visualizzazione dei pti di controllo etc)//// HISTORY : 11/08/2000//———————————————————#ifndef FB VISIT AND CHANGE DESIGN ASPECT H#define FB VISIT AND CHANGE DESIGN ASPECT H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000#include fbVisitor.h
#include fbConfig.h
/∗!fbVisitAndChangeDesignAspect :classe visitatrice che si preoccupa di cambiare
l’aspetto di visualizzazione degli oggetti(aggiunge la visualizzazione dei pti di controllo etc)
∗/class fbVisitAndChangeDesignAspect : public fbVisitor{
fbLong m nType;
public:fbVisitAndChangeDesignAspect(const fbLong nType)
:m nType(nType){}
virtual ∼fbVisitAndChangeDesignAspect(){}
//! Cambia le proprieta’ delle curvevoid ActionInCurve(fbCurve∗ pCurve){
pCurve→SetObjectType(m nType);
160
A. APPENDICE A
}
};
#endif // !defined( FB VISIT AND CHANGE DESIGN ASPECT H )//———————————————————// End Of File//———————————————————
A.1.40 fbVisitAndChangeTolerance//———————————————————//// FILENAME : fbVisitAndChangeTolerance.h//// CLASS(ES) : fbVisitAndChangeTolerance//// DESCRIPTION : classe visitatrice che si preoccupa di cambiare// la tolleranza di visualizzazione degli elementi//// HISTORY : 11/08/2000//———————————————————#ifndef FB VISIT AND CHANGE TOLERANCE H#define FB VISIT AND CHANGE TOLERANCE H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000#include fbVisitor.h
#include fbConfig.h
#include fbGrOb.h
class fbVisitAndChangeTolerance : public fbVisitor{
fbDouble m dTol;
public:fbVisitAndChangeTolerance(const fbDouble dVal=FB TOLERANCE)
:m dTol(dVal){}
virtual ∼fbVisitAndChangeTolerance(){}
void ActionInGrOb(fbGrOb∗ pObj){
// fissa la tolleranzapObj→SetTolerance(m dTol);// ed indica che l’oggetto deve essere rinizializzatopObj→SetInitGL(false);
}};
#endif // !defined( FB VISIT AND CHANGE TOLERANCE H )//———————————————————// End Of File//———————————————————
A.1.41 fbVisitAndChangeVisibility//———————————————————//// FILENAME : fbVisitAndChangeVisibility.h//// CLASS(ES) : fbVisitAndChangeVisibility//// DESCRIPTION : classe visitatrice che si preoccupa di cambiare// la visibilita’ se fuori schermo//// HISTORY : 11/11/2000//———————————————————#ifndef FB VISIT AND CHANGE VISIBILITY H#define FB VISIT AND CHANGE VISIBILITY H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbVisitor.h
#include fbConfig.h
#include fbRectangle.h
/∗!fbVisitAndChangeVisibility classe visitatrice che si preoccupa di cambiare
la visibilita’ se fuori schermo∗/
class fbVisitAndChangeVisibility : public fbVisitor{
161
A. APPENDICE A
private://! BBox della World WindowfbRectangle m BBox;
public:fbVisitAndChangeVisibility (const fbRectangle& BBox)
:m BBox(BBox){}virtual ∼fbVisitAndChangeVisibility (){}
//! Cambia la visibilita’ della curva se il MinMax e’ completamente fuori dallo schermovoid ActionInCurve(fbCurve∗ pCurve){
if(fbRectangleCheckOverlap(pCurve→GetMinMaxBox(),m BBox))pCurve→SetVisibility(true);
elsepCurve→SetVisibility(false);
}};#endif//#ifdef FB VISIT AND CHANGE VISIBILITY H//———————————————————// End Of File//———————————————————
A.1.42 fbVisitor//———————————————————//// FILENAME : fbVisitor.h//// CLASS(ES) : fbVisitor//// DESCRIPTION : classe di gestione delle azioni da fare nei nodi// della lista//// HISTORY : 11/08/2000//———————————————————#ifndef FB VISITOR H#define FB VISITOR H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
class fbGrOb;class fbFrame;class fbCurve;class fbStraightLine;class fbSegment;class fbWindow;class fbBSpline;class fbArc;class fbCircle;class fbTool;class fbPolyLine;
class fbVisitor{public:
fbVisitor();virtual ∼fbVisitor();
/// Metodo per l’azione su un generico oggetto graficovirtual void ActionInGrOb(fbGrOb∗){};
/// Metodo per l’azione su una curvavirtual void ActionInCurve(fbCurve∗){};
/// Metodo per l’azione su un framevirtual void ActionInFrame(fbFrame∗){};
/// Metodo per l’azione su una rettavirtual void ActionInStraightLine(fbStraightLine∗){};
/// Metodo per l’azione su un segmentovirtual void ActionInSegment(fbSegment∗){};
/// Metodo per l’azione su una windowvirtual void ActionInWindow(fbWindow∗){};
/// Metodo per l’azione su una BSplinevirtual void ActionInBSpline(fbBSpline∗){};
/// Metodo per l’azione su un cerchiovirtual void ActionInCircle(fbCircle∗){};
/// Metodo per l’azione su un tool
162
A. APPENDICE A
virtual void ActionInTool(fbTool∗){};
/// Metodo per l’azione su una polylinevirtual void ActionInPolyLine(fbPolyLine∗){};
};
#endif // !defined( FB VISITOR H )//———————————————————// End Of File//———————————————————
A.1.43 fbWindow//———————————————————//// FILENAME : fbWindow.h//// CLASS(ES) : fbWindow//// DESCRIPTION : Classe per la gestione di una finestra//// HISTORY ://———————————————————#ifndef FB WINDOW H#define FB WINDOW H
#if MSC VER > 1000#pragma once
#endif // MSC VER > 1000
#include fbRectangle.h
#include fbGrOb.h
#include fbConfig.h
#include fbPoint.h
/∗! fbWindow : Classe che descrive una finestra, contiene i metodinecessari per le trasformazioni di coordinateda finestra a mondo ed i metodi per la gestionedegli eventi
∗/class fbWindow : public fbGrOb{private:
//! permette di scalare le coordinate a valori opportunifbDouble m dScaleFactor;
//! dimensione del pixel della finestrafbDouble m dPixelSize;
//! Centro (in coordinate mondo) della finestrafbPoint m Center;
//! Valore della lettera del tasto premutofbUInt m nKeyPressed;
//! Valore del modificatore da tastierafbUInt m nKeyModifyer;
public:
fbWindow();virtual ∼fbWindow();
//! Conversione coordinate mondo schermo (rasterizza)fbPoint WorldToScreen(const double dX, const double dY) const;
//! Conversione coordinate schermo mondofbPoint ScreenToWorld(const fbInt x,const fbInt y) const;
//!Metodo che ritorna il centro della finestra in coordinate mondoinline const fbPoint& GetCenterOfWindow() const;
//!Metodo che fissa il centro della finestra in coordinate mondoinline void SetCenterOfWindow(const fbPoint&);
//!Metodo che ritorna la dimensione del pixel della finestrainline fbDouble GetPixelSize() const;
//!Metodo che fissa la dimensione del pixel della finestrainline void SetPixelSize(const fbDouble);
//1 Ritorna la BBox a World della finestra (la tolleranza della bbox e quella di disegno)fbRectangle GetWorldBBox() const;
//!void Draw(){};
//! Fissa il fattore di scala (da ricontrollare)
163
A. APPENDICE A
inline void SetScaleFactor(const fbDouble& dScaleFactor);
virtual void OnResize(const fbInt nW,const fbInt nH) {};virtual void OnLMouseMove (const fbInt,const fbInt){}virtual void OnLMouseDown (const fbInt,const fbInt){}virtual void OnLMouseUp (const fbInt,const fbInt){}virtual void OnRMouseMove (const fbInt,const fbInt){}virtual void OnRMouseDown (const fbInt,const fbInt){}virtual void OnRMouseUp (const fbInt,const fbInt){}virtual void OnMouseMove (const fbInt,const fbInt){}
// Eventi di tastiera// Contiene anche il campo con l’ultima posizione del mousevirtual void OnKeyUp (const fbChar , const fbUInt,const fbInt,const fbInt){}virtual void OnKeyDown (const fbChar , const fbUInt,const fbInt,const fbInt){}virtual void OnSpecialKeyUp (const fbChar , const fbUInt,const fbInt,const fbInt){}virtual void OnSpecialKeyDown (const fbChar , const fbUInt,const fbInt,const fbInt){}
//! Valore dell’ultimo tasto premutoinline fbUInt GetKeyPressed() const;
//! Valore dell’ultimo modificatore premutoinline fbUInt GetKeyModifyer() const;
//! Fissa il valore dell’ultimo tasto premutoinline void SetKeyPressed(const fbUInt nKeyPressed);
//! Fissa il valore dell’ultimo modificatore premutoinline void SetKeyModifyer(const fbUInt nKeyModifyer);
//! Metodo per la manipolazione mediante visitorvoid AcceptVisitor(fbVisitor∗);
};
/∗———————————————————∗\|∗ Espansione inline per la class fbWindow ∗||∗ BEGIN ∗|\∗———————————————————∗/
inline const fbPoint& fbWindow::GetCenterOfWindow() const{
return m Center;}
inline fbUInt fbWindow::GetKeyPressed() const{
return m nKeyPressed;}
inline fbUInt fbWindow::GetKeyModifyer() const{
return m nKeyModifyer;}
inline void fbWindow::SetKeyPressed(const fbUInt nKeyPressed){
m nKeyPressed = nKeyPressed;}
inline void fbWindow::SetKeyModifyer(const fbUInt nKeyModifyer){
m nKeyModifyer = nKeyModifyer;}
inline void fbWindow::SetPixelSize(const fbDouble dVal)
{m dPixelSize=dVal;
}
inline fbDouble fbWindow::GetPixelSize() const{
return m dPixelSize;}
inline void fbWindow::SetCenterOfWindow(const fbPoint &Point){
m Center=Point;OnResize((fbInt)W(),(fbInt)H());
}
inline void fbWindow::SetScaleFactor(const fbDouble &dScaleFactor){
m dScaleFactor = dScaleFactor;}
/∗———————————————————∗\|∗ END ∗||∗ Espansione inline per la class fbWindow ∗|\∗———————————————————∗/
164
A. APPENDICE A
#endif // !defined( FB WINDOW H )//———————————————————// End Of File//———————————————————
165
Bibliografia
[1] James Foley Andries van Dam Steven K.Feiner John F.Hughes.
Computer Graphics. Addison Wesley, second edition, 1993.
[2] Erik Bizouarn and Jean-Daniel Fekete. Tictactoon: A paper-
less system for professional 2d animation. Computer Graphics
Proceedings.
[3] Gianluca Natale. Riconoscimento di aree chiuse in immagini
vettoriali. PhD thesis, Universta degli studi l’Aquila, 4 2000.
[4] Macromedia. Flash 4.0, 1999.
[5] Supoj Sutanthavibul. Xfig 3.2.3, 1999.
[6] Adam Finkelstein David H.Salesin. Multiresolution curves.
Computer Graphics Proceedings, 1:261–268, 1994.
[7] Thomas W.Sederberg Eugene GreenWood. A physically based
approach to 2-d shape blending. Computer Graphics, 26:25–34, 1992.
[8] Yu-Kuang Chang and Alyn P.Rockwood. A generalized de Ca-
steljau approach to 3d free-form deformation. Computer Graphics
Proceedings, 1:257–260, 1994.
[9] A.R. Forrest. The twisted cubic curve: A computer-aided geometric
design. Computer Aided Design, 12:165–172, 1980.
166
BIBLIOGRAFIA BIBLIOGRAFIA
[10] Gerald Farin. Curves an Surface for Computer-Aided Geometric
Design. Academic Press, 1994.
[11] P.Schneider. An algoritm for automatically fitting digitized curves.
Graphic Gems, pages 612–626, 1990.
[12] Koichi Itoh and Yoshio Ohno. A curve fitting algorihm for character
font. Electronic Publishing, 6:195–205, 1993.
[13] http://www.sgi.com/technology/stl/. Visitato il: 29/06/2001.
[14] http://www.opengl.org. Visitato il: 29/06/2001.
[15] Microsoft. http://www.microsoft.com. Visitato il: 29/06/2001.
[16] Trolltech. http://www.trolltech.com/. Visitato il: 29/06/2001.
[17] Margaret A.Ellis Bjarne Stroustrup. The Annotated C++ Reference
Manual. Addison Wesley, 1997.
[18] Barbara Robertson. Digital toons. Computer Graphics World, pages
40–46, 1994.
[19] Xiaogang Jin Youfu Li Qunsheng Pen. General constrained de-
formation base on generalized metaballs. Computer and Graphics,
24:219–231, 2000.
[20] Erich Gamma Richard Helm Ralph Johnson John Vlissides. Design
Patterns. Addison Wesley, first edition, 1995. Elements of Reusable
Object-Oriented Software.
[21] Bjarne Stroustrup. The C++ programming language. Addison
Wesley, third edition, 1997.
[22] Josie Wernecke. The Inventor Mentor. Addison Wesley, 1994. Pro-
gramming Object-Oriented 3D Graphics with Open InventorTM ,
Release 2.
167
BIBLIOGRAFIA BIBLIOGRAFIA
[23] Mark Willey. libstroke. http://www.etla.net/∼willey/projects/libstroke/.
Visitato il: 29/06/2001.
168