UNIVERSITÀ DEGLI STUDI DI TRENTO Facoltà di Scienze Matematiche, Fisiche e Naturali Corso di Laurea (triennale) in Informatica Elaborato Finale Costruzione di un'interfaccia-utente per Lavagne Interattive Multimediali nel caso di simulazioni bidimensionali di fisica Relatore: Marco Ronchetti Laureando: Nicola Dorigatti Anno Accademico 2007-2008 Indice generale Indice delle illustrazioni ii Capitolo 1: Introduzione 1 Capitolo 2: Lavagne Interattive Multimediali 3 2.1 L.I.M. a tecnologia analogico-resistiva ............................................................................ 6 2.2 L.I.M. a tecnologia laser .................................................................................................. 7 2.3 L.I.M. a tecnologia elettromagnetica ............................................................................... 8 2.4 L.I.M. a tecnologia ottico-infrarossa ................................................................................ 9 2.5 L.I.M. a tecnologia ultrasonica ....................................................................................... 11 Capitolo 3: Scrittura di codice per L.I.M. 13 3.1 Descrizione API Java ..................................................................................................... 14 3.2 Considerazioni su User Interface ................................................................................... 21 3.3 Possibili soluzioni .......................................................................................................... 22 3.3.1 - Il “menu di penna” ................................................................................................. 22 3.3.2 - Handwriting Recognition ....................................................................................... 23 3.3.3 - Controllo tramite Gestures ..................................................................................... 24 Capitolo 4: Fisica 2D 4.1 27 Pacchetti di fisica 2D disponibili ................................................................................... 29 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 - Box2D ................................................................................................................... 29 JBox2D .................................................................................................................. 31 Phun .................................................................................................................... 31 Tokamak ................................................................................................................ 32 Phyz ....................................................................................................................... 34 4.2 Analisi delle librerie ....................................................................................................... 35 Capitolo 5: Descrizione dell'architettura 37 5.1 “Menu di penne” sviluppato .......................................................................................... 37 5.2 Cattura degli eventi generati dalla LIM ......................................................................... 45 5.3 Interfacciamento a JBox2D ............................................................................................ 49 Capitolo 6: Caso d'uso: Smart Box 2D 53 Capitolo 7: Conclusioni 65 Bibliografia 67 i Indice delle illustrazioni Figura 2.1........................................................................................................................................4 Figura 2.2........................................................................................................................................5 Figura 2.3........................................................................................................................................7 Figura 2.4........................................................................................................................................9 Figura 2.5......................................................................................................................................10 Figura 2.6......................................................................................................................................11 Figura 3.1......................................................................................................................................20 Figura 3.2......................................................................................................................................23 Figura 4.1......................................................................................................................................30 Figura 4.2......................................................................................................................................32 Figura 4.3......................................................................................................................................33 Figura 4.4......................................................................................................................................34 Figura 5.1......................................................................................................................................44 Figura 6.1......................................................................................................................................54 Figura 6.2......................................................................................................................................60 Figura 6.3......................................................................................................................................61 Figura 6.4......................................................................................................................................62 Figura 6.5......................................................................................................................................63 ii Capitolo 1: Introduzione Nel corso degli anni lo sviluppo delle applicazioni ha visto crescere sempre di più il contributo degli utenti finali. Attraverso questo contributo si è andati verso una modifica radicale dei programmi, rendendoli sempre più interattivi e di facile utilizzo. Di pari passo è aumentata l'offerta delle periferiche di input multimediali, capaci sempre più di semplificare le azioni più o meno comuni dell'utente che fa uso del calcolatore. Di conseguenza il bisogno di applicazioni interattive è aumentato ulteriormente, con la necessità di adattare il software a queste nuove periferiche. Un'interfaccia utente che sia semplice da comprendere e sfruttare permette quindi un uso migliore di questi strumenti di input anche da parte di coloro che sono meno esperti o non conoscono le periferiche in questione. Il fine di questa tesi è quindi l'analisi di una soluzione a questa necessità nel caso delle lavagne interattive multimediali, ed in seguito lo sviluppo del sistema ideato in precedenza, associando ad esso un software che fa uso delle sue caratteristiche. Il problema richiede innanzitutto un'analisi dell'approccio grafico da elaborare, ponendo contemporaneamente attenzione alle tecniche da implementare per sfruttare gli input della lavagna interattiva. Una volta creata l'interfaccia si passerà alla cattura degli input che verranno associati a specifiche azioni del software sviluppato che nel nostro caso è rappresentato da un simulatore di ambiente fisico bidimensionale. Nel prossimo capitolo verranno analizzati con attenzione gli strumenti di input utilizzati (le lavagne interattive multimediali), mostrandone le diverse tipologie a disposizione, e differenziando le varie tecnologie implementate al loro interno. Nel terzo capitolo l'attenzione sarà maggiormente posta sulle potenzialità messe a disposizione dalle periferiche scelte, analizzando gli strumenti messi a disposizione dal costruttore delle lavagne. Verranno quindi verificati i requisiti necessari all'interfaccia grafica per poter essere adottata in questo ambito, proponendo ed approfondendo in seguito pregi, difetti ed attuabilità di alcune soluzioni al problema. Nel capitolo quattro troviamo la descrizione della libreria che verrà associata all'interfaccia grafica per ottenere la soluzione finale. Inizialmente sarà presentata una panoramica dei pacchetti di fisica bidimensionale disponibili, procedendo quindi ad una breve analisi di alcuni di quelli reperibili 1 attraverso internet, per poi valutare quale tra di essi può essere adottato nel programma finale. Nel quinto capitolo verrà descritto lo sviluppo dell'interfaccia creata, utilizzabile come libreria adattabile a contesti diversi e completamente personalizzabile. Mostreremo inoltre le tecniche utilizzate per avere una corrispondenza fisico-grafica tra lavagna (superficie e strumenti) e applicazione sviluppata. Infine mostreremo nello specifico i metodi adottati per lo scambio di dati tra simulatore fisico utilizzato ed interfaccia per la lavagna. Nel capitolo sei vedremo in dettaglio le caratteristiche e le tecniche implementate nel programma finale (SmartBox2D), analizzando il funzionamento di alcune particolari azioni come la modifica delle proprietà degli oggetti fisici, approfondendo la associazione tra gli eventi generati attraverso la lavagna interattiva e la creazione degli oggetti che verranno inseriti all'interno del motore fisico. 2 Capitolo 2: Lavagne Interattive Multimediali Le lavagne interattive multimediali (LIM) sono dei dispositivi elettronici studiati e ideati principalmente per un uso didattico. Con la crescente capacità delle nuove generazioni di interagire ed utilizzare computer e dispositivi elettronici questo tipo di lavagne aiuta l’apprendimento, coinvolgendo l’attenzione degli studenti[1]. Nonostante la base prettamente didattica, le LIM possono essere utilizzate per qualsiasi fine e, tra le varie applicazioni, va citato sicuramente l'uso nelle conferenze, dove una lavagna che permette la modifica e l'evidenziazione di concetti e immagini è sicuramente uno strumento utile se non, al giorno d'oggi, indispensabile[2]. Vi sono in commercio diversi tipi di LIM, ognuna con caratteristiche diverse ma in linea generale tutte sono molto simili tra loro, differenziandosi particolarmente per la tecnologia di funzionamento. Le dimensioni sono simili a quelle di una lavagna tradizionale ma al posto della classica superficie in ardesia, troviamo una area a disposizione dell'utente che può essere: – – Un pannello sensibile al tocco (lavagne a proiezione frontale). Uno schermo di vetro (lavagne a retroproiezione). Per ciò che riguarda la prima tipologia, come si può vedere in figura 2.1, la lavagna è costituita da una superficie bianca e da un proiettore posto nella parte superiore, solitamente incorporato nel corpo della lavagna stessa. Toccando il pannello in un determinato punto si invierà al computer, in base alla tecnologia utilizzata ed allo strumento con cui si effettua l'azione, un evento, indicante strumento e punto di contatto. Il computer e la lavagna sono nella maggior parte dei casi connessi attraverso cavo dati USB o tramite connessione senza fili (Bluetooth), ma non sono collegati direttamente. La LIM contiene difatti al suo interno un controller, responsabile della ricezione dei dati riguardanti l'azione, del calcolo del punto di contatto, e dell'invio al software delle informazioni necessarie a quest'ultimo per eseguire un comando precedentemente stabilito dall'utente. Il maggior vantaggio di questa tecnologia, è dato in gran parte dalla facilità di montaggio e dal poco spazio occupato, con il difetto però che, essendo l'immagine proiettata, è necessaria una 3 calibrazione dello schermo ed in aggiunta, si può facilmente incontrare il problema dell'effetto ombra, classico nell'ambito dei proiettori. Figura 2.1: Lavagna Interattiva Multimediale a proiezione frontale. Nella parte superiore dell'immagine si può notare il proiettore, ancorato al corpo della lavagna, nella parte inferiore invece, il gruppo contenente pennarelli, pulsanti per tastiera e tasto destro e cancellino. Le lavagne a retroproiezione invece, sono caratterizzate da un ingombro maggiore, le dimensioni sono simili ad un televisore a tubo catodico di circa 40 pollici (tale dato può variare per ogni tipo di modello). Le maggiori dimensioni sono causate dal motivo seguente: all’interno dello chassis, è presente un proiettore (come si può vedere in figura 2.2) che mostra l’immagine del desktop sullo schermo tramite riflessione interna, ed è quindi necessario dello spazio per inserire proiettore ed i due specchi per la riflessione dell’immagine. Lo schermo è, guardandolo esternamente, una semplice superficie di vetro sulla quale il contatto non viene differenziato per l'utente rispetto al contatto con un qualsiasi televisore, ma viene comunque riconosciuto dalla lavagna tramite sensori posti nelle locazioni in cui le penne vengono poggiate. Per questa tipologia di lavagna, come per la precedente, la connessione tra essa ed il PC è affidata alla trasmissione dati via cavo USB oppure ad una connessione Bluetooth. Per questo tipo di LIM, le tecniche utilizzate per il riconoscimento del tocco possono essere molte, in generale viene utilizzata la tecnologia ottico-infrarossa, ma in nessun caso quella analogicoresistiva, poiché tale tecnica richiede (come vedremo in seguito nella sezione 2.1) due strati di materiale non trasparente, quindi non permetterebbe il passaggio dell'immagine tra la parte interna e quella esterna dello schermo. 4 Anche in questo caso, tra computer e lavagna troviamo il controller per il processamento delle informazioni. A vantaggio della tecnologia a retroproiezione, troviamo la libertà di posizionamento in quanto, non è necessario il montaggio a parete, seppur sia permessa l'integrazione in essa [3]. Altro fattore a favore delle lavagne a retroproiezione, è la differente tecnologia di riconoscimento dei contatti, la quale può in molti casi permettere il riconoscimento di più punti di contatto simultaneamente, rendendo quindi possibile lo sviluppo di applicazioni che fanno uso del multitouch, cioè l'uso della lavagna di più persone simultaneamente. Figura 2.2: LIM con tecnologia a retroproiezione. Come mostrato nella figura di destra,il proiettore non mostra direttamente l'immagine sullo schermo, ma la proietta su di un piccolo specchio, il quale, appoggiandosi ad un altro più grande mostra il desktop sullo schermo. Sulla superficie viene quindi proiettata l’immagine ottenuta da un computer collegato alla LIM, permettendo così all’utente che utilizza tale lavagna, di avere un vero e proprio desktop su un’area più grande di un classico monitor per PC. Un’altra caratteristica è rappresentata dalla possibilità di utilizzare pennarelli appositi per poter scrivere sul desktop, utilizzare il cancellino per cancellare ciò che si è scritto, oppure di poter controllare il puntatore del mouse con le dita. Toccando infatti la superficie su cui l’immagine è proiettata, si ha una risposta da parte del software abbinato alla LIM, il quale, se si sta utilizzando un pennarello, riprodurrà sullo schermo quello che l’utente ha scritto o disegnato, se invece si utilizza un dito od un qualsiasi altro oggetto, non appartenente agli strumenti della lavagna, si potrà controllare il mouse. Oltre alla semplice scrittura di note sul PC, è possibile sfruttare lo stesso per usi comuni,quali la 5 navigazione web, presentazioni (tramite plugin appositi è possibile scrivere le note con i pennarelli direttamente nelle slide della presentazione) o avviare programmi appositamente scritti per l'uso con le LIM. Sulla base della tecnologia utilizzata per interagire con il computer si possono attualmente classificare cinque tipi di lavagne interattive multimediali: analogico-resistive (sezione 2.1), laser (sezione 2.2), elettromagnetiche (sezione 2.3), ottiche basate sull'infrarosso (sezione 2.4) ed ultrasoniche (sezione 2.5). Verranno quindi passate in rassegna le varie tecniche, elencandone il funzionamento base per ognuna e, quando possibile, aggiungendo un'immagine esplicativa per rendere più chiaro il concetto. 2.1 L.I.M. a tecnologia analogico-resistiva Le lavagne che utilizzano la tecnologia analogico-resistiva, sono composte esternamente da una superficie bianca in poliestere sulla quale l’immagine è proiettata. Internamente presentano due strati flessibili, rivestiti con un materiale resistivo e divisi tra loro da un sottilissimo strato d’aria (dell’ordine di 2x10-3 millimetri). La pressione di dito, di una penna o qualsiasi altro oggetto su un qualsiasi punto della lavagna, mette a contatto questi due strati resistivi (figura 2.3); questo genera un segnale elettrico che viene inviato prima al controller integrato nella lavagna, e da li, successivamente, al PC tramite l’interfaccia di collegamento tra i due dispositivi. Tramite questa tecnologia è possibile effettuare un solo tocco per volta, ciò vuol dire che il multitouch (uso contemporaneo di due mani o di più persone) non è possibile, difatti, il controller nel caso in cui riceve più di una coordinata di contatto nello stesso istante, esegue un'interpolazione tra i valori ricevuti, col risultato che, il software ottiene come punto interessato, la posizione a metà tra le due posizioni scelte. L'azione appena descritta, viene eseguita in tal modo a causa della metodologia di costruzione della tecnologia stessa, poiché cliccando anche solo in un punto, fisicamente non si mettono a contatto gli strati resistivi solo per quel pixel, ma anche per le posizioni circostanti (ciò è causato dalla grandezza dello strumento utilizzato, che supera quella dei pixel), ed è quindi il controller che fa in modo che il PC riceva le coordinate di contatto corrette, effettuando appunto l'interpolazione dei punti di quella zona. Il difetto appena descritto dunque riduce le possibili applicazioni della lavagna con questa tecnologia, limitando di fatto i casi d'uso ad applicazioni a tocco singolo. Senza il multitouch difatti, molte semplificazioni nell'uso dell'interfaccia vengono a mancare. Se questa tecnica fosse utilizzabile, sarebbe possibile configurare ed implementare gestures particolari per eseguire determinate azioni. Le gestures sono però eseguibili anche a tocco singolo, ma sono più difficili da riprodurre correttamente e sono inoltre complesse da controllare. Solitamente la risoluzione dello strato resistivo è di 4096x4096 DPI, caratteristica che rende abbastanza preciso il riconoscimento dei punti di contatto. 6 Di seguito, possiamo vedere una rappresentazione schematica della tecnologia appena descritta. Figura 2.3: Contatto su superficie basata sul riconoscimento analogico-resistivo. Da come si può vedere nell'immagine, al momento del tocco con il dito (7) sulla superficie esterna (1), il piccolo gap di aria (5) tra i due strati resistivi (3) viene a mancare, creando cosi un segnale elettrico, che viene inviato al controller della lavagna (8). Per completezza, segnaliamo uno strato flessibile di spessore tra la superficie esterna/posteriore e la parte resistiva (2-4) e lo strato rigido posto nella parte posteriore della lavagna (6). 2.2 L.I.M. a tecnologia laser Per ciò che riguarda questa tipologia di lavagne, quattro laser infrarossi sono situati nei quattro angoli della lavagna. Il fascio di luce laser investe la superficie utilizzata, senza tornare indietro al punto di partenza. Le penne o gli altri strumenti a disposizione utilizzati con questa tecnologia presentano sulla superficie che va a contatto con lo schermo un materiale lucido, il quale riflette il fascio di luce facendolo tornare alla sorgente, rendendo quindi possibile il riconoscimento delle coordinate riguardanti il punto di tocco. Il difetto più grosso di questa tecnologia è rappresentato per l'appunto dalla necessità di avere penne e strumenti riflettenti la luce laser, limitando quindi l’uso della lavagna ai soli strumenti messi a disposizione, e rendendo impossibile l’utilizzo delle dita per controllare il mouse, ostacolando la possibilità di sfruttare la lavagna come desktop e con più utenti, poiché rende necessario il continuo scambio degli strumenti necessari al suo utilizzo. D'altra parte però, va detto che con questa tecnologia precisione e sensibilità sono molto alte e si 7 riescono ad evitare eventi non voluti come ad esempio il caso in cui si poggia erroneamente la mano sulla lavagna; con la tecnologia laser tale evento non viene considerato evitando cosi il rilevamento del contatto indesiderato. 2.3 L.I.M. a tecnologia elettromagnetica Le lavagne interattive multimediali che sfruttano la tecnologia elettromagnetica contengono nella parte posteriore della superficie esterna, una griglia cablata che interagisce con le penne per determinare le coordinate. Ci sono quindi nello strato posteriore allo schermo sensori magnetici che reagiscono al contatto (Figura 2.4) ed inviano messaggi al controller interno, da quest'ultimo dati e coordinate passano al computer nel momento in cui la griglia cablata viene attivata dall'uso delle penne magnetiche. Gli strumenti utilizzati per sfruttare questa tecnologia, possono essere attivi (a batterie) o passivi (Non magnetici, sfruttano i segnali elettrici prodotti dalla lavagna). Per il primo caso, l'oggetto utilizzato sfrutta l'energia delle batterie interne per creare un campo elettromagnetico che, al momento del contatto con la lavagna, farà reagire la corrispondente parte di griglia. Nel secondo caso invece, la superficie di contatto è sensibile sia alle penne attive, sia al semplice tocco di un altro strumento, quindi i sensori vengono attivati con impulsi elettromagnetici, ma anche con semplici contatti sulla superficie (ad esempio con un dito). Con queste lavagne è possibile avere una simulazione totale (click, drag&drop, click destro) dell’uso del mouse, tramite una tecnica molto simile a quella implementata nelle tavolette grafiche per PC utilizzate da grafici e disegnatori professionisti. La superficie utilizzata per il riconoscimento degli eventi, può essere come detto sensibile alla pressione e può permettere il multitouch. Ad esempio, in molte LIM che sfruttano la tecnologia elettromagnetica, il click destro del mouse è rappresentato dalla pressione di due punti vicini tra loro; questa gesture è facilmente effettuabile con due dita. Solitamente, la risoluzione delle celle utilizzate è di 30000 x 30000 DPI, dato che, confrontato con quello precedentemente visto per la tecnologia resistiva (2.1), non può che far capire come la griglia di cavi sensibili agli impulsi elettro-magnetici sia molto più precisa dei film resistivi visti in precedenza. [4] Di seguito, è possibile vedere una rappresentazione grafica della sezione di un angolo di una lavagna che fa uso della tecnologia elettromagnetica, con una breve descrizione delle parti caratteristiche. 8 Figura 2.4: Tecnologia Elettromagnetica: Come mostra la figura (sezione di un angolo) la parte fondamentale di questo tipo di riconoscimento, è data dal reticolo di cavi-sensori posto immediatamente dietro alla superficie esterna. Il tutto viene poi irrobustito da una cornice solida e da un pannello posteriore in legno o altro materiale a nido d'ape, il quale permette di aver una buona leggerezza ed un'ottima robustezza. 2.4 L.I.M. a tecnologia ottico-infrarossa Scrivendo o toccando sul di una superficie costruita per questo tipo di tecnologia, si genera un riflesso di luce infrarossa che viene catturato da un sensore integrato nella lavagna. Il sensore è solitamente incorporato nella cornice che fa da perimetro allo schermo. In caso di tocco, con penna, dito o quant'altro, il sensore viene attivato, invia al software le informazioni riguardanti l’evento accaduto. Il software della LIM, basandosi sui dati ricevuti calcola la locazione esatta in cui è avvenuto l’evento ed esegue l'azione prevista dal programma che si sta utilizzando, o una scelta in precedenza dall'utente. Questa tecnologia permette l’utilizzo di qualsiasi penna o strumento per la scrittura, poiché non richiede particolari caratteristiche per quanto riguarda i marcatori utilizzati (dito o penne). Nel 2007, uno studente PhD dell’università Carnegie Mellon ha dimostrato che è possibile sfruttare questa tecnologia per creare una lavagna interattiva senza particolari strumenti. Johnny Chung Lee ha mostrato infatti che, utilizzando il sensore infrarosso presente nel controller di gioco della console Nintendo Wii [5] è possibile utilizzare quest'ultimo come penna, senza alcuna specifica superficie. Oltre ai metodi sopracitati ne esistono altri per il riconoscimento del movimento della mano (mediante trasmettitore led sulla base della penna con cui si scrive), oppure per il tracking di un punto di luce laser presente su di una penna o di un telecomando (per esempio quelli dei proiettori), con il vantaggio di avere una portabilità maggiore, e sciogliendo il vincolo della dimensione della lavagna, poiché non è necessario alcun tipo di schermo con caratteristiche specifiche, ma è 9 sufficiente una superficie monocromatica e di colore diverso dal raggio generato dal telecomando. Figura 2.5: Presentatore e Ricevitore Laser. Con soli questi due oggetti è possibile eseguire la maggior parte delle azioni eseguibili con le altre tecnologie. Non è possibile il multitouch, ma la portabilità è massima. Da sottolineare, all’interno di questa categoria è la tecnologia DViT (Digital Vision Touch, Figura 2.6)[6] in fase di brevetto, ma già applicata ai suoi prodotti, da SMART Technologies Inc® che sfrutta tre camere poste in tre dei quattro angoli dello schermo (due superiori ed uno inferiore) per riconoscere il punto esatto in cui si sta operando, inviando al software proprietario i dati rilevati, il quale tramite opportuni calcoli procederà al corretto rilevamento del punto di contatto. Oltre a ciò è possibile anche il multitouch, con il quale ad esempio si possono eseguire gestures memorizzabili e personalizzabili tramite il programma di controllo SMART. Un esempio di gesture è rappresentato dal click destro del mouse, effettuabile toccando lo schermo con un dito, quindi con un altro dito premere subito a destra del primo punto scelto. Per ciò che riguarda il riconoscimento dello strumento utilizzato, le tre camere poste nella cornice si occupano, oltre che a rilevare i dati della posizione, della cattura di un'immagine dell'oggetto che si sta controllando, riconoscendone forma e colore e abbinandolo ad una relativa azione del software. Questa tecnologia, che non richiede alcun tipo di schermo attivo (come può essere uno schermo sensibile elettromagneticamente oppure uno analogico resistivo) ed è presente solamente per le lavagne a retroproiezione e quelle a display piatto ( che quindi sfruttano uno schermo LCD).[7] 10 Figura 2.6: DViT (Digital Vision Touch): Lo schermo appare come un semplice vetro, non presenta parti attive o sensori, poiché questi sono posti negli angoli (Figura destra). 2.5 L.I.M. a tecnologia ultrasonica Questo tipo di tecnica per il rilevamento sfrutta trasmettitori e ricevitori ultrasonici. I primi sono posti in due dei quattro angoli della cornice, i secondi sono situati negli angoli opposti ai trasmettitori. Sfruttando le onde ultrasoniche, trasmesse sulla superficie e riflesse da oggetti riflettenti posti nella cornice della lavagna, è possibile creare una fitta griglia di onde che servirà a controllare qualsiasi tocco. Andando appunto a contatto con un dito o con una penna, si causerà l’interruzione di una zona di questa griglia, interrompendo quindi l'arrivo di un'onda al sensore, il quale informerà il controller della LIM dell’accaduto. Dopo aver calcolato la posizione ottenuta in base a calcoli relativi ai dati ricevuti, il controller invierà le coordinate al software che eseguirà un’opportuna funzione (disegno o uso del mouse). Da come si può intuire dalla precedente descrizione, anche con questa tecnologia si possono utilizzare penne e strumenti senza particolari peculiarità. E' prevista inoltre la compatibilità con il multitouch. 11 12 Capitolo 3: Scrittura di codice per L.I.M. Dopo aver visionato le varie tecnologie a disposizione, i rispettivi limiti e caratteristiche delle stesse, è possibile analizzare l'espandibilità delle lavagne, scrivendo codice sorgente in modo da sviluppare programmi capaci di aumentare ulteriormente le potenzialità di questa tecnologia.[8] Nel nostro caso ci siamo incentrati sulle lavagne SMART, per la precisione su quelle a proiezione frontale integranti la tecnologia analogico-resistiva. Tale scelta non è stata dettata da particolari esigenze ma dal fatto che tale lavagna era già a nostra disposizione e soddisfa le nostre richieste. Questi modelli di lavagne grazie ad alcune librerie danno la possibilità di interfacciare gli eventi della lavagna ad un programma scritto appositamente per essa. Per poter quindi sfruttare le caratteristiche della lavagna nel proprio programma è necessario, all'interno del codice, richiamare delle apposite funzioni. Per prima cosa è necessario attivare una connessione tra PC e lavagna richiamando il metodo di creazione dell'oggetto SBSDK (Smart Board Software Development Kit) che, sfruttando una libreria DLL (da importare nel programma scritto) ottiene il numero di porta tramite il quale la lavagna è connessa, ed abilita cosi correttamente lo scambio di dati tra software e controller della lavagna. Il passo successivo consiste nel comunicare all'oggetto SDK qual è la parte del programma (graficamente) su cui abilitare la cattura degli eventi. Tramite le funzioni di interfacciamento tra PC e lavagna, è infatti possibile abilitare il controllo delle azioni solo su una parte della finestra, semplificando cosi la gestione dei dati riguardanti la posizione degli eventi. Catturando infatti le azioni solo all'interno di un pannello e non su tutta la finestra, il Software Development Kit non restituirà i punti di contatto relativi all'angolo sinistro della finestra, ma a quello del pannello monitorato (il punto <0,0> risulta quindi il primo pixel in alto a sinistra nel pannello controllato dall'applicazione). E' quindi d'obbligo la chiamata della funzione di attacco (collegamento) alla finestra principale dell'applicazione. Il passo successivo invece, consiste nel comunicare all'SDK il pannello su cui effettuare il controllo delle azioni. Assieme a tale oggetto, è necessario passare all'oggetto SBSDK anche un actionListener specifico per la lavagna. Difatti per gestire il monitoraggio, e semplificare all'utente la cattura degli eventi, è 13 presente un'interfaccia che, dopo esser stata implementata, fa da listener (SBSDKListener) e fornisce metodi per controllare ogni evento riguardante la lavagna ( se una penna viene rimossa o posata nella sua sede, se il proiettore è acceso o meno, se, sfruttando il software apposito della lavagna, sono abilitate azioni speciali per le penne). Una volta effettuati questi piccoli passi (un esempio di codice java è rappresentato dal Codice 1) è possibile sfruttare appieno la lavagna, si ha il controllo su tutte le modifiche di stato per gli oggetti della lavagna, ed è possibile quindi personalizzare la risposta del PC a qualsiasi input proveniente dalla LIM. SBSDK sdk = new SBSDK(); sdk.attach( Window topFrame , boolean XMLAnnotations ); sdk.registerComponent( Component panel , SBSDKListener BoardListener ); Codice 1: Codice per la connessione alla Lavagna Interattiva SmartBoard. Dapprima è necessario istanziare l'oggetto principale per l'interfacciamento con il PC, quindi, si richiama il metodo attach, comunicandogli la finestra del programma da monitorare. Una volta fatto ciò, la funzione registerComponent definisce il pannello su cui controllare, tramite l'oggetto BoardListener, le azioni eseguite dall'utente. Quello che è stato mostrato fin'ora è la routine generica per abilitare l'ascolto degli eventi della lavagna, valida per i linguaggi di programmazione supportati (C++, C#, VisualBasic, Java, Flash ) ma che può avere, in base al linguaggio scelto un'estensione più lunga e complessa come nel caso del C#, oppure può essere semplice e veloce come per l'esempio visto, in Java. Proprio grazie al fatto che il codice necessario è più veloce e leggibile, si è scelto di incentrarsi sul linguaggio Java, il quale offre innumerevoli metodi e classi, soprattutto per ciò che riguarda software multimediali ed ad alto tasso di interattività. 3.1 Descrizione API Java Un ambiente di sviluppo che risulti semplice da programmare soprattutto per quel che riguarda la parte interattiva-grafica è rappresentato dal linguaggio Java. Grazie a quest'ultimo è possibile sviluppare applicazioni ad alto tasso interattivo e con una grafica semplice da utilizzare e visualizzare, senza dover appoggiarsi a programmi o librerie esterne. L'unico pacchetto da includere in un software interfacciato ad una LIM è quello appunto descritto nella sezione precedente. La libreria “sbsdk.jar” fornita da SMART INC, accoppiata al file DLL, è sufficiente a mettere a nostra disposizione tutti gli strumenti necessari a sviluppare la nostra applicazione interattiva. 14 Principalmente, la libreria è composta da una classe ed un'interfaccia da implementare, con in aggiunta altre due classi, una statica contente campi di tipo static indicanti gli strumenti o le penne della lavagna, l'altra rappresentate le informazioni del software SmartBoard installato nel PC. La classe principale, come detto ad inizio capitolo è com.smarttech.board.sbsdk.SBSDK, che ha la principale funzione di associare il codice che si sta scrivendo con il software Smart, e permette inoltre di ricavare informazioni riguardo lo strumento in uso, alla sua dimensione o il colore della penna, o di controllare lo stato della connessione di una o più lavagne, e di verificare se si sta lavorando in modalità “projected” (con proiettore acceso), o “non-projected” (con proiettore spento). Per la connessione ad una o più lavagne il metodo della classe è: boolean attach(Window topLevelWindow, boolean bSendXMLNote) Con la chiamata alla funzione attach, si eseguirà l'associazione e la connessione tra la lavagna e la finestra del software da controllare. Una volta specificata la finestra principale, ogni Component che è contenuto al suo interno, è un possibile candidato all'associazione con un ascoltatore di eventi (come vedremo in seguito) della lavagna. Il secondo parametro di questo metodo rappresenta un flag utilizzato per decidere se si vogliono ricevere informazioni in formato XML dal software-driver della LIM. Una volta effettuata la connessione tra PC e lavagna, il metodo restituisce true e l'oggetto SDK a cui appartiene questa funzione, abilita un flusso di dati sugli eventi avvenuti all'interno della finestra specificata. void registerComponent(Component c, SBSDKListener l) void unregisterComponent(Component c) In seguito all'interfacciamento con la lavagna eseguito con la precedente funzione attach, la chiamata a registerComponent associa l'ascoltatore di eventi SBSDKListener al componente specificato. E' quindi possibile differenziare il comportamento del nostro programma in seguito all'uso di un determinato strumento su quello specifico oggetto della finestra, permettendo la creazione di interfacce complesse e con ampie possibilità di personalizzazione. Prendiamo come esempio un bottone cliccabile dai pennarelli ed uno utilizzabile solo via mouse, è possibile ottenere questo comportamento associando SBSDKListener solo sul primo dei due pulsanti. Analogamente, unregisterComponent permette di dissociare il listener al componente specificato in 15 fase di chiamata. Per ricavare le informazioni sulla lavagna connessa e per poter verificare se, in caso di connessione multipla, una determinata lavagna è connessa al PC o meno, si possono utilizzare i metodi seguenti: int getBoardCount() int getConnectedBoardCount() boolean isABoardConnected() Tramite getBoardCount si ottiene il numero di lavagne collegate fisicamente alla macchina, anche nel caso che esse siano spente o non correttamente configurate. Per ottenere invece, il numero di lavagne effettivamente funzionanti, è necessario richiamare il metodo getConnectedBoardCount che restituisce appunto tale dato. isABoardConnected invece, ha l'utilità di controllare se almeno una lavagna è collegata al PC; questa condizione va necessariamente verificata, prima di effettuare qualsiasi tentativo di connessione tra software e controller. int getBoardNumberFromPointerID(int iPointerID) boolean isBoardProjected(int iPointerID) Color getToolColor(int iPointerID) int getToolType(int iPointerID) int getToolWidth(int iPointerID) Come è già stato detto in precedenza, le API in questione permettono la scrittura di codice per la gestione di più lavagne. Fino ad ora, non è stato detto però come poter riconoscere e differenziare le varie lavagne; ogni L.I.M. Smart, è caratterizzata da un identificativo iPointerID univoco. Tramite questo ID, si possono ricavare numerose informazioni sulla lavagna corrispondente. In caso di più lavagne utilizzate simultaneamente, l'oggetto che gestisce la connessione ad esse as segna, al momento del collegamento,l'indice di un array per ognuna di esse; tale indice è ottenibile dalla funzione getBoardNumberFromPointerID. 16 Per verificare invece se si sta lavorando in modalità proiezione, tramite iPointerID è possibile chiamare il metodo isBoardProjected, il quale, tramite valore booleano ci dirà la modalità utilizzata. Per quello che riguarda invece le informazioni riguardanti gli strumenti utilizzati, i tre metodi getToolColor, getToolType e getToolWidth in base al iPointerID ricevuto come parametro, restituiscono rispettivamente il colore dello strumento (per il cancellino, il colore ottenuto è il bianco), il tipo di oggetto in uso (le penne fanno parte del tipo 1, mentre il cancellino ha l'intero 2) e lo spessore dell'oggetto (per le penne lo spessore standard è circa di 4 pixel, mentre per il cancellino i pixel sono all'incirca 40). Grazie a questi metodi, è possibile conoscere con esattezza cosa l'utente stia utilizzando con la lavagna. Ogni funzione, si basa sulle risposte ricevute dal software-driver SMART, il quale permette di modificare l'azione che ogni pennarello deve eseguire. E' possibile infatti cambiare il colore attuale ad ogni penna, come ad esempio specificare il colore grigio per la penna nera, oppure fare in modo che una determinata penna disegni un cerchio ad ogni tocco, invece che il normale disegno a mano libera. In caso di modifiche di questo tipo, la chiamata alla funzione getToolColor restituirà il colore grigio e non quello nero, mentre la funzione getToolType avrà come valore di ritorno quello corrispondente al cerchio (per esempio 1 è il valore della penna a mano libera, 2 il cancellino e così via). Con i metodi appena visti, tramite iPointerID, si ricavano le informazioni del tool nel momento del loro utilizzo. Di primo acchito quindi, sembra che sia sufficiente la classe appena vista per avere tutti i dati necessari all'uso della lavagna, invece, è d'obbligo l'uso di SBSDKListener. Implementando l'interfaccia del listener SmartBoard si possono personalizzare i metodi, quindi, ciò che il nostro programma computa ad ogni evento della lavagna. SBSDKListener fornisce vari metodi i quali vengono richiamati automaticamente al momento dell'azione corrispettiva. Gli eventi catturati coprono tutte le possibili azioni della lavagna, ed in particolare dobbiamo segnalare innanzitutto quello che controlla la connessione/disconnessione con la lavagna: void onBoardSettingsChange() In caso di disconnessione della lavagna dal PC, o di cambio delle impostazioni dell'applicazionedriver SMARTBoard, questo metodo viene richiamato. Una sua corretta implementazione permette il controllo e l'adattamento del software sviluppato ad ogni cambiamento della lavagna. Passando ai metodi degli strumenti, ne troviamo alcuni che tra loro si assomigliano per ciò che riguarda la firma. Tutti quanti infatti vengono richiamati ed eseguiti con un parametro solo, iPointerID, che è lo stesso visto poco prima in questo capitolo. 17 Ogni metodo ha come prefisso nel nome la sillaba on, che tradotto dall'inglese vale come dire: “in caso di”, e quindi il nome effettivo dello strumento causa della chiamata a questa funzione. void onCircle(int iPointerID) void onRectangle(int iPointerID) void onLine(int iPointerID) void onPen(int iPointerID) void onEraser(int iPointerID) void onNoTool(int iPointerID) I primi tre metodi nella cornice sopra, sono quelli che vengono richiamati in caso di azione eseguita nel programma di controllo SmartBoard. In caso infatti di selezione dei pulsanti dalla floating bar (la barra non ancorata allo schermo facente parte del software di controllo della lavagna), che siano il pulsante cerchio, rettangolo o linea, il listener riceverà la richiesta del metodo relativo (onCircle, onRectangle, onLine) ed eseguirà il codice scritto in fase di implementazione del listener. I tre metodi successivi invece, corrispondono alle funzioni richiamate automaticamente durante l'uso fisico della lavagna (senza toccare quindi, il software di controllo Smart). Per primo, troviamo onPen, il quale in seguito all'azione di rimozione di una penna dal pen tray (la barra delle penne) viene eseguito per un dato iPointerID. Tramite questo iPointerID quindi, è possibile, sfruttando la classe SBSDK descritta ad inizio sezione, ed i suoi metodi getToolColor, getToolType e getToolWidth (Pag. 16), ottenere tutte le informazioni necessarie a capire quale penna è stata utilizzata e cosa il nostro software deve fare a seguito di questo evento. Analogamente, in caso di rimozione del cancellino dalla barra, la chiamata ad onEraser risulterà automatica, e le possibili azioni e informazioni da ricavare tramite iPointerID sono le stesse. Va ricordato però che il getToolType in questo caso restituirà un valore diverso da quello ottenuto per il metodo onPen, poiché il cancellino appartiene ad una diversa tipologia di strumento rispetto alle penne. Infine, troviamo onNoTool, il quale, da come si può intuire dal nome, è il metodo conseguente all'evento di posa di tutte le penne nella tray. Infatti, dopo aver posato tutti gli strumenti, il listener avvierà questa funzione, per poter informare il nostro programma che ogni successiva azione è da considerare come effettuata con il mouse o con un dito, o comunque con uno strumento che non appartiene alla lavagna. Gli altri metodi appartenenti alla classe SBSDKListener riguardano le azioni di pressione sulla 18 lavagna. Sono infatti rappresentati dalle varie funzioni che vengono avviate in caso di pressione, dragging o rilascio di uno strumento sulla superficie sensibile al tocco. Anche per questo gruppo di metodi, possiamo dividerli in due sottogruppi: void onXYDown(int x, int y, int z, int iPointerID) void onXYMove(int x, int y, int z, int iPointerID) void onXYUp(int x, int y, int z, int iPointerID) void onXYNonProjectedDown(int x, int y, int z, int iPointerID) void onXYNonProjectedMove(int x, int y, int z, int iPointerID) void onXYNonProjectedUp(int x, int y, int z, int iPointerID) Il primo è rappresentato dalle funzioni che vengono chiamate nel caso in cui la lavagna stia funzionando in modalità proiezione (quanto il proiettore è attivo). I tre metodi di questa suddivisione, vengono quindi eseguiti al momento della rispettiva azione sulla lavagna nel caso in cui il proiettore sia acceso. onXYDown esegue il codice al suo interno al momento della sola pressione sulla lavagna di un qualsiasi oggetto. Non importa infatti con cosa venga effettuata la pressione, poiché lo strumento è ottenibile tramite uno dei parametri di funzione, iPointerID che grazie ai metodi della classe SBSDK ci permette di ricavare le informazioni sull'oggetto utilizzato. Gli altri parametri del metodo rappresentano la coordinata orizzontale (int x), quella verticale (int y) ed un altro valore che rappresenta la quantità di pressione (int z) che non è però utilizzato nel caso di lavagne analogicoresistive. Analogamente al metodo appena descritto, troviamo onXYMove, il quale, tramite gli stessi parametri, esegue il codice al suo interno a seguito di un dragging con un oggetto sulla lavagna ( pressione e spostamento del cursore simultaneamente). Il terzo metodo, onXYUp, da come si può intuire dal nome, viene richiamato al momento dello spostamento dello strumento dalla superficie della lavagna. Il secondo sottoinsieme di metodi, è caratterizzato da tre funzioni speculari a quelle appena descritte, ma che vengono eseguite in caso di proiettore spento, e di uso quindi della lavagna come semplice superficie sensibile al tocco. I tre metodi si comportano esattamente come i corrispettivi per il caso di proiettore attivo; onXYNonPojectedDown è associabile a onXYDown, onXYMove con onXYNonProjectedMove e onXYUp con onXYNonProjectedUp. 19 Una particolare nota da segnalare a riguardo di queste funzioni e dell'action Listener per la lavagna, riguarda il multitouch. Come detto in fase di descrizione della tecnologia analogico-resistiva (Capitolo 2.1 ), questo tipo di lavagne non permette la cattura di più pressioni simultanee. In caso infatti, di tale evento, i dati che si ottengono nelle varie funzioni di cattura dell'evento ( onXYDown per esempio) non corrispondono ad entrambi i punti premuti o ad uno solo dei due, ma ad un'interpolazione lineare tra le due posizioni. Per fare più chiarezza su questo fatto, prendiamo in esempio due semplici punti: P1(x,y) = (1,1) P2(x,y) = (7,1) In questo caso, le coordinate visualizzabili nel metodo onXYDown risulteranno: P3(x,y) = (4,1) Figura 3.1: Rappresentazione schematica dell'esempio descritto: Nell'immagine sopra è disegnata una griglia rappresentate i possibili punti di contatto, in maniera tale da rendere più comprensibile il concetto. In rosso il primo punto di contatto, in blu il secondo. In verde vediamo la risultante calcolata dalla lavagna tramite interpolazione dei punti 1 e 2. Il valore cosi ottenuto quindi, non rappresenta alcun tipo di dato utile al nostro fine, poiché in base alle informazioni all'interno del software, tale risultato può significare un singolo tocco in quel punto, un tocco doppio (come nell'esempio di P1 e P2) oppure tocco multiplo (con interpolazione di più di due punti). A seguito delle considerazioni appena fatte quindi, non si ha nessuna possibilità di sfruttare il multitouch con gli strumenti a disposizione. 20 3.2 Considerazioni su User Interface Dopo aver visto le caratteristiche delle API disponibili con la lavagna, si possono valutare i pro ed i contro, ma soprattutto, è possibile capire cosa la nostra applicazione può fare, e quali eventi possono essere catturati o gesti. Come ampiamente scritto in precedenza, il multitouch non è sfruttabile, e questo ci limita notevolmente nella scrittura di applicazioni ad alto tasso di interattività. Considerando la conformazione e la grandezza della lavagna, bisogna fare attenzione a limitare al massimo gli input dell'utente. La lavagna infatti, è uno strumento facile e veloce da utilizzare, a patto però che le applicazioni che si usano siano studiate per appositamente per le LIM, o richiedano il minimo sforzo da parte dell'utilizzatore. Prendiamo in esempio il caso di una presentazione multimediale, dove gli input sono ridotti ai minimi termini: cambio slide, inserimento di un commento o di una nota, avvio o terminazione della presentazione. In questo caso non c'è bisogno di un'interfaccia grafica complessa o particolare, poiché i comandi necessari al corretto svolgimento dell'azione sono pochi, e facilmente collocabili all'interno di un programma (per le lavagne Smart Board esiste un plugin apposito per Powerpoint, fornito assieme al software di controllo). Se contrariamente invece, prendiamo il caso di un software di elaborazione fogli di calcolo (ad esempio Excel), non è difficile immaginare le difficoltà dell'utente. Ogni volta che si vuole inserire in una cella un qualsiasi carattere alfanumerico, è necessario premere il pulsante nella tray bar corrispondente alla tastiera, attendere la comparsa della tastiera a schermo, premere con un dito o uno strumento i caratteri da inserire e quindi tornare al nostro software di fogli di calcolo. Il procedimento è molto macchinoso da effettuare, soprattutto se le celle da compilare sono in numero elevato. In alternativa, è possibile utilizzare un programma apposito della lavagna che riconosce la scrittura manuale, ma come per la tastiera a schermo, è necessario attivare la finestra, spostarci su di essa, scrivere manualmente il carattere e verificare che il programma lo abbia riconosciuto correttamente, ed in caso contrario si dovrà eliminare il carattere appena inserito, e riprovare a scriverlo. Il tool sarebbe quindi un'ottima alternativa all'uso della tastiera, se solo fosse più comodo e veloce da attivare, e se riconoscesse perfettamente i caratteri immessi (sarebbe sufficiente una fase di apprendimento della propria scrittura). Come ultima soluzione al problema di cui sopra, rimane sempre la possibilità di utilizzare la normale tastiera connessa al PC, tenendo conto però del fatto che non si troverà mai attaccata alla lavagna, ed in alcuni casi non è situata in posizioni semplici da raggiungere con frequenza. Altro esempio utile a sottolineare questo problema è la navigazione internet, per la quale la lavagna può essere un comodo strumento per mostrare a molte persone un determinato sito e navigarci all'interno (i click si effettuano premendo proprio fisicamente i link), è dato dal fatto che l'inserimento manuale dei link, come detto sopra, risulta troppo macchinoso. Inserire un link utilizzando il riconoscimento della scrittura è troppo complesso, le probabilità di riconoscimento errato degli input è molto alta. L'inserimento tramite tastiera a schermo invece è più semplice, ma è un procedimento lento e frustrante, si pensi ad un qualsiasi sito con indirizzo esteso o con caratteri particolari, per inserirlo completamente si spreca troppo tempo inutilmente. 21 Come abbiamo visto quindi, ogni applicazione che contiene sezioni relative all'immissione di campi di testo, risulta troppo difficoltosa nell'ambito delle lavagne multimediali. Il concetto di multimedialità infatti, per quanto riguarda le LIM, è limitato all'uso di pennarelli, mouse o facendo pressione con le dita, e di conseguenza, l'interfaccia utilizzata in questo ambito deve essere semplice da vedere e da utilizzare, possibilmente con pulsanti, e menù contestuali. In aggiunta, è necessario evitare una grafica troppo complessa, che potrebbe essere una limitazione all'utilizzo, infatti, una LIM ha dimensioni superiori ad uno schermo di un PC e quindi, l'utente che la utilizza non ha la visione ampia come su un normale monitor. Si può quindi giungere alla conclusione che l'interfaccia deve presentare gli oggetti indispensabili alle funzioni primarie dell'applicazione, con la possibilità di mostrare con semplicità anche gli strumenti secondari ( ma solo in seguito ad una richiesta esplicita in fase di uso del programma), rendendo così più veloci le operazioni più comuni, essendo quindi, la user-interface, minimale e funzionale, evitando gli elementi ridondanti o di scarso utilizzo. 3.3 Possibili soluzioni Le considerazioni fatte fino ad ora sono servite come materiale di studio riguardante una possibile interfaccia semplice e pulita, che si adatti possibilmente ad ogni tipo di software che sfrutta le lavagne interattive multimediali. Il metodo di interfacciamento da sviluppare deve inoltre, garantire a chi utilizza la lavagna di poter apprendere velocemente il funzionamento del programma stesso, e di fare in modo che ogni componente sia facile da trovare ed utilizzare. Ne sono derivate quindi tre possibili soluzioni candidate al nostro fine. La prima soluzione proposta, è rappresentata dall'utilizzo di una toolbar come “menu di penna” (Capitolo 3.3.1), lo sfruttamento di un motore in grado di riconoscere la scrittura manuale dell'utente (Capitolo 3.3.2), e la gestione delle azioni tramite interpretazione di gesture con gli strumenti a disposizione (Capitolo 3.3.3). Vengono in seguito analizzate le tre soluzioni in maniera più approfondita, mostrando pregi, difetti e dove possibile, esempi concreti del loro utilizzo. 3.3.1 - Il “menu di penna” L'idea del menù di penna inizialmente può sembrare fin troppo banale; si tratta di una semplicissima menù bar generata tramite la libreria Swing di Java, contenente un gruppo di elementi per ogni colore di penna a disposizione della lavagna (più un menù per il cancellino). Nonostante quindi l'idea di base molto semplice, questa tecnica permette di avere sotto mano uno strumento completo e facile da utilizzare. Il punto di forza principale è sicuramente rappresentato dalla posizione e dalla conformazione del menù. La toolbar si trova infatti nella parte bassa dello schermo, poco sopra il vassoio su cui le penne sono appoggiate. Ogni penna (cancellino compreso) ha un menù ad essa corrispondente, che è situato in posizione relativa allo strumento (ad esempio vicino alla posizione della penna nera sul vassoio, vi è il menù corrispondente alle funzioni per lo strumento nero) ed è caratterizzato da un colore di sfondo uguale a quello della penna associata. La posizione, le dimensioni contenute ed i pochi bottoni presenti, 22 rappresentano quindi risposte positive alle richieste presentate in precedenza, rendendo quindi la soluzione valida. Il passo successivo, è sicuramente quello di verificare la fattibilità della toolbar in questione con il linguaggio Java. La flessibilità del linguaggio di programmazione da noi scelto, ci permette per l'appunto di poter creare e posizionare il menù nella posizione per esso migliore. E' possibile inoltre creare menù separati per ogni penna e dividerli con dello spazio libero, così da rendere più pulita l'interfaccia. Un altrettanto utile elemento a disposizione è dato dalla possibilità di associare ad ogni elemento del menù una icona caratteristica per quella funzione. Infine segnaliamo anche la presenza di sottomenù utili a dividere in categorie le varie azioni da assegnare alle penne. Figura 3.2: La toolbar è formata da cinque menù differenziati per colore, ognuno della stessa rappresentazione cromatica della penna corrispondente. Ogni menù è reso isolato dagli altri tramite dello spazio libero, così da rendere più intuitivo l'uso della toolbar. Se vogliamo alcuni esempi di azioni attuabili con questa toolbar, è sufficiente pensare all'esempio descritto nella sezione precedente, riguardante la scrittura di testo; tramite l'utilizzo del “menù di penna” possiamo associare ad ogni penna una determinata parola (inserendo quelle di uso più comune, o dando all'utente la possibilità di specificare la parola da impostare per la penna) e fare in modo che ogni volta che si clicca con quello strumento, la parola venga automaticamente inserita. In alternativa, si possono sfruttare le azioni personalizzabili per assegnare ad una determinata penna la simulazione della pressione di determinati tasti, o di combinazioni di essi, come ad esempio la pressione del tasto destro del mouse (tale pulsante è presente sulla lavagna, ma per sfruttarlo è necessaria una sequenza di operazioni molto macchinosa). Ne deriva da quanto detto, che la soluzione proposta è valida e realizzabile concretamente, e verrà quindi tenuta in considerazione nella scelta per la realizzazione dell'interfaccia. 3.3.2 - Handwriting Recognition Un'altra valida risposta alla semplificazione dell'interfaccia è rappresentata dall'utilizzo di un Handwriting Recognition. Tale tecnica altro non è che una scansione OCR su di un'immagine in cui l'utente ha scritto a mano del testo. Il funzionamento è di per se semplice, poiché è sufficiente creare una zona “sensibile” in cui l'utente scrive del testo. Questo testo verrà poi preso in input e riconosciuto, trasformando in stringa l'immagine presa in oggetto. Data la stringa quindi, è facile immaginare e decidere, dal lato del programmatore, cosa il software deve fare (inserire quel testo in un campo, piuttosto che cercare un'azione corrispondente al testo inserito). L'idea di base sembra ottima, ma ci sono delle limitazioni; nonostante i molti tentativi fatti, ed i vari 23 programmi presenti nel web, non è stato possibile trovare una soluzione soddisfacente. Cercando informazioni a riguardo, ci siamo imbattuti in un discorso interessante da parte di una sviluppatrice Java (Dana Nourie [9]) che aveva iniziato il progetto di un HWOCR (Handwriting OCR) per la tecnologia Java, ma, di aver dovuto abbandonare il progetto, poiché la tecnologia Java non permetteva un corretto riconoscimento dell'immagine da processare. L'idea di base di tale progetto si basava sul modello di Markov ( [10] ) e l'algoritmo di Viterbi ( [11] ).[12] In aggiunta a questo problema, va considerato anche il fatto che, nei dispositivi in cui HWOCR è funzionante, viene imposto all'utente di scrivere i caratteri secondo un certo schema logico e seguendo determinate dimensioni. Queste restrizioni, nel nostro caso non potrebbero essere attuate, poiché se l'interfaccia presentasse tutte queste limitazioni, si uscirebbe dai canoni di semplicità e duttilità descritti in precedenza. Ai problemi appena incontrati, è però possibile ovviare, con due eventuali casi: Il primo è rappresentato dall'opportunità di inviare ad un programma esterno l'immagine riportante la scrittura manuale. Tale pratica renderebbe le cose semplici poiché il programma esterno, data un'immagine in input, restituisce la stringa corrispondente al nostro software. In realtà però questo meccanismo non è cosi semplice, dal momento che non è stato trovato alcun programma che riceve in input un'immagine. Tutte le proposte trovate infatti, non accettano input se non quello in tempo reale del mouse, e questo fatto, come ampiamente detto fin'ora non è tollerabile ai fini di un software che segue le richieste ipotizzate. La seconda soluzione è rappresentata dal HWOCR integrato nel software della tastiera. Questo semplice riconoscitore è uguale a quello che si può trovare in un palmare; presenta un campo in cui inserire il testo a mano, il quale verrà poi trasformato in stringa ed inserito nell'ultima posizione in cui era situato il mouse. La precisione nel riconoscere la scrittura, dopo alcuni tentativi, è risultata piuttosto buona, tuttavia la lentezza nel ricevere l'input e dare l'output, e la macchinosità della finestra (per visualizzarla è necessario aprire la tastiera a schermo e quindi selezionare la voce relativa al riconoscimento della scrittura) rendono l'alternativa inaccettabile. Abbiamo quindi visionato anche questa alternativa, reputandola valida per quanto riguarda l'ipotesi, considerandola improponibile per la sua fattibilità, ma soprattutto troppo macchinosa per poter soddisfare il requisito di user-interface minimale e veloce da utilizzare. 3.3.3 - Controllo tramite Gestures La terza soluzione proposta per lo sviluppo dell'interfaccia è basata sul riconoscimento di gestures, predefinite o personalizzabili, per specificare l'azione da eseguire. Questa tecnica sfrutta, in maniera simile all'HandWriting Character Recognition, un pannello dell'interfaccia grafica sensibile ai movimenti ( che può essere specifico o l'interfaccia per tutta la sua estensione) di click, dragging e pressione degli strumenti sulla lavagna. Definendo preventivamente delle gesture standard è possibile differenziare ogni azione da svolgere e classificarla in base al movimento richiesto. Per poter abilitare una penna ad una determinata azione, è sufficiente svolgere la gesture ad essa associata nella zona sensibile. Il software si incarica di leggere i movimenti fatti dall'utente e di interpretarli come comandi. 24 L'idea di base è intuitiva e sembrerebbe, tra quelle proposte, la più “user-friendly”. La tecnica in questione però presenta anche alcuni problemi, primo tra tutti il fatto che l'utente debba imparare o impostare le gestures e, sebbene la personalizzazione sia semplice, l'apprendimento rappresenta un ostacolo. Se ogni utente infatti, dovesse imparare più di tre o quattro gesture, il tempo di apprendimento crescerebbe notevolmente, riducendo la semplicità di utilizzo. Tuttavia superata la fase di studio delle gesture, l'esecuzione risulta veloce e scorrevole. Analizzando quindi meglio i metodi per poter sviluppare tale interfaccia, incappiamo nuovamente nella necessità di riconoscere ed interpretare forme specifiche, rimandandoci al problema del riconoscimento della scrittura a mano, che, come detto in precedenza non è nel nostro caso una soluzione attuabile. 25 26 Capitolo 4: Fisica 2D Con l'aumentare della potenza di calcolo delle CPU è cresciuto anche il desiderio di sfruttare pienamente tali processori. Tralasciando i vari programmi di benchmarking, l'alternativa allo sfruttamento della sempre maggior frequenza delle unità di elaborazione è data dall'utilizzo di programmi di simulazione fisica. La categoria è estremamente ampia, basti pensare ai motori fisici nei videogiochi, a quelli utilizzati per il testing delle autovetture da competizione o dalla riproduzione del volo di un aeroplano, ma nel nostro caso ci limiteremo alla riproduzione di un ambiente di fisica bidimensionale. Per avere un'idea reale di questa situazione pensiamo al gioco del biliardo. Ogni movimento è eseguito seguendo due assi cartesiani (tralasciando i tiri impropri del gioco in cui la pallina si solleva dalla superficie del tavolo ) ed ogni contatto tra due o più elementi è elastico, cioè nessun componente modifica la sua forma, gli oggetti mobili (le palle) reagiscono ad ogni contatto subendo una forza ed un'accelerazione muovendosi, mentre gli oggetti che chiamiamo impropriamente “fissi” (le sponde del tavolo) subisco e restituiscono forze, ma non presentano alcun movimento. Per rappresentare quindi l'ambiente appena descritto, sarà necessario un motore fisico in grado di simulare azioni e reazioni in base a valori fisici decisi a priori (come massa, densità, attrito) e di estrapolare tutte le informazioni, rappresentandole sotto forma di dati (valori interi, stringhe di caratteri, valori booleani) che verranno poi valutati ed utilizzati da un motore grafico apposito. Il motore fisico che si vuole utilizzare, deve essere però il più preciso possibile, poiché, anche un semplice errore può significare una simulazione totalmente discostata dalla realtà. Per esempio possiamo pensare al momento della collisione tra una palla ed una sponda del tavolo da biliardo, in questo caso un piccolo errore di calcolo delle forze in gioco, può consegnare al motore grafico dei dati molto diversi da quelli attesi, con conseguente risultato di vedere magari, la pallina che tocca perpendicolarmente una sponda ed invece che tenere la stessa direzione con verso opposto, cambia direzione, rimbalzando in maniera inaspettata. Riguardo la precisione dei simulatori di fisica, va precisato che quest'ultimi si dividono in due categorie; ad alta precisione o real-time. Nel primo gruppo troviamo i motori utili a calcoli precisi ed estremamente fedeli alla realtà, come nel caso della simulazione del comportamento dei pneumatici. Per poter avere dei dati precisi che 27 rispecchino le vere caratteristiche della gomma, è necessario testare le possibili soluzioni utilizzando un motore fisico che simuli esattamente una situazione reale. Questo motore fisico per poter essere preciso deve necessariamente fare uso di algoritmi complessi, con un conseguente carico elevato per il processore. Al fine di poter ottenere i dati in un tempo accettabile il simulatore compie tutti i calcoli prima di mostrare i risultati, con la conseguenza di non poter modificare i dati in fase di visualizzazione della simulazione, ma solo di modificarli ed effettuare una nuova situazione. Questo accorgimento permette al software di sfruttare tutta la potenza elaborativa sulle proprietà degli oggetti da simulare, evitando quindi l'utilizzo di cicli macchina nella verifica e nel calcolo degli input dati dall'utente. In opposizione ai motori fisici ad alta precisione, troviamo quelli real-time che permettono all'utente di modificare l'ambiente, le proprietà degli oggetti (massa, posizione, direzione, verso) e di aggiungerne di nuovi o eliminare quelli presenti. Ne deriva quindi che i calcoli alla base del motore saranno semplificati, in modo da permettere al software di rispondere in tempo reale agli input dell'utente e di calcolare ad ogni istante i parametri fisici caratteristici degli oggetti. Di norma, i motori fisici real-time, calcolano le proprietà fisiche degli oggetti in movimento, mentre per quelli che stanno in una situazione di inerzia rispetto all'ambiente, le rispettive proprietà vengono bloccate fino al momento in cui l'oggetto non viene mosso da un input dell'utente o dal contatto con un altro oggetto, risultando così per quel lasso di tempo, oggetti statici. Il riconoscimento delle collisioni all'interno del sistema viene effettuato dal motore fisico per mezzo di un algoritmo apposito. Non vi sono particolari specifiche riguardo l'algoritmo da utilizzare, ma può sfruttare due tecniche diverse, che sono il calcolo della collisione a priori (prima del contatto) o a posteriori (dopo il contatto). Sfruttando il calcolo a priori, l'algoritmo utilizzato deve essere basato sul tempo, e deve prevedere esattamente la velocità, la traiettoria ed i movimenti degli oggetti nel sistema. Grazie a queste previsioni, i contatti tra gli oggetti vengono calcolati con una precisione molto alta, evitando che i corpi si sovrappongano nella stessa posizione. Questo metodo viene chiamato a priori perché la collisione tra gli oggetti è calcolata nell'istante prima di aggiornare la configurazione dei corpi in questione, anticipando il contatto. A vantaggio del calcolo a priori, troviamo una fedeltà maggiore nei movimenti dei corpi ed una maggior stabilità nella visualizzazione grafica, poiché con il calcolo anticipato, non ci si troverà mai ad avere due oggetti sovrapposti (situazione che non rispecchia la realtà). Il calcolo a posteriori invece sfrutta una tecnica totalmente differente, ma che semplifica di molto la gestione della fisica. La simulazione grafica viene appositamente mostrata un'istante dopo quella risultante dagli algoritmi implementati, quindi, ad ogni istante della simulazione, l'algoritmo che si incarica di riconoscere le collisioni riceve in input tutti gli oggetti presenti nel sistema e si occupa di verificare se ci sono contatti tra di essi. Questa tecnica è chiamata a posteriori poiché la collisione viene riconosciuta solo dopo essere avvenuta, mancando di fatto il momento del contatto. A favore di questo metodo però, troviamo sicuramente la maggior semplicità nello scrivere l'algoritmo di cattura delle collisioni e una maggior velocità nell'eseguirlo. Non c'è infatti il bisogno di prendere in considerazione tutte le possibili variabili che interferiscono con il sistema ed i suoi componenti, ed in caso di una quantità elevata di oggetti, l'algoritmo dovrà soltanto prendere in input una lista di tali elementi, e verificarne la possibile sovrapposizione, senza curarsi di tutte le proprietà fisiche in gioco.[13] 28 4.1 Pacchetti di fisica 2D disponibili Come abbiamo visto, i motori fisici possono essere definiti attraverso alcune semplici suddivisioni in base al loro fine ed alle tecniche sfruttate per il funzionamento. Considerando il nostro interesse nello sviluppare un'interfaccia utente per un software multimediale che fa uso delle lavagne interattive, sicuramente possiamo indirizzarci verso un motore fisico real-time, il quale permette all'utente di interagire attivamente e di modificare il sistema in azione. Di seguito quindi, passeremo ad analizzare alcuni dei pacchetti di fisica a disposizione. Per brevità non li potremo elencare tutti, sia per la vastità di proposte, sia per la difficoltà nel reperire informazioni a riguardo di alcuni di loro. Tra le varie soluzioni che non abbiamo proposto, segnaliamo Farseer Physics Engine e Physics2D.Net per quanto riguarda i pacchetti open source, mentre per quelli a livello commerciale Havok e PhysiX, i quali sono due motori fisici molto famosi e ampiamente utilizzati nel ramo dei videogame (in questo caso la fisica è spesso tridimensionale). Oltre a quelli appena citati ce ne sarebbero molti altri, tra i quali ne abbiamo selezionati cinque, per valutarli come possibili motori per la nostra applicazione interattiva. 4.1.1 - Box2D Box2D è una libreria open source che contiene un motore fisico a due dimensioni. I corpi simulati tramite Box2D sono solidi, cioè non hanno la caratteristica di modificare la propria forma in base agli impulsi ( non possono quindi deformarsi ). Il movimento degli oggetti viene effettuato grazie ad algoritmi che calcolano posizione, velocità e direzione in base soprattutto a tre parametri caratteristici del corpo, che sono densità, elasticità e attrito. Il contatto tra gli oggetti è garantito tramite un algoritmo che viene eseguito in real time e semplificando inizialmente gli oggetti secondo una schema AABB ( ogni oggetto è compreso in un quadrato ). In tal modo, ogni volta che due quadrati AABB vengono a contatto, il motore verificherà un'eventuale collisione tra i relativi elementi del sistema. Il funzionamento e l'utilizzo del motore sono semplificati e veloci; dopo aver creato il “mondo” (per noi il sistema) si procederà a definire delle “forme” (shapes) indicanti il tipo di oggetto da creare, attribuendolo di seguito all'insieme degli elementi del sistema, assegnandogli una massa e, se necessario, unendolo ad altri oggetti (creando cosi nuove forme date dalla composizione di forme base). Completati i passi di creazione, è sufficiente avviare la simulazione per poterla testare e poter muovere gli oggetti. La libreria contiene svariate funzioni per molte azioni, riducendo i limiti riguardo a ciò che si può creare nel sistema. A tale proposito, oltre ad indicare la possibilità dell'unione degli elementi e la cancellazione di quelli già creati, Box2D permette il posizionamento di oggetti statici nell'ambiente, rendendo possibile la simulazione del terreno o lasciandoci creare uno schema predefinito (come può essere una discesa con conseguente salita). In aggiunta a tutto ciò, vi è un elemento di grande spicco reso disponibile dal motore fisico. Questo elemento, chiamato sensore, è uno “strato di ambiente” 29 invisibile che, lancia un'eccezione se qualche oggetto entra nei suoi limiti. L'eccezione, se catturata, può permettere all'utente di eseguire una determinata azione. Questa peculiarità di Box2D, realizzato in C++ da Erin Catto, ha posto le basi per un videogioco commerciale, Crayon Physics Deluxe (Sviluppato da Petri Purho)[14], il quale grazie a questo motore fisico ed all'idea innovativa ha lasciato profondamente il segno, vincendo un premio all'Independent Game Festival. Il gioco mostra al giocatore un mondo caratterizzato da una palla (oggetto da muovere), una serie di ostacoli (da evitare per arrivare alla soluzione) ed una bandiera, che rappresenta graficamente il sensore sopra descritto. Figura 4.1: Crayon Physics: Una schermata del gioco. La stella gialla rappresenta la meta, da raggiungere con la palla rossa. La palla non è controllata direttamente dall'utente ma solo tramite la creazione e lo spostamento di oggetti supplementari, come in questo caso la barra blu ed i quadrati verde e rosso (è stata creata una leva per far muovere la palla). Tramite l'uso del mouse l'utente deve disegnare degli oggetti che serviranno a portare la palla alla bandiera. Il grande successo di questa versione in C++, ha reso possibile la nascita di svariati porting in altri linguaggi di programmazione come Flash, Phyton, JavaScript, Nintendo DS e, come vedremo nella prossima sezione, Java. 30 4.1.2 - JBox2D JBox2D è, come accennato poco sopra, una trasposizione da C++ a Java di Box2D. Le caratteristiche e le funzionalità rimangono pressoché invariate, fatta eccezione per alcuni piccole mancanze. Tra tutte spicca il supporto non completo ai sensori, quest'ultimi infatti sono stati da poco implementati e presentano ancora numerosi problemi, risultando quindi inutilizzabili ai nostri fini. Ad ogni modo, fatta eccezione per questa mancanza tutte le principali e più comuni caratteristiche di Box2D sono presenti anche in questo porting, per esempio la funzione di “sleeping” per gli oggetti non in movimento. La tecnica appena descritta fa si che, se un elemento non è in fase di movimento, trovandosi quindi in una situazione di inerzia rispetto al sistema, tale elemento viene escluso dalla simulazione realtime, diminuendo cosi i calcoli ed i controlli che il software deve eseguire, riducendo di conseguenza il consumo di risorse. Va sicuramente segnalata la feature in programma da parte degli sviluppatori, che permette la serializzazione del mondo e degli oggetti in esso, dando quindi all'utente la possibilità di salvare e continuare in futuro la simulazione. JBox2D è in continuo sviluppo, è presente un gruppo di discussione ed un server SVN ad esso dedicati. 4.1.3 - Phun Il motore fisico Phun è, da come è stato definito dal suo creatore: “un gioco freeware classificato come sandbox ( un gioco cioè che non ha un fine preciso) dove è possibile sfruttare la fisica come mai in precedenza”. La frase definisce effettivamente ciò che Phun rappresenta, infatti tramite questo motore fisico è possibile disegnare qualsiasi cosa, senza limitazioni (disegno a mano libera), animando quindi ogni oggetto creato. Phun è quindi un'applicazione che ci permette di sfruttare interattivamente il motore fisico. A differenza di Box2D (Paragrafo 4.1.1) infatti, con Phun è possibile disegnare liberamente gli elementi che agiranno nell'ambiente simulato. Il simulatore supporta i corpi rigidi (non permette quindi cambi di forma degli stessi), contatti, attriti, molle, motori ed una particolare simulazione riguardante i movimenti dei fluidi. E' possibile inoltre specificare per ogni elemento un colore di riempimento, con in aggiunta l'eventuale modifica dei parametri caratteristici degli elementi, come attrito, coefficiente di rimbalzo, e velocità per i motori. 31 Figura 4.2: Immagini di Phun. Nelle due figure possiamo vedere il motore fisico in azione. A sinistra una esempio in cui una palla si scontra con un muro di mattoni, a destra la finestra per la personalizzazione dei motori. Phun sfrutta un motore fisico interno, sviluppato inizialmente da Emil Ernerfeldt (Umea University, Svezia) come tesi di laurea magistrale e perfezionato in seguito dall'azienda Algoryx[15]. A seguito del passaggio a tale azienda, il programma, pur rimanendo freeware per uso non commerciale, è diventato closed-source e quindi, non è possibile utilizzarlo per la nostra applicazione interattiva a causa dell'impossibilità di interfacciare il nostro software con Phun. 4.1.4 - Tokamak La successiva libreria di fisica simulata è rappresentata da Tokamak. Questo motore fisico open source e sviluppato in C++, è studiato e creato appositamente per simulare la fisica nei videogiochi e sfrutta le librerie Direct X di Microsoft Windows. Il suo funzionamento è caratterizzato da un alto tasso di velocità e precisione. In Figura 4.3 possiamo vedere una schermata durante una simulazione di corpi composti, lasciati cadere liberamente su di una scalinata. 32 Figura 4.3: Caduta di oggetti in Tokamak. In questo esempio possiamo vedere degli oggetti (a forma di persone) che cadono liberamente su degli scalini. Si può vedere la tridimensionalità dell'ambiente. Gli algoritmi di calcolo delle collisioni e del movimento degli oggetti infatti, utilizzano un metodo iterativo molto complesso, che permette agli sviluppatori di avere a disposizione una simulazione precisa e veloce. Questa velocità elevata è garantita appunto dall'algoritmo implementato, il quale non fa uso di pesanti e grosse matrici di dati, evitando allo stesso tempo il problema dell'uso eccessivo di memoria. Grazie a questo accorgimento infatti, Tokamak ha la peculiarità di essere un buon candidato nei casi in cui gli oggetti da muovere e gestire siano piuttosto elevati. Come per le altre librerie viste fin'ora, anche in Tokamak è possibile unire più oggetti tra loro (tramite apposite funzioni di Joint). Da segnalare di particolare, c'è il sistema di riconoscimento dei contatti tra gli oggetti che, oltre ad essere molto efficiente, permette la cattura di collisioni tra forme base ( cubi, sfere e cilindri ) ma anche tra combinazioni di esse ( unioni o joints ), e tra oggetti convessi, riconoscendo con esattezza la parte di elemento che sporge dalla forma base. Una caratteristica particolare non ancora vista nei motori fisici precedenti, è la presenza dei cosiddetti Breakage i quali, dopo essere stati creati, in caso di collisione con un altro oggetto, vengono scomposti in frammenti ( dividendo l'elemento in parti primitive) che seguono traiettorie automaticamente calcolate in base alla collisione. Per quanto riguarda il nostro fine la libreria non è però utilizzabile perché lavora in un ambiente tridimensionale, il quale risulta troppo complesso per essere gestito ed utilizzato interattivamente tramite una semplice lavagna interattiva multimediale, soprattutto dopo aver verificato l'impossibilità di utilizzare il tocco multiplo sulla lavagna stessa. 33 4.1.5 - Phyz L'ultima libreria che andiamo ad analizzare è Phyz, un motore fisico bidimensionale sviluppato per ambiente Windows. Questa restrizione è data dal fatto che il programma fa uso delle librerie di sistema Microsoft DirectX (versione 9) per accelerare la rappresentazione grafica e per poter offrire all'utente suoni che rappresentano la situazione in corso. L'obbiettivo primario è quello di simulare alcuni fenomeni fisici e modellare a proprio piacimento un ambiente fisico e per questo motivo, non è permessa all'utente la modifica dei dati caratteristici degli oggetti (come attrito, peso e coefficiente di rimbalzo). Nonostante questi impedimenti, il motore permette la modellazione di una grande quantità di oggetti senza soffrire di rallentamenti dovuti ai troppi calcoli, sia grazie agli algoritmi implementati, sia in seguito all'adozione di un principio basato su particelle (ogni oggetto è formato dall'unione di più particelle, ad esempio una scatola non ha delle linee diritte, ma più particelle collegate tra loro). Tramite l'utilizzo di questo sistema a particelle inoltre, Phyz implementa la simulazione dinamica dei corpi, dando cosi la possibilità di modificare la loro forma, di romperli o di unirli. Figura 4.4: Immagine rappresentativa di Phyz. Nella figura vediamo una schermata del simulatore. A sinistra una visione dell'insieme del motore, in primo piano sulla destra invece, le “particelle” su cui il motore si basa. Come detto inizialmente però, Phyz è sviluppato in ambiente Windows, facendo uso di DirectX e, fattore non di poca importanza è si libero per l'uso, ma non è open source. Sebbene tramite un'applicazione esterna, presente assieme al motore fisico, permetta la gestione ed il controllo degli eventi, non è possibile nel nostro caso farne uso, poiché quest'applicazione, chiamata PhyzLizp è basata sul linguaggio di programmazione Lisp, il quale non è presente tra i linguaggi abilitati all'interfacciamento con il software e la lavagna SmartBoard. 34 4.2 Analisi delle librerie Dopo aver analizzato alcuni dei molti pacchetti disponibili ci troviamo a dover trarre delle conclusioni riguardo a quali di questi possono risultare utili al nostro fine, cercando anche di valutare le motivazioni per quelli che invece non rappresentano una valida proposta. Tra tutte le considerazioni che possono influire sulla scelta del motore fisico, la prima in assoluto è quella che riguarda il codice sorgente. Il nostro simulatore di fisica, per poter essere utilizzato in abbinamento alla lavagna, deve darci la possibilità di collegarlo alla nostra interfaccia. Nel caso in cui si scelga quindi una libreria non open source, bisogna avere la possibilità di richiamare le funzioni interne al motore fisico direttamente dalla nostra interfaccia. Questa operazione non è però possibile nella quasi totalità delle librerie closed source, poiché non abbiamo questa disponibilità e l'unico modo di utilizzare il simulatore è tramite il mouse, fatto che renderebbe inutile la nostra user interface creata. Analizzando quindi i pacchetti con sorgenti liberi, ci troviamo a dover considerare l'ambiente in cui il software verrà sviluppato. Alcuni motori fisici sono stati implementati appositamente in un dato linguaggio o tramite un'architettura specifica. Per fare un esempio, il simulatore Tokamak, sviluppato in C++ e scritto specialmente per Microsoft Windows, restringe l'ambito di utilizzo al solo sistema operativo sopra elencato. E' tuttavia possibile modificare il codice per farlo funzionare su un altro sistema operativo come ad esempio un sistema Unix, ma questo comporterebbe una verifica e modifica totale del codice. Dalle considerazioni fin qui fatte, ne deriva che la libreria candidata all'utilizzo nel nostro software, dovrebbe teoricamente essere libera da vincoli dati dal sistema operativo. Oltre all'analisi prettamente informatica, per valutare il motore fisico più affine alle nostre necessità, dobbiamo considerare anche il fattore puramente fisico della libreria, ed il suo impatto interattivo se applicato ad una periferica di input come la lavagna interattiva multimediale. Per quel che riguarda il fattore fisico, le caratteristiche del simulatore devono essere si ampie, ma non troppo complesse o macchinose. A questo scopo un motore fisico pesante ed elaborato, che valuta esattamente ogni minimo aspetto della fisica reale (tridimensionalità, effetto del vento, oggetti deformabili), creerebbe già al primo utilizzo delle perplessità da parte della persona che lo utilizza. Poniamo l'esempio dell'ultima libreria visualizzata (Phyz) che è, come visto in precedenza, un simulatore fisico caratterizzato da un'interfaccia grafica poco intuitiva e troppo vincolata ad uno schema standard. Ogni elemento che si vuole creare o modificare deve essere posizionato con precisione, e l'utilizzo di questo simulatore è poco intuivo considerando anche il fatto che, dopo aver posizionato un elemento, per esempio una molla, non è possibile cambiare le sue caratteristiche fisiche (nel nostro caso la costante elastica). Questo approccio semplifica da una parte l'uso, ma riduce l'interattività dell'utente e vincola la simulazione ad una rappresentazione grafica che ha poco a che fare con la realtà. Prendendo in esame la libreria Box2D invece ci troviamo davanti ad un motore fisico totalmente open source, abbiamo assieme ad esso una documentazione completa sulle caratteristiche, sulle funzioni e sul metodo da seguire per sfruttare la fisica simulata. A differenza infatti delle altre librerie, Box2D (e il corrispettivo Java JBox2D) è di fatto un motore fisico non vincolato ad uno specifico codice per il disegno degli oggetti e dell'ambiente. Le funzioni infatti sono fatte per restituire dati generici, lasciando a chi implementa la parte grafica, la scelta del metodo di 35 rappresentazione degli elementi. Questo fatto semplifica, soprattutto nel nostro caso, lo sviluppo di un'applicazione grafica specifica per l'uso che vogliamo fare. Difatti, per sviluppare un simulatore di fisica applicato al contesto delle LIM, dobbiamo innanzitutto avere la piena libertà di modificare e personalizzare il codice di controllo degli input dell'utente, quindi adottare dei metodi appositi per rispondere correttamente agli input ricevuti, ed in seguito rappresentare il tutto tramite una finestra pulita e sgombra da oggetti ridondanti. Tutto questo ci è permesso con il motore fisico analizzato inizialmente. In seguito però alle decisioni prese nei capitoli precedenti però, abbiamo deciso di utilizzare la libreria JBox2D, la quale essendo un porting di Box2D, comprende le stesse caratteristiche, ma aggiunge la flessibilità del linguaggio creato da Sun Microsystem, permettendoci di scrivere un software adatto a più di una specifica architettura. Inoltre, utilizzando JBox2D e sfruttando le API SmartBoard viste nel Capitolo 3.1, possiamo interfacciare in maniera veloce il motore fisico e la nostra user interface appositamente studiata per l'uso con le lavagne interattive multimediali. 36 Capitolo 5: Descrizione dell'architettura Dopo aver analizzato gli strumenti ed i mezzi a nostra disposizione per la creazione del programma oggetto di questa tesi possiamo passare alla scrittura del codice sorgente. Il primo e più importante passo da fare è quello di creare l'interfaccia collegabile alla lavagna, facendo in modo che tale interfaccia sia il più possibile flessibile ed adattabile ad ogni eventuale programma da sviluppare appositamente per la lavagna. Facendo ciò ci ritroveremo con delle classi Java indipendenti dal software che le utilizza, diventando quindi di fatto, una libreria stand alone. Analizzeremo quindi la nostra soluzione mostrandone la flessibilità ed il suo funzionamento nel caso proposto. In seguito alla creazione di questo gruppo di classi vi sarà la necessità di connettere le azioni selezionabili tramite interfaccia con gli eventi reali attuati dagli utenti. Tutto ciò viene fatto sfruttando le API Java Smart Board viste nel Capitolo 3.1, le quali ci permettono di gestire tutti i possibili eventi che possono verificarsi durante l'utilizzo da parte dell'utente. Mostreremo quindi la tecnica adottata per effettuare il controllo degli input sulla lavagna, basandoci sulla soluzione da noi implementata e mostrando come differenziare gli input da strumenti diversi. Infine per completare i passi da compiere troviamo l'interfacciamento delle due parti principali del software, la prima composta da menu grafico e sezione dedicata alla cattura degli eventi, la seconda è rappresentata dal motore fisico. Analizzeremo in questo caso il procedimento da seguire per poter ottenere una corrispondenza biunivoca tra input dell'utente e comportamento del simulatore di fisica, facendo riferimento alla soluzione adottata, mostreremo la creazione degli oggetti a seguito di un'azione eseguita sulla lavagna. 5.1 “Menu di penne” sviluppato Come è stato ampiamente detto nei precedenti capitoli la migliore interfaccia utente per il caso delle lavagne interattive multimediali è sicuramente quella che risulta più semplice da utilizzare, senza però essere complessa o occupare troppe porzioni di schermo. La soluzione che abbiamo scelto di implementare è quella riguardante il “menu di penne”. Tramite questa toolbar infatti possiamo 37 controllare pienamente tutti gli strumenti della lavagna semplicemente selezionando dalla barra stessa il menù che descrive l'oggetto scelto. La posizione della barra potrebbe essere relativa alla scelta dell'utente ma, per evitare confusione e per dare all'utente un punto di riferimento fisso, si è deciso di bloccare il menù in una posizione statica, assegnandogli l'attributo di finestra in primo piano. Tramite questo accorgimento, ogni azione eseguita all'interno del programma associato alla toolbar, non influisce sulla visibilità della barra stessa, che continuerà ad essere un punto di riferimento per l'interfaccia. La classe da creare dovrà quindi essere un'estensione di una classe base per creare le finestre (JFrame), in modo da avere già a disposizione i metodi per la gestione delle finestre stesse. public ToolbarFrame(String s) { super(s); this.addWindowListener(this); this.addComponentListener(this); Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); int hscreen = (int) screen.getHeight(); int wscreen = (int) screen.getWidth(); this.setSize(this.FRAMEW, this.FRAMEH); int frameposx = (wscreen / 2) - (FRAMEW / 2); int frameposy = (hscreen) - (FRAMEH * 3) / 2; this.setLocation(frameposx, frameposy); this.setResizable(false); this.setUndecorated(true); createToolBar(); position = this.getLocationOnScreen(); this.setVisible(true); } Codice 2: Il metodo riportato qui sopra è una parte del costruttore del menu di penna. Dopo aver assegnato alla toolbar i listener per il controllo della posizione, si passerà a specificare la dimensione e la posizione sullo schermo. Infine si procederà con la chiamata alla funzione dei menù interni, per poi rendere visibile la finestra. Nel riquadro sopra, possiamo vedere il costruttore per il menù. Particolare attenzione va posta per quello che riguarda il dimensionamento ed il posizionamento della barra. Dopo aver verificato la risoluzione in uso con il metodo statico Toolkit.getDefaultToolkit().getScreenSize() sfruttiamo tale dato per calcolare la posizione più adatta alla nostra toolbar. Questa posizione è idealmente nella parte bassa dello schermo, centrando orizzontalmente la finestra. La posizione verticale invece, è 38 rapportata all'altezza della nostra barra. Nel nostro caso, il valore FRAMEH (altezza) del menù è di 21 pixel, la sua posizione in verticale è (3/2 *21) pixel sopra il limite basso dello schermo. Con questo calcolo, otteniamo la posizione della barra che è esattamente quella voluta, centrandola orizzontalmente rispetto allo schermo (avendo quindi la corrispondenza tra pentray della lavagna e toolbar), e trovandola a fondo schermo ma con un piccolo spazio al di sotto di essa per ciò che riguarda l'asse delle ordinate. Altre due funzioni che meritano una particolare attenzione sono setResizable e setUndecorated. La prima nega la possibilità di ridimensionare la finestra attraverso il mouse o la tastiera mentre la seconda nasconde la cornice grafica classica dei frame, mostrando all'utente solamente il menù stesso. Grazie a questi due metodi, otteniamo una soluzione a due dei problemi posti all'inizio cioè, semplifichiamo l'uso della toolbar, limitando l'utente a svolgere le sole azioni per cui l'interfaccia è studiata e rendiamo il nostro menù di penna pulito e snello, senza rappresentare graficamente quella sezione di finestra che non si deve e può sfruttare e che rappresenterebbe solo un ostacolo alla visione degli altri frame. Il metodo che viene richiamato subito dopo, createToolbar, contiene il codice che può essere considerato il cuore della toolbar, poiché è quello che compone i menù per ogni penna. private void createToolBar() { listener = new ToolBarListener(this); menubar = new JMenuBar(); menubar.setOpaque(true); menubar.setBackground(Color.WHITE); black = new JMenuTool("Black Pen"); piblack = new PenToolsInfo(Color.BLACK); black.setOpaque(true); black.setBackground(Color.BLACK); black.setForeground(Color.WHITE); black.setMnemonic(KeyEvent.VK_B); black.getAccessibleContext().setAccessibleDescription("Black Pen"); createMenu(black, ActionEvent.ALT_MASK, "Black"); menubar.add(black); menubar.add(Box.createHorizontalGlue()); ... eraser = new JMenuTool("Eraser"); pieraser = new PenToolsInfo(Color.WHITE,"Eraser"); eraser.setOpaque(true); eraser.setBackground(Color.WHITE); 39 eraser.setForeground(Color.BLACK); eraser.setMnemonic(KeyEvent.VK_E); eraser.getAccessibleContext().setAccessibleDescription("Eraser"); menubar.add(eraser); menubar.add(Box.createHorizontalGlue()); this.setJMenuBar(menubar); } Codice 3: Metodo per la creazione dei menù per le penne. In questo riquadro possiamo vedere il codice utilizzato per creare i vari menù per ogni penna. Nell'esempio vediamo solo quelli per la penna nera e per il cancellino, poiché per gli altri colori il procedimento è lo stesso. Come vediamo nel riquadro sopra, dopo aver impostato un ToolbarListener (che vedremo in seguito) si crea un oggetto JMenuBar appartenente alle classi swing di Java. Questo oggetto rappresenta il menù di penne che viene visualizzato in condizione di idle dell'interfaccia. Abbiamo deciso di colorare questa nostra toolbar di bianco, e per poterlo fare è necessario applicare l'attributo setOpaque, altrimenti i metodi di colorazione verrebbero ignorati, e quindi assegnare il colore alla barra tramite setBackground, ottenendo la base per proseguire nella creazione delle sezioni delle penne. Nell'esempio troviamo il procedimento per la creazione del menù della penna nera, che inizia con l'istanziazione di un oggetto JMenuTool, il quale è un'estensione della classe JMenu di Java, con in aggiunta i metodi per impostare e conoscere il tool attualmente selezionato per quella penna (getTool e setTool) ed il metodo per avere il codice colore della penna associata a quel menù. In seguito viene creato un oggetto PenToolsInfo (verrà analizzato meglio in seguito) che rappresenta la classe incaricata di preservare i dati impostati dall'utente per quel che riguarda le caratteristiche fisiche di ciò che si vuole disegnare. Per rendere esteticamente semplice la barra abbiamo deciso di adottare per ogni penna il colore che la caratterizza come sfondo del menù. Questo lo possiamo vedere dal codice, dove troviamo i metodi setOpaque, setBackground e setForeground, dove nell'esempio vediamo che il colore di sfondo impostato è il nero, e quello della descrizione del menù bianco. E' stato inoltre adottato un tasto apposito per richiamare il menù tramite tastiera, sfruttando il metodo di JMenu setMnemonic. Altra funzione adottata molto importante per dare al software che fa uso del menù di penna, un riferimento facile da trovare e verificare e per ricevere le informazioni sui tool selezionati, è data da getAccessibleContext().setAccessibleDescription, che imposta per quel menù una descrizione accessibile al momento dell'azione eseguita su quel menù. Come vedremo in seguito infatti, al momento della selezione di una qualsiasi azione di un qualsiasi menù, tramite la suddetta funzione è possibile ricavare in maniera veloce di quale penna stiamo cambiando strumento. Arrivati a questo punto, ci si trova alla chiamata della funzione che crea i sottomenù delle penne. Tramite il metodo createMenu(JMenu menu,int actionevent, String Pencolor) si possono creare i 40 sottomenù personalizzati per le penne. Nel nostro esempio la funzione viene richiamata con i parametri dati dal JMenuTool della penna nera, dall'action event dato dalla pressione del tasto ALT, e dalla stringa “Black” che caratterizza il colore della penna per la quale si vogliono creare le possibili azioni. Dopo aver quindi creato menù e sottomenù non rimane altro che aggiungere il tutto alla toolbar, inserendo subito dopo la parte della penna uno spazio vuoto, al fine di avere ogni menu di penna staccato dagli altri, tramite la funzione Box.createHorizontalGlue. Questo procedimento viene applicato in maniera identica per tutte le penne, fatta eccezione per il colore e le stringhe impostate, a parte per il cancellino. In questo caso, vi sono due variazioni: la prima è verificata al momento della creazione dell'oggetto PenToolsInfo, dove invece che specificare solamente il colore (per il cancellino è il bianco) definiamo anche il tool da assegnare al cancellino stesso (abbiamo deciso che, vista la scarsa precisione di questo strumento, l'unica azione che potrebbe eseguire correttamente è l'eliminazione degli oggetti). L'altra variazione è data dalla mancanza della chiamata al metodo createMenu poiché, per la motivazione appena descritta, abbiamo deciso di assegnare al cancellino una sola possibile azione. Infine, dopo aver istanziato tutti i menù e le loro relative azioni, impostiamo la barra dei menù appena creata come JMenuBar della finestra principale. Con i passi eseguiti fino ad ora, ci troviamo ad avere una finestra senza decorazioni, che presenta 5 menù, ognuno di un determinato colore. La funzione createMenu non verrà descritta in questo paragrafo, poiché va scritta in base alle proprie esigenze di software, e non è legata al funzionamento base della toolbar, ma a quello specifico del programma interfacciato alla barra stessa. Facendo un passo indietro troviamo un oggetto particolare, creato ad inizio funzione. Si tratta di ToolBarListener che definisce un action listener per la toolbar appena descritta. public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); JMenuItem menuitem = (JMenuItem) event.getSource(); AccessibleContext ac = menuitem.getAccessibleContext(); AccessibleContext penac = ac.getAccessibleParent().getAccessibleContext(); String penname = penac.getAccessibleDescription(); int menunumb = -1; if (penname.equals("Black Pen")) menunumb=0; else if (penname.equals("Red Pen")) menunumb=2; else if (penname.equals("Eraser")) menunumb=4; else if (penname.equals("Green Pen")) menunumb=6; else if (penname.equals("Blue Pen")) menunumb=8; 41 if (!command.equals("Setup")){ barFrame.setToolByNumber(menunumb,command); barFrame.resetValues(menunumb,command); AccessibleContext mac = barFrame.getMenubar().getAccessibleContext(); JMenu selectedmenu = (JMenu) mac.getAccessibleChild(menunumb); selectedmenu.setName(command); selectedmenu.setText(command); } if (command.equals("Setup")){ String tool = barFrame.getToolByMenuNumber(menunumb); if (tool.equals("Move")){ JOptionPane.showMessageDialog(null,"ATTENZIONE:\n Lo strumento di movimento non\n necessita di opzioni","Warning", JOptionPane.WARNING_MESSAGE); } else if (tool.equals("Eraser")){ JOptionPane.showMessageDialog(null,"ATTENZIONE:\n Lo strumento per la cancellazione\n non necessita di opzioni","Warning", JOptionPane.WARNING_MESSAGE); } else { new SetupFrame(tool,menunumb,barFrame); } } } Codice 4: Codice relativo alla funzione di gestione delle azioni eseguite sulla toolbar. Nell'esempio vediamo cosa la nostra classe esegue in base all'azione ricevuta. In particolare segnaliamo la modifica del testo all'interno dei menù e la gestione del frame di setup delle proprietà degli oggetti. Di questa classe poniamo particolare attenzione al metodo richiamato in caso di selezione di un elemento di uno dei menù (actionPerformed). Nel momento in cui viene scelto un elemento del menù per una qualsiasi penna, il codice nel riquadro sopra (Codice 4) viene automaticamente eseguito. All'interno di questa funzione possiamo vedere che, per prima cosa vengono letti il comando e l'oggetto che hanno causato la chiamata del metodo. Tramite questi due elementi ricaviamo, attraverso la struttura dei contenuti accessibili implementati all'interno dei menù Java, la stringa che descrive la penna utilizzata (nel nostro caso otteniamo una stringa rappresentante il colore della penna). Dopo aver ottenuto il nome della penna, verifichiamo quale sia quella in uso impostando un numero 42 che identifica univocamente il menù. Questo valore (menunumb) è ottenuto a seguito della numerazione automatica dei menù all'interno di una JMenuBar poiché ad ognuno è assegnato un numero intero, come se la barra fosse un array. Va detto che come si vede nel codice, la numerazione manca dei valori dispari, ma questo non è un errore o un difetto ma è semplicemente dato dal fatto che oltre ai menù anche gli “spazi” inseriti tra un menù e l'altro appartengono agli oggetti numerati. Passando invece all'istruzione condizionale successiva, troviamo un gruppo di codice racchiuso dalla condizione sul comando che ha scaturito la chiamata al listener. Se tale comando non è infatti il comando di setup (nel nostro caso è l'elemento che ci permette di cambiare i valori fisici caratteristici degli oggetti), viene impostato come strumento per tale penna quello scelto dal menù, quindi vengono reimpostati i valori per quel tipo di oggetto che si vuole utilizzare (in seguito vedremo meglio il perché). Come ultima cosa viene cambiata la label grafica del menù, che cambia rappresentando il nome della funzione scelta. Questo fatto merita di una menzione particolare, perché in fase di utilizzo del programma se consideriamo la disponibilità di quattro penne e che in mancanza di questa caratteristica l'utente debba ricordarsi per ogni penna quale strumento sia stato scelto, capiamo immediatamente la difficoltà della persona che utilizza il programma nel memorizzare tutte le combinazioni. Con questo piccolo accorgimento invece, non c'è bisogno di ricordare nulla, è sufficiente invece guardare sullo schermo il nome rappresentato nel menù (il colore della penna è lo sfondo della scritta) per sapere quale penna prendere in mano per utilizzare un determinato strumento. In caso invece di scelta del sottomenù di setup si effettua il controllo del tool attualmente in uso per quella penna, il quale se è Move o Eraser non presenta possibili modifiche alle caratteristiche (non ci sono proprietà per questi due strumenti) e compare quindi un pop up che avverte l'utente della scelta errata. Per tutti gli altri casi invece viene istanziato un nuovo frame di setup. Questa finestra di setup è estensibile in base all'ambiente software in cui si inserisce la toolbar, con il vincolo di richiamarne il costruttore con questa firma: SetupFrame(String tool,int menunumb,ToolbarFrame barFrame) Seguendo questa direttiva, ed implementando una funzione per il salvataggio dei dati (nel nostro caso la funzione saveValues modifica i valori all'interno della classe PenToolsInfo) è possibile sfruttare la chiamata alla finestra di setup come visto sopra. Nel nostro caso specifico abbiamo implementato la finestra in modo che al momento della sua apertura, i valori precedentemente scelti (o quelli di default) vengano automaticamente visualizzati in un'area di testo apposita, e che tali valori vengano utilizzati come dati iniziali all'interno dei campi di selezione (come si vede in Figura 5.1 i campi sono rappresentati tramite degli spinner, poiché una classica riga di testo obbligherebbe l'utente a fare uso della tastiera, azione che come detto fin dall'inizio vogliamo evitare). 43 Figura 5.1: Selezione della finestra di setup. Come mostra la figura l'azione per la penna nera è impostata sullo strumento cerchio. Al momento della selezione dell'opzione di setup (parte sinistra), viene visualizzata la finestra di modifica delle proprietà (destra) che permette di cambiare le caratteristiche fisiche dell'oggetto, nello specifico del cerchio. Vediamo ora invece quello che rappresenta il cuore della dinamicità della barra, cioè la classe che si incarica di memorizzare e preservare i dati riguardanti azioni e proprietà di ogni penna. Stiamo parlando di PenToolsInfo. All'interno di questa classe troviamo alcuni importanti campi privati, che serviranno appunto alla gestione del flusso di informazioni con ToolBarListener. I due più importanti sono senz'altro LastTool ( tipo String, rappresenta il nome del tool attualmente in uso per ogni penna) e PenColor ( tipo di classe Color, necessario per differenziare ogni oggetto PenToolsInfo dagli altri). Per poter adattare l'interfaccia utente ad un qualsiasi programma esterno bisognerà quindi estendere questa classe, personalizzando i metodi per il “get” e per il “set” dei valori. Nel nostro esempio abbiamo creato i metodi di get e di set per ogni proprietà degli oggetti ( density, friction, restitution, squareside, linewidth, arrowdistance e circleradius) ed abbiamo aggiunto delle funzioni per il reset dei dati. Come abbiamo visto infatti nella funzione actionPerformed (Codice 4) ogni volta che si seleziona uno strumento diverso da “Setup” viene chiamato il metodo resetValues(menunumb, command) il quale dato un ID del menù (per esempio 0 per la penna nera) ed un comando (nel nostro esempio può essere il comando cerchio) richiama a sua volta il corrispondente metodo di reset dei valori ( per noi setupCircle ) che reimposterà i parametri a quelli di default scelti in fase di programmazione. I dati più volte menzionati fino ad ora, vengono tutti immagazzinati all'interno della classe PTI (PenToolsInfo) che include i campi privati appositi per contenere tali valori (nel caso della fisica 2D, abbiamo la classe PenToolsInfo che racchiude i campi per densità, attrito, raggio del cerchio e tutti gli altri più volte menzionati). 44 5.2 Cattura degli eventi generati dalla LIM Una volta sviluppata la toolbar che gestisce e comanda le azioni associate alle penne ci troviamo davanti al problema della gestione degli input sulla lavagna. In linea teorica l'ascolto degli eventi non dovrebbe essere problematico poiché, come visto nel Capitolo 3.1 (Descrizione API Java), la libreria JAR messa a disposizione da SmartBoard ci permette di effettuare in maniera automatizzata tale gestione. A conti fatti però il listener fornitoci dalle API non è sufficiente ad eseguire la nostra cattura degli eventi. Il problema infatti nasce dal metodo in cui il motore fisico viene mostrato graficamente, cioè tramite le vecchie classi AWT e non tramite quelle più recenti appartenenti al package SWING. A causa di questa scelta implementativa attuata dagli ideatori di JBox2D, i nostri oggetti appartenenti alla libreria SBORDA non riescono a catturare correttamente gli eventi. Dopo svariati tentativi e dopo aver provato qualsiasi tipo di raggiro al problema, siamo giunti ad una conclusione indolore e con le stesse funzionalità rispetto all'idea iniziale. Dopo essere riusciti a fare in modo che SBSDKListener riconosca gli eventi sulla pentray della lavagna e che arrivati a questo punto, ogni pressione delle penne sulla lavagna viene riconosciuta come un click del mouse, abbiamo implementato un apposito “mouseListener”, collegato all'action listener BoardListener, che cattura gli eventi sulla pentray. La tecnica adottata per far si che le azioni compiute con la penna siano differenziate da quelle del mouse si basa su alcuni flag incrociati tra le due classi listener (BoardListener e Man, che sta per Mouse Action Notify), i quali fanno in modo che, una volta presa in mano una penna, la classe Man riconosca questa situazione ed in caso di evento di pressione, click o drag si comporti di conseguenza, simulando di fatto le chiamate apposite integrate in BoardListener. All'interno di quest'ultima classe, troviamo le classi viste nel Capitolo 3.1, con alcune di esse implementate. public void onEraser(int iPointerID) { mouse.setPenMethod(true); } public void onNoTool(int iPointerID) { mouse.setPenMethod(false); } public void onPen(int iPointerID) { mouse.setPenMethod(true); } Codice 5: Nel riquadro le funzioni implementate in BoardListener. In caso di evento da parte della lavagna del tipo onPen o onEraser (nel caso in cui si tolga uno dei suddetti strumenti dalla pentray) la funzione setPenMethod della classe Man verrà richiamata con parametro true, in caso di posa di qualsiasi strumento, si avrà la stessa chiamata,ma con parametro false. Come possiamo vedere nel riquadro, in base all'evento occorso, il metodo della classe Man viene eseguito con parametro true o false. Abbiamo true se uno strumento viene preso in mano dall'utente, rimuovendolo dalla pentray, mentre il parametro sarà false in caso di posa di un qualsiasi strumento (sia esso il cancellino o una penna). Tutte le altre funzioni di BoardListener non sono state implementate per più motivi, alcune per inutilizzo (onCircle, onLine ed onPrint per esempio non vengono mai eseguite) altre per scelta, come nel caso dei metodi riguardanti la modalità senza proiezione che abbiamo scelto di non implementare perché il nostro proposito di interfaccia si basa 45 proprio sull'idea che l'utente faccia uso dell'immagine proiettata sulla lavagna. Seguendo quindi la gerarchia dei metodi arriviamo alla classe che sostituisce di fatto BoardListener nell'esecuzione del codice in caso di eventi. Di questa classe vedremo in dettaglio per prime le funzioni automaticamente richiamate al momento dell'azione corrispondente, in seguito analizzeremo nel dettaglio i metodi adottati per l'attivazione dell'azione scelta. public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { if (pen){ if (sd.parent.shiftKey) { return; } OnPen(e); } } } public void mouseReleased(MouseEvent e) { shapedrawing.mouse(); if (e.getButton() == MouseEvent.BUTTON1){ if (pen){ if (sd.parent.shiftKey) { return; } OnPenEnd(e); } } } Codice 6: Metodi implementati per la gestione dei click del mouse. La gestione della pressione sulla lavagna viene verificata e controllata principalmente tramite due funzioni, OnPen per l'azione di pressione, e OnPenEnd per l'azione di rilascio. 46 Vediamo in qui questo caso le funzioni attive in fase di pressione e rilascio del mouse. Al momento della pressione con il mouse (o grazie alla nostra implementazione, con la lavagna) viene eseguito il controllo sul pulsante del mouse utilizzato. Tutte le routine necessarie al disegno degli oggetti, vengono infatti eseguite solo se si preme il tasto sinistro del mouse (MouseEvent.BUTTON1) oppure se si preme con un pennarello. Questo va fatto perché il tasto destro e la rotella vengono utilizzati per lo zoom e lo spostamento della visuale. Una volta eseguito questo controllo quindi si passerà alla verifica del flag di penna che è indicato da un valore booleano (pen) il quale viene appunto modificato all'interno di BoardListener (come visto nelle funzioni del Codice 5). Tale flag ci indica se stiamo utilizzando una penna (valore a true) oppure se tutti gli strumenti della lavagna sono nella loro sede della pentray (valore a false). In caso di esito positivo troviamo un altro controllo riguardo alla pressione del tasto Shift sulla tastiera ( in tal caso non viene disegnato niente ) e quindi si può procedere alla funzione OnPen, che analizza ed esegue le azioni corrette in base alla situazione. Analogamente al momento del rilascio del tasto del mouse ( o la fine della pressione con la penna ) viene eseguito del codice che termina con la chiamata ad OnPenEnd, che di fatto richiamerà il corretto metodo di disegno. Ad inizio metodo invece troviamo la chiamata alla funzione appartenente a ShapeDrawing, che serve nello specifico al motore fisico e quindi la vedremo in seguito. public void OnPen(MouseEvent e){ Color pencolor = tbm.getSDK().getToolColor(iPointerID); int tooltype = tbm.getSDK().getToolType(iPointerID); if (tooltype == 2) {} else { int menunumber = this.getNumberByPencolor(pencolor); mousecheck = true; String tool = barFrame.getToolByMenuNumber(menunumber); if (tool.equals("Circle")){ int x = e.getX(); int y = e.getY(); startpos.setX(x); startpos.setY(y); shapedrawing.mouse(new Vec2(x,y), Man.PRESSED); } ... } 47 public void OnPenEnd(MouseEvent e){ Color pencolor = tbm.getSDK().getToolColor(iPointerID); int ttype = tbm.getSDK().getToolType(iPointerID); if (ttype == 2) { String tool = barFrame.getToolByMenuNumber(4); if (tool.equals("Eraser")){ shapedrawing.destroy(startpos); } } else { int menunumber = this.getNumberByPencolor(pencolor); pti = barFrame.getPTIbyMenuNumber(menunumber); String tool = barFrame.getToolByMenuNumber(menunumber); if (tool.equals("Circle")){ int x = e.getX(); int y = e.getY(); endpos.setX(x); endpos.setY(y); shapedrawing.createCircle(startpos, endpos, pti); mousecheck=false; } } Codice 7: OnPen e OnPenEnd. I due metodi rappresentano le funzioni in fase di pressione e rilascio del mouse che si incaricano di gestire ed analizzare la situazione in corso. Al momento della pressione sulla lavagna, come visto in precedenza, viene chiamato il metodo OnPen, il quale da come si vede nel riquadro acquisisce innanzitutto le proprietà dello strumento con cui il tocco è stato effettuato, leggendo colore e tipo dello strumento ( le penne appartengono al tipo 1, il cancellino al tipo 2) tramite iPointerID ( definito nel Capitolo 3.1). In base al tipo di strumento utilizzato, ci troviamo in due possibili situazioni: se abbiamo utilizzato il cancellino non viene eseguita nessuna azione, poiché come detto in precedenza questo elemento ha la sola utilità di cancellare un oggetto, e quindi gli unici dati che ci servono alla cancellazione li possiamo ricavare al momento del rilascio della pressione. Se invece stiamo utilizzando una penna dobbiamo acquisire i dati dello strumento (colore della penna e tool attualmente attivo per essa), attivare il flag per l'inizio disegno (questo flag viene utilizzato dalla classe grafica del motore fisico, e quindi lo vedremo in seguito) ed infine selezionare la routine relativa al tool attivo. Nell'esempio mostriamo il codice nel caso dello strumento Cerchio (il codice è molto simile per le altre azioni) che salva in un array interno alla classe i parametri relativi alle coordinate dell'evento, inviandoli anche alla classe ShapeDrawing tramite il metodo mouse, al fine di poter rendere graficamente un'anteprima 48 delle posizioni percorse con il cursore. Dopo aver terminato la pressione sulla lavagna invece ci troviamo all'interno del codice di OnPenEnd che come ad inizio evento acquisisce le informazioni sullo strumento (questo per una questione di sicurezza) ed anche in questo caso differenzia l'esecuzione in base al tipo di strumento utilizzato: utilizzando il cancellino effettuiamo un controllo di sicurezza sul comando selezionato per il menù di questo strumento, ed in caso di esito positivo eseguiamo la funzione di eliminazione dell'oggetto cliccato, appartenente alla classe ShapeDrawing. Utilizzando invece una penna, seguendo l'esempio del comando Cerchio vediamo come vengano salvate anche questa volta le coordinate dell'evento per essere utilizzate, assieme a quelle salvate all'interno di OnPen e all'oggetto PenToolsInfo relativo alla penna utilizzata, come parametri del metodo di disegno di ShapeDrawing apposito per quel comando ( in questo caso createCircle ). Effettuata la chiamata a questo metodo si reimposta il flag mousecheck a false per indicare la fine del disegno. 5.3 Interfacciamento a JBox2D Il menù di penne creato deve quindi essere interfacciato al software che ne fa uso, nel nostro caso il motore di JBox2D. La parte più importante del collegamento tra toolbar e simulatore di fisica, è rappresentata dalla gestione dei dati e da come vengono utilizzate le informazioni derivate dai listener già descritti. Per quanto riguarda il primo punto come detto in precedenza sfruttiamo una classe apposita che preserva i dati degli oggetti da creare e fornisce metodi per la lettura e la scrittura di tali valori. La classe in questione è PenToolsInfo, che contiene dei campi privati per il mantenimento delle proprietà fisiche degli oggetti da disegnare. public class PenToolsInfo { private String LastTool; private Color PenColor; private float radius = 0.6f; private float side = 1.0f; private float distance = 1.0f; private float width = 1.0f; private float density = 2f , restitution = 0.2f , friction = 0.3f; public PenToolsInfo(Color penna){ this.PenColor = penna; this.LastTool = "Move"; } 49 public void resetValues(String command){ if (command.equals("Circle")) setupCircle(); if (command.equals("Rectangle")) setupSquare(); if (command.equals("Arrow")) setupArrow(); if (command.equals("Line")) setupLine(); Color temp = PenColor; PenColor = temp; } public void setupCircle(){ radius = 0.6f; density = 5.0f; restitution = 0.3f; friction = 0.2f; Codice 8: Campi e alcuni metodi della classe PenToolsInfo. Nel riquadro vediamo i campi privati della classe, ed alcuni metodi utili alla gestione dei valori. Come si può vedere nel riquadro la classe possiede tutti i parametri necessari alla gestione dei dati e delle azioni per le penne. Creando infatti un oggetto di questa classe per ogni penna diversa (anche per il cancellino), possiamo differenziare il comportamento per ognuna di esse. Se viene richiamato il costruttore della classe senza specificare lo strumento da associare ad una determinata penna ( PenColor è il colore della penna), a quell'oggetto verrà automaticamente assegnato il comando di “Move” (spostamento) degli oggetti simulati. Subito dopo invece possiamo notare i campi che contengono i parametri delle forme che verranno create in JBox2D. Ogni volta che viene selezionato un determinato tool all'interno del menù di penne viene richiamato (come visto in precedenza) il metodo resetValues di questa classe. In base al tool specificato nella firma di questa funzione verrà eseguita l'azione corrispondente. Per farne un esempio, vediamo quella del cerchio; ogni parametro relativo a tale oggetto viene ripristinato al valore di default specificato da parte del programmatore. Così facendo otteniamo il classico effetto di ritorno alle impostazioni iniziali. Per quanto riguarda il collegamento tra toolbar va detto il metodo con cui JBox2D utilizza i dati ricevuti al momento della cattura degli eventi della lavagna. Come abbiamo visto nella sezione precedente ogniqualvolta si rilascia la penna dalla superficie il listener esegue il metodo relativo all'oggetto specificato, utilizzando tre parametri specifici (abbiamo visto l'esempio del cerchio shapedrawing.createCircle(startpos, endpos, pti) ). Questi tre parametri rappresentano il punto di inizio dell'evento dato dalla penna, il punto di fine, e l'oggetto PenToolsInfo come appena visto. Per ora ci limiteremo ad analizzare i primi due (analizzeremo l'utilità del terzo nel prossimo capitolo), per spiegare come disegnare correttamente gli oggetti. Abbiamo dovuto scartare l'idea di un riconoscimento delle forme poiché non abbiamo trovato 50 pattern già pronti, e come per la scrittura a mano ci saremmo trovati di fronte ad un problema difficilmente risolvibile. Abbiamo quindi optato per una soluzione comunque semplice per l'utente, che ci permetta di disegnare rapidamente l'oggetto. La scelta si basa quindi sulla distanza percorsa dal cursore come parametro principale per il disegno. I due vettori startpos ed endpos infatti appartengono ad una classe scritta appositamente per questa situazione. public class VecPos2 { private float xpos; private float ypos; public float getDist(VecPos2 vp) { float x2 = vp.getX(); float y2 = vp.getY(); return getDist(x2,y2); } public float getDist(float x2,float y2) { float x1 = xpos; float y1 = ypos; float radice = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2); float distanza = (float) Math.sqrt(radice); return distanza; } public Vec2 toVec2(){ Vec2 vet = new Vec2(xpos,ypos); return vet; } public void vecToPos(Vec2 vet){ this.xpos = vet.x; this.ypos = vet.y; } } Codice 9: Classe VecPos2 utilizzata per ottenere le dimensioni dell'oggetto da disegnare. Nel riquadro possiamo vedere come la classe si riduca a due campi privati ed alcune semplici funzioni. I campi rappresentano coordinate orizzontali e verticali del punto in cui è avvenuto l'evento, per i metodi invece li vediamo singolarmente. 51 Innanzitutto vediamo come ci sia due volte il metodo getDist con due diverse firme, questo è stato fatto per permettere la chiamata dall'esterno in due diversi modi, infatti il risultato è lo stesso. Specificando un oggetto dello stesso tipo infatti, viene eseguito il metodo con le due coordinate prese singolarmente. Ottenuti questi due valori, tramite una semplicissima formula matematica basata sul teorema di Pitagora, viene restituita la distanza tra i due punti (quello dell'oggetto tramite cui è stata chiamata la funzione e quello specificato nella firma della funzione). Gli altri due metodi invece servono per il casting dell'oggetto restituendo un vettore Vec2 utile per le classi di JBox2D (toVec2 ), oppure per modificare l'oggetto attuale, impostando le sue coordinate come quelle del vettore dato in input (vecToPos). Al momento della creazione di un cerchio o altra figura in JBox2D i due oggetti VecPos2 verranno utilizzati per avere una dimensione esatta di quello che si andrà a disegnare. Per fare un esempio, nel caso del cerchio viene calcolata la distanza tra inizio e fine pressione sulla lavagna, si calcola la distanza tra i due punti che sarà utilizzata come diametro del cerchio disegnato. Così facendo si ha una corrispondenza esatta tra quello che si fa fisicamente e la rappresentazione grafica risultante. 52 Capitolo 6: Caso d'uso: Smart Box 2D Come anticipato in precedenza il software sviluppato rappresenta una simulazione di fisica bidimensionale, sfruttando il menù di penna visto nei capitoli precedenti. Abbiamo visto che le funzioni di disegno degli oggetti vengono richiamate attraverso un mouse listener che fa da filtro tra gli eventi generati dalla lavagna ed il motore fisico. Grazie a questo “filtro” abbiamo la possibilità di personalizzare il comportamento della nostra toolbar, semplicemente facendo l'operazione di overriding dei metodi di creazione dei sotto menù ( createMenù ) e quelli di chiamata delle azioni specifiche (nel nostro caso i metodi di disegno degli oggetti) richiamate tramite la classe Man, in particolare da OnPen e OnPenEnd. E' stato inoltre brevemente descritto il metodo utilizzato per permettere all'utente di scegliere le dimensioni degli oggetti da creare. La tecnica scelta per il disegno infatti, si basa sull'input continuato dell'utente. In fase di dragging del cursore all'interno del sistema fisico simulato l'utente potrà infatti vedere il percorso tracciato tramite una sottile linea bianca che verrà poi eliminata nel momento in cui si rilascerà il cursore (smettendo la pressione sulla lavagna). Questo metodo è stato adottato per ovviare ad un approccio meno multimediale come quello di imporre all'utente una dimensione prefissata per l'oggetto da disegnare, eseguendo la creazione dopo un click. Tramite la soluzione proposta invece qualsiasi elemento si voglia creare avrà una dimensione pari alla distanza minima percorsa tra il punto di inizio ed il punto di fine dell'evento generato dalle penne. Abbiamo specificato la distanza minima, proprio perché è stato deciso di non misurare l'intero percorso effettuato dal cursore poiché se, ad esempio, si vuole disegnare un quadrato e si “disegna” il lato voluto, difficilmente si eseguirà una linea retta, ma più facilmente si traccerà un percorso simile ad una retta, ma leggermente curvato, con il risultato di aver percorso una distanza maggiore di quella desiderata. Espandendo il caso ad un esempio limite possiamo pensare ad un tracciato fatto da una mano poco ferma che, invece che effettuare una linea leggermente ricurva, ne disegna una molto altalenante, ottenendo cosi una distanza percorsa di molto maggiore rispetto a quella voluta. 53 Figura 6.1: Disegno degli oggetti. Nella figura vediamo due differenti tracciati. A sinistra un tracciato abbastanza fermo, l'oggetto disegnato (ad esempio il cerchio) avrà come dimensione quella del tracciato effettuato. A destra vediamo un tracciato poco fermo. La dimensione utilizzata è rappresentata dalla distanza tra il primo e l'ultimo punto,e non dal percorso completo del cursore, che è di molto maggiore rispetto alla distanza effettiva. Dopo aver analizzato il metodo per la scelta della dimensione degli oggetti, vediamo come tali oggetti possono essere ricoperti da una texture grafica. All'interno di JBox2D vi è già un metodo per applicare tale immagine agli elementi ma, tale funzione, non è stata implementata del tutto. Utilizzandola così come la si trova infatti riusciamo si ad applicare le texture, ma se creiamo più oggetti velocemente viene generata un'eccezione in runtime che manda in stallo il software. Applicando correttamente la cattura di tale eccezione abbiamo evitato il crash del programma, negando il permesso per il disegno di quell'oggetto. Per cancellare gli oggetti invece ci siamo basati sulla funzione apposita già presente all'interno della libreria fisica utilizzata. Tale metodo permette di cancellare l'elemento indicato (Body) eliminandolo sia graficamente che dalla lista degli oggetti dell'ambiente simulato. Per questo motivo nel nostro programma è permessa la cancellazione degli oggetti dinamici, ma non di quelli statici ( Linee ) che vengono ancorati all'ambiente e perdono il loro indicatore di massa ( Body ). Il problema riscontrato in fase di cancellazione è apparso nel momento in cui si cancellava un elemento che aveva una texture. In quel caso l'oggetto veniva correttamente eliminato, mentre la texture rimaneva all'interno della rappresentazione grafica, ma non avendo più nessun corpo a cui rimanere ancorata scivolava verticalmente nel basso dello schermo. Per poter quindi ovviare a questo problema abbiamo implementato un sistema di mantenimento della corrispondenza tra immagine ed oggetto tramite una classe apposita, ImageInfo. 54 public class ImageInfo { private BoundImage immagine; private ShapeDrawing shaped; public Hashtable<Body, BoundImage> tableimages; public ImageInfo(ShapeDrawing sdraw){ this.shaped=sdraw; tableimages = new Hashtable<Body, BoundImage>(); tableimages.clear(); } public void add(BoundImage bi){ Body bd = bi.getBody(); tableimages.put(bd,bi); } public void remove(Body body){ if (tableimages.containsKey(body)){ immagine = (BoundImage) tableimages.get(body); shaped.removeImage(immagine); } } } Codice 10: Classe ImageInfo. La classe nel riquadro mostra i metodi ed i campi utilizzati per verificare e gestire la creazione e la cancellazione delle texture applicate agli oggetti. Da come si può vedere, le informazioni sono gestite tramite un Hash Table. La classe inserita nel riquadro contiene alcuni campi tra cui un oggetto BoundImage che rappresenta per JBox2D l'immagine da applicare a ciò che si disegna, un puntatore a ShapeDrawing, cioè la classe che rappresenta il sistema in cui ci muoviamo, ed un HashTable caratterizzata da una chiave (Body) ed un oggetto (BoundImage). Tramite questa hashtable possiamo per ogni oggetto Body, differente per ogni elemento del sistema, ottenere la corrispettiva texture. Possiamo vedere questo fatto attraverso le funzioni add e remove, nella prima troviamo un solo parametro composto da BoundImage che di fatto preserva un collegamento al corpo associato. Tramite questo collegamento otteniamo il Body corrispondente ad aggiungiamo la coppia ad hashtable. In fase di rimozione 55 dell'oggetto invece viene passato il Body, tramite il quale si verifica la sua presenza tra le chiavi di hashtable, ed in caso positivo si rimuove l'immagine tramite la funzione inserita all'interno della classe ShapeDrawing, che si incaricherà di eliminare ogni occorrenza della texture in questione. public void removeImage(BoundImage immagine){ if (boundImages.contains(immagine)) boundImages.remove(immagine); } Come possiamo vedere nel riquadro, la funzione verifica l'esistenza della texture tra quelle attualmente inserite nel sistema, ed in caso affermativo la rimuove dalla lista, evitando cosi che venga ridisegnata anche in caso di mancanza del corpo ad essa associato. public void destroy(VecPos2 pos){ Vec2 pos2 = m_debugDraw.screenToWorld(pos.toVec2()); assert m_mouseJoint == null; Vec2 d = new Vec2(0.001f, 0.001f); AABB aabb = new AABB(pos2.sub(d), pos2.add(d)); int k_maxCount = 10; Shape shapes[] = m_world.query(aabb, k_maxCount); Body body = null; for (int j = 0; j < shapes.length; j++) { Body shapeBody = shapes[j].getBody(); if (shapeBody.isStatic() == false) { boolean inside = shapes[j].testPoint(shapeBody.getXForm(),pos2); if (inside) { body = shapes[j].m_body; break; } } } if (body != null) { iinfo.remove(body); MouseJointDef md = new MouseJointDef(); md.body1 = m_world.getGroundBody(); md.body2 = body; md.target.set(pos2); md.maxForce = 1000.0f * body.m_mass; m_mouseJoint = (MouseJoint) m_world.createJoint(md); 56 } Shape s = body.getShapeList(); while (s != null){ body.destroyShape(s); Shape.destroy(s); s = body.getShapeList(); } } Codice 11: Metodo per l'eliminazione degli oggetti. La funzione destroy si incarica dell'eliminazione dei corpi dal sistema simulato. In grassetto possiamo vedere la riga di codice aggiunta a quello già presente in JBox2D per rimuovere correttamente le textures. Per quanto riguarda il metodo che effettua l'eliminazione degli oggetti dalla simulazione e dall'interfaccia grafica è stato sfruttato quello già presente in JBox2D, aggiungendoci però una riga di codice per rimuovere la texture corrispondente al corpo da eliminare. Inizialmente troviamo una routine che, date delle coordinate in input, verifica se in quell'istante ed a quelle coordinate vi è presente un elemento di tipo Body. In grassetto nel riquadro sopra troviamo iinfo.remove(body) che, dato un oggetto iinfo (ImageInfo) come descritto qualche pagina addietro, richiama la sua funzione per l'eliminazione dell'immagine da hashtable che preserva le corrispondenze tra corpi e texture. La rimanente parte di codice serve al motore fisico per rimuovere totalmente il corpo selezionato ed eventuali parti unite ad esso tramite una JointUnion. Arrivati a questo punto possiamo vedere come gli oggetti vengono creati. Per farlo però, è necessario ricordare dove le varie proprietà degli oggetti sono state salvate. Vi sono infatti quattro oggetti PenToolsInfo, uno per ogni penna della lavagna. Ogni oggetto preserva per la penna corrispondente le informazioni utili a disegnare il tool visualizzato nel menù. Per fare un esempio se abbiamo scelto per la penna nera l'azione corrispondente al cerchio, vi sarà un oggetto PenToolsInfo apposito per il pennarello nero che conterrà i dati per il disegno del cerchio. Questi dati come già visto sono personalizzabili attraverso la selezione del menù setup. public void createCircle(VecPos2 startpos, VecPos2 endpos,PenToolsInfo pti) { try { Vec2 wstart = m_debugDraw.screenToWorld(startpos.toVec2()); Vec2 wend = m_debugDraw.screenToWorld(endpos.toVec2()); VecPos2 pwstart = new VecPos2(); VecPos2 pwend = new VecPos2(); pwstart.vecToPos(wstart); pwend.vecToPos(wend); 57 float distance = pwstart.getDist(pwend); CircleDef shapedef = new CircleDef(); if (distance > 0) shapedef.radius = distance; else { shapedef.radius = pti.getRadius(); distance = pti.getRadius(); } shapedef.density = pti.getDensity(); shapedef.restitution = pti.getRestitution(); shapedef.friction = pti.getFriction(); BodyDef bodydef = new BodyDef(); Vec2 worldvec = wstart; bodydef.position = new Vec2(worldvec.x, worldvec.y); Body myBody = m_world.createBody(bodydef); myBody.createShape(shapedef); myBody.setMassFromShapes(); PImage myImage = parent.loadImage("circ.png"); Vec2 localOffset = new Vec2(0.0f, 0f); float scale = distance*2 / myImage.width; float rot = 0f; bindImage(myImage, localOffset, rot, scale,myBody); } catch (NullPointerException npe) { String error = npe.toString(); System.err.println("[DEBUG]::[ERRORS] --- "+ error ); } } Codice 12: Metodo per la creazione dei cerchi. Il primo passo eseguito nel metodo di createCircle è dato dalla trasformazione delle coordinate deri vate dall'evento della penna, in coordinate del mondo simulato. Una volta eseguito tale passo a par tire da queste coordinate vengono creati due oggetti VecPos2 che vengono usati subito dopo per cal colare la distanza tra inizio e fine dell'evento. 58 Dopo aver creato un nuovo oggetto CircleDef, cioè una definizione di forma di un elemento cer chio, verifichiamo la distanza percorsa dal cursore. La verifica viene fatta perché in caso di click con la penna e non di pressione continuata la distanza risulta nulla e viene creato un cerchio senza proprietà fisiche (a causa di alcune scelte degli sviluppatori del motore fisico). Per evitare ciò abbia mo fatto in modo che se la distanza è maggiore di zero (cioè se c'è stata una pressione continuata) il raggio utilizzato per il cerchio è la distanza effettiva, altrimenti viene utilizzata la grandezza specifi cata nel setup del cerchio (se non specificata viene utilizzata una grandezza di default). Una volta definito il raggio ( o ad esempio per il quadrato la dimensione del lato), procediamo a de finire gli altri parametri caratteristici dell'oggetto. La densità, il coefficiente di rimbalzo e quello di attrito vengono infatti letti dalla classe PenToolsInfo come visto prima, sfruttando quindi le impo stazioni scelte dall'utente. La riga successiva definisce un nuovo oggetto BodyDef che rappresenta la definizione delle caratteristiche del corpo che si andrà a creare. Si procede quindi con la defini zione della posizione del corpo del cerchio che verrà creato sfruttando le coordinate di inizio pres sione. Arrivati a questo punto troviamo due funzioni essenziali per la fisica degli oggetti: createShape serve per creare fisicamente la forma del cerchio cioè per inserirlo nella simulazione, mentre setMassFromShape è la funzione che abilita il calcolo delle forze fisiche in gioco per il movimento simulato dell'oggetto. Senza questa importantissima funzione infatti l'oggetto che andiamo a creare viene interpretato dal motore fisico come un ostacolo o come un delimitatore (ad esempio il terreno). Abbiamo sfruttato questa caratteristica del motore per poter dare all'utente la possibilità di creare appunto terreni, percorsi o “scatole” in cui contenere gli oggetti creati. Lo strumento linea infatti permette il disegno allo stesso modo del cerchio, ma manca dell'opzione setMassFromShape, risultando appunto inamovibile anche se non poggiata sulla base dell'ambiente. Le righe di codice che seguono servono per applicare la texture all'immagine. Per prima cosa impo stiamo un offset per l'immagine, nel nostro caso ha valore zero perché vogliamo inserire la figura al centro del cerchio. In seguito calcoliamo il valore tramite il quale l'immagine verrà scalata. Rica viamo questo dato grazie al rapporto tra il diametro del cerchio creato e la larghezza della texture (essendo un cerchio larghezza ed altezza si equivalgono), ottenendo un valore di scala preciso per ogni oggetto creato. È possibile inoltre impostare un valore di rotazione per la texture nel caso in cui ci sia bisogno, ma nel nostro caso non ne abbiamo la necessità e quindi gli daremo un valore pari a zero. Dopo aver valutato tutti i parametri appena descritti possiamo eseguire il metodo di associazione dell'immagine al cerchio tramite bindImage, la quale avrà tra i suoi parametri oltre a quelli appena descritti, anche l'oggetto Body rappresentante il cerchio. Attraverso questo metodo vengono richiamata la funziona di riempimento di hashtable vista nel riquadro Codice 10. Tutto quanto è stato visto a riguardo di questi passi per la creazione degli oggetti è racchiuso all'interno di un blocco di cattura delle eccezioni, in particolare l'eccezione catturata è del tipo NullPointerException e viene generata dal metodo createShape. Il bug è attribuito a JBox2D dai suoi sviluppatori, ma non ne è nota la sua natura, poiché avviene in diverse circostanze ogni volta diverse e non dipende in alcun modo dai parametri personalizzati. In caso di sollevamento di tale eccezione l'oggetto non viene creato per evitare che come da noi riscontrato, ci si trovi con un cerchio che non ha una sua massa e quindi privo di movimento. Va comunque specificato che l'eccezione si presenta occasionalmente se la creazione degli oggetti viene effettuata senza un'eccessiva rapidità. Continuando infatti a generare eventi ed a creare diversi oggetti il motore fisico tende a sollevare l'eccezione con maggior frequenza. 59 Per quanto riguarda il programma completo possiamo seguire i passi per mostrare come vengono elaborati i dati ricevuti e come il programma si comporta. Analizzeremo il processo da eseguire per creare un cerchio utilizzando la penna nera, visualizzando passo per passo dove è possibile effettuare le azioni e le scelte descritte nei capitoli visti fino ad ora. Figura 6.2: Uno screenshot del programma appena aperto. Nell'immagine possiamo vedere l'ampio spazio a disposizione per il disegno e l'interazione con gli oggetti. Come premesso nei capitoli precedenti, l'interfaccia sviluppata è semplice ed occupa il minimo spazio se confrontata con la GUI del motore fisico. In Figura 6.2 possiamo vedere come appare graficamente il programma una volta aperto. Troviamo una grande sezione di schermo nera che rappresenta lo spazio aperto, cioè il posto dove gli oggetti si muoveranno, nella parte bassa in verde possiamo vedere la rappresentazione nel motore fisico del terreno ed in fronte ad essa troviamo il menù come appare inizialmente. Ogni penna è caratterizzata dal proprio colore ed il titolo che caratterizza i menù è in questo momento uguale al nome della penna corrispondente. Per continuare con il nostro intento, dobbiamo quindi selezionare il menù Black Pen e quindi la voce Circle, ottenendo come risultato all'interno del software l'attivazione del 60 listener per la toolbar ed il conseguente avvio del metodo associato all'azione eseguita. Figura 6.3: Selezione delle proprietà per l'oggetto cerchio. Selezionando dal menù della penna interessata la voce Setup compare la finestra per la personalizzazione dei parametri fisici per l'oggetto da disegnare. In figura vediamo il caso preso in esame nell'esempio; il setup del cerchio. Dopo aver selezionato il cerchio per la penna nera modifichiamo le proprietà del cerchio da creare. Selezionando nuovamente Black Pen apriamo tale menù e clicchiamo sulla voce Setup. Dal punto di vista dei metodi in questa situazione il listener della toolbar vista l'azione di setup controlla il tool precedentemente attivo per quella penna e quindi avvia e mostra la sezione di setup per lo strumento selezionato. Compare quindi la finestra per la modifica dei parametri del cerchio, possiamo variare a nostra discrezione il valore del raggio di default da utilizzare in caso di click (come visto in precedenza). Le altre proprietà personalizzabili sono la densità del corpo, il suo coefficiente di rimbalzo (o elasticità) e quello di attrito. Una volta specificati i valori è sufficiente chiudere la finestra per far si che questi vengano salvati, tramite la funzione saveValues appartenente alla classe SetupFrame. A questo punto ci troviamo ad aver preparato il motore fisico per il disegno del cerchio con le proprietà specificate e procederemo quindi con il tracciare la dimensione del raggio con la penna nera. Come già anticipato prima il percorso da effettuare con il pennarello non è la circonferenza del cerchio da creare, ma il suo raggio. Nella figura successiva possiamo vedere il tracciato indicante il raggio del cerchio per il nostro esempio. 61 Figura 6.4: Tracciatura della distanza del cerchio. Tracciando con la penna un percorso semi lineare come in Figura 6.4 otteniamo la chiamata in primo luogo della funzione OnPen al momento della pressione con la penna, quindi quando la penna verrà tolta dalla superficie della lavagna verrà eseguito il metodo OnPenEnd. Come visto in precedenza questo metodo verifica il tool attivo per il pennarello utilizzato e quindi effettua la chiamata al metodo createCircle, il quale si incaricherà della creazione di un cerchio che ha come raggio la distanza percorsa dal cursore, e come parametri fisici quelli indicati nella finestra di setup (Figura 6.3). Una volta creato il cerchio lo troveremo all'interno dello spazio come in Figura 6.5. 62 Figura 6.5: L'oggetto cerchio è stato creato e ne viene simulato il comportamento fisico. Il cerchio è stato creato dalla funzione createCircle ed è stato inserito nel sistema. Ogni suo movimento risulta ora come una simulazione fisica. Le forze in gioco vengono calcolate, misurate ed utilizzate per modificare il movimento del cerchio. È possibile inoltre utilizzando il mouse (o il dito sulla lavagna) spostare l'oggetto e posizionarlo dove si desidera. 63 64 Capitolo 7: Conclusioni È stata proposta una soluzione al problema posto in fase di introduzione riguardo allo sviluppo di un'interfaccia sfruttabile nell'ambiente delle lavagne interattive multimediali. La soluzione proposta rappresenta quindi un valido candidato all'utilizzo con tali strumenti. L'attenzione è stata particolarmente posta sulla semplicità d'uso e sull'alto tasso di interattività che l'interfaccia deve permettere. Abbiamo mostrato, oltre alla soluzione sviluppata, altre eventuali proposte, elencandone i motivi per cui non sono state adottate. Sono state innanzitutto mostrate le peculiarità delle lavagne interattive multimediali elencandone le caratteristiche, i punti di forza e le mancanze di questa tecnologia. Dopo aver differenziato le diverse tecniche adottate per la gestione degli input, abbiamo analizzato le API messe a disposizione dal costruttore della lavagna utilizzata, che in questa sede è la SmartBoard di SMART Technologies. Sono stati mostrati i metodi appartenenti alle API, i possibili utilizzi di tali classi ed i loro limiti. In seguito sono state effettuate delle considerazioni sulle caratteristiche che l'interfaccia utente oggetto di questa tesi deve avere, descrivendo le motivazioni che inducono a tali scelte. Una volta stabilite le richieste da soddisfare per lo sviluppo dell'interfaccia abbiamo proposto alcune valide soluzioni al problema, tra le quali è stata scelta per semplicità d'uso e di implementazione quella della creazione di una piccola toolbar posizionata sullo schermo vicino alla pentray della lavagna. Lo sviluppo dell'interfaccia non può di per se rappresentare una soluzione ed un esempio, poiché la toolbar creata deve essere associata ad un programma interattivo che ne faccia uso. È stato quindi scelto come caso d'uso l'adattamento del menù di penna ad un simulatore di fisica bidimensionale. Per poter fare ciò però abbiamo analizzato i pacchetti open source a disposizione, valutandone per ognuno l'espandibilità e la possibilità di utilizzarlo in concomitanza con la toolbar in oggetto. La soluzione proposta è stata quindi analizzata attraverso il codice sviluppato. La toolbar, o menù di penna, è stata creata in modo da rappresentare un modulo applicabile a diversi casi, mettendo a disposizione degli sviluppatori un'interfaccia facilmente utilizzabile. Un altro fattore su cui è stata posta particolare attenzione è stato quello riguardante la cattura degli eventi generati attraverso la lavagna. È stato mostrato il metodo per interpretare correttamente gli input ricevuti attraverso le API della lavagna interattiva multimediale, analizzando le tecniche per 65 permettere all'utente l'esecuzione di azioni personalizzate in base allo strumento della lavagna ed all'oggetto scelto all'interno della toolbar creata. Infine è stato mostrato un caso in cui si sia fatto uso dell'interfaccia proposta. SmartBox2D rappresenta di fatto un'applicazione ad alto tasso di interattività che permette l'utilizzo degli strumenti messi a disposizione della lavagna per simulare un ambiente a due dimensioni che rispetta le leggi della fisica. Sono stati mostrati i metodi utilizzati per interfacciare gli input e la toolbar all'output mostrato attraverso il motore fisico. Si può quindi notare come l'interfaccia sviluppata attraverso il linguaggio Java, permette l'utilizzo della stessa in contesti differenti in maniera poco invasiva e facile da comprendere per l'utente che ne fa uso. Il caso posto in questa sede può avere degli sviluppi futuri, tra i quali ci sono quelli di veloce e semplice creazione come l'aggiunta di altre forme da inserire nel sistema, la possibilità di disegnare il perimetro dell'oggetto da creare. Nel programma da noi sviluppato abbiamo scelto le texture staticamente, ma è possibile in futuro implementare la possibilità di scegliere la texture da applicare ad ogni oggetto senza dover cambiare radicalmente il programma. In tal caso risulterebbe di più semplice comprensione la simulazione di un numero elevato di elementi. Altre funzioni possono essere aggiunte ed implementate all'interno di SmartBox2D, ma la soluzione proposta presenta di per se una valida soluzione ed un esempio esplicativo della flessibilità dell'interfaccia proposta. 66 Bibliografia <1> Painter, D Whiting, E and Wolters, B , ( 2005 ) "The Use of an Interactive Whiteboard in promoting interactive teaching and learning" <2> British Educational Communications and Technology Agency , Coventry, U.K. ( 2003 ) "What Research Says About Interactive Whiteboard" < http://web.archive.org/web/20061208064641/http://www.becta.org.uk/page_documents/research/wtrs_white boards.pdf > <3> SMART Technologies , ( ) "In wall rear projection System" : < http://www.wedgwoodav.com/Download.aspx?ProductRef=Q6CSE4959 > <4> Numonics Corp. , ( ) "Electromagnetic Technologyand Its Advantages in Interactive Products" : < www.interactivewhiteboards.com/www/files/techdocs/electromagnetic_whatisit.pdf > <5> Johnny Chung Lee , ( 2007 ) "Projects - Wii Remote Interactive Smartboard" : < http://www.cs.cmu.edu/%7Ejohnny/projects/wii/ > <6> SMART Technologies , ( ) "DViT Technology (Digital Vision Touch)" : < http://smarttech.com/dvit/ index.asp > <7> Bobsguide , ( 2003 ) "SMART Technologies Inc. invents new technology for touch-sensitive displays" : < http://www.bobsguide.com/guide/news/2003/May/23/SMART_Technologies_Inc._invents_new_technology _for_touch-sensitive_displays.html > <8> Beauchamp, G and Parkinson, J , ( 2005 ) "Beyond the wow factor: developing interactivity with the interactive whiteboard" <9> Dane Nourie , ( ) "Dane Nourie's Blog: Handwriting Recognition with Java Technology" : < http:// weblogs.java.net/blog/dnourie/archive/2006/05/handwriting_rec.html > <10> Wikipedia, the free encyclopedia , ( ) "Hidden Markov model" : < http://en.wikipedia.org/wiki/Hidden_Markov_model > <11> Wikipedia, the free encyclopedia , ( ) "Viterbi Algorithm" : < http://en.wikipedia.org/wiki/Viterbi_algorithm > <12> Diana Negoescu , ( ) "Handwriting Recognition: Project Description" : < http://www.cs.princeton.edu/academics/ugradpgm/spe/summer06/negoescu/ > <13> Andrew Witkin and David Baraff , Los Angeles ( Agosto 1997 ) "Physically Based Modeling: Principles and Practice" : Association for Computing Machinery Special Interest Group on Graphics < http://www.cs.cmu.edu/~baraff/sigcourse/ > <14> Chris Baker , ( Marzo 2008 ) "Crayon Physics Deluxe, an ingenious video game that looks like it was designed by a third-grader" : Slate < http://www.slate.com/id/2186848/ > <15> "Phun research project at VRlab, Umeå University" : < http://www.vrlab.umu.se/research/phun/ > 67