SlideShare a Scribd company logo
1 of 15
Download to read offline
bSpline(), bSplineCalc(float u), bSplineN()................................................................12
       bSplineAlloc()................................................................................... .........................13
       scale().......................................................................................... .............................13
       Altri metodi.............................................................................. .................................13
    Struttura dell'albero di scena........................................................................................14




2
Sommario
Il progetto sviluppato consiste in un semplice editor per curve cubiche scritto in C++,
avvalendosi delle librerie grafiche Coin3D. L'editor permette di aggiungere, modificare,
copiare e cancellare curve di tipo Hermite, Spline, B-Spline, Bezier. E' inoltre possibile
definire un piano di mirroring per creare delle curve speculari. Il numero di punti di
controllo, per le curve di tipo Spline, B-Spline e Bezier, non è predeterminato.


Guida all'uso del programma

Utilizzo dell'handle principale
L'handle principale si compone di 3 parallelepipedi. L'utente può agire su ognuno di essi,
una volta che viene attivata la funzionalità associata mediante il tasto “t”. Il movimento
del mouse su un parallelepipedo coincide con uno spostamento lungo l'asse specifico, sia
dell'handle che di tutto ciò che è selezionato in quel momento.
Quando la traslazione è disattivata, è possibile inserire dei nuovi punti per la definizione
di una nuova curva o per la definizione di un piano per il mirroring premendo il tasto
destro mentre il cursore si trova sull'handle. Il nuovo punto inserito ha le coordinate
corrispondenti al centro dell'handle stesso.

Creazione di una curva
La creazione di una curva consiste nell'inseriremento di un numero sufficiente di punti di
controllo. Prima di completare l'operazione di inserimento (tramite la pressione del tasto
“e”), è possibile cambiare il tipo di curva che verrà calcolata. Il tipo di default è Hermite.
Le scelte possibili sono:
    ●   “h” per Hermite
    ●   “p” per B-Spline
    ●   “s” per Spline
    ●   “b” per Bezier
Una volta che il numero di punti di controllo è sufficiente al calcolo di una curva, questa
viene disegnata. Ogni successivo inserimento di un punto di controllo modifica la curva,
quindi questa viene ricalcolata e ridisegnata. Dato che le curve di Hermite implementate
sono definite per 4 punti di controllo, solo i primi quattro punti inseriti saranno
effettivamente usati per il calcolo.
Prima di finalizzare la creazione di una curva è possibile cambiare per un numero
arbitrario di volte il tipo della curva. Una volta premuto il tasto “e” questa possibilità non
è più valida per la curva appena finalizzata.

Modifica di curve
Il programma consente la modifica di una curva mediante l'azione sui punti di controllo o
sul dragger che gestisce lo scaling. E' possibile spostare un qualsiasi numero di punti di
controllo, selezionati mediante “Shift+LClick”, simultaneamente, anche se questi
appartengono a curve diverse. Per spostare una curva con movimento rigido, è possibile


3
selezionare uno ad uno tutti i punti oppure selezionare la curva nel suo insieme mediante
“Shift+LClick” sulla curva stessa.
L'operazione di scaling, che avviene in tempo reale, mantiene fisso il punto in cui è posto
il dragger.
E' possibile modificare il grado di una curva b-spline in tempo reale. Per fare questo è
necessario selezionare una (o più) curve di tipo b-spline e premere i tasti PgUp e PgDown.
Ogni pressione aumenta (o diminuisce) il grado della curva di 1, se questa operazione è
consentita dal numero di punti di controllo della curva.

Cancellazione di curve
La pressione del tasto “d” è associata alla cancellazione delle curve selezionate in
quell'istante. Per essere cancellata, una curva deve essere selezionata nel suo insieme,
mediante la combinazione “Shift+LClick” sulla curva stessa.

Mirroring
Il piano che definisce l'operazione di mirroring può essere definito dopo la pressione della
combinazione “Ctrl+m”. Dopo la pressione della combinazione di tasti, il programma si
aspetta l'inserimento di tre punti di controllo (che non possono essere selezionati e
spostati). Per ridefinire il piano è necessario inserire di nuovo tutti e tre i punti.
Una volta che il piano è stato definito in modo soddisfacente, è possibile creare delle
immagini specchiate delle curve selezionate, mediante la pressione del tasto “m”.

Copia di curve
E' possibile creare delle copie di un qualsiasi numero di curve. Per fare questo è
necessario selezionare le curve da copiare e quindi premere il tasto “c”. Le copie vengono
create nella stessa posizione delle curve originali; queste ultime rimangono selezionate,
quindi pronte ad essere traslate.


Descrizione del codice

main/main.cpp
In questo file è definito il punto d'ingresso dell'applicazione, così come le callback per la
gestione degli eventi generati dalla tastiera e dal mouse e per la gestione della selezione
dei punti di controllo e delle curve.
In questo file sono inoltre inizializzate le strutture dati necessarie al funzionamento del
programma.

Funzione main() e variabili globali
In questa funzione vengono inizializzati l'albero di scena, e le strutture dati generali del
programma. In quest'ultima categoria, ad esempio, ci sono:
    ●   la lista delle curve già create (list<CubicCurve*> curves)
    ●   la lista delle curve selezionate, rappresentata dalla lista di chiavi della mappa
        selectedCurves (map<CubicCurve *, SoSeparator *> selectedCurves)

4
●   il tipo di curva corrente
    ●   le flag per le varie operazioni interattive, rispettivamente per la traslazione, per la
        definizione del piano usato nell'operazione di mirroring e per la corretta
        inizializzazione della posizione del cursore per la traslazione
        (bool translationActive, definingMirrorPlane, setPlanePosition)
    ●   valori per la posizione di base della traslazione. Questi valori vengono inizializzati
        quando il cursore del mouse si porta sopra l'handle principale e la traslazione è
        attiva. (SbVec3f planePosition0)
    ●   la curva che si sta creando correntemente (CubicCurve * nextcc)


La struttura di base dell'albero di scena viene solo inizializzata all'interno di questa
funzione, dato che essa è definita nella classe SceneGraphInstance, che implementa il
pattern Singleton per permettere una condivisione sicura delle informazioni attraverso
tutte le istanze delle curve create.

Funzione mouseEventGrabber()
Questa funzione viene utilizzata come callback per gli eventi generati dal mouse, sia
quelli generati dalle pressioni dei tasti, sia quelli generati dal movimento del puntatore.
Una pressione del tasto destro sopra l'handle principale (con traslazione disattivata)
comporta l'inserimento di un nuovo punto di controllo per la curva corrente oppure di un
punto per la definizione del piano di mirroring, se questa flag è attivata. In entrambi i casi
il punto inserito ha le stesse coordinate del centro dell'handle.
Una pressione del tasto sinistro su una parte di una curva (poligono di controllo, curva
stessa) in concomitanza con la pressione del tasto Shift, nel caso in cui la flag per la
traslazione sia disattivata, corrisponde alla selezione della curva cubica. Come
conseguenza, tutti i punti di controllo vengono evidenziati e un riferimento all'istanza
dell'oggetto CubicCurve viene inserito come chiave nella mappa delle curve selezionate.
Il valore corrispondente a questa chiave è il separatore corrispondente all'oggetto (di tipo
linea) su cui era il cursore.
Nel caso in cui la flag relativa alla traslazione sia attiva ed il cursore del mouse si
posizioni sopra all'handle principale, è possibile traslare tutti gli oggetti selezionati.
Quando il cursore si posiziona sopra l'handle, la posizione attuale viene registrata nella
variabile planePosition. Questa variabile viene reinizializzata ogni volta che il cursore
viene spostato fuori e poi riposizionato sull'handle, così come il valore della traslazione
relativa all'handle (nelle variabili tx, ty, tz). Grazie a questi valori “di base” e alla
posizione del punto pickato sull'handle è possibile calcolare il valore della traslazione del
cursore, sia rispetto a quando la traslazione è cominciata, sia rispetto all'ultima
traslazione effettuata.
Se il pulsante sinistro del mouse viene premuto mentre il cursore non è sopra alcun
oggetto, la selezione viene azzerata.

Funzione keyboardEventGrabber()
Questa callback gestisce le azioni da compiere in base ai diversi tasti premuti. I tasti
riconosciuti e trattati sono:
    ●   “t”: attiva o disattiva la possibilità di traslare l'handle principale con tutto ciò che è

5
selezionato. Per fare questo, viene impostata la flag translationActive.
    ●   “h”, “b”, “p”, ”s”: selezionano il tipo di curva (Hermite, Bezier, B-Spline, Spline
        naturale). In ogni caso la curva viene aggiornata e ridisegnata.
    ●   “PgUp”, “PgDown”: nel caso in cui siano selezionate delle curve di tipo B-Spline, la
        pressione di uno di questi tasti si traduce nell'aumento (diminuzione) del grado
        della curva, se questa operazione è consentita. Le strutture dati della curva
        vengono aggiornate e la curva è ridisegnata.
    ●   “c”: permette di copiare le curve selezionate. Le nuove curve sono create usando il
        costruttore di copia (CubicCurve(CubicCurve &c)), quindi risultano equivalenti a
        quella di partenza. La selezione rimane attiva, quindi le curve originali sono pronte
        per essere traslate.
    ●   “d”: permette di cancellare le curve selezionate. Solo curve finalizzate con “e”
        possono essere cancellate.
    ●   “Ctrl-m”: attiva la definizione di un piano per il mirroring. L'applicazione, dopo la
        pressione di questa combinazione, si aspetta l'inserimento di tre punti (sempre
        attraverso l'handle principale).
    ●   “m”: attiva il mirroring per le curve selezionate. I tre punti definiti al passo
        precedente (o quelli di default) definiscono un piano (GenericPlane plane) su
        cui, per ogni curva selezionata, vengono proiettati i punti di controllo. Replicando la
        distanza dal punto di partenza al punto proiettato, si ottiene il punto specchiato
        rispetto al piano. In questo modo è possibile creare una curva specchiata rispetto a
        quella di partenza.
    ●   “e” : finalizza la curva corrente. I punti di controllo inseriti da questo momento in
        avanti saranno associati ad una nuova curva cubica.

Funzioni selMade() e unSelMade()
Queste callback, associate all'istanza della classe SoSelector, aggiornano la
rappresentazione dei punti di controllo delle curve, quando queste vengono selezionate o
deselezionate.

Funzione pickingInputDevice()
Questa è una semplice funzione di controllo, usata per capire quale dei tre cubi che
formano l'handle principale è correntemente usato.

Classe GenericPlane (util/GenericPlane.cpp)
Questa classe, come accennato prima, viene usata per il mirroring di una curva. In uno
spazio tridimensionale, un piano è definito da un'espressione del tipo axbyczd=0
. Per definire completamente un piano di questo tipo è necessario identificare tre punti
  pa , pb , pc appartenenti al piano o, in alternativa, un punto pa e un vettore normale al
piano n , con origine nel punto definito. Il vettore [ a , b , c ] , a , b , c∈ℝ a , b , c≠0
         
rappresenta sempre un vettore normale al piano. Supponendo di avere l'informazione
relativa a tre punti nel piano, si possono quindi determinare le prime tre variabili
calcolando n =[ a , b , c ]= pc − pa × pb− pa  . Il quarto parametro può essere determinato
                                        
calcolando d=− pa⋅n 
                     


6
GenericPlane(SbVec3f &pa,&pb,&pc)
Questo costruttore definisce un piano per mezzo di tre punti. Prima di tutto viene
calcolata la norma n (normalizzata a 1), quindi viene calcolato d.
                   

project(SbVec3f &p, &res)
La proiezione di un punto p= x , y , z sul piano  può essere vista come
l'applicazione di un vettore v normale al piano nel punto da proiettare, tale che
                             
  ∣v∣=dist  p ,   .
   

util/risolTridiag.cpp
In questo file è implementata la funzione che risolve un sistema tridiagonale. La funzione
implementata si riferisce ad un metodo sviluppato specificatamente per questo genere di
sistemi: dato un sistema del tipo Ax =b , la matrice A viene scomposta in due matrici:
   A=LU . Nel metodo usato, la diagonale maggiore della matrice U è posta uguale ad 1.
Il sistema da risolvere diventa Lz=b , z=Ux .
Le matrici L, U vengono quindi così inizializzate:




    {
    l 11 =a1 1
               ai−1i
    ui−1 i=                i=2,, n
               l i−1i−1
    l i i=ai i −ai i−1 u i−1 i
Dopo questo passaggio è possibile risolvere il sistema Lz=b




    {
               b1
        z 1=
               l 11
                bi −ai i z i−1 
        z i=                        i=2,, n
                      lii
Conoscendo z è possibile risalire alla soluzione originale del sistema


    {   x n =z n
        x i =z i−ui i1 x i 1      i=n−1, , 1

Classe SceneGraphInfo (util/SceneGraphInfo.cpp)
Questa classe rappresenta poco più di un contenitore per i riferimenti ai separatori
generici inseriti nell'albero di scena e ad alcune strutture condivise (per risparmiare
spazio in memoria) tra tutte le curve cubiche create. Il costruttore è stato scritto in modo
da implementare il pattern Singleton, cosicchè solo una istanza di questa classe può
essere creata; in questo modo le informazioni generiche rispetto all'albero di scena,
necessarie ad ogni curva cubica, possono essere condivise in modo sicuro.
In particolare, le informazioni condivise riguardano:
         ●     la radice dell'albero di scena e i separatori lineSeparator, pointSeparator,
               inputSeparator

7
●   Il selezionatore (istanza della classe SoSelector)
    ●   la matrice per il calcolo delle curve cubiche di tipo Hermite (con 4 punti di
        controllo)
    ●   i punti che definiscono il piano di mirroring (non necessario per le curve, ma
        inserito in questa classe per pulizia)
    ●   i riferimenti alla finestra principale e all'ExaminerViewer

Classe CubicCurve (curves/CubicCurve.cpp)
Questa classe rappresenta la parte più corposa del progetto. Ogni istanza di questa
classe è composta da numerosi attributi.
    ●   curveType rappresenta il codice del tipo di curva attualmente impostato
    ●   bezierBinomial è un vettore di coefficienti binomiali che vengono salvati per non
        dover essere ricalcolati ad ogni aggiornamento della curva
    ●   bSplineT è il vettore dei nodi per una curva di tipo B-Spline
    ●   bSplineK è il grado della curva, se questa è di tipo B-Spline
    ●   spline* sono vettori necessari al calcolo di una curva di tipo Spline
    ●   fourConditions è un contenitore usato nel calcolo delle curve di Hermite
    ●   boundingBoxPoint è la lista di punti che definiscono il bounding box della curva
    ●   sgi è il riferimento all'oggetto che contiene le informazioni generiche sull'albero di
        scena.
    ●   Step rappresenta la distanza fra due punti consecutivi in cui la curva viene
        calcolata. Questa distanza è relativa al parametro u
    ●   sis è l'oggetto (SoLineSet) che rappresenta la curva cubica, rappresentata con
        colore rosso
    ●   bbils è l'oggetto (SoIndexedLineSet) che rappresenta il bounding box della curva
        cubica, di colore giallo
    ●   controlSis è l'oggetto (SoLineSet) che rappresenta la linea (di colore blu) che
        congiunge i punti di controllo della curva
    ●   ptsTr è la lista delle coordinate dei punti di controllo per la curva, in ordine di
        inserimento. Anche se le informazioni sono ridondanti con quelle contenute nella
        lista translations, per comodità e leggibilità le informazioni sono state duplicate.
        In realtà le liste si riferiscono agli stessi valori, dato che nella lista ci sono i
        riferimenti (puntatori) agli oggetti veri e propri
    ●   spheres è la lista di oggetti di tipo SoSphere che rappresentano i punti di controllo
    ●   translations è la lista di oggetti di tipo SoTranslation che rappresentano i punti di
        controllo
    ●   graphicPts è la struttura dati di base su cui viene costruito l'oggetto che
        rappresenta la curva cubica
    ●   lineSeparator e pointSeparator rappresentano le radici per i sottoalberi relativi
        alla curva. In Figura 2 si possono vedere quattro istanze di ciascuno (i punti sotto i
        nodi line e point)
    ●   scaleSeparator è il separatore sotto cui viene posto il dragger per riscalare la


8
curva cubica. Per non permetterne la selezione, la sua posizione nell'albero di
        scena è quella di nodo figlio del separatore line
    ●   tmax rappresenta il punto del bounding box con le coordinate maggiori. In questo
        punto viene posto il dragger per lo scaling
    ●   scaler è il dragger per lo scaling. Una modifica al campo scaleFactor si traduce
        in una chiamata alla callback scale(), quindi in un ridimensionamento della curva
    ●   min, max rappresentano le coordinate dei punti minimo e massimo del bounding
        box della curva

init()
Questo metodo viene richiamato da tutti i costruttori. Le sue funzioni sono quelle di
istanziare tutti i separatori, le liste e le altre strutture dati appena presentate e di
aggiornare correttamente l'albero di scena.

refresh()
Questo metodo rappresenta il cuore della classe, in quanto controlla la computazione per
l'aggiornamento e la visualizzazione di tutte le parti della curva (poligono di controllo,
bounding box, curva stessa).
La linea congiungente i punti di controllo viene sempre computata. Per la visualizzazione
viene usata un'istanza della classe SoLineSet. In base al tipo di curva, viene poi
chiamato il metodo principale per l'aggiornamento dell'oggetto che rappresenta la curva
interpolante. Dopo questo passaggio viene calcolato e visualizzato il bounding box. Gli
ultimi due passaggi vengono compiuti solo se il numero di punti di controllo è sufficiente
alla computazione corretta della curva.
Il calcolo del bounding box avviene partendo dalle informazioni recuperate da un'istanza
della classe SoGetBoundingBoxAction, che viene applicata al separatore lineSeparator
(che contiene le tre diverse tipologie di linea per la rappresentazione di una curva
cubica). Con queste informazioni viene impostato un oggetto di tipo SoIndexedLineSet,
che rappresenta il bounding box stesso.
Con le informazioni relative ai punti minimo e massimo del bounding box viene inoltre
calcolata la rotazione relativa al dragger per lo scaling. La direzione dell'asse principale di
questo oggetto, posto nel punto massimo, è infatti “(max – min)”. La rotazione
complessiva viene vista come la composizione di due rotazioni, la prima attorno all'asse y
e la seconda relativa ad all'asse z. Per la prima si considerano quindi solo le coordinate
x,z e si calcola la rotazione nel punto max, per ottenere la corretta direzione verso il
punto min. Questo procedimento si può meglio capire facendo riferimento alla Figura 1.




9
Figura 1
La seconda rotazione viene calcolata nello stesso modo. Per ottenere la corretta ampiezza
dell'angolo, però, i calcoli vanno effettuati nel sistema di riferimento ruotato.

hermite4Points()
Questo metodo aggiorna la curva trattandola come una curva di Hermite. I primi quattro
punti di controllo inseriti vengono usati per definire le condizioni che permettono di
calcolare i punti della curva. In particolare, il secondo ed il terzo punto rappresentano
l'inizio e la fine della curva. Il primo ed il quarto, rispettivamente, le derivate nel punto di
partenza ed in quello finale. In questo modo si definiscono quattro condizioni che
permettono di descrivere completamente la curva




 {
  c1 =p1−p 2
  c2 =p2
  c3 =p3
  c4 =p4−p 3
Agendo separatamente su ogni coordinata e iterando il processo di calcolo sul parametro
u, u∈[0,1] , è possibile calcolare (con il metodo hermiteCalc()) un'approssimazione
della curva di Hermite identificata dalle quattro condizioni espresse.

hermiteCalc(float u)
Con questo metodo, supponendo che le quattro condizioni relative ad una curva di
Hermite siano state correttamente impostate, è possibile calcolare una delle tre
coordinate relative al parametro u. Il procedimento consiste nel pesare le quattro
condizioni per mezzo dei polinomi di Hermite




 {
            3       2
  h1 u=2u −3u 1
                3       2
  h2 u=−2u 3u
  h3 u=u 3−2u2 u
  h4 u=u 3−u2
In questo modo la curva di Hermite può essere definita come


10
[                              ]
                2 −2  1  1
               −33 3 −2 −1
                      2
 Hu=[u u u1]             [c1 c2 c3 c4 ]T
                0  0  1  0
                1  0  0  0
Nell'implementazione questo calcolo viene ripetuto tre volte per ogni punto, dato che per
comodità le condizioni si riferiscono ad una sola coordinata.

naturalSpline()
Questo metodo interpola la curva calcolata con il metodo delle spline cubiche naturali.
Dati n+1 punti da interpolare, la curva S viene genericamente scritta come




           {
          S0  x  x ∈[ x 0, x 1 ]
 S  x = S1  x  x ∈[ x 1, x 2 ]
          
          Sn−1  x  x∈[ x n−1 , x n ]

Si impongono le condizioni di continuità: la curva S deve essere continua in [u0, un] e deve
essere derivabile due volte (anche le derivate, nei punti di congiunzione delle Si, devono
essere continue). Le derivate seconde vengono descritte come delle funzioni lineari,
quindi le derivate prime sono al più quadratiche, e quindi le funzioni Si sono al più
cubiche.

     II         x −x i −1      x i− x
 Si  x =                zi         z i −1            ,       hi = x i−x i−1
                   hi             hi
                             2                   2
     I       x −x i −1                 x i −x 
 S  x =
     i                           z i                z i−1C1i
                     2hi                   2hi
                             3                   3

 S  x =
           x− x i −1           z
                                     x i− x        z i−1C1i  x− x i C 0i
     i                            i
                     6hi                   6hi
E' possibile determinare le funzioni C 1i e C0i ragionando su come si comporta la funzione S i
negli estremi in cui è definita.
                     h2
                      i                                               h2
                                                                       i
 S i  x i−1 =           z i −1 C0i=y i−1  C0i =y i−1 −                 z i−1
                     6                                                 6
                 2
                hi                              yi− y i−1 hi
 S i  x i =      z iC1i hi C0i= y i  C1i =          −  z i−z i −1 
                6                                  hi     6
Ponendo l'uguaglianza delle derivate prime negli estremi degli intervalli si ottiene la
definizione del sistema che permette di calcolare le zi. Questo sistema può essere
riscritto in forma matriciale, riconoscendo nella matrice dei coefficienti la forma
tridiagonale




11
{
  z 0 =0

  hi z i −12 z i  hi hi1 h i1 z i 1=6
                                                    yi 1 −y i
                                                       hi 1
                                                                  −
                                                                      y i− y i−1
                                                                         hi        
  z n =0
E' necessario risolvere tre sistemi, uno per ogni dimensione. La risoluzione di questi tre
sistemi è demandata alla funzione risolTridiag().
Una volta che le zi sono state calcolate, il metodo compie un ciclo sul parametro u per il
calcolo dei punti interpolanti la curva cubica. Il calcolo di ogni singolo punto è demandato
alla funzione naturalSplineCalc().

naturalSplineCalc(float u)
Questa funzione implementa semplicemente l'espressione per le funzioni Si(u) descritte al
punto precedente. Il calcolo presuppone che i sistemi relativi alla curva siano stati
correttamente risolti. Il risultato è il punto nello spazio 3D relativo al parametro u.

bezier(), bezierCalc(float u)
Questi due metodi, raggruppati in questa spiegazione per la loro relativa semplicità,
implementano la formulazione generica di una curva di Bezier.
La formulazione generica (per un qualsiasi numero di punti di controllo superiore a uno) è
           n


       i=0 i     
 B u=∑ n P i 1−un−i ui                , u∈[ 0,1]

 Il calcolo dei fattori binomiali che compaiono nella formula avviene solo quando alla
curva viene aggiunto un punto di controllo; se questo non è il caso, i valori vengono
recuperati dal vettore bezierBinomial, inizializzato dal metodo fillBezierBinomial().
Il metodo bezierCalc(), che implementa la suddetta formula, restituisce le coordinate di
un punto nello spazio 3D corrispondente al parametro u. Il metodo bezier(), d'altro
canto, si occupa di inserire tutti i punti calcolati nell'oggetto sis, che rappresenta la
curva interpolante.

bSpline(), bSplineCalc(float u), bSplineN()
Questi tre metodi implementano la formulazione generica di una curva B-Spline. Questo
tipo di curva è caratterizzato dalla formulazione
           n
 P u=∑ p i bi , n u
           i=0

dove le funzioni di miscelamento sono definite come




                {
                 1                                                            se       k=0∧u∈[u i , ui 1 ]
                 0                                                            se       k=0∧u∉[u i , ui 1 ]
 b i , k u=
                     u−ui                    uik1−u
                              bi , k−1 u                b         u altrimenti
                    ui k −ui               ui k 1 −u i1 i1, k−1

12
Le funzioni di miscelamento così costruite consentono un controllo locale: si ha infatti che
  bi ,1 u=0 se u∉[ui , u i1 ] . Questo, per la definizione della funzione di miscelamento,
significa che b i , k u0 if u∈[ui , u ik ] .
Quando il grado della curva è massimo, quest'ultima è identica ad una curva di Bezier.
Il vettore dei nodi della curva è calcolato dal metodo bSplineAlloc().

bSplineAlloc()
Questo metodo viene richiamato quando ad una curva B-Spline viene aggiunto un punto
di controllo, oppure quando il grado della curva viene modificato. In particolare, questo
metodo inizializza il vettore dei nodi della curva, seguendo la formulazione



       {
      0                i k
 u i= i−k 1           k ≤ipts   , 0≤i ptsk 
      pts−k1          i pts
pts è il numero di punti di controllo definiti e ui rappresenta l'i-esimo nodo.

scale()
Questa funzione (non è un metodo della classe CubicCurve, ma una funzione friend) è la
callback che viene richiamata ogniqualvolta lo stato del dragger per lo scaling viene
modificato. Lo stato è rappresentato dal primo valore contenuto del campo scaleFactor
dell'oggetto, che di default è impostato a 1.
Un valore diverso dal valore di default corrisponde ad un'operazione di scaling
proporzionale al valore stesso. Il processo mantiene fisso il punto in cui si trova il dragger
(identificato con max). La procedura implementata per ottenere questo risultato può
essere scomposta in passi
     1. per ogni punto di controllo viene calcolata la distanza rispetto a max
     2. questa distanza viene moltiplicata per il nuovo fattore di scaling, calcolato come il
        rapporto tra nuovo il dato recuperato dal dragger e il vecchio fattore di scaling
        (mantenuto in memoria)
     3. il risultato rappresenta il nuvo valore per la traslazione dei punti di controllo
     4. quando ogni punto di controllo è stato modificato, la curva viene ridisegnata
Lo stato del dragger non viene modificato dalla procedura descritta.
Il risultato di questa funzione corrisponde ad una operazione di scaling descritta dalla
matrice S




      [ ]
    t      0   0   0
    0      t   0   0           new
 S=                     , t=
    0      0   t   0           old
    0      0   0   1




13
Altri metodi
La classe CubicCurve contiene altri metodi, che non riguardano specificatamente la
computazione dell'interpolazione delle curve cubiche, ma che sono necessari per il buon
funzionamento del programma.
     ●   multSb4f() calcola il prodotto scalare fra due vettori di quattro elementi
     ●   reset() prepara la curva ad essere ricalcolata per intero, dopo un cambio di tipo o
         un inserimento di un nuovo punto di controllo
     ●   select() fa sì che l'istanza della classe SoSelector inserita nell'albero di scena
         visualizzi il box di selezione attorno ai punti di controllo quando una curva cubica è
         selezionata con la combinazione “Shift + Lclick”
     ●   setBSplineDegree() imposta il grado della curva (implicitamente si assume che
         essa sia di tipo B-Spline). Vengono compiuti dei controlli affinchè il grado sia
         compatibile con la situazione attuale della curva.
     ●   showScaler() aggiunge il dragger per lo scaling all'albero di scena, facendo sì che
         esso venga visualizzato
     ●   fact() e binom() vengono utilizzati per il calcolo del coefficiente binomiale
     ●   getMinPointCounter() ritorna il numero minimo di punti necessari per poter
         calcolare una curva cubica, in funzione del tipo (2 per B-Spline e Bezier, 4 per
         Spline cubiche ed Hermite)

Struttura dell'albero di scena
In Figura 2 è possibile vedere una rappresentazione schematica dell'albero di scena. Sono
esplicitati gli otto oggetti principali, con gli elementi secondari rappresentati con dei
cerchi.




                           Figura 2: schema della struttura dell'albero di scena


     1. root: la radice dell'albero.
     2. pcam: un'istanza della classe SoPerspectiveCamera.
     3. interaction: il gestore delle callback generate dagli eventi di tastiera e mouse.
     4. mirrorPlaneSeparator: separatore per i punti che definiscono il piano usato
        nell'operazione di mirror.
     5. lineSeparator: separatore per i nodi che definiscono le linee che compongono le
        varie curve cubiche. A colori diversi corrispondono dei separatori diversi, uno per
        ogni curva. Ognuno di questi separatori ha come nodi le tre diverse linee che
        compongono una curva: la curva vera e propria, il bounding box e il poligono di
        controllo.
     6. selector: oggetto per la gestione della selezione e highlighting degli oggetti grafici


14
(istanze di SoSphere) che rappresentano i punti di controllo.
     7. pointSeparator: seguendo lo stesso principio del lineSeparator, viene introdotto un
        diverso separatore per ogni curva presente.
     8. inputSeparator: ha come nodi i tre cubi che definiscono l'handle principale.


Nella descrizione e nello schema si sono volutamente omessi dei nodi (come le istanze di
SoTranslation) per questioni di leggibilità, cercando di non inficiare la completezza della
descrizione.
Nello schema presentato, come si può capire dalla struttura dei sottoalberi in
lineSeparator e pointSeparator, sono presenti quattro diverse curve cubiche. Le istanze
delle linee e dei punti associati alla stessa curva sono rappresentate con lo stesso colore
(nell'ordine: verde, blu, rosso, arancio).
Dato che il separatore mirrorPlane ha dei nodi sotto di esso, nell'esempio presentato è
stato definito dall'utente un piano per il mirroring diverso da quello di default (piano xy). I
punti che definiscono il piano, infatti, sono rappresentati nell'albero di scena solamente
quando essi vengono inseriti manualmente.




15

More Related Content

Similar to Editor for Cubic Curves based on Coin3D Library

Caravillani lezione 5
Caravillani lezione 5Caravillani lezione 5
Caravillani lezione 5Anna Fetta
 
Modellazione tramite geometria frattale
Modellazione tramite geometria frattaleModellazione tramite geometria frattale
Modellazione tramite geometria frattaleMassimiliano Leone
 
Autocad lezione 1
Autocad lezione 1Autocad lezione 1
Autocad lezione 1Carella2014
 
Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...
Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...
Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...Ginestra_
 
Corso Pat Jgrasspeakflow 2008 10 23
Corso Pat Jgrasspeakflow 2008 10 23Corso Pat Jgrasspeakflow 2008 10 23
Corso Pat Jgrasspeakflow 2008 10 23silli
 
Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...Giovanni Casabianca
 

Similar to Editor for Cubic Curves based on Coin3D Library (9)

JCurves
JCurvesJCurves
JCurves
 
Caravillani lezione 5
Caravillani lezione 5Caravillani lezione 5
Caravillani lezione 5
 
Modellazione tramite geometria frattale
Modellazione tramite geometria frattaleModellazione tramite geometria frattale
Modellazione tramite geometria frattale
 
Autocad lezione 1
Autocad lezione 1Autocad lezione 1
Autocad lezione 1
 
Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...
Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...
Tutorial Free Cad (italiano) – Kentstrapper per Ginestra Fabbrica della Conos...
 
Correzioni Esercizioverifica300109
Correzioni Esercizioverifica300109Correzioni Esercizioverifica300109
Correzioni Esercizioverifica300109
 
Corso Pat Jgrasspeakflow 2008 10 23
Corso Pat Jgrasspeakflow 2008 10 23Corso Pat Jgrasspeakflow 2008 10 23
Corso Pat Jgrasspeakflow 2008 10 23
 
Robotic Arm Simulation
Robotic Arm SimulationRobotic Arm Simulation
Robotic Arm Simulation
 
Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...Riconfigurazione automatica di processi di asportazione in termini di Network...
Riconfigurazione automatica di processi di asportazione in termini di Network...
 

More from graphitech

A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationgraphitech
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationgraphitech
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationgraphitech
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screensgraphitech
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screensgraphitech
 
Graph Matching
Graph MatchingGraph Matching
Graph Matchinggraphitech
 
Shape Analysis
Shape AnalysisShape Analysis
Shape Analysisgraphitech
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Librarygraphitech
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Librarygraphitech
 
WebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World WindWebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World Windgraphitech
 
Street Builder
Street BuilderStreet Builder
Street Buildergraphitech
 
Street Builder
Street BuilderStreet Builder
Street Buildergraphitech
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Windgraphitech
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Windgraphitech
 
Terrain Modification
Terrain ModificationTerrain Modification
Terrain Modificationgraphitech
 
Terrain Modification
Terrain ModificationTerrain Modification
Terrain Modificationgraphitech
 
YARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) ProjectYARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) Projectgraphitech
 
YARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) ProjectYARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) Projectgraphitech
 
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...graphitech
 
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...graphitech
 

More from graphitech (20)

A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
 
A graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolationA graphic library and an application for simple curve manipolation
A graphic library and an application for simple curve manipolation
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
 
Multiple Screens
Multiple ScreensMultiple Screens
Multiple Screens
 
Graph Matching
Graph MatchingGraph Matching
Graph Matching
 
Shape Analysis
Shape AnalysisShape Analysis
Shape Analysis
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Library
 
Human Interaction Library
Human Interaction LibraryHuman Interaction Library
Human Interaction Library
 
WebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World WindWebCams Mapping on Nasa World Wind
WebCams Mapping on Nasa World Wind
 
Street Builder
Street BuilderStreet Builder
Street Builder
 
Street Builder
Street BuilderStreet Builder
Street Builder
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
 
Live Video in World Wind
Live Video in World WindLive Video in World Wind
Live Video in World Wind
 
Terrain Modification
Terrain ModificationTerrain Modification
Terrain Modification
 
Terrain Modification
Terrain ModificationTerrain Modification
Terrain Modification
 
YARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) ProjectYARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) Project
 
YARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) ProjectYARCA (Yet Another Raycasting Application) Project
YARCA (Yet Another Raycasting Application) Project
 
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
 
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
Implementation of Hybrid Terrain Representation in Nasa WorldWind: Regular Gr...
 

Recently uploaded

Lorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptx
Lorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptxLorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptx
Lorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptxlorenzodemidio01
 
Lorenzo D'Emidio_Francesco Petrarca.pptx
Lorenzo D'Emidio_Francesco Petrarca.pptxLorenzo D'Emidio_Francesco Petrarca.pptx
Lorenzo D'Emidio_Francesco Petrarca.pptxlorenzodemidio01
 
Presentazioni Efficaci e lezioni di Educazione Civica
Presentazioni Efficaci e lezioni di Educazione CivicaPresentazioni Efficaci e lezioni di Educazione Civica
Presentazioni Efficaci e lezioni di Educazione CivicaSalvatore Cianciabella
 
Confronto tra Sparta e Atene classiche.ppt
Confronto tra Sparta e Atene classiche.pptConfronto tra Sparta e Atene classiche.ppt
Confronto tra Sparta e Atene classiche.pptcarlottagalassi
 
Quadrilateri e isometrie studente di liceo
Quadrilateri e isometrie studente di liceoQuadrilateri e isometrie studente di liceo
Quadrilateri e isometrie studente di liceoyanmeng831
 
Lorenzo D'Emidio_Vita di Cristoforo Colombo.pptx
Lorenzo D'Emidio_Vita di Cristoforo Colombo.pptxLorenzo D'Emidio_Vita di Cristoforo Colombo.pptx
Lorenzo D'Emidio_Vita di Cristoforo Colombo.pptxlorenzodemidio01
 
Lorenzo D'Emidio_Vita e opere di Aristotele.pptx
Lorenzo D'Emidio_Vita e opere di Aristotele.pptxLorenzo D'Emidio_Vita e opere di Aristotele.pptx
Lorenzo D'Emidio_Vita e opere di Aristotele.pptxlorenzodemidio01
 

Recently uploaded (7)

Lorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptx
Lorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptxLorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptx
Lorenzo D'Emidio- Lavoro sulla Bioarchittetura.pptx
 
Lorenzo D'Emidio_Francesco Petrarca.pptx
Lorenzo D'Emidio_Francesco Petrarca.pptxLorenzo D'Emidio_Francesco Petrarca.pptx
Lorenzo D'Emidio_Francesco Petrarca.pptx
 
Presentazioni Efficaci e lezioni di Educazione Civica
Presentazioni Efficaci e lezioni di Educazione CivicaPresentazioni Efficaci e lezioni di Educazione Civica
Presentazioni Efficaci e lezioni di Educazione Civica
 
Confronto tra Sparta e Atene classiche.ppt
Confronto tra Sparta e Atene classiche.pptConfronto tra Sparta e Atene classiche.ppt
Confronto tra Sparta e Atene classiche.ppt
 
Quadrilateri e isometrie studente di liceo
Quadrilateri e isometrie studente di liceoQuadrilateri e isometrie studente di liceo
Quadrilateri e isometrie studente di liceo
 
Lorenzo D'Emidio_Vita di Cristoforo Colombo.pptx
Lorenzo D'Emidio_Vita di Cristoforo Colombo.pptxLorenzo D'Emidio_Vita di Cristoforo Colombo.pptx
Lorenzo D'Emidio_Vita di Cristoforo Colombo.pptx
 
Lorenzo D'Emidio_Vita e opere di Aristotele.pptx
Lorenzo D'Emidio_Vita e opere di Aristotele.pptxLorenzo D'Emidio_Vita e opere di Aristotele.pptx
Lorenzo D'Emidio_Vita e opere di Aristotele.pptx
 

Editor for Cubic Curves based on Coin3D Library

  • 1.
  • 2. bSpline(), bSplineCalc(float u), bSplineN()................................................................12 bSplineAlloc()................................................................................... .........................13 scale().......................................................................................... .............................13 Altri metodi.............................................................................. .................................13 Struttura dell'albero di scena........................................................................................14 2
  • 3. Sommario Il progetto sviluppato consiste in un semplice editor per curve cubiche scritto in C++, avvalendosi delle librerie grafiche Coin3D. L'editor permette di aggiungere, modificare, copiare e cancellare curve di tipo Hermite, Spline, B-Spline, Bezier. E' inoltre possibile definire un piano di mirroring per creare delle curve speculari. Il numero di punti di controllo, per le curve di tipo Spline, B-Spline e Bezier, non è predeterminato. Guida all'uso del programma Utilizzo dell'handle principale L'handle principale si compone di 3 parallelepipedi. L'utente può agire su ognuno di essi, una volta che viene attivata la funzionalità associata mediante il tasto “t”. Il movimento del mouse su un parallelepipedo coincide con uno spostamento lungo l'asse specifico, sia dell'handle che di tutto ciò che è selezionato in quel momento. Quando la traslazione è disattivata, è possibile inserire dei nuovi punti per la definizione di una nuova curva o per la definizione di un piano per il mirroring premendo il tasto destro mentre il cursore si trova sull'handle. Il nuovo punto inserito ha le coordinate corrispondenti al centro dell'handle stesso. Creazione di una curva La creazione di una curva consiste nell'inseriremento di un numero sufficiente di punti di controllo. Prima di completare l'operazione di inserimento (tramite la pressione del tasto “e”), è possibile cambiare il tipo di curva che verrà calcolata. Il tipo di default è Hermite. Le scelte possibili sono: ● “h” per Hermite ● “p” per B-Spline ● “s” per Spline ● “b” per Bezier Una volta che il numero di punti di controllo è sufficiente al calcolo di una curva, questa viene disegnata. Ogni successivo inserimento di un punto di controllo modifica la curva, quindi questa viene ricalcolata e ridisegnata. Dato che le curve di Hermite implementate sono definite per 4 punti di controllo, solo i primi quattro punti inseriti saranno effettivamente usati per il calcolo. Prima di finalizzare la creazione di una curva è possibile cambiare per un numero arbitrario di volte il tipo della curva. Una volta premuto il tasto “e” questa possibilità non è più valida per la curva appena finalizzata. Modifica di curve Il programma consente la modifica di una curva mediante l'azione sui punti di controllo o sul dragger che gestisce lo scaling. E' possibile spostare un qualsiasi numero di punti di controllo, selezionati mediante “Shift+LClick”, simultaneamente, anche se questi appartengono a curve diverse. Per spostare una curva con movimento rigido, è possibile 3
  • 4. selezionare uno ad uno tutti i punti oppure selezionare la curva nel suo insieme mediante “Shift+LClick” sulla curva stessa. L'operazione di scaling, che avviene in tempo reale, mantiene fisso il punto in cui è posto il dragger. E' possibile modificare il grado di una curva b-spline in tempo reale. Per fare questo è necessario selezionare una (o più) curve di tipo b-spline e premere i tasti PgUp e PgDown. Ogni pressione aumenta (o diminuisce) il grado della curva di 1, se questa operazione è consentita dal numero di punti di controllo della curva. Cancellazione di curve La pressione del tasto “d” è associata alla cancellazione delle curve selezionate in quell'istante. Per essere cancellata, una curva deve essere selezionata nel suo insieme, mediante la combinazione “Shift+LClick” sulla curva stessa. Mirroring Il piano che definisce l'operazione di mirroring può essere definito dopo la pressione della combinazione “Ctrl+m”. Dopo la pressione della combinazione di tasti, il programma si aspetta l'inserimento di tre punti di controllo (che non possono essere selezionati e spostati). Per ridefinire il piano è necessario inserire di nuovo tutti e tre i punti. Una volta che il piano è stato definito in modo soddisfacente, è possibile creare delle immagini specchiate delle curve selezionate, mediante la pressione del tasto “m”. Copia di curve E' possibile creare delle copie di un qualsiasi numero di curve. Per fare questo è necessario selezionare le curve da copiare e quindi premere il tasto “c”. Le copie vengono create nella stessa posizione delle curve originali; queste ultime rimangono selezionate, quindi pronte ad essere traslate. Descrizione del codice main/main.cpp In questo file è definito il punto d'ingresso dell'applicazione, così come le callback per la gestione degli eventi generati dalla tastiera e dal mouse e per la gestione della selezione dei punti di controllo e delle curve. In questo file sono inoltre inizializzate le strutture dati necessarie al funzionamento del programma. Funzione main() e variabili globali In questa funzione vengono inizializzati l'albero di scena, e le strutture dati generali del programma. In quest'ultima categoria, ad esempio, ci sono: ● la lista delle curve già create (list<CubicCurve*> curves) ● la lista delle curve selezionate, rappresentata dalla lista di chiavi della mappa selectedCurves (map<CubicCurve *, SoSeparator *> selectedCurves) 4
  • 5. il tipo di curva corrente ● le flag per le varie operazioni interattive, rispettivamente per la traslazione, per la definizione del piano usato nell'operazione di mirroring e per la corretta inizializzazione della posizione del cursore per la traslazione (bool translationActive, definingMirrorPlane, setPlanePosition) ● valori per la posizione di base della traslazione. Questi valori vengono inizializzati quando il cursore del mouse si porta sopra l'handle principale e la traslazione è attiva. (SbVec3f planePosition0) ● la curva che si sta creando correntemente (CubicCurve * nextcc) La struttura di base dell'albero di scena viene solo inizializzata all'interno di questa funzione, dato che essa è definita nella classe SceneGraphInstance, che implementa il pattern Singleton per permettere una condivisione sicura delle informazioni attraverso tutte le istanze delle curve create. Funzione mouseEventGrabber() Questa funzione viene utilizzata come callback per gli eventi generati dal mouse, sia quelli generati dalle pressioni dei tasti, sia quelli generati dal movimento del puntatore. Una pressione del tasto destro sopra l'handle principale (con traslazione disattivata) comporta l'inserimento di un nuovo punto di controllo per la curva corrente oppure di un punto per la definizione del piano di mirroring, se questa flag è attivata. In entrambi i casi il punto inserito ha le stesse coordinate del centro dell'handle. Una pressione del tasto sinistro su una parte di una curva (poligono di controllo, curva stessa) in concomitanza con la pressione del tasto Shift, nel caso in cui la flag per la traslazione sia disattivata, corrisponde alla selezione della curva cubica. Come conseguenza, tutti i punti di controllo vengono evidenziati e un riferimento all'istanza dell'oggetto CubicCurve viene inserito come chiave nella mappa delle curve selezionate. Il valore corrispondente a questa chiave è il separatore corrispondente all'oggetto (di tipo linea) su cui era il cursore. Nel caso in cui la flag relativa alla traslazione sia attiva ed il cursore del mouse si posizioni sopra all'handle principale, è possibile traslare tutti gli oggetti selezionati. Quando il cursore si posiziona sopra l'handle, la posizione attuale viene registrata nella variabile planePosition. Questa variabile viene reinizializzata ogni volta che il cursore viene spostato fuori e poi riposizionato sull'handle, così come il valore della traslazione relativa all'handle (nelle variabili tx, ty, tz). Grazie a questi valori “di base” e alla posizione del punto pickato sull'handle è possibile calcolare il valore della traslazione del cursore, sia rispetto a quando la traslazione è cominciata, sia rispetto all'ultima traslazione effettuata. Se il pulsante sinistro del mouse viene premuto mentre il cursore non è sopra alcun oggetto, la selezione viene azzerata. Funzione keyboardEventGrabber() Questa callback gestisce le azioni da compiere in base ai diversi tasti premuti. I tasti riconosciuti e trattati sono: ● “t”: attiva o disattiva la possibilità di traslare l'handle principale con tutto ciò che è 5
  • 6. selezionato. Per fare questo, viene impostata la flag translationActive. ● “h”, “b”, “p”, ”s”: selezionano il tipo di curva (Hermite, Bezier, B-Spline, Spline naturale). In ogni caso la curva viene aggiornata e ridisegnata. ● “PgUp”, “PgDown”: nel caso in cui siano selezionate delle curve di tipo B-Spline, la pressione di uno di questi tasti si traduce nell'aumento (diminuzione) del grado della curva, se questa operazione è consentita. Le strutture dati della curva vengono aggiornate e la curva è ridisegnata. ● “c”: permette di copiare le curve selezionate. Le nuove curve sono create usando il costruttore di copia (CubicCurve(CubicCurve &c)), quindi risultano equivalenti a quella di partenza. La selezione rimane attiva, quindi le curve originali sono pronte per essere traslate. ● “d”: permette di cancellare le curve selezionate. Solo curve finalizzate con “e” possono essere cancellate. ● “Ctrl-m”: attiva la definizione di un piano per il mirroring. L'applicazione, dopo la pressione di questa combinazione, si aspetta l'inserimento di tre punti (sempre attraverso l'handle principale). ● “m”: attiva il mirroring per le curve selezionate. I tre punti definiti al passo precedente (o quelli di default) definiscono un piano (GenericPlane plane) su cui, per ogni curva selezionata, vengono proiettati i punti di controllo. Replicando la distanza dal punto di partenza al punto proiettato, si ottiene il punto specchiato rispetto al piano. In questo modo è possibile creare una curva specchiata rispetto a quella di partenza. ● “e” : finalizza la curva corrente. I punti di controllo inseriti da questo momento in avanti saranno associati ad una nuova curva cubica. Funzioni selMade() e unSelMade() Queste callback, associate all'istanza della classe SoSelector, aggiornano la rappresentazione dei punti di controllo delle curve, quando queste vengono selezionate o deselezionate. Funzione pickingInputDevice() Questa è una semplice funzione di controllo, usata per capire quale dei tre cubi che formano l'handle principale è correntemente usato. Classe GenericPlane (util/GenericPlane.cpp) Questa classe, come accennato prima, viene usata per il mirroring di una curva. In uno spazio tridimensionale, un piano è definito da un'espressione del tipo axbyczd=0 . Per definire completamente un piano di questo tipo è necessario identificare tre punti pa , pb , pc appartenenti al piano o, in alternativa, un punto pa e un vettore normale al piano n , con origine nel punto definito. Il vettore [ a , b , c ] , a , b , c∈ℝ a , b , c≠0  rappresenta sempre un vettore normale al piano. Supponendo di avere l'informazione relativa a tre punti nel piano, si possono quindi determinare le prime tre variabili calcolando n =[ a , b , c ]= pc − pa × pb− pa  . Il quarto parametro può essere determinato      calcolando d=− pa⋅n    6
  • 7. GenericPlane(SbVec3f &pa,&pb,&pc) Questo costruttore definisce un piano per mezzo di tre punti. Prima di tutto viene calcolata la norma n (normalizzata a 1), quindi viene calcolato d.  project(SbVec3f &p, &res) La proiezione di un punto p= x , y , z sul piano  può essere vista come l'applicazione di un vettore v normale al piano nel punto da proiettare, tale che  ∣v∣=dist  p ,   .  util/risolTridiag.cpp In questo file è implementata la funzione che risolve un sistema tridiagonale. La funzione implementata si riferisce ad un metodo sviluppato specificatamente per questo genere di sistemi: dato un sistema del tipo Ax =b , la matrice A viene scomposta in due matrici: A=LU . Nel metodo usato, la diagonale maggiore della matrice U è posta uguale ad 1. Il sistema da risolvere diventa Lz=b , z=Ux . Le matrici L, U vengono quindi così inizializzate: { l 11 =a1 1 ai−1i ui−1 i= i=2,, n l i−1i−1 l i i=ai i −ai i−1 u i−1 i Dopo questo passaggio è possibile risolvere il sistema Lz=b { b1 z 1= l 11  bi −ai i z i−1  z i= i=2,, n lii Conoscendo z è possibile risalire alla soluzione originale del sistema { x n =z n x i =z i−ui i1 x i 1 i=n−1, , 1 Classe SceneGraphInfo (util/SceneGraphInfo.cpp) Questa classe rappresenta poco più di un contenitore per i riferimenti ai separatori generici inseriti nell'albero di scena e ad alcune strutture condivise (per risparmiare spazio in memoria) tra tutte le curve cubiche create. Il costruttore è stato scritto in modo da implementare il pattern Singleton, cosicchè solo una istanza di questa classe può essere creata; in questo modo le informazioni generiche rispetto all'albero di scena, necessarie ad ogni curva cubica, possono essere condivise in modo sicuro. In particolare, le informazioni condivise riguardano: ● la radice dell'albero di scena e i separatori lineSeparator, pointSeparator, inputSeparator 7
  • 8. Il selezionatore (istanza della classe SoSelector) ● la matrice per il calcolo delle curve cubiche di tipo Hermite (con 4 punti di controllo) ● i punti che definiscono il piano di mirroring (non necessario per le curve, ma inserito in questa classe per pulizia) ● i riferimenti alla finestra principale e all'ExaminerViewer Classe CubicCurve (curves/CubicCurve.cpp) Questa classe rappresenta la parte più corposa del progetto. Ogni istanza di questa classe è composta da numerosi attributi. ● curveType rappresenta il codice del tipo di curva attualmente impostato ● bezierBinomial è un vettore di coefficienti binomiali che vengono salvati per non dover essere ricalcolati ad ogni aggiornamento della curva ● bSplineT è il vettore dei nodi per una curva di tipo B-Spline ● bSplineK è il grado della curva, se questa è di tipo B-Spline ● spline* sono vettori necessari al calcolo di una curva di tipo Spline ● fourConditions è un contenitore usato nel calcolo delle curve di Hermite ● boundingBoxPoint è la lista di punti che definiscono il bounding box della curva ● sgi è il riferimento all'oggetto che contiene le informazioni generiche sull'albero di scena. ● Step rappresenta la distanza fra due punti consecutivi in cui la curva viene calcolata. Questa distanza è relativa al parametro u ● sis è l'oggetto (SoLineSet) che rappresenta la curva cubica, rappresentata con colore rosso ● bbils è l'oggetto (SoIndexedLineSet) che rappresenta il bounding box della curva cubica, di colore giallo ● controlSis è l'oggetto (SoLineSet) che rappresenta la linea (di colore blu) che congiunge i punti di controllo della curva ● ptsTr è la lista delle coordinate dei punti di controllo per la curva, in ordine di inserimento. Anche se le informazioni sono ridondanti con quelle contenute nella lista translations, per comodità e leggibilità le informazioni sono state duplicate. In realtà le liste si riferiscono agli stessi valori, dato che nella lista ci sono i riferimenti (puntatori) agli oggetti veri e propri ● spheres è la lista di oggetti di tipo SoSphere che rappresentano i punti di controllo ● translations è la lista di oggetti di tipo SoTranslation che rappresentano i punti di controllo ● graphicPts è la struttura dati di base su cui viene costruito l'oggetto che rappresenta la curva cubica ● lineSeparator e pointSeparator rappresentano le radici per i sottoalberi relativi alla curva. In Figura 2 si possono vedere quattro istanze di ciascuno (i punti sotto i nodi line e point) ● scaleSeparator è il separatore sotto cui viene posto il dragger per riscalare la 8
  • 9. curva cubica. Per non permetterne la selezione, la sua posizione nell'albero di scena è quella di nodo figlio del separatore line ● tmax rappresenta il punto del bounding box con le coordinate maggiori. In questo punto viene posto il dragger per lo scaling ● scaler è il dragger per lo scaling. Una modifica al campo scaleFactor si traduce in una chiamata alla callback scale(), quindi in un ridimensionamento della curva ● min, max rappresentano le coordinate dei punti minimo e massimo del bounding box della curva init() Questo metodo viene richiamato da tutti i costruttori. Le sue funzioni sono quelle di istanziare tutti i separatori, le liste e le altre strutture dati appena presentate e di aggiornare correttamente l'albero di scena. refresh() Questo metodo rappresenta il cuore della classe, in quanto controlla la computazione per l'aggiornamento e la visualizzazione di tutte le parti della curva (poligono di controllo, bounding box, curva stessa). La linea congiungente i punti di controllo viene sempre computata. Per la visualizzazione viene usata un'istanza della classe SoLineSet. In base al tipo di curva, viene poi chiamato il metodo principale per l'aggiornamento dell'oggetto che rappresenta la curva interpolante. Dopo questo passaggio viene calcolato e visualizzato il bounding box. Gli ultimi due passaggi vengono compiuti solo se il numero di punti di controllo è sufficiente alla computazione corretta della curva. Il calcolo del bounding box avviene partendo dalle informazioni recuperate da un'istanza della classe SoGetBoundingBoxAction, che viene applicata al separatore lineSeparator (che contiene le tre diverse tipologie di linea per la rappresentazione di una curva cubica). Con queste informazioni viene impostato un oggetto di tipo SoIndexedLineSet, che rappresenta il bounding box stesso. Con le informazioni relative ai punti minimo e massimo del bounding box viene inoltre calcolata la rotazione relativa al dragger per lo scaling. La direzione dell'asse principale di questo oggetto, posto nel punto massimo, è infatti “(max – min)”. La rotazione complessiva viene vista come la composizione di due rotazioni, la prima attorno all'asse y e la seconda relativa ad all'asse z. Per la prima si considerano quindi solo le coordinate x,z e si calcola la rotazione nel punto max, per ottenere la corretta direzione verso il punto min. Questo procedimento si può meglio capire facendo riferimento alla Figura 1. 9
  • 10. Figura 1 La seconda rotazione viene calcolata nello stesso modo. Per ottenere la corretta ampiezza dell'angolo, però, i calcoli vanno effettuati nel sistema di riferimento ruotato. hermite4Points() Questo metodo aggiorna la curva trattandola come una curva di Hermite. I primi quattro punti di controllo inseriti vengono usati per definire le condizioni che permettono di calcolare i punti della curva. In particolare, il secondo ed il terzo punto rappresentano l'inizio e la fine della curva. Il primo ed il quarto, rispettivamente, le derivate nel punto di partenza ed in quello finale. In questo modo si definiscono quattro condizioni che permettono di descrivere completamente la curva { c1 =p1−p 2 c2 =p2 c3 =p3 c4 =p4−p 3 Agendo separatamente su ogni coordinata e iterando il processo di calcolo sul parametro u, u∈[0,1] , è possibile calcolare (con il metodo hermiteCalc()) un'approssimazione della curva di Hermite identificata dalle quattro condizioni espresse. hermiteCalc(float u) Con questo metodo, supponendo che le quattro condizioni relative ad una curva di Hermite siano state correttamente impostate, è possibile calcolare una delle tre coordinate relative al parametro u. Il procedimento consiste nel pesare le quattro condizioni per mezzo dei polinomi di Hermite { 3 2 h1 u=2u −3u 1 3 2 h2 u=−2u 3u h3 u=u 3−2u2 u h4 u=u 3−u2 In questo modo la curva di Hermite può essere definita come 10
  • 11. [ ] 2 −2 1 1 −33 3 −2 −1 2 Hu=[u u u1] [c1 c2 c3 c4 ]T 0 0 1 0 1 0 0 0 Nell'implementazione questo calcolo viene ripetuto tre volte per ogni punto, dato che per comodità le condizioni si riferiscono ad una sola coordinata. naturalSpline() Questo metodo interpola la curva calcolata con il metodo delle spline cubiche naturali. Dati n+1 punti da interpolare, la curva S viene genericamente scritta come { S0  x  x ∈[ x 0, x 1 ] S  x = S1  x  x ∈[ x 1, x 2 ]  Sn−1  x  x∈[ x n−1 , x n ] Si impongono le condizioni di continuità: la curva S deve essere continua in [u0, un] e deve essere derivabile due volte (anche le derivate, nei punti di congiunzione delle Si, devono essere continue). Le derivate seconde vengono descritte come delle funzioni lineari, quindi le derivate prime sono al più quadratiche, e quindi le funzioni Si sono al più cubiche. II x −x i −1 x i− x Si  x = zi  z i −1 , hi = x i−x i−1 hi hi 2 2 I  x −x i −1   x i −x  S  x = i z i z i−1C1i 2hi 2hi 3 3 S  x =  x− x i −1  z  x i− x  z i−1C1i  x− x i C 0i i i 6hi 6hi E' possibile determinare le funzioni C 1i e C0i ragionando su come si comporta la funzione S i negli estremi in cui è definita. h2 i h2 i S i  x i−1 = z i −1 C0i=y i−1  C0i =y i−1 − z i−1 6 6 2 hi yi− y i−1 hi S i  x i = z iC1i hi C0i= y i  C1i = −  z i−z i −1  6 hi 6 Ponendo l'uguaglianza delle derivate prime negli estremi degli intervalli si ottiene la definizione del sistema che permette di calcolare le zi. Questo sistema può essere riscritto in forma matriciale, riconoscendo nella matrice dei coefficienti la forma tridiagonale 11
  • 12. { z 0 =0 hi z i −12 z i  hi hi1 h i1 z i 1=6  yi 1 −y i hi 1 − y i− y i−1 hi  z n =0 E' necessario risolvere tre sistemi, uno per ogni dimensione. La risoluzione di questi tre sistemi è demandata alla funzione risolTridiag(). Una volta che le zi sono state calcolate, il metodo compie un ciclo sul parametro u per il calcolo dei punti interpolanti la curva cubica. Il calcolo di ogni singolo punto è demandato alla funzione naturalSplineCalc(). naturalSplineCalc(float u) Questa funzione implementa semplicemente l'espressione per le funzioni Si(u) descritte al punto precedente. Il calcolo presuppone che i sistemi relativi alla curva siano stati correttamente risolti. Il risultato è il punto nello spazio 3D relativo al parametro u. bezier(), bezierCalc(float u) Questi due metodi, raggruppati in questa spiegazione per la loro relativa semplicità, implementano la formulazione generica di una curva di Bezier. La formulazione generica (per un qualsiasi numero di punti di controllo superiore a uno) è n i=0 i  B u=∑ n P i 1−un−i ui , u∈[ 0,1] Il calcolo dei fattori binomiali che compaiono nella formula avviene solo quando alla curva viene aggiunto un punto di controllo; se questo non è il caso, i valori vengono recuperati dal vettore bezierBinomial, inizializzato dal metodo fillBezierBinomial(). Il metodo bezierCalc(), che implementa la suddetta formula, restituisce le coordinate di un punto nello spazio 3D corrispondente al parametro u. Il metodo bezier(), d'altro canto, si occupa di inserire tutti i punti calcolati nell'oggetto sis, che rappresenta la curva interpolante. bSpline(), bSplineCalc(float u), bSplineN() Questi tre metodi implementano la formulazione generica di una curva B-Spline. Questo tipo di curva è caratterizzato dalla formulazione n P u=∑ p i bi , n u i=0 dove le funzioni di miscelamento sono definite come { 1 se k=0∧u∈[u i , ui 1 ] 0 se k=0∧u∉[u i , ui 1 ] b i , k u= u−ui uik1−u bi , k−1 u b u altrimenti ui k −ui ui k 1 −u i1 i1, k−1 12
  • 13. Le funzioni di miscelamento così costruite consentono un controllo locale: si ha infatti che bi ,1 u=0 se u∉[ui , u i1 ] . Questo, per la definizione della funzione di miscelamento, significa che b i , k u0 if u∈[ui , u ik ] . Quando il grado della curva è massimo, quest'ultima è identica ad una curva di Bezier. Il vettore dei nodi della curva è calcolato dal metodo bSplineAlloc(). bSplineAlloc() Questo metodo viene richiamato quando ad una curva B-Spline viene aggiunto un punto di controllo, oppure quando il grado della curva viene modificato. In particolare, questo metodo inizializza il vettore dei nodi della curva, seguendo la formulazione { 0 i k u i= i−k 1 k ≤ipts , 0≤i ptsk  pts−k1 i pts pts è il numero di punti di controllo definiti e ui rappresenta l'i-esimo nodo. scale() Questa funzione (non è un metodo della classe CubicCurve, ma una funzione friend) è la callback che viene richiamata ogniqualvolta lo stato del dragger per lo scaling viene modificato. Lo stato è rappresentato dal primo valore contenuto del campo scaleFactor dell'oggetto, che di default è impostato a 1. Un valore diverso dal valore di default corrisponde ad un'operazione di scaling proporzionale al valore stesso. Il processo mantiene fisso il punto in cui si trova il dragger (identificato con max). La procedura implementata per ottenere questo risultato può essere scomposta in passi 1. per ogni punto di controllo viene calcolata la distanza rispetto a max 2. questa distanza viene moltiplicata per il nuovo fattore di scaling, calcolato come il rapporto tra nuovo il dato recuperato dal dragger e il vecchio fattore di scaling (mantenuto in memoria) 3. il risultato rappresenta il nuvo valore per la traslazione dei punti di controllo 4. quando ogni punto di controllo è stato modificato, la curva viene ridisegnata Lo stato del dragger non viene modificato dalla procedura descritta. Il risultato di questa funzione corrisponde ad una operazione di scaling descritta dalla matrice S [ ] t 0 0 0 0 t 0 0 new S= , t= 0 0 t 0 old 0 0 0 1 13
  • 14. Altri metodi La classe CubicCurve contiene altri metodi, che non riguardano specificatamente la computazione dell'interpolazione delle curve cubiche, ma che sono necessari per il buon funzionamento del programma. ● multSb4f() calcola il prodotto scalare fra due vettori di quattro elementi ● reset() prepara la curva ad essere ricalcolata per intero, dopo un cambio di tipo o un inserimento di un nuovo punto di controllo ● select() fa sì che l'istanza della classe SoSelector inserita nell'albero di scena visualizzi il box di selezione attorno ai punti di controllo quando una curva cubica è selezionata con la combinazione “Shift + Lclick” ● setBSplineDegree() imposta il grado della curva (implicitamente si assume che essa sia di tipo B-Spline). Vengono compiuti dei controlli affinchè il grado sia compatibile con la situazione attuale della curva. ● showScaler() aggiunge il dragger per lo scaling all'albero di scena, facendo sì che esso venga visualizzato ● fact() e binom() vengono utilizzati per il calcolo del coefficiente binomiale ● getMinPointCounter() ritorna il numero minimo di punti necessari per poter calcolare una curva cubica, in funzione del tipo (2 per B-Spline e Bezier, 4 per Spline cubiche ed Hermite) Struttura dell'albero di scena In Figura 2 è possibile vedere una rappresentazione schematica dell'albero di scena. Sono esplicitati gli otto oggetti principali, con gli elementi secondari rappresentati con dei cerchi. Figura 2: schema della struttura dell'albero di scena 1. root: la radice dell'albero. 2. pcam: un'istanza della classe SoPerspectiveCamera. 3. interaction: il gestore delle callback generate dagli eventi di tastiera e mouse. 4. mirrorPlaneSeparator: separatore per i punti che definiscono il piano usato nell'operazione di mirror. 5. lineSeparator: separatore per i nodi che definiscono le linee che compongono le varie curve cubiche. A colori diversi corrispondono dei separatori diversi, uno per ogni curva. Ognuno di questi separatori ha come nodi le tre diverse linee che compongono una curva: la curva vera e propria, il bounding box e il poligono di controllo. 6. selector: oggetto per la gestione della selezione e highlighting degli oggetti grafici 14
  • 15. (istanze di SoSphere) che rappresentano i punti di controllo. 7. pointSeparator: seguendo lo stesso principio del lineSeparator, viene introdotto un diverso separatore per ogni curva presente. 8. inputSeparator: ha come nodi i tre cubi che definiscono l'handle principale. Nella descrizione e nello schema si sono volutamente omessi dei nodi (come le istanze di SoTranslation) per questioni di leggibilità, cercando di non inficiare la completezza della descrizione. Nello schema presentato, come si può capire dalla struttura dei sottoalberi in lineSeparator e pointSeparator, sono presenti quattro diverse curve cubiche. Le istanze delle linee e dei punti associati alla stessa curva sono rappresentate con lo stesso colore (nell'ordine: verde, blu, rosso, arancio). Dato che il separatore mirrorPlane ha dei nodi sotto di esso, nell'esempio presentato è stato definito dall'utente un piano per il mirroring diverso da quello di default (piano xy). I punti che definiscono il piano, infatti, sono rappresentati nell'albero di scena solamente quando essi vengono inseriti manualmente. 15