Politecnico di Torino - elite.polito.itelite.polito.it/files/thesis/fulltext/garbo.pdf · Corso di...

184
Politecnico di Torino Facoltà di Ingegneria Corso di Laurea in Ingegneria Informatica Dipartimento di Automatica e Informatica Studio e realizzazione di un sistema di puntamento oculare per l’accesso al computer da parte di disabili motori gravi Relatore: Ing. Fulvio CORNO CoRelatore: Ing. Laura FARINETTI Tesi di Laurea di: Alessandro GARBO Anno Accademico 2002/2003

Transcript of Politecnico di Torino - elite.polito.itelite.polito.it/files/thesis/fulltext/garbo.pdf · Corso di...

Politecnico di Torino

Facoltà di Ingegneria

Corso di Laurea in Ingegneria Informatica

Dipartimento di Automatica e Informatica

Studio e realizzazione di un sistema di

puntamento oculare

per l’accesso al computer

da parte di disabili motori gravi

Relatore: Ing. Fulvio CORNO

CoRelatore: Ing. Laura FARINETTI

Tesi di Laurea di: Alessandro GARBO

Anno Accademico 2002/2003

In tutti questi anni non ho mai capito in che misura la fortuna abbia influenzato l’andare

delle cose. Probabilmente anche in questo caso si deve utilizzare una sorta di teorema

sulla relatività. In quanto, ciò che per qualcuno è fortuna per altri è sfortuna oppure, ciò

che in un primo momento sembra malasorte col tempo si addolcisce e prende i contorni

di un’immensa fortuna.

Le avversità che ho incontrato costantemente sul mio cammino mi hanno fatto spesso

pensare alle relative fortune di altri che, puntavano al mio stesso obiettivo. Esami

superati con una preparazione insufficiente o voti elevati conseguiti solo per aver

studiato quello che veniva di solito chiesto all’esame facevano parte di un mondo a cui

io non appartenevo. I lunghissimi mesi passati a studiare l’intero programma di ogni

esame se non, in alcuni casi, accresciuto da testi ausiliari, mi portavano a maledire la

sorte che mi obbligava a sottostare a certe regole.

Vedere la strada semplice e soleggiata dove molti si avventuravano, mentre ero costretto

a percorrere un sentiero critico e irto d’insidie, mi ha insegnato a lottare per ottenere ciò

che volevo contro tutto e tutti.

Ora, a distanza di anni, vedo quelle avversità non come una prova inutile e

insignificante ma una possibilità d’inestimabile valore che mi è stata offerta. Il luogo

che ho raggiunto è simile solo in parte a quello raggiunto da chi ha sempre intrapreso il

percorso più semplice. In realtà quello che io ho raggiunto è il vero obiettivo che la

scuola ha cercato di dare a tutti noi. Ora ho un’istruzione sufficiente per parlare con

qualsiasi persona mi rivolga la parola, sia essa un fisico, un chimico, un elettronico, un

meccanico o, e questo è ancor più importante per me, un operaio o un religioso.

Per questa ragione dedico questa tesi alla sfortuna e alle avversità che mi hanno

accompagnato nel mio percorso. Senza di esse non sarei quello che sono ora. Inoltre il

lavoro qui svolto è dedicato a persone verso le quali le mie sfortune impallidiscono,

spero che questa tesi permetta a loro un pizzico di libertà in più.

Infine ringrazio il Professor Corno per la possibilità che mi ha dato e la famiglia per

l’aiuto indiretto costante negli anni.

Indice 1 Introduzione 1 2 Motivazione e obiettivi 5 2.1 Ausili 6 2.1.1 Sensori 6 2.1.2 Tastiere 7 2.1.3 Ausili per la comunicazione 8 2.1.4 Dispositivi di puntamento 9 2.1.5 Sistemi di puntamento oculare 10 2.2 Contesto 12 2.2.1 Definizione degli obiettivi 13 2.3 Analisi dei problemi 14 2.3.1 Ambiente di lavoro 15 2.3.2 Architettura del sistema 17 2.3.3 Acquisizione ed elaborazione dati 18 2.3.4 Integrazione nel sistema di applicazioni indipendenti 22 2.3.5 Calibrazione 23 3 BackGround 27 3.1 Paradigma di programmazione 28 3.1.1 Programmazione procedurale 28 3.1.2 Programmazione basata sugli oggetti 28 3.1.3 Programmazione orientata agli oggetti 29 3.2 Windows e i messaggi 29 3.2.1 Registrazione della window class 30 3.2.2 Creazione e visualizzazione della finestra 31 3.2.3 Ciclo dei messaggi 31 3.2.4 Window procedure 31 3.3 Multitasking e multithreading 32 3.4 Victor e le immagini 33 3.4.1 Codifica delle immagini 34 3.4.2 DIB 36 3.4.3 Victor 36 4 Architettura e algoritmi 39 4.1 Architettura 40 4.1.1 Acquisizione immagini 43 4.1.2 Scelta del riferimento 44 4.1.3 Motion capture 45 4.1.4 Definizione campo visivo 45 4.1.5 Indicazione dello sguardo 47 4.1.6 Gestione del sistema 48

4.2 Algoritmi 48 4.2.1 Scelta del riferimento 50 4.2.2 Cattura del movimento 53 4.2.3 Analisi del campo visivo 56 4.2.4 Definizione del punto fissato 58 5 Implementazione 61 5.1 Scelta del riferimento 63 5.1.1 Impostazione 64 5.1.2 Analisi immagine 65 5.1.3 Visualizzazione 68 5.1.4 Scelta 68 5.2 Cattura del movimento 69 5.2.1 Definizione parametri funzionali 71 5.2.2 Elaborazione svolta in una sequenza 72 5.2.3 Filtro 74 5.3 Definizione campo visivo 76 5.3.1 Introduzione di uno stato 77 5.3.2 HandShake 79 5.3.3 Elaborazione del singolo stato 79 5.3.4 Conclusione 81 5.3.5 Cablaggio manuale 81 5.4 Indicazione dello sguardo 82 5.4.1 Elaborazioni svolte in una sequenza 83 5.4.2 Messaggi alle applicazioni 86 6 Valutazioni sperimentali 87 6.1 I controlli 88 6.1.1 Controllo sulla velocità di esecuzione

del thread principale 89 6.1.2 Numero di catture per attuare il filtro 90 6.1.3 Clic temporale 91 6.1.4 Regione relativa al clic 92 6.1.5 Focus 93 6.2 Le applicazioni 94 6.2.1 Applicazione Uno: Addestramento 94 6.2.2 Applicazione Due: Tastiera 96 7 Sviluppi futuri 99 7.1 Legami esterni 100 7.2 Struttura 101 7.3 Algoritmi 102 7.4 Applicazioni 103 7.5 Conclusioni 106 8 Conclusioni 109

Appendice A 1 Elementi principali 1 Main 1 Dichiarazioni 6 Messaggi 11 Supervisore 14 Appendice B 25 Oggetti principali 25 Cattura 25 Palette 26 Intercetta 29 Decisore 35 Thread principale 37 Thread di calibrazione 38 Appendice C 39 Windows procedure delle sottofinestre 39 Tavolozza 39 Immagine 40 Controlli 41 Posiziona 47 Appendice D 51 Windows procedure delle applicazioni indipendenti 51 Applicazione Uno: addestramento 51 Applicazione Due: tastiera 52 Spaziatore 55 Applicazione Tre 56 Appendice E 59 Messaggi definiti da Windows 59 Resource 59 Oggetti relativi alla gestione 60 Menu 60 Errore 64 Finestre 64 Procedure di selezione comandi 68 Menu Cattura 68 Menu Intercetta 68 Menu Posiziona 69 Menu Applicazioni 69 Menu Finestre 69

1

1 Introduzione

Nel 1984 l’Ufficio del Servizio di Educazione Speciale e Riabilitazione del dipartimento

dell’Educazione statunitense insieme alla Casa Bianca prese l’iniziativa di condurre un

processo che portasse i produttori degli elaboratori, del software e gli utilizzatori a riflettere

sulla questione riguardante l’accesso e l’uso dell’elaboratore e del software da parte di

persone con disabilità. Il primo incontro di tale iniziativa si tenne nel Febbraio del 1984

presso la Casa Bianca.

Il risultato ottenuto fu il riconoscimento del problema dell’accessibilità ad un ambiente

informatico di comune utilizzo e la richiesta, da parte dei produttori, di ulteriori informazioni

inerenti ai tipi di disabilità, alle limitazioni che incontra una persona disabile nell’utilizzare un

elaboratore tradizionale, alle strategie che avrebbero potuto incrementare l’usabilità

dell’elaboratore stesso.

L’accesso al computer e ai programmi software deve garantire a chiunque, incluse le

persone disabili, la possibilità di usare vantaggiosamente la tecnologia in ambito lavorativo,

educativo e ricreativo. In seguito all’incontro di Febbraio, fu stesa la prima versione della

White Paper che venne distribuita come preparazione per un secondo incontro tenutosi

nell’Ottobre del 1985. Il risultato di questo secondo incontro fu la formazione di un gruppo di

2

lavoro volto ad identificare e documentare idee e considerazioni aventi lo scopo di

incrementare l’accessibilità degli elaboratori standard da parte sia di persone disabili, che

normodotate.

A grandi linee il problema generale può essere suddiviso in una serie di questioni più

semplici.

Una porzione importante della popolazione presenta delle disabilità (acquisite alla nascita,

o in seguito ad un incidente, a causa di una malattia o semplicemente dovute alla vecchiaia)

che impediscono loro di utilizzare i computer standard e i software tradizionali.

I costi per le modifiche necessarie da apportare agli elaboratori, per venire incontro alle

esigenze delle persone disabili, sarebbero non eccessivi o addirittura irrisori ed

incrementerebbero il numero di possibili utenti.

La caratteristica di accessibilità di un prodotto ne rende anche più semplice l’utilizzo da

parte delle persone non disabili, in quanto queste possono usufruire di benefici quali minor

fatica, maggiore velocità e minor probabilità di errore.

La maggior parte delle modifiche al design dovrebbero realizzarsi gratuitamente oppure ad

un prezzo contenuto, per ottenere un beneficio diretto sul mercato di massa.

La maggior parte dei cambiamenti necessari a rendere un prodotto accessibile dovrebbero

essere già inclusi direttamente nel prodotto stesso.

La White Paper rappresenta il primo passo compiuto dalle industrie del software per

andare incontro alle esigenze dei disabili. Lo scopo di questo documento è triplice. In primo

luogo si propone di informare le persone che operano nel campo della riabilitazione. In

secondo luogo di documentare l’importanza e le ragioni del software accessibile. Infine di

riuscire a sensibilizzare i produttori stimolandoli a lavorare, al fine di garantire alle persone

disabili la possibilità di usufruire di tutte le prestazioni in condizioni di adeguata affidabilità

ad autonomia.

Le ripercussioni scaturite dalla presentazione della White Paper si sono fatte sentire anche

in Europa. Nel Marzo del 1992 iniziò un progetto di elaborazione delle Nordic Guidelines for

Computer Accessibility da parte della NNH Nordic Committee an Disability. La NNH è una

cooperazione, sotto il Nordic Council of Minister, comprendente i governi danese, finlandese,

norvegese e svedese. Lo scopo dell’NNH è la realizzazione di obiettivi nazionali di piena

partecipazione nella società e di uguaglianza per le persone disabili. Il Nordic Guidelines for

Computer Accessibility è il risultato di un progetto riguardante l’informazione tecnologica

(IT). L’IT è una parte delle infrastrutture della società moderna.

3

Gli elaboratori sono ormai parte integrante nell’educazione e nel lavoro. È stato stimato

che il 70% del lavoro d’ufficio in Europa è svolto in posti di lavoro informatizzati.

La Cooperazione Internordica di Standardizzazione delle informazioni Tecnologiche

(INSTAT/IT) ha supportato questo progetto con la seguente dichiarazione:

“INSTAT/IT crede che si debba provvedere affinché le persone disabili

abbiano la possibilità di usare attrezzature IT. INSTAT/IT accoglie

favorevolmente il progetto e lo considera un buon inizio in questo settore.

Stiamo attendendo i risultati, sotto forma di guida, e ci aspettiamo di poterli

usare per un lavoro di standardizzazione regionale, nazionale ed

internazionale”.

Alla base di questa pubblicazione ci sono due documenti, il primo è la White Paper, mentre

l’altro è una specifica di requisiti prodotta in collaborazione dall’Agenzia Svedese per lo

Sviluppo Amministrativo e dall’Istituto Svedese di Handicap.

L’Organizzazione Mondiale della Sanità stima la percentuale di popolazione disabile

almeno nel 10%. Queste persone non costituiscono un gruppo omogeneo e statico, anche il

tipo di disabilità può essere temporaneo o permanente e variare d’intensità. Nei paesi

industrializzati si vive di più, ma le nascite diminuiscono. La persone hanno acquisito le

abitudini introdotte dalle attuali tecnologie, ma per alcuni l’accesso ad esse può essere

progressivamente ridotto a causa di limitazioni dovute all’età avanzata. Queste riflessioni

devono sicuramente aiutare a riflettere sull’importanza della questione dell’accessibilità

all’elaboratore. Con l’avvento e la diffusione della telematica, nuove possibilità di

integrazione economica e sociale si aprono per la popolazione disabile ed anziana. Tuttavia a

quest’interessante prospettiva si contrappongono problematiche che, se non opportunamente

considerate, possono aumentare l’isolamento di queste categorie di persone. Rendere un

terminale o servizio di facile uso per una persona disabile è garanzia di alta usabilità per ogni

utente. Per fare ciò è però necessario lo sviluppo di nuove aree di conoscenza, che includono i

tipi e il grado di disabilità, le conseguenze e le limitazioni che deve affrontare una persona

disabile, gli aspetti psicologici ed economici ed infine le strategie per incrementare l’usabilità

del prodotto.

Resta ancora da definire cosa si intenda per accessibilità. L’accessibilità si riferisce alla

progettazione dell’ambiente costruito, dei prodotti e dei servizi di uso generale in modo che

questi possano essere utilizzati agevolmente dalla più ampia percentuale di popolazione,

incluse le persone disabili od anziane. Più specificatamente per accessibilità informatica si

intende la possibilità, anche da parte di persone con impedita capacità motoria, di accedere ad

4

un ambiente informatico di comune utilizzo e di fruire di tutte le prestazioni in condizioni di

adeguata affidabilità ed autonomia.

Oggi in Europa 37 milioni di cittadini soffrono varie forme di handicap. L’Italia, in base ad

una stima della Divisione della popolazione dell’ONU, è fra i paesi a elevato tasso di

invecchiamento per la più alta percentuale di ultrasessantenni (24%) e la più bassa percentuale

(14%) di giovani di età inferiore ai 15 anni. In base alle previsioni dell’Istituto di ricerche

sulla popolazione del CNR in futuro nel nostro paese ci saranno 17 milioni di anziani e 9

milioni di giovani.

È evidente l’importanza crescente del tema della disabilità negli anni a venire. Il

progressivo invecchiamento della popolazione italiana, infatti rende necessari interventi,

anche attraverso tecnologie innovative, per migliorare le condizioni di vita delle persone che,

a causa dell’età, soffrono di disabilità di varia natura. La continua innovazione tecnologica è

riuscita a raggiungere risultati di rilievo, garantendo a molti disabili una vita uguale a quella

delle persone normodotate è possibile per loro studiare, lavorare, viaggiare e praticare perfino

sport a livello agonistico. In futuro la società si dovrà porre l’obiettivo di ricercare una sempre

migliore integrazione dei disabili.

Lo scopo di questa tesi è di apportare un piccolo contributo all’obiettivo appena citato. Tra

i numerosi campi in cui è possibile agire si è scelto di aiutare le persone affette da problemi

motori gravi. L’utilizzo da parte di queste persone del Personal Computer può essere

compromesso dall’impossibilità di utilizzare le varie periferiche di acquisizione dati come il

mouse e la tastiera. Tuttavia è possibile enfatizzare ed utilizzare le capacità motorie residue

dell’utente al fine di ottenere un metodo alternativo per indicare all’elaboratore i comandi da

eseguire. Grazie all’utilizzo di una videocamera connessa all’elaboratore si cercherà di carpire

il movimento del viso dell’utente per comprendere dove sia rivolto il suo sguardo. Sulla base

di una delle possibili soluzioni descritte verrà implementato un sistema che permetterà

all’utente di selezionare degli oggetti sullo schermo del Personal Computer. Per ottenere

questo risultato l’utente non dovrà far altro che rivolgere il viso nella direzione dell’oggetto.

Infine si discuteranno le prestazioni e i possibili sviluppi futuri del sistema.

5

2 Motivazione e obiettivi

Molte sono le barriere che una persona disabile deve affrontare e superare nella vita di tutti

i giorni. Si va da quelle architettoniche fino a quelle più subdole, nascoste negli apparecchi di

uso quotidiano. Spesso si dimentica come sia difficile, per non dire impossibile utilizzare gli

elettrodomestici se, per esempio, non si possono usare le mani per premere i pulsanti che li

controllano. Oppure leggere le istruzioni che regolano un dato apparecchio quando si hanno

notevoli problemi di vista.

Il problema più grave in cui ci si imbatte, quando si cercano delle soluzioni per migliorare

l’esistenza delle persone disabili è che purtroppo non esistono insiemi ben definiti di

disabilità. Esistono vari tipi di handicap che possono colpire una persona in modo più o meno

invalidante, non è possibile generalizzare ma è opportuno studiare i vari problemi uno alla

volta.

Questo sfortunatamente implica che, la disabilità che colpisce una gruppo numeroso di

persone sia studiata in maniera più estesa, mentre un handicap più raro venga accantonato in

quanto solo poche persone ne sono affette.

L’apporto che la tecnologia può riservare allo sviluppo di aiuti validi per le varie disabilità

è estremamente elevato. Ora più che mai, in quanto tutta la società moderna si appoggia ai

6

servigi resi dagli elaboratori. È possibile, grazie alla versatilità che offre l’informatica,

studiare ausili che aiutino in modo concreto tutti i portatori di handicap, trovando per ogni

disabilità la soluzione più consona.

2.1 Ausili L’ausilio è un oggetto che permette a chi lo utilizza di instaurare delle relazioni con le

persone che lo circondano. In questo sta la vera utilità dell’ausilio, dare alla persona disabile

la possibilità di entrare in relazione con gli altri, a casa, a scuola, al lavoro, etc. Questa

possibilità fa sì che la persona disabile abbia un’immagine di sé migliore, perché si sente parte

attiva nei suoi rapporti con gli altri e non un emarginato o un diverso. Anche l’ausilio

informatico è un oggetto, che serve a creare relazioni comunicative, che determinano

apprendimenti cognitivi che possono aumentare e facilitare contesti relazionali e sociali, tutto

ciò contribuisce al rafforzamento dell’identità della persona. Volendo dare una definizione

tecnica, l’ausilio è uno strumento che consente al disabile di fare ciò che altrimenti non

riuscirebbe a fare, oppure di farlo in maniera più sicura, veloce, meno faticosa, più accettabile

psicologicamente o infine per prevenire l’instaurarsi o l’aggravarsi di una disabilità.

2.1.1 Sensori

Un sensore (o pulsante, o switch) è un dispositivo che serve per trasformare una grandezza

fisica (pressione, spostamento, suono, soffio …) in una grandezza elettrica che viene utilizzata

per comandare dei dispositivi (giocattoli, computer …). In sostanza un sensore è un

dispositivo capace di inviare un comando (acceso, spento) ad un altro strumento.

È di grande utilità nei casi in cui l’utente non sia in grado di azionare un dispositivo

utilizzando i normali interruttori.

I sensori si dividono in due grandi categorie, da un lato si trovano i sensori singoli,

dall’altro i sensori multipli.

I sensori singoli sono sensori con un’unica funzione. Acceso oppure spento. Esistono

sensori con modalità di attivazione a testa, a tocco, pneumatici. Esistono sensori per chi è in

grado di muovere un solo dito, sensori che richiedono solo un movimento di pochi millimetri

o sensori che richiedono invece movimenti più ampi. I sensori singoli della AbleNet sono dei

pulsanti di diverse dimensioni azionabili premendo la superficie superiore. Il Big Red è un

pulsante di grandi dimensioni, attivabile tramite gli arti superiori ed inferiori, il Jelly Beam di

media dimensione, attivabile anche con il capo, ed infine lo Specs è un pulsante di piccole

dimensioni, attivabile anche col movimento delle labbra o del mento. Il sensore Jaggle della

Penny&Giles è caratterizzato dal fatto che la sua superficie può ruotare in senso orario o

7

antiorario facendo variare la resistenza del dispositivo (da un massimo di 1.5 Kg fino ad un

minimo di 200 g) con quindici diverse posizioni. Altri sensori singoli sono quelli della Tash.

Micra Light è un piccolo sensore molto sensibile e facilmente posizionabile. Left è un sensore

a stelo costituito da un’asta metallica ed un’estremità rigida ricoperta con un disco di spugna.

Quando si esercita la forza, per l’attivazione del sensore, l’estremità si flette leggermente. È

indicato per essere utilizzato con il capo da parte di chi abbia un buon controllo fine del

movimento del capo.

I sensori multipli sono sensori con più funzioni raggruppate in un unico strumento. Si

rilevano spesso ottime soluzioni perché in poco spazio racchiudono molte funzioni. Si

possono trovare sensori multipli due in uno (due sensori in uno) come nel Pneumatic Switch.

Questo è un sensore a pressione attiva o passiva (inspirazione o espirazione). Per attivare uno

switch si soffia, per attivare l’altro si inspira. Nel Racker Switch ci sono due sensori, uno per

ogni lato, che vengono attivati premendo ora sull’una ora sull’altra parte. Esistono poi sensori

5 a 1 come il Wafer il quale è costituito da cinque sensori a membrana disposti in sequenza.

Penta è formato da cinque piccoli sensori disposti a croce, per persone con un buon controllo

motorio fine. Nel Mini Joystick with Push quattro sensori vengono attivati col movimento

della leva nelle quattro direzioni, il quinto è attivato premendo la leva del joystick.

2.1.2 Tastiere

L’utilizzo della comune tastiera può essere complicato a causa di problemi nel controllo

degli arti superiori. A volte è sufficiente adottare delle modifiche alle impostazioni software

dell’elaborazione che si sta utilizzando, altre volte è possibile identificare delle soluzioni

alternative. A tale scopo esistono tastiere ridotte o espanse, tastiere a membrana,

riconfigurabili e a video su cui l’interazione avviene tramite un dispositivo di puntamento, o

tastiere con display, indipendenti dal computer. Un’altra soluzione consiste ne ricorrere ai

sistemi di controllo del computer a scansione, tramite l’uso di sensori esterni.

Le tastiere ridotte sono tastiere speciali caratterizzate da una dimensione ridotta e tasti

piccoli, ravvicinati e molto sensibili, al contrario le tastiere espanse sono tastiere di grandi

dimensioni, con tasti ingranditi. Un esempio di tastiera espansa è la Big Keys Color Plus della

Grayston Inc., in cui i tasti sono grandi il quadruplo di quelli di una tasiera normale. Un

esempio di tastiera ridotta è la WinMini della Tash Inc., in cui i tasti hanno un’area di 1.3 cm

quadrati e sono molto ravvicinati tra loro.

Nelle tastiere a membrana o riconfigurabili si può trovare la tastiera Discover Board della

Don Johnston Inc. Questa si presenta come una tavoletta su cui è possibile inserire delle

membrane plastificate e delle membrane cartacee personalizzate. Anche la tastiera espansa

8

dell’IntelliKeys, avente un’area maggiore rispetto a quella delle tastiere standard, è

riprogrammabile.

SoftType è una tastiera a video, che permette alle persone con difficoltà motorie, di inviare

comandi a qualsiasi applicazione di Windows. SoftType contiene un sistema di predizione di

parola che permette di ridurre il numero di tasti da premere. I clic del mouse possono essere

ottenuti usando la tecnica dell’Autoclic. Il clic del tasto sinistro del mouse è “realizzato” dopo

che il puntatore è rimasto immobile, per un intervallo di tempo programmabile, sul punto

desiderato. Si può avere accesso a qualsiasi applicazione o utilità Windows con l’uso

esclusivo di un sensore, di SoftType e di un sistema di puntamento col capo come

HeadMouse.

AlphaSmart è una tastiera indipendente dal computer con un display a quattro righe,

facilmente trasportabile ed ideale per chi ha problemi di scrittura.

Esistono infine sistemi a scansione come il Discover Switch della Don Johnston Inc..

Questo è un sistema integrato hardware e software per Personal Computer per l’utilizzo ed il

pieno controllo del computer attraverso la scansione. E possibile dare qualsiasi comando

all’elaboratore azionando un sensore (scansione automatica e selezione indiretta) o due

(scansione manuale e selezione diretta). Discover Switch consiste in un grosso sensore e in un

software di gestione. Una volta installato, il software riproduce, sul monitor, delle tabelle su

cui verrà attivata la scansione. Le tabelle sono personalizzabili.

2.1.3 Ausili per la comunicazione

Con il termine AAC (Comunicazione Aumentativi ed Alternativa) si trattano tutte le

strategie di comunicazione che sono alternative o incrementano la comunicazione verbale e

grafica. La AAC si pone come obiettivo la compensazione di una disabilità temporanea o

permanente del linguaggio espressivo. Vengono infatti create le condizioni affinché il disabile

abbia l’opportunità di comunicare in modo efficace, ovvero di tradurre il proprio pensiero in

una serie di segni intelligibili per l’interlocutore.

Esistono sia prodotti hardware che software per la comunicazione aumentativa. Si parla di

sistemi per la AAC con uscita in voce oppure senza uscita il voce.

Nei prodotti hardware si può trovare il Big Mack. Questo è un ausilio per la AAC con

uscita in voce, molto semplice. Attraverso la pressione di un grosso tasto si ottiene la

riproduzione di un messaggio precedentemente registrato. Al messaggio può essere associata

un’immagine o un simbolo. SpeakEasy è di nuovo un ausilio per la AAC con uscita in voce,

che permette di registrare e riprodurre dodici messaggi di durata variabile. Ad ogni messaggio

9

corrisponde un’area della tastiera sulla quale viene fissato un disegno associato al messaggio

registrato.

Comunica, al contrario, è un software di comunicazione con uscita in voce, ideato e

sviluppato dalla EasyLabs. Con Comunica è possibile costruire tabelle con celle di testo e di

immagine, cui è possibile associare file audio personalizzati e l’integrazione nel programma

della libreria completa dei simboli PCS (Picture Communication Symbols). Comunica è un

valido programma di comunicazione per persone che necessitano dell’uscita in voce.

I metodi di accesso vanno da quelli standard, quali mouse e tastiera, a quelli speciali come

dispositivi di puntamento col capo, tastiere alternative, trackball, sensori utilizzati insieme alle

modalità di scansione previste e touchscreen.

2.1.4 Dispositivi di puntamento

Esistono mouse speciali in alternativa al mouse standard per coloro che hanno difficoltà

nel controllare il movimento del puntatore e le funzioni dei tasti. Gli emulatori di mouse sono

dispositivi che permettono di controllare, attraverso sistemi diversi, il movimento del

puntatore e di svolgere tutte le funzioni del mouse. In alcuni casi si utilizza, per il movimento

del puntatore, un mouse standard e per la selezione si fa ricorso a sistemi che consentano di

realizzare il clic con l’ausilio di uno o più sensori esterni, tramite dispositivi d’interfaccia. In

altri casi è possibile ricorrere a soluzioni software per l’esecuzione dei comandi del mouse.

Dragger per Windows permette l’emulazione con un singolo clic delle funzioni del mouse.

Dragger può essere utilizzato insieme alla tecnica definita Autoclic. Chi presenta disabilità

motorie può avere facilmente accesso al mouse e alla maggior parte delle applicazioni

Windows grazie a Dragger e un qualsiasi dispositivo di puntamento.

L’accesso facilitato per Windows consente l’attivazione di un’opzione che permette di

utilizzare il tastierino numerico come emulatore di mouse.

Magic Touch è un pannello sensibile e trasparente che viene montato sullo schermo

dell’elaboratore. Per spostare il puntatore del mouse è sufficiente toccare la superficie

sensibile del pannello, in corrispondenza della posizione desiderata.

Il Palm Mouse rientra nella categoria dei mouse speciali. Questo è un mouse molto

piccolo, che consente di spostare il puntatore sul monitor utilizzando un minimo movimento

delle dita. È indicato soprattutto per distrofici e persone con sclerosi. I mouse della

Kensington, Expert mouse e Orbit Mouse, sono mouse di tipo trackball, in cui ruotando la

sfera posta sulla base è possibile spostare il puntatore sul monitor. Ai tasti funzione (quattro

nel primo e due nel secondo) è permesso associare funzioni specifiche.

10

I mouse speciali della Penny&Giles sono sia mouse a leva, sia Joystick, che Trackball.

Sono muniti di uno scudo che consente di appoggiare il polso e la mano sul piano del mouse,

impedendo di premere inavvertitamente i pulsanti abbinati alle funzioni del mouse.

NoHandsMouse della Hunter Digital consente di spostare il puntatore del mouse sul

monitor utilizzando il movimento dei piedi. Con un piede si controlla il movimento del

puntatore, con l’altro si esegue il clic.

HeadMouse della Origin Instruments consente di effettuare tutte le operazioni di un

normale mouse attraverso i movimenti del capo. È il mouse standard per persone che non

possono usare le mani, è infatti indicato per persone tetraplegiche e per chiunque sia in grado

di usare solo i movimenti del capo.

2.1.5 Sistemi di puntamento oculare

I dispositivi di puntamento rappresentano diversi modi per controllare il puntatore sul

monitor al fine di eseguire le azioni che si desiderano, tuttavia i dispositivi trattati nel

paragrafo precedente sono difficilmente accessibili a disabili motori per la difficoltà di

compiere due azioni distinte, lo spostamento sul piano e la pressione del tasto di selezione. In

questo, vengono in aiuto i sistemi basati sulla cattura dei movimenti dello sguardo per

direzionare il puntatore sullo schermo.

Questi dispositivi possono essere costituiti da una fascia o un caschetto che l’utente deve

indossare, oppure possono essere dispositivi da fissare sulla montatura di un paio di occhiali.

La soluzione migliore è però rappresentata da quei sistemi che non necessitano di alcun

dispositivo collegato all’utente, che quindi rimane più libero nei movimenti.

Il loro funzionamento si basa sulla

presenza di una telecamera e in alcuni casi di

un LED (emettitore di luce infrarossa), a

seconda del metodo utilizzato nella predizione

del punto di fissazione. Il sistema è costituito

da un monitor, sotto cui è montata una

telecamera ed un LED ad infrarossi. Il LED

emette dei raggi infrarossi a bassa potenza, che illuminano l’occhio. La superficie della cornea

riflette questi raggi. La riflessione genera il cosiddetto effetto occhi rossi. Questo effetto

migliora l’immagine della pupilla alla telecamera e rende più facile alle funzioni di

elaborazione delle immagini localizzare il centro pupilla. Il sistema calcola il punto fissato

dall’occhio basandosi sulla posizione relativa del centro pupilla e sulla riflessione della cornea

nell’immagine dell’occhio.

11

EyeGaze System della LC Technologies Inc. è un

sistema progettato per individuare su quale punto dello

schermo è fissato lo sguardo dell’utente. Il clic del

mouse è realizzato tramite una pausa di fissazione

dell’occhio sul punto desiderato. Le operazioni di

elaborazione delle immagini e di calcolo del punto

fissato vengono eseguite da un software apposito che

viene installato nell’elaboratore dell’utente. Le rilevazioni vengono effettuate ad una velocità

di campionamento di 60 Hertz. L’utilizzo di questo sistema richiede un buon controllo del

capo, l’utente deve essere in grado di mantenere lo sguardo fisso su di un punto per almeno

mezzo secondo, deve avere una buona vista e possedere adeguate capacità mentali. EyeGaze

deve essere utilizzato lontano dalle finestre e in presenza di un numero limitato di sorgenti di

luce infrarossa.

Quick Glace Eye-tracking System della Eye Tech Digital Sistem è un dispositivo che emula

il comportamento del mouse utilizzando il movimento degli occhi. Una telecamera montata

sotto il monitor del Personal Computer si focalizza sull’occhio dell’utente. Due LED a bassa

potenza sono montati ai lati del monitor. Quick Glace determina dove l’utente stia guardando

sul video, cioè il punto fissato, analizzando la posizione della riflessione della cornea e il

centro pupilla. Il cursore viene posizionato proprio sul punto calcolato in questo modo. Il clic

del mouse è realizzato con un battito di ciglia oppure con una pausa dell’occhio o ancora

tramite un sensore esterno. L’inseguimento dell’occhio avviene ad una velocità di 30

campioni al secondo.

ERICA System della ERICA Inc. permette di inseguire e

memorizzare il movimento degli occhi e la dilatazione della

pupilla di una persona, sullo schermo di un computer. ERICA

sta infatti per Eyegaze Response Interface Computer Aid

System. Inseguendo il movimento degli occhi di una persona il

sistema permette alla persona stessa di controllare il proprio

elaboratore utilizzando lo sguardo. Come i due prodotti

descritti sopra, anche ERICA non è un dispositivo intrusivo per

l’utente, infatti si ha di nuovo una telecamera ed un LED, questa volta posti in una scatola

sotto il monitor. Il sistema lavora alla velocità di 60 Hertz.

12

2.2 Contesto Prima di illustrare il contesto e con esso la strada che questa discussione intraprenderà nei

capitoli successivi è importante rilevare una caratteristica molto importante sui metodi

utilizzati per definire nuovi ausili utili alla comunità.

Come è già stato introdotto precedentemente è pressoché impossibile definire con esattezza

tutte le tipologie di handicap possibili. I disturbi sono così numerosi da rendere inutile la

ricerca di una soluzione unica per ogni disabilità. La comparsa di una minorazione può

associarsi ad altre e anche l’intensità con cui si presenta può variare notevolmente.

Due sono le possibili vie da percorre per ottenere un risultato apprezzabile nel superamento

delle barriere che l’handicap erige. La prima consiste nell’utilizzare a proprio favore la

caratteristica di eterogeneità nelle diverse forme di disabilità. Come l’handicap si presenta

sotto vari aspetti anche gli ausili devono proporre il più grande numero di soluzioni.

Progettare e produrre ausili che risolvano in modo diretto un solo e singolo problema ma che

non si ostacolino a vicenda e possano essere impiegati insieme. Questo permette di utilizzare,

o adattare, un certo numero di ausili in maniera concorrente per soddisfare il maggior numero

di utenti disabili, proponendo a ciascuno di loro il mix di ausili adatto alle loro necessità.

L’altra via percorribile è progettare e generare degli ausili la cui caratteristica principale sia

la versatilità, al fine di ottenere un numeroso bacino di utenza costituito da un gruppo

abbastanza eterogeneo di utenti disabili. Ciò significa puntare su un sistema unico che aiuti a

superare le diverse barriere erette da diversi handicap che hanno solo una radice comune, ma

che per intensità e caratteristiche marginali possono essere molto diversi fra loro.

Analizzando i diversi ausili presenti attualmente sul mercato balza subito all’occhio come

essi orbitino intorno al primo metodo descritto precedentemente. Tutti sono abbastanza

specifici e risolvono solo un problema alla volta, tuttavia l’utilizzo di un ausilio non preclude

l’uso concorrente di altri sistemi. Ciò nonostante è sempre possibile non tenere conto in fase

di progetto di importanti caratteristiche che il sistema deve possedere. Questo introduce un

aspetto abbastanza subdolo, che interviene quando più ausili vengono utilizzati insieme. Le

prestazioni globali sono vincolate al più modesto degli ausili utilizzati. Se, per esempio, uno

degli ausili impiegati in concorrenza non tiene conto della caratteristica relativa alla comodità

offerta all’utente è molto probabile che l’intero pacchetto venga menomato da questo

problema, anche se gli altri ausili sono ben progettati. Infine la versatilità di un certo ausilio

spesso è abbastanza rigida e non permette un impiego dell’ausilio diverso da quello

programmato.

13

L’utilizzo di un sistema unico e più completo, potrebbe essere in grado di eliminare questi

problemi. È infatti possibile avere sotto controllo tutti i passi nella progettazione e definire le

linee guida che determineranno i compiti che il sistema dovrà portare a termine. È possibile

discutere e riunire in fase di progettazione tutte le caratteristiche che faranno parte

dell’ausilio, senza dipendere da fattori esterni che potrebbero ostacolare le prestazioni del

sistema. Ovviamente una cattiva progettazione e la possibilità di trascurare fattori

determinanti possono essere deleteri al fine del risultato, alla pari o ancor peggio della

soluzione precedente.

2.2.1 Definizione degli obiettivi

È arrivato il momento di trasferire i concetti appena descritti nella pratica, per definire in

maniera completa il contesto che farà da sfondo per tutto il resto della discussione. A questo

punto molte sono le possibili vie da percorrere, ogni disabilità merita una corretta analisi,

tuttavia si deve compiere una scelta, non è possibile trattare in modo completo tutti i diversi

tipi di handicap. Si deve focalizzare un problema tra i tanti e cercare di risolverlo.

Quali risposte possono offrire le due correnti di pensiero se, per esempio, si vuole aiutare

le persone incapaci di utilizzare gli arti per comandare le periferiche di input del Personal

Computer come il mouse, ma che possiedono ancora un buon controllo del capo e una vista

sufficiente.

I seguaci della prima filosofia potrebbero consigliare a queste persone l’utilizzo del Magic

Touch unito all’utilizzo di un bastoncino da tenere in bocca per indicare al sistema le scelte

effettuate. Oppure l’utilizzo di uno dei tanti sistemi di puntamento oculare. Tuttavia sia l’una

che l’altra soluzione comportano numerosi difetti. Alla prima si può associare quanto detto in

precedenza sulla caratteristica di comodità, l’utente può affaticarsi se la sessione di lavoro si

protrae nel tempo. In quanto il Magic Touch non è stato progettato per questi scopi ma per un

utilizzo che includesse l’uso delle mani nella selezione. La seconda soluzione potrebbe

risolvere in modo corretto il problema, ma chi l’ha proposta non ha tenuto conto della

componente economica che spesso può costituire un aspetto importante.

Una soluzione in linea con la seconda corrente di pensiero potrebbe, in questo caso,

risolvere brillantemente i problemi che affliggono la soluzione precedente. La costituzione di

un sistema unico e progettato espressamente per questo gruppo di disabilità può essere la

risposta alla domanda. Avere la possibilità di progettare e costruire un sistema dalla base

permette di avere sotto controllo l’intero processo e definire con precisione tutte le

caratteristiche che l’ausilio deve possedere. In questo modo si può conservare ciò che di

buono ha introdotto la soluzione precedente eliminando nel contempo i difetti.

14

Utilizzare il movimento del capo dell’utente per selezionare gli oggetti sul terminale video

non è una cattiva idea, tuttavia si deve rimediare all’uso della bacchetta e nel contempo

cercare di eliminare il Magic Touch con una soluzione più confortevole ed economica. La

risposta si può ottenere dai sistemi di puntamento. Sarebbe ideale usare le potenzialità offerte

dalla cattura video per sostituire la bacchetta e il Magic Touch. Elaborare le immagini che

descrivono il volto dell’utente, al fine di cogliere il punto sul video dove l’utente ha rivolto il

viso. Infine, per la componente economica, attuare questa cattura con una WebCam.

Questa soluzione è estremamente confortevole per l’utente, in quanto quest’ultimo ha il

volto libero e deve solo muovere il capo nella direzione dell’oggetto sullo schermo che vuole

selezionare. Inoltre è possibile sfruttare in modo completo le potenzialità di un sistema

progettato dalle fondamenta, rendendolo assai versatile. Questa caratteristica permette di

utilizzare il sistema con vati tipi di handicap, in quanto è possibile spingersi fino alle

prestazioni di un sistema di puntamento oculare. Ciò significa poter utilizzare l’ausilio anche

per gli utenti che non hanno più la capacità motoria del capo ma che possiedono ancora un

buon movimento oculare. Il sistema in questo caso elabora solo la parte di immagine relativa

all’occhio dell’utente, divenendo una specie di versione economica di un sistema Eye-

tracking.

In conclusione l’obiettivo proposto è cercare di simulare il comportamento di un mouse

standard per Personal Computer utilizzando una camera economica come per esempio una

WebCam che cattura immagini relative al volto dell’utente. Questo per aiutare il maggior

numero di persone che, per vari motivi, non possono utilizzare le mani per imprimere i

comandi ad un normale mouse. Infine si può intravedere anche un utilizzo da parte di persone

normodotate che per qualche ragione hanno le mani occupate e devono poter utilizzare il

Personal Computer.

2.3 Analisi dei problemi Purtroppo in un contesto del genere non esistono sistemi già esistenti a cui si possa fare

riferimento e a cui ci si possa appoggiare per sviluppare un ausilio con le caratteristiche

determinate in precedenza. In questo caso l’ausilio deve essere creato dal nulla e perciò nella

determinazione dei problemi bisogna tenere conto anche di questo fatto. Questo porta ad una

prima generale divisione dei problemi in due gruppi diversi, da un lato ci sono le incertezze

dovute alla definizione di un nuovo sistema, dall’altro le incognite insite nella fase di sviluppo

che si accompagnano sempre ad un’operazione di miglioramento di un dato sistema in

crescita. Inoltre questi due insiemi di problemi non sono separati, ma interagiscono fra di loro

15

e la soluzione di un dato dubbio può influire positivamente o in modo negativo sulla

trattazione di un altro problema. Quindi per risolverli occorre avere sempre in mente il

disegno generale o contesto in cui il sistema si deve collocare.

Un’ulteriore suddivisione dei problemi si intravede nella sostanza stessa dell’ausilio, esso

infatti, oltre ad avere incertezze legate esclusivamente alla sua costituzione e di attinenza

riservata agli sviluppatori, ha al suo interno un’ulteriore peculiarità. Deve, infatti, rendere un

servizio ad una certa classe di utenza che, per sua natura, porta con sé alcune caratteristiche da

prendere seriamente in esame. Questi utenti non interagiscono con i sistemi e gli ausili

informatici allo stesso modo della maggioranza dei consumatori. Oltre ai normali problemi di

generazione di un nuovo sistema, si vengono a creare delle incognite relative alle capacità

residue e limitate degli utenti a cui questo ausilio deve dare il massimo aiuto possibile, per

questo motivo il fine ultimo nella progettazione e a cui dobbiamo sempre fare riferimento,

sarà ottimizzare le capacità residue di un numero limitato di utenti.

In accordo con quanto appena descritto è possibile dividere la parte successiva del

paragrafo in sezioni separate che descrivono i vari problemi che si incontreranno. Per quanto

riguarda le incertezze relative alla definizione di un nuovo sistema, si possono individuare la

scelta dell’ambiente di lavoro e la definizione dell’architettura del sistema. Per i problemi

relativi alla parte di sviluppo si incontrano i dubbi relativi al numero, all’utilizzo e al

posizionamento delle periferiche di acquisizione dati e la difficoltà di innestare nel sistema

applicazioni indipendenti. Un’ulteriore incognita risiede nella possibilità di calibrare il

sistema per le sessioni di lavoro. Quest’ultimo deve essere posto nell’insieme di incertezze

legate all’ausilio stesso in quanto per risolverlo è necessario tenere in considerazione le

capacità limitate dell’utente finale.

È d’obbligo aggiungere che in questo capitolo non si può fare altro che elencare e analizzare i

vari problemi, mentre la scelta delle varie opzioni possibili e la soluzione dei problemi è

demandata ai capitoli successivi.

2.3.1 Ambiente di lavoro

Il primo e più importante problema, la cui soluzione porrà le basi per lo sviluppo

successivo, è la scelta del Sistema Operativo che verrà usato da piattaforma software per

sviluppare e rendere attivo l’ausilio informatico. Decidere quale sia l’ambiente di lavoro non è

un’impresa facile, infatti le qualità che dovranno caratterizzare l’ausilio sono spesso in

contrasto fra di loro. Da una parte c’è il desiderio degli sviluppatori di avere una piattaforma

software completamente raggiungibile e analizzabile come per esempio una piattaforma Unix.

16

Dall’altra c’è la necessità di ottenere il maggior numero di utenti possibile, e per far questo,

bisogna orientare il lavoro verso ambienti Microsoft.

Sia l’una che l’altra possibilità offrono vantaggi e svantaggi. Se si orienta lo sviluppo verso

un Sistema Operativo della famiglia Unix come per esempio Linux, lo sviluppatore può avere

sotto controllo tutto il sistema e quindi verificare autonomamente l’intera catena di

acquisizione dati. Inoltre tale sistema offre delle librerie software estremamente potenti che

permettono allo sviluppatore di creare una struttura complessa, ottimizzata ed elegante da un

punto di vista architetturale, per esempio Unix nasce, tra le altre cose, con il supporto IPC

(Inter Process Comunication) che permette agli sviluppatori la possibilità di progettare

sistemi che lavorano in modalità concorrente e quindi, se il sistema viene progettato in

maniera corretta, ottimizzata. Tuttavia esistono degli svantaggi: per esempio la maggioranza

delle periferiche di acquisizione video non vengono fornite, dalla casa costruttrice, con i

relativi driver per questa piattaforma, questo implica che la scelta sui dispositivi di cattura

video è limitata. Questo Sistema Operativo, inoltre, viene visto come un sistema per gli

“addetti ai lavori” e non ha incontrato ancora l’approvazione del grande pubblico che lo

reputa una piattaforma difficile da usare.

Anche l’ambiente Microsoft presenta pregi e difetti, ad esempio la sua vastissima

diffusione, fa sì che un ausilio ideato per questa famiglia di Sistemi Operativi possa

raggiungere un numero molto grande di utenti ed inoltre la maggioranza delle periferiche di

acquisizione video sono fornite con gli elementi necessari all’installazione su questa

piattaforma. Oltre a ciò numerosi sono gli ambienti di sviluppo, estremamente validi, che

accompagnano questo ambiente, il che vuol dire avere un aiuto energico in fase di sviluppo e

di controllo degli errori. Dall’altra parte, utilizzando queste piattaforme, è obbligatorio

scendere a compromessi per quanto riguarda la visibilità sul sistema, infatti molte parti di esso

sono vincolate dal copyright e quindi non permettono allo sviluppatore di accedere in modo

libero al sorgente, questo può causare dei fastidi se si vuole andare molto in profondità nello

sviluppo della catena di acquisizione dati. Inoltre questa famiglia di Sistemi Operativi non

agevola lo sviluppo di applicazioni con strutture informatiche complesse come per esempio

l’IPC di Unix, tuttavia permette agli sviluppatori di creare sistemi con una struttura ottima ed

elegante anche se più semplice.

Per affrontare in modo completo ed esauriente i dubbi sulla scelta dell’ambiente di lavoro,

entrano in gioco anche gli obiettivi che abbiamo descritto a proposito del contesto a cui

l’ausilio deve adattarsi. La possibilità di rendere il sistema molto versatile e raggiungibile dal

più grande numero di utenti possibile indica la direzione da intraprendere per definire il

17

Sistema Operativo che farà da piattaforma software per in nostro sistema, infatti le garanzie

per ottenere un simile risultato le può offrire solamente la famiglia Microsoft. Quindi si arriva

al compromesso nella scelta dell’ambiente di sviluppo: a fronte di un sistema più semplice ma

ugualmente ben strutturato ed elegante c’è la possibilità di raggiungere una popolazione di

utenti enormemente vasta.

In conclusione il sistema sviluppato permetterà il suo utilizzo sui seguenti Sistemi

Operativi : Windows 98 e Windows 2000.

2.3.2 Architettura del sistema

Scelta la piattaforma software la strada da percorrere per raggiungere il gli obiettivi

preposti è, per così dire, solamente tracciata, i successivi passi sono l’individuazione di un

adeguato linguaggio di programmazione e la definizione dell’architettura del sistema. Per

quanto riguarda il primo dubbio in questa sezione è possibile dare già una soluzione, tuttavia

sull’architettura in questo contesto non si può far altro che definire le varie possibilità e

indicare quali caratteristiche il progetto deve possedere, sarà compito dei capitoli successivi

descrivere le soluzioni che si sono adottate.

La scelta del linguaggio di programmazione è nella maggior parte dei casi fortemente

influenzata dalle preferenze soggettive dello sviluppatore, tuttavia questa peculiarità può

indirizzare la scelta su un linguaggio non adatto che non è in grado cioè di soddisfare le

proprietà che la struttura del sistema richiede. Infatti le caratteristiche che un sistema deve

possedere variano a seconda del contesto in cui ci si ritrova ad operare, per esempio un

programma che risiede in una pagina Web ha bisogno di un linguaggio di programmazione

quale Java o JavaScript che permette a chi sviluppa di utilizzare al massimo le potenzialità

che la rete offre. Un sistema che deve interrogare un Data Base e che vuole visualizzare in

una finestra Windows i suoi risultati può, con successo, utilizzare Visual Basic e interrogare

la banca dati con comandi SQL embedded oppure ODBC (OpenDataBaseConnectivity). Nel

caso in cui all’utilizzo delle finestre Windows si unisce l’obbligo di definire una struttura

complessa il linguaggio più adatto alle è il C++. Quest’ultimo offre la possibilità di utilizzare

le librerie MFC, svariate altre potenzialità come la programmazione concorrente e le librerie

Video for Windows che permettono la cattura e l’elaborazione delle immagini grazie alle

videocamere.

Definire l’architettura del sistema è analogo a progettare le fondamenta di un grattacielo, se

il progetto non rispetta certi modelli o vincoli c’è il pericolo che tutta la struttura divenga

poco solida e rischi di crollare quando diviene molto complessa ed elaborata. Questo esempio

fa capire come una sua definizione accurata sia indispensabile per il sistema stesso.

18

Una prima qualità che la struttura deve possedere è la semplicità, un’architettura semplice

porta innumerevoli vantaggi sia agli sviluppatori che al sistema stesso. Infatti, i primi possono

godere di un codice sorgente facile, elegante e preciso da scrivere e da leggere, che permetta

una chiara localizzazione degli errori e la capacità di comprendere velocemente in che punti

agire per integrare nuove soluzioni. Inoltre nel concetto di semplicità è insita anche un’altra

qualità estremamente importante per un sistema votato a crescere nel tempo. La capacità di

permettere enormi sviluppi futuri senza sovraccaricare la struttura. Evitando, in questo modo,

agli sviluppatori di arrivare al punto in cui non è più possibile aggiungere nuove potenzialità

al sistema, senza stravolgere la struttura stessa.

Anche la versatilità è un’importante qualità, infatti un’architettura che rispetta questa

imposizione permette ai suoi sviluppatori di modificarne i contenuti, senza sconvolgerli, al

fine di superare i problemi che vengono alla luce mentre l’implementazione procede. Inoltre

se l’architettura è ben strutturata è possibile modificare gli obiettivi del sistema mentre il

sistema stesso viene creato, questo significa che le finalità iniziali, spesso limitate, possono

venire corrette o addirittura ampliate nella fase di sviluppo del sistema.

2.3.3 Acquisizione ed elaborazione dati

Contrariamente alle due sezioni precedenti nel quale si discutevano incertezze che

accompagnano sempre la fase di costituzione di una nuova applicazione e che perciò tendono

a vedere l’ausilio nella sua totalità e nel suo fine ultimo, qui vengono descritti e analizzati i

problemi dovuti alle caratteristiche dell’ausilio stesso, in pratica come l’applicazione dovrà

operare.

Tenendo ben presente quanto osservato nella parte relativa al contesto, l’ausilio deve

essere in grado, tramite la cattura di immagini dell’utente posizionato davanti allo schermo

del Personal Computer, di indicare quale zona del video ella stia osservando. Questa

mansione porta con sé svariati problemi che, per una corretta osservazione, devono essere

suddivisi in varie categorie.

Nascono per esempio domande su quante videocamere possano essere utilizzate, se sia più

utile usare motion capture intrusiva o non intrusiva, la posizione delle sorgenti video ed infine

come i dati acquisiti debbano essere elaborati.

La prima categoria di incognite, legate al numero delle videocamere, introduce un

problema abbastanza spinoso. Infatti, non è possibile definire con precisione il numero esatto

di videocamere ottimale per il sistema. Solo lo sviluppo può rispondere a questo dubbio, in

questo contesto è possibile indicare solamente il massimo numero di videocamere collegabili

al Personal Computer. Risulta evidente che più videocamere il sistema è in grado di utilizzare,

19

maggiore sarà l’insieme di dati che esso potrà elaborare. Tuttavia, si deve tener conto del fatto

che un maggior numero di dati non è indice di maggior precisione. Sarà compito

dell’implementazione gestire al meglio i dati ottenuti, anche con l’utilizzo di un numero

minore di videocamere, al fine di ottenere buoni risultati. Inoltre c’è un limite fisico al numero

di camere collegabili al sistema. Infatti, oltre al numero limitato di porte USB che un Personal

Computer di solito supporta (in genere due), c’è il limite imposto dalla banda massima del

Bus che collega le porte alla scheda madre. È questo il limite più restrittivo sui moderni

elaboratori. Questo significa che, sebbene ci sia la possibilità di installare due o più camere

grazie a diverse porte USB, fornite dal Personal Computer, quest’ultime non potranno

acquisire ed elaborare i dati in maniera concorrente. Questo perché la banda disponibile

risulta insufficiente per più di una camera. Anche l’elaborazione dei dati, attuata dal

processore, potrebbe essere inscritta tra le limitazioni al numero di sorgenti video. Tuttavia

una struttura del sistema ottimizzata e dei buoni algoritmi per l’acquisizione permettono di

superare questa incertezza. Quindi l’unico limite fondamentale al numero delle camere è dato

dal collo di bottiglia localizzato nel Bus dati. Se questo, in futuro, sarà più capiente ci sarà la

possibilità di aggiungere ed elaborare più sorgenti video. Tuttora si arriva all’utilizzo di non

più di due videocamere nei PC più recenti, mentre la maggioranza degli elaboratori ne

supporta, in concorrenza, solo una.

A questo punto è d’obbligo discutere come il sistema interpreta e percepisce il movimento

nelle immagini catturate. Due sono le possibili strade da intraprendere per arrivare ad una

soluzione, utilizzare motion capture non intrusiva oppure intrusiva. La tecnica non intrusiva

ha il vantaggio di rendere più comoda la sessione di lavoro all’utente. In quanto quest’ultimo

non ha l’obbligo di indossare nessun elemento estraneo. La cattura del movimento viene

totalmente affidata all’elaboratore. Tuttavia ha lo svantaggio di essere poco precisa, onerosa

per l’elaborazione ed estremamente suscettibile alle condizioni ambientali di luce. Questo

comporta che, nella maggioranza dei casi, si propenda per la seconda ipotesi, cioè utilizzare

motion capture intrusiva. Il vantaggio più evidente è la semplicità con cui essa porta a termine

il suo compito. Grazie all’utilizzo di un riferimento colorato, per esempio un pallino fissato

sull’elemento di cui si vuole conoscere il movimento, l’elaborazione risulta più semplice.

Vengono evitati molti problemi relativi alla definizione delle immagini e ridotti quelli

sull’illuminazione. Questo comporta una precisione maggiore e una gestione di errori

relativamente più contenuti. Tuttavia ha lo svantaggio di costringe l’utente ad indossare un

elemento estraneo che permetta la cattura del movimento.

20

Occorre, prima di parlare della collocazione delle videocamere, definire la posizione

ottimale che l’utente utilizza per accedere all’ausilio. Più la posizione è ergonomia più

l’utente può affrontare lunghe sessioni di lavoro, senza affaticarsi. Questo problema non

comporta un esame laborioso, infatti non sono molte le posizioni che l’utente può avere

davanti al video di un Personal Computer, l’unico obiettivo è l’obbligo di rendere comoda la

sessione. Anche se questa decisione è molto soggettiva sembra ovvia la scelta di posizionarsi

in asse con il centro dello schermo ed avere il bordo superiore dello stesso pressappoco alla

medesima altezza degli occhi.

Per discutere, ora, la collocazione delle videocamere è necessario definire i pregi e difetti

di ogni possibile posizione, infatti ogni ubicazione valorizza certi aspetti mentre ne limita

altri. Per esempio, se disponiamo di una sola videocamera, una posizione centrata con l’asse

verticale dello schermo rende l’elaborazione dei dati simmetrica e lineare. Questo perché

l’asse verticale dell’utente stesso è centrato su quello del video. Tuttavia esiste ancora un

grado di libertà che porta a due ubicazioni differenti: sopra o sotto il video. Ognuna di queste

posizioni porta con sé diverse caratteristiche. Infatti la collocazione inferiore consente alla

camera di avere sempre sotto controllo la zona dell’occhio dell’utente. Comunque il

riferimento colorato, se si usa motion capture intrusiva, subisce degli spostamenti non lineari

e difficili da compensare in fase di elaborazione. La soluzione superiore d’altra parte

intercetta movimenti lineari e costanti del riferimento ma rende precaria l’identificazione

dell’occhio. Se per esempio, l’utente sta guardando verso il basso, l’occhio può venire coperto

dal sopracciglio.

Tuttavia l’utilizzo di una sola videocamera posta in asse con l’utente non offre al sistema

dati sufficienti per comprendere tutti i movimenti che l’utilizzatore è libero di fare. Questo

porta ad errori sulla valutazione e l’interpretazione di rotazioni o spostamenti laterali eseguite

da chi usa il sistema. In questo caso diviene essenziale l’uso concorrente di almeno due

videocamere per l’acquisizione delle immagini al fine di compensare questi errori di

valutazione. Infatti la seconda videocamera posta lateralmente all’utente può fornire al

sistema il modo con cui capire qualsiasi movimento che l’utilizzatore compie davanti allo

schermo. Tuttavia anche questa soluzione porta con sé dei problemi, in questo caso puramente

pratici, sulla collocazione fisica della videocamera. Difatti essa deve essere posta lateralmente

all’altezza del capo dell’utente e raramente in questa posizione si può appoggiare qualcosa in

quanto c’è aria libera.

Un’ulteriore soluzione che utilizza due videocamere, nata grazie allo sviluppo stesso del

sistema e che risulta abbastanza ingegnosa, è quella di posizionare la prima sopra il video, la

21

seconda sotto ed entrambe sull’asse verticale dello schermo. Una tale soluzione anche se non

permette al sistema di elaborare strane rotazioni, fa sì che si abbia sempre sotto controllo il

riferimento e la zona dell’occhio dell’utente. La videocamera posta in alto può, grazie al

riferimento e alla motion capture intrusiva, definire con una buona precisione il punto dello

schermo verso il quale l’utente ha rivolto il viso. La videocamera in basso può definire più

accuratamente dove si posa lo sguardo, al fine di simulare le prestazioni di un sistema eye-

tracking. Oppure, non elaborare informazioni relative ai movimenti dell’occhio, ma attuare

una sorta di selezione (clic) agendo sul riconoscimento della palpebra abbassata per alcuni

istanti. Inoltre non ci sono problemi per quanto riguarda la posizione fisica nello spazio delle

due videocamere, si ha sempre un base dove appoggiarle, la prima sul ripiano superiore del

video, la seconda sul tavolo dove risiede il video stesso.

Restano, a questo punto, da trattare le incertezze relative all’elaborazione delle immagini

che le videocamere sono in grado di catturare. Anche in questo caso ci sono varie strade

percorribili e ognuna ha pregi e difetti. C’è la possibilità di utilizzare i dati forniti dalle

videocamere per generare un modello virtuale della posizione nello spazio dell’utente, per

calcolare in maniera geometrica il punto fissato da quest’ultimo. Tuttavia una soluzione che

adotta un tale sistema porta con sé più svantaggi che vantaggi. Diviene pressoché impossibile

cablare il sistema in maniera corretta a causa dei parametri variabili che entrano in gioco. Per

esempio devono essere definite le posizioni nello spazio delle camere, dello schermo e

dell’utente nonché le sue dimensioni fisiche, cosicché le equazioni geometriche aumentano in

maniera non controllabile. Quindi a fronte di una trattazione più rigorosa ed esatta dal punto

di vista matematico e geometrico c’è il pericolo reale di introdurre nel sistema errori troppo

grandi. I quali possono pregiudicare in maniera devastante l’elaborazione dei dati acquisiti

grazie alle videocamere. Un altro percorso, al contrario, permette di evitare un tale salto nel

buio e, sebbene porti ad una soluzione meno elaborata da un punto di vista matematico,

promette allo sviluppatore di poter raggiungere una possibile risoluzione del problema. L’idea

di base che caratterizza questa soluzione sta nello sfruttare la natura statica delle videocamere.

Si può utilizzare un sistema di riferimento geometrico riconducibile ad una finestra da

sovrapporre all’immagine stessa. Questa finestra delimiterà il campo d’azione del riferimento

che è, se si usa motion capture intrusiva, solidale con il volto dell’utente. Se, per esempio,

quest’ultimo fissa la parte superiore dello schermo, l’immagine catturata dalla camera rileverà

che il riferimento è in prossimità del lato superiore della finestra e così di seguito, anche se

rovesciati come in uno specchio, tutti gli altri punti fissati dall’utente sul video. Questa

soluzione inoltre, ha il pregio estremamente importante di non creare difficoltà e rendere la

22

fase di calibrazione più leggera. Per rendere il sistema in grado di elaborare i dati relativi al

riferimento, nella fase di taratura, l’utente non deve fare altro che definire, fissando gli estremi

del video, la finestra utilizzata come sistema di riferimento. Ovviamente è in questa fase che

si deve tener conto delle non linearità apportate dalla posizione scelta della videocamera. Se la

posizione è quella relativa alla zona superiore del video, si otterranno dati che comportano

meno problemi. La soluzione che prevede la videocamera al di sotto del terminale video

invece apporta le non linearità che il sistema dovrà poi compensare.

In conclusione, i problemi relativi all’acquisizione e all’elaborazione dei dati sono

molteplici, tuttavia in questa, e nelle altre sezioni del capitolo, non si è fatto altro che esporli e

descriverli in maniera esauriente. Solo nel capitolo successivo dedicato all’architettura e agli

algoritmi si cercherà di definire delle soluzioni per questi problemi.

2.3.4 Integrazione nel sistema di applicazioni indipendenti

Ha preso piede, ormai da parecchi anni, il concetto di programmazione orientata o basata

sugli oggetti. Oltre ad avere un sorgente più ordinato e facile da comprendere, questa tecnica

ha sconvolto il modo di concepire la struttura di qualsiasi sistema informatico. Infatti ha

introdotto l’idea per cui un oggetto può essere utilizzato in molteplici modi, senza dover

conoscere come le operazioni sono svolte al suo interno o gli algoritmi che il suo creatore ha

utilizzato per il funzionamento. L’oggetto è visto come una scatola nera che risponde a

determinate domande. La rivoluzione sta nel fatto che uno sviluppatore può utilizzare oggetti

creati da altri, per produrre oggetti a loro volta utili agli sviluppatori.

Anche codesto sistema dovrebbe seguire questa logica per attuare una sorta di

stratificazione tra il sistema vero e proprio e le applicazioni ad esso correlate. Questo al fine di

separare i due compiti e permettere a futuri sviluppatori di inserire le loro applicazioni

all’interno dalla struttura in modo semplice. Evitando a quest’ultimi l’obbligo di dover

comprendere il funzionamento interno del sistema come la catena di acquisizione dati o gli

algoritmi di elaborazione, ma richiedere solamente l’elaborazione di pochi segnali che lo

strato del sistema manda alle loro applicazioni.

Una struttura che permette l’esistenza di questa caratteristica porta con sé un ulteriore

vantaggio, in quanto è possibile differenziare il lavoro di sviluppo su due fronti distinti.

Infatti, mantenendo fissi i messaggi che i due strati si scambiano, si è in grado di permettere

ad un potenziale gruppo di sviluppatori di lavorare su applicazioni indipendenti tra di loro.

Mentre sul versante opposto il sistema stesso può venire sviluppato da altri, per esempio

ottimizzando e rendendo più complessi gli algoritmi di elaborazione dati utilizzati, senza

compromettere o rendere inservibile il lavoro sulle applicazioni.

23

2.3.5 Calibrazione

I dubbi insiti nella calibrazione dell’ausilio devono essere analizzati, come spiegato

precedentemente, tenendo sempre in considerazione le capacità relativamente limitate

dell’utente finale.

Occorre, prima di tutto, definire con esattezza cosa si intende per calibrazione. Tutte le

periferiche che si possono installare su un Personal Computer, siano esse di acquisizione che

di visualizzazione dati, richiedono, subito dopo la fase di installazione, una fase di

calibrazione dei parametri di funzionamento. Per esempio un mouse possiede alcuni parametri

che devono essere definiti sulla base delle sensazioni che l’utente vuole avere quando utilizza

l’ausilio. Una di queste può essere l’accelerazione che la freccia subisce quando si muove il

mouse, oppure il tempo che trascorre tra un click e il successivo per definire il doppio click o

ancora l’opportunità di rendere l’utilizzo più semplice a una persona mancina, agendo sulla

possibilità di invertire i pulsanti. Tutti questi ed altri parametri vengono definiti grazie ad

un’applicazione fornita dalla casa costruttrice della periferica. Dopodiché è possibile usare

l’ausilio, senza più cablarlo, nelle sessioni di lavoro che l’utente realizza successivamente sul

Personal Computer.

Inoltre esiste un’ulteriore concetto che deve essere considerato prima di poter procedere

nell’analisi di questo ausilio. Le periferiche utilizzano due principali tecniche per interpretare

i dati, da un lato ci sono gli ausili che utilizzano un sistema di riferimento relativo, per

elaborare e rendere coerenti gli input che ottengono. Dall’altro, quelli che usano un sistema

assoluto di riferimento. Del primo insieme fanno parte la maggioranza delle periferiche di

acquisizione dati, per esempio, un mouse utilizza questo metodo per visualizzare gli

spostamenti della freccia sul video. Infatti ogni spostamento viene calcolato come distanza

vettorizzata tra la posizione iniziale e finale che il mouse occupa nello spazio. Tuttavia esso

non possiede un sistema di riferimento fisso perché l’utente è in grado di annullare il suo

funzionamento, semplicemente sollevandolo dal tavolo. Questo implica che, ad ogni

sollevamento esso modifica il suo sistema di riferimento. Un joystick, al contrario fa parte del

secondo insieme perché utilizza un sistema di riferimento fisso e assoluto, centrato nella sua

posizione di riposo.

Ora è possibile analizzare con facilità le caratteristiche che definiscono la calibrazione

dell’ausilio. Il problema può essere visto come due operazioni da svolgere in sequenza. Il

primo passo consiste nel preparare il sistema alle condizioni ambientali in cui si troverà ad

operare. Ciò significa che si devono impostare i parametri caratteristici dell’immagine, per

esempio la luminosità, il contrasto e il colore che l’immagine catturata deve possedere, per

24

una buona elaborazione da parte del sistema. Questa prima fase di impostazione porta con sé

alcuni dubbi. Infatti sarebbe ideale dover affrontare questa operazione una volta sola, subito

dopo la fase di installazione del sistema sul Personal Computer. Tuttavia il funzionamento

dell’ausilio è fortemente vincolato, in quanto elabora immagini del mondo reale, alle

condizioni di luminosità insite intorno ad esso. Quindi, se per esempio, il sistema viene usato

con la luce del giorno, potrebbe richiedere un ulteriore settaggio dei parametri di luminosità se

successivamente viene usato di sera grazie ad un’illuminazione artificiale. Questo porta al

problema di rendere semplice e poco dispendioso il compito di cablare le periferiche di

acquisizione dati.

La natura stessa dell’ausilio porta alla definizione dei compiti che il sistema deve

affrontare per completare il secondo, ed ultimo, passo della calibrazione. Come spiegato

precedentemente gli ausili utilizzano due correnti di pensiero differenti per elaborare i dati che

si procurano, nel nostro caso c’è l’obbligo di usare un sistema di riferimento assoluto. Su

questa asserzione non ci sono dubbi infatti, una volta cablato, l’ausilio utilizza il suo sistema

di riferimento per tutta la sessione di lavoro. Tuttavia, sebbene si sia definita in modo univoco

la natura del sistema di riferimento, la fase del suo settaggio porta con sé alcune incertezze

che devono essere analizzate prima di poter proseguire.

Per studiare in maniera esauriente il problema si potrebbe iniziare col pensare a come

l’utente utilizzerà l’ausilio. Si può immaginare che quest’ultimo venga posizionato davanti

allo schermo del Personal Computer grazie all’aiuto di un assistente, dopodiché il

collaboratore dovrà, tramite le normali periferiche, attivare il sistema. A questo punto verrà

richiesto all’utente di definire il sistema di riferimento dell’ausilio e alla conclusione di questa

fase ci sarà la possibilità di accedere alle applicazioni. Il problema di maggior rilievo per

definire questo passo è rappresentato dal fatto che ad ogni sessione di lavoro l’utente non sarà

mai nella posizione esatta in cui era nella sessione precedente. Tra un utilizzo e il successivo

dell’ausilio può trascorrere parecchio tempo nel quale l’utente ha la facoltà di cambiare

posizione nello spazio tramite l’aiuto degli assistenti.

Grazie a questo possibile scenario sull’utilizzo del sistema, si osserva che questa fase deve

essere estremamente semplice dal punto di vista dell’utente, in quanto deve essere ripetuta ad

ogni sessione di lavoro e quindi non deve affaticare o essere onerosa per chi usa l’ausilio.

Dopo questa trattazione si arriva alla conclusione che ambedue le fasi della calibrazione

devono essere estremamente semplici. La prima deve concedere all’assistente la possibilità di

variare i parametri per l’acquisizione dell’immagine in modo corretto e veloce in quanto c’è la

probabilità non del tutto remota che essi possano variare da una sessione di lavoro all’altra. La

25

seconda fase deve tenere inoltre in considerazione le caratteristiche dell’utente e fare in modo

che non sia gravoso il lavoro che costui deve compiere.

26

27

3 Background

I problemi che sono apparsi non appena si è tentato di creare un ausilio utile per il

prossimo, e che hanno caratterizzato tutto il capitolo precedente hanno reso chiarissimo il

concetto che il sistema che si vuole creare non è così semplice come poteva sembrare a prima

vista. Tuttavia non si è indifesi nel combattere questi problemi, come supporto ci sono i

decenni spesi nella ricerca informatica, i quali hanno portato a risultati che possono

contribuire alla risoluzione delle incertezze.

Una prima freccia per il nostro arco è costituita dalla scelta del linguaggio di

programmazione, il C++ infatti consente una programmazione orientata agli oggetti che

permette di scrivere un codice sorgente molto elegante e facile da comprendere, inoltre grazie

al C++ è possibile utilizzare gli oggetti predefiniti di Windows e la capacità di scrivere

applicazioni multithreaded.

Per completare la nostra faretra non può mancare l’aiuto che ci viene dato dalle procedure

Video For Windows per la cattura delle immagini provenienti dalle camere e il pacchetto

software della Victor che permette di elaborarle, senza dimenticare l’aiuto dato dalla

RamDisk.

A questo punto, come avrebbe detto Manzoni :

28

“Son cose che chi conosce la storia le deve sapere; ma siccome, per un giusto

sentimento di noi medesimi, dobbiam supporre che quest’opera non possa essere letta se

non da ignoranti, così non sarà male che ne diciamo qui quanto basti per infarinare chi

n’avesse bisogno”

Perciò seguono varie sezioni dove si cerca di spiegare a grandi linee gli ausili informatici che

ci hanno sorretto e aiutato nella costituzione del sistema.

3.1 Paradigma di programmazione Con questo termine si indica la relazione esistente tra gli algoritmi e i dati, che

costituiscono le due componenti fondamentali di un programma. Entrambi sono rimasti, nella

breve storia dell’informatica, aspetti invarianti, mentre si è evoluta la relazione esistente fra di

loro.

All’inizio si parlava di programmazione procedurale, poi col passare del tempo, verso gli

anni settanta l’attenzione si è spostata dal paradigma procedurale a quello dei tipi di dato

astratto ora noto come programmazione basata sugli oggetti, infine con il concetto di

ereditarietà e di collegamento dinamico si è arrivati ad una programmazione orientata agli

oggetti.

Il C++ è un linguaggio multiparadigma, pur essendo considerato principalmente un

linguaggio orientato agli oggetti, esso è di supporto anche per la programmazione procedurale

e per quella basata sui tipi di dato astratto. Il vantaggio è che è possibile fornire una soluzione

più adatta al problema: in pratica, infatti, nessun paradigma rappresenta la soluzione finale di

un problema. Lo svantaggio sta nel fatto che ciò implica un linguaggio più grande e più

complesso.

3.1.1 Programmazione procedurale

In questo caso il problema è illustrato direttamente mediante un insieme di algoritmi o da

una serie di procedure. I dati sono memorizzati separatamente, ed è possibile accede ad essi in

una posizione globale oppure passandoli alle procedure. Tra i più importanti linguaggi

procedurali ci sono il FORTRAN, il C e il Pascal. Come menzionato precedentemente il C++

supporta tutte le capacità offerte dal suo predecessore C.

3.1.2 Programmazione basata sugli oggetti

Secondo questo paradigma il problema è modellato direttamente da un insieme di

astrazioni sui dati: nel C++ queste astrazioni sono dette classi. Ad esempio, il sistema di

gestione per una biblioteca con questo paradigma è rappresentato come l’interazione fra

29

oggetti che sono istanze di classi quali Libro, Utente, Prestito e cosi via, che rappresentano le

astrazioni possibili in una biblioteca. Gli algoritmi associati a ogni classe sono chiamati

interfaccia pubblica della classe. I dati sono memorizzati in maniera privata all’interno di

ogni oggetto e non è possibile accedere da essi dal programma generale. CLU, Ada, Modula-2

e il C++ sono solo alcuni dei linguaggi che supportano questo paradigma.

3.1.3 Programmazione orientata agli oggetti

In questo caso i tipi di dato astratto vengono estesi mediante i meccanismi di ereditarietà e

di collegamento dinamico, il cui significato è riutilizzare un’implementazione oppure

un’interfaccia pubblica già esistente. Quest’ultima caratteristica verrà ampliamente descritta

nella sezione dedicata agli oggetti predefiniti di Windows.

In questo modo sono rese disponibili relazioni speciali tipo/sottotipo fra tipi

precedentemente indipendenti. Un libro, una videocassetta, un disco, sono tutti una sorta di

materiale di biblioteca, anche se ciascuno ha le sue regole per il prestito. L’interfaccia

pubblica condivisa e i dati privati sono posti in una classe astratta MaterialeBiblioteca. Ogni

singolo materiale eredita il comportamento comune dalla classe astratta MaterialeBiblioteca e

deve fornire soltanto gli algoritmi e i dati che supportano il suo comportamento. Tre

importanti linguaggi, escluso il C++, che supportano il paradigma sono Simula, Smalltalk e

Java.

3.2 Windows e i messaggi Quando si programma per Windows, si esegue un tipo di programmazione orientata agli

oggetti. Per rendersene conto, basta osservare l’oggetto con cui si lavora più spesso in

Windows: la finestra. Le finestre più ovvie che spiccano sul Desktop sono quelle delle

applicazioni. Queste finestre comprendono una barra del titolo che riporta il nome del

programma, un menu ed eventualmente una barra degli strumenti e una barra di scorrimento.

L’utente vede queste finestre come oggetti sullo schermo e interagisce direttamente con

esse utilizzando la tastiera o il mouse. Come fa l’applicazione a capire che l’utente sta

cercando di comunicare con la finestra stessa? Per i programmatori abituati solo alla

convenzionale programmazione in modo caratteri, non esiste mezzo che consenta al Sistema

Operativo di trasmettere all’utente informazioni di questo tipo. Ne consegue che questa

domanda è essenziale per comprendere l’architettura di Windows. Quando l’utente interagisce

con una finestra per esempio modificandone le dimensioni, Windows invia un messaggio al

programma per indicare le nuove dimensioni della finestra. Il programma può quindi regolare

il contenuto della finestra per adattarlo alle nuove dimensioni.

30

L’affermazione “Windows invia un messaggio al programma” significa che Windows

chiama una funzione all’interno del programma, una funzione che deve essere scritta e che

costituisce una parte essenziale del programma. I parametri di questa funzione descrivono il

messaggio specifico che viene inviato da Windows e ricevuto dal programma. Questa

procedura all’interno del programma è detta window procedure.

L’idea che un programma effettui chiamate al Sistema Operativo è familiare a tutti.

Questo, per esempio, è il metodo utilizzato per aprire un file su disco. Al contrario, l’idea di

un Sistema Operativo che effettua chiamate a un programma può risultare più estranea e

tuttavia è fondamentale per l’architettura di Windows.

Ogni finestra creata da un programma ha una window procedure associata. Quest’ultima è

una funzione che potrebbe trovarsi nel programma stesso o in una libreria. Windows invia un

messaggio a una finestra chiamando la window procedure, che provvede a eseguire la

necessaria elaborazione e al termine restituisce il controllo a Windows.

Più precisamente, una finestra viene sempre creata in base a una window class. La window

class identifica la window procedure che elabora i messaggi rivolti alla finestra.

Nella programmazione orientata agli oggetti, un oggetto è una combinazione di codice e

dati: la finestra è l’oggetto, il codice costituisce una window procedure, mentre i dati sono le

informazioni conservate dalla window procedure e quelle conservate da Windows per ogni

finestra e window class che esiste nel sistema.

Quando un programma Windows inizia l’esecuzione, Windows crea una coda dei

messaggi, dove vengono archiviati i messaggi indirizzati a tutte le finestre che un programma

potrebbe creare. In ogni applicazione Windows esiste uno spezzone di codice definito ciclo

dei messaggi avente lo scopo di prelevare i messaggi della coda e trasmetterli alla window

procedure appropriata. Altri messaggi vengono inviati direttamente alla window procedure

senza essere inseriti nella coda.

3.2.1 Registrazione della window class

Una finestra viene sempre creata sulla base di una window class. La window class

identifica la window procedure che elabora i messaggi indirizzati alla finestra.

È possibile creare più finestre basate sulla medesima window class. Quest’ultima definisce

non solo la window procedure ma anche altre caratteristiche delle finestre create sulla base di

detta classe. Prima di poter creare una finestra dell’applicazione, occorre registrare una

window class chiamando RegisterClass. Grazie a questa funzione siamo in grado di iscrivere

nel sistema la nostra applicazione definendo in maniera univoca le caratteristiche che la

finestra dovrà avere.

31

3.2.2 Creazione e visualizzazione della finestra

La window class definisce le caratteristiche generali di una finestra, per cui consente di

utilizzare la stessa window class per creare numerose finestre diverse. Quando si crea una

finestra chiamando CreateWindow, si specificano informazioni più dettagliate sulla finestra.

In Windows, ogni finestra è dotata di un handle. Il programma utilizza quest’ultimo per

riferirsi alla finestra, molte funzioni di Windows lo richiedono come argomento affinché

Windows capisca a quale finestra si riferisce la funzione. Se un programma crea svariate

finestre, ciascuna avrà un handle differente.

Quando la chiamata della funzione CreateWindow ha restituito un risultato, significa che la

finestra è stata creata internamente a Windows. Ma la finestra non compare ancora sullo

schermo. Sono infatti necessarie ancora due chiamate: ShowWindow e UpdateWindow.

3.2.3 Ciclo dei messaggi

Dopo la chiamata a UpdateWindow, la finestra viene completamente visualizzata sullo

schermo. A questo punto, il programma deve prepararsi a leggere gli input della tastiera e del

mouse operati dall’utente. Windows gestisce una coda di messaggi per ogni programma

attualmente in esecuzione. Quando si verifica un evento di input, Windows traduce l’evento in

un messaggio che viene inserito nella coda dei messaggi del programma.

Un programma preleva i messaggi dalla coda eseguendo un blocco di codice conosciuto

come ciclo dei messaggi

3.2.4 Window procedure

Tutto quanto è stato descritto fino a questo punto non è che una semplice impostazione. La

window class è stata registrata, la finestra è stata creata ed è stata visualizzata sullo schermo e

il programma ha avviato un ciclo di messaggi per prelevare i messaggi dalla coda. L’azione

vera e propria si verifica nella window procedure, che determina che cosa la finestra debba

visualizzare al suo interno e in che modo la finestra debba rispondere all’input dell’utente.

In genere, i programmi non chiamano le window procedure direttamente. La window

procedure viene quasi sempre chiamata da Windows stesso. Un programma può chiamare

indirettamente la propria window procedure grazie alla funzione denominata SendMessage.

Al momento della creazione di una finestra, Windows chiama la window procedure e la

richiama anche quando la finestra viene eliminata. Inoltre, Windows richiama questa

procedura quando la finestra viene ridimensionata, spostata o ridotta a icona, quando un

utente fa clic sulla finestra con il mouse, quando vengono digitati dei caratteri utilizzando la

tastiera, quando è stata scelta una voce da un menu, quando viene utilizzata una barra di

32

scorrimento e quando l’area deve essere ridisegnata. Tutte queste chiamate alla window

procedure avvengono sotto forma di messaggi. Nella maggior parte dei programmi in

Windows, gran parte del codice è dedicato alla gestione di questi messaggi. In genere, i

messaggi che Windows può inviare a un programma sono identificati da nomi che cominciano

con le lettere WM e sono definiti nel file di intestazione WINUSER.H.

L’idea di una routine all’interno del programma che venga chiamata dall’esterno dello

stesso non è una novità nella programmazione in modo caratteri. In Unix, la funzione signal è

in grado di intercettare un’interruzione Ctrl-C o altri interrupt emessi dal Sistema Operativo. I

programmi più vecchi scritti per l’MS-DOS intercettavano spesso gli interrupt hardware. In

Windows questo concetto è stato esteso a tutto. Tutto ciò che interessa una finestra viene

trasferito alla window procedure sotto forma di messaggio. Dopodiché quest’ultima risponde

al messaggio in qualche modo o passa il messaggio a DefWindowProc per un’elaborazione di

default.

3.3 Multitasking e multithreading Il multitasking è la capacità di un Sistema Operativo di eseguire più programmi

simultaneamente. In linea di principio, il Sistema Operativo utilizza un clock hardware per

allocare intervalli di tempo a ogni processo in esecuzione. Se gli intervalli di tempo sono

sufficientemente piccoli e la macchina non è sovraccaricata da troppi programmi che tentano

di fare qualcosa, l’utente ha l’impressione che tutti i programmi vengano eseguiti

simultaneamente.

Il multitasking non è niente di nuovo. Sui grandi mainframe, il multitasking è un dato di

fatto. A questi mainframe sono spesso collegati centinaia di terminali e ogni utente che lavora

su un terminale ha l’impressione di godere di un accesso esclusivo a tutte le risorse della

macchina. Il multitasking sui Personal Computer ha impiegato un tempo molto maggiore

prima di divenire realtà. Le versioni a 32 bit di Windows supportano tutte sia il multitasking

vero e proprio sia il multithreading.

Il multithreading è la capacità di un programma di implementare il multitasking

internamente. Il programma può dividersi in thread o thread di esecuzione separati che in

apparenza vengono eseguiti contemporaneamente.

La progettazione, la scrittura e il collaudo di un’applicazione multithreaded complessa è

uno dei compiti più difficili per un programmatore Windows. Poiché un sistema multitasking,

nella sua versione preemptive, può interrompere un thread in qualsiasi punto per passare il

33

controllo ad un altro thread, eventuali interazioni non desiderabili tra due thread possono

rivelarsi occasionalmente, come se si verificassero casualmente.

Il race condition, un bug comune in un programma multithreaded, si verifica quando un

programmatore presume che un thread finisca di svolgere un determinato lavoro (per esempio,

la preparazione di alcuni dati) prima che un altro thread richieda quei dati. Per aiutare a

coordinare l’attività dei thread, i Sistemi Operativi richiedono varie forme di

sincronizzazione. Una di queste viene chiamata semaforo e consente al programmatore di

fermare l’esecuzione di un thread in un determinato punto del codice fino a quando un altro

thread segnala che è possibile riprendere l’esecuzione. Ci sono tuttavia delle sezioni critiche,

cioè porzioni di codice che non possono essere interrotte. I semafori inoltre introducono un

altro bug comune nell’uso dei thread, che viene chiamato deadlock. Ciò si verifica quando

due thread hanno fermato reciprocamente la loro esecuzione e possono sbloccarla solo

procedendo.

La funzione API per creare un nuovo thread di esecuzione si chiama CreateThread.

Tuttavia, la maggior parte dei programmatori in Windows preferiscono utilizzare una libreria

runtime del C chiamata _beginthread che viene dichiarata nel file di intestazione

PROCESS.H.

Infine i thread hanno bisogno di una loro temporizzazione, in quanto la loro elaborazione

potrebbe avvenire troppo velocemente. La soluzione è fornita dalla funzione Sleep. Un thread

può chiamare la funzione Sleep per sospendere volontariamente la sua esecuzione. L’unico

parametro richiesto è un periodo di tempo espresso in millisecondi. La funzione Sleep non

ripassa il controllo fino al termine del tempo specificato. Durante quel periodo, il thread viene

sospeso e non vengono più allocate porzioni di tempo, sebbene ovviamente, il thread necessiti

ancora di piccole quantità di tempo di elaborazione durante i clic del timer, quando il sistema

deve determinare il thread che deve essere ripreso. Un argomento uguale a 0 passato a Sleep

fa perdere al thread la parte di tempo rimanente assegnatagli.

Quando un thread chiama la funzione Sleep, solo quel thread viene sospeso per il periodo

di tempo specificato. Il sistema continua l’esecuzione degli altri thread, sia nello stesso

processo sia in un altro.

3.4 Victor e le Immagini È essenziale, al fine di una corretta analisi sulla libreria Victor Image Processing prodotta

dalla Catenary Systems, portare l’attenzione sui metodi utilizzati per definire, conservare e

visualizzare le immagini su un moderno Personal Computer. Solo grazie a questa discussione

34

saremo in grado di studiare in maniera corretta le funzioni Victor in grado di elaborare le

immagini.

3.4.1 Codifica delle immagini

Diversi problemi devono trovare soluzione per ottenere questo risultato, per esempio si

deve discutere in che modo possano essere caratterizzati i colori, oppure quale sia la struttura

più adatta per contenere un’immagine.

La natura stessa dei colori sostiene la ricerca della soluzione attinente al primo quesito.

Infatti in natura qualsiasi colore è scomponibile nei tre colori primari rosso, verde e blu

(RGB). Con questa tecnica è possibile definire qualunque sfumatura si voglia ottenere. Quindi

non si deve far altro che definire una struttura che contenga i pesi dei tre colori primari per

ottenere abbastanza informazioni sulla sfumatura desiderata.

Di solito il peso di ogni colore primario è contenuto in un byte che permette un scala da 0 a

255, cosicché, per esempio il nero sarà definito da una struttura che contiene 0 in ogni campo.

Una sfumatura di giallo avrà un valore molto alto sia nel campo attinente al rosso sia nel

verde mentre conterrà un valore tendente a 0 nel campo relativo al blu. Infine un grigio avrà

un valore costante e simile in tutti i tre campi dove, valori molto bassi porteranno ad un grigio

scuro, mentre valori elevati produrranno grigi chiari. Con questa tecnica è possibile definire

più di sedici milioni di colori quindi abbiamo a nostra disposizione una gamma estremamente

valida per descrivere immagini del mondo reale.

Per definire una struttura relativamente semplice che ci permetta di visualizzare le

immagini in modo corretto possiamo sfruttare le tecniche utilizzate dai terminali video per il

loro funzionamento. Quest’ultimi utilizzano dei punti, chiamati pixel, per dare la sensazione

di colore all’occhio umano. Ogni pixel utilizza il metodo descritto prima, nel quale ci sono tre

aree molto piccole e ravvicinate. Ciascuna delle quali può modulare l’intensità di un colore

primario cosicché, da lontano, l’occhio percepisce una sola sfumatura, risultato della fusione

dei tre colori primari. Più pixel formano la superficie dello schermo più elevata è la

risoluzione dello stesso.

L’immagine non è altro che una matrice di pixel organizzati in righe e colonne che

definiscono i colori di ogni singolo punto. Quindi, per esempio, un’immagine indicata

480X640 significa che contiene 480 righe e 640 colonne di pixel. I pixel vengono

immagazzinati, una riga dopo l’altra partendo dall’angolo in basso a sinistra, in un vettore.

Varie sono le tecniche utilizzate per relazionare i pixel dell’immagine con l’informazione

sul loro colore. Tuttavia, solo un paio di queste sembrano aver acquisito una notevole

importanza ed entrambe hanno sia pregi che difetti.

35

La prima impone la creazione di una palette, che è paragonabile alla tavolozza che il

pittore usa per contenere i colori che utilizza sulla tela. Questa palette è un vettore di strutture

RGB che contengono al loro interno i colori primari per ogni sfumatura dell’immagine. Con

questa tecnica il vettore che contiene i dati relativi ad ogni pixel dell’immagine può essere

visto come un insieme di indici utilizzati per ritrovare i vari colori nella palette.

Grazie a questo metodo è possibile indicare la definizione dell’immagine. Infatti con un

solo bit per l’indice si può indirizzare solo due colori nella palette, uno con lo 0 e l’altro con

1, di solito sono il bianco e il nero. In questo caso ogni pixel viene caratterizzato da un solo

bit nella struttura che li conserva. Quindi un byte contiene il colore di 8 pixel sulla stessa riga.

Se si utilizza un byte per definire l’indice, si può indirizzare 256 colori nella palette e

quindi nella struttura che contiene i pixel ognuno di essi è caratterizzato da 8 bit.

Il vantaggio nell’utilizzare questa tecnica sta nel fatto di poter compattare in maniera

corretta le informazioni sul colore dei pixel, in quanto pixel vicini, che potrebbero avere lo

stesso colore, indicano lo stesso indice nella palette, con un considerevole risparmio di spazio,

un byte al posto di tre. Tuttavia se si utilizza per esempio una palette con 256 colori, non è

possibile avere immagini con più di 256 sfumature al loro interno, anche se quest’ultime

possono reperire una sfumatura diversa in un insieme di sedici milioni.

Per prevenire questo svantaggio si adotta la seconda tecnica che, a discapito di un vettore più

capiente per contenere l’informazione sul colore dei pixel, permette di avere immagini con

sedici milioni e più di colori, contemporaneamente al loro interno.

Le immagini che utilizzano questa tecnica abbandonano la palette e i tre byte che

corrispondono ai tre colori primari di ogni sfumatura vengono stoccati nel vettore che

contiene i colori dei pixel, cosicché ogni pixel occupa tre byte in questo vettore. Queste

immagini vengono, per questo, chiamate a 24 bit.

È importante notare che numerose sono le variazioni che si possono ottenere. Per esempio

si possono avere immagini caratterizzate da sfumature di grigio ottenute in quanto nella

palette ci sono 256 grigi differenti che vanno dal più chiaro al più scuro. Oppure è possibile

avere immagini che utilizzano quattro e non tre byte per definire i tre colori primari, in questo

caso si privilegia il verde donandogli più bit, in quanto l’occhio umano percepisce

maggiormente le sfumature di verde.

Tuttavia per risolvere i problemi descritti nel capitolo precedente si utilizzano immagini a

24 bit, quindi la struttura che si andrà a trattare non avrà la palette ma ogni pixel verrà definito

grazie a tre byte nel vettore che ha il compito di contenerli tutti.

36

3.4.2 DIB

Un argomento di grande interesse che non è stato ancora trattato ma che riveste un ruolo

importante nell’analisi, è come Windows identifichi le immagini. Quest’ultimo utilizza un

oggetto chiamato DIB (Bitmap Indipendente dal Dispositivo) nel quale trovano collocazione

le strutture che abbiamo analizzato precedentemente.

La DIB è stata introdotta in Windows 3.0 allo scopo di fornire quel formato tanto

necessario per lo scambio di immagini. In realtà, altri formati di file come GIF e JPEG sono

molto più comuni su Internet e questo soprattutto perché i formati GIF e JPEG implementano

schemi di compressione in grado di ridurre in maniera significativa i tempi di download.

Sebbene sia stato definito uno schema di compressione anche per le DIB, questo viene usato

di rado. Nella maggior parte delle DIB, i bit della Bitmap conservano la loro forma non

compressa. Questo è senza dubbio un vantaggio se si desidera manipolare i bit della Bitmap

nel proprio programma. Contrariamente ai file GIF e JPEG, la DIB è direttamente supportata

dalla API di Windows. Se in memoria c’è una DIB, è possibile associarle puntatori come

argomenti a svariate funzioni che consentono di visualizzarla.

Un oggetto DIB comprende quattro sezioni principali: un’intestazione dell’oggetto,

un’intestazione informativa, una tabella dei colori RGB (ma non sempre) e i bit per pixel della

Bitmap. È possibile pensare alle prime due parti come a strutture di dati del C e alla terza

parte come a un array di strutture di dati. Queste strutture sono documentate nel file di

intestazione di Windows WINGDI.H. Una DIB salvata in memoria nel formato DIB

compresso comprende tre sezioni: un’intestazione informativa, la palette (ma non sempre) e i

bit per pixel della Bitmap.

La parte più interessante che descrive l’immagine è contenuta nell’intestazione informativa

è qui, infatti, che sono descritte le caratteristiche della Bitmap. Per esempio ci sono dei campi

che contengono la dimensione dell’immagine in pixel e un campo che indica il numero di bit

per pixel. Per le DIB questo può essere 1, 4, 8 oppure 24, che caratterizzano rispettivamente

una DIB a 2, 16, 256 oppure full-color. Nei primi tre casi l’intestazione informativa e seguita

da una palette, che invece non esiste per le DIB a 24 bit. La palette è un array di strutture

RGB da tre byte, una per ciascun colore dell’immagine.

3.4.3 Victor

Le funzioni Victor possono operare su un sotto gruppo delle DIB. La differenza sta nella

compressione, nei bit per pixel e nella palette. Una DIB può essere compressa o non

compressa. Le funzioni della librerie Victor possono operare solo su DIB non compresse.

37

Questo permette alle funzioni di lavorare sulle sezioni della DIB. Inoltre questa caratteristica

è di vitale importanza nel sistema che si va a costituire. Infatti gli algoritmi di cattura ed

elaborazione dati che si occuperanno di manipolare le immagini devono operare direttamente

sulle sezioni della DIB. Windows permette alle DIB di avere 1, 4, 8 oppure 24 bit per pixel.

Le funzioni Victor possono operare solo su DIB con 1, 8 o 24 bit per pixel. Le differenze sulla

palette non vengono contemplate in quanto, per gli scopi prefissati, si utilizzano immagini a

24 bit che non impiegano la tabella dei colori.

La più importante struttura nel pacchetto Victor è il descrittore dell’immagine. Tutte le

informazioni necessarie alle funzioni, per operare su un’immagine sono contenute nel

descrittore dell’immagine. Quest’ultimo è definito in VICDEFS.H ed è un argomento

richiesto per molte funzioni Victor.

Le potenzialità offerte dalla libreria Victor sono numerose. Ci sono funzioni che

manipolano i colori dell’immagine per ottenere gli effetti più vari. Dall’effetto neve all’effetto

metallico all’effetto bianco e nero. Esistono inoltre funzioni che permettono di analizzare

l’immagine per produrre diagrammi relativi alle caratteristiche cromatiche o luminose. Infine

è possibile utilizzare funzioni che operano sulle dimensioni dell’immagine oppure sono in

grado di modificare il numero di bit per pixel.

Per poter utilizzare le funzioni contenute nella libreria si devono effettuare alcune

operazioni preliminari. Per prima cosa si deve allocare memoria per l’immagine, dopodiché si

carica l’immagine. Solo a questo punto è possibile manipolarla grazie alle funzioni Victor.

Un’altra caratteristica, che rende interessante l’oggetto incaricato da Victor di contenere

l’immagine, è la possibilità di manipolare direttamente i bit che contengono il colore dei

pixel. Quest’ultima opportunità avrà enorme rilievo nell’implementazione degli algoritmi che

supportano l’elaborazione dell’immagine. È possibile aggiungere, già da ora, che questa

operazione sarà l’unica effettuata sulle immagini contenute nell’oggetto Victor. Infatti per

conseguire gli obiettivi preposti non è importante manipolare le immagini per ottenere

qualche strano effetto. L’unica caratteristica della libreria Victor che viene sfruttata in questo

contesto è la facilità con cui si accede ai dati che descrivono i pixel dell’immagine.

Per allocare memoria si utilizza la funzione allocimage, per caricare nella memoria appena

definita un’immagine si utilizza loadbmp. Infine per liberare la memoria, dopo l’elaborazione,

si utilizza la funzione freeimage.

38

39

4 Architettura e algoritmi

Grazie ai capitoli precedenti è possibile avere di fronte un disegno generale nel quale

l’ausilio si colloca, si sono definiti sia un punto di partenza che di arrivo, i problemi che si

devono affrontare, nonché gli aiuti esterni che ci possono sostenere nel nostro compito. Ora la

strada è stata tracciata e possediamo i mezzi per costruirla, non dobbiamo fare altro che

iniziare con i lavori. In questo capitolo si intraprenderà la discussione delle soluzioni proposte

nel sistema. Tuttavia a questo punto dell’analisi le soluzioni non possono essere che teoriche,

in quanto alla pratica si dovrà attendere il capitolo successivo relativo all’implementazione,

infatti solo in esso compariranno le soluzioni concrete adottate nel sorgente.

Il titolo di questo capitolo contiene già al suo interno un’informazione importante ed

essenziale per il suo assetto. Infatti la discussione si dividerà su due aspetti fondamentali in

stretta relazione fra di loro. Il primo tratta delle scelte intraprese al fine di risolvere i dubbi,

che sono apparsi quando l’analisi si è rivolta all’architettura che il sistema deve possedere, in

quel particolare momento sono emerse in realtà alcune caratteristiche che tutti i complessi

devono ottenere per il loro buon funzionamento. Dall’altra parte devono essere discusse le

soluzioni che sono state contemplate per risolvere i problemi relativi alla natura stessa

40

dell’ausilio, in questo caso gli algoritmi che sono stati definiti per portare a termine i compiti

che il sistema deve offrire agli utenti.

4.1 Architettura Vengono trattate in questa sezione le soluzioni adottate per definire la struttura che il

sistema deve possedere. La discussione successiva non ha altro scopo che produrre una base

solida e stabile per la costruzione degli algoritmi che verranno trattati nella prossima sezione e

che permettono al sistema di portare a termine il suo compito.

Grazie al C++, il linguaggio di programmazione scelto per scrivere questo sistema, è

possibile definire una architettura che utilizza gli oggetti come base per la sua struttura. È

all’interno di questi oggetti che vengono incapsulati gli algoritmi, un singolo algoritmo per

ogni oggetto. Quindi ogni oggetto porterà al suo interno determinate caratteristiche che lo

collocheranno in un ruolo ben preciso all’interno del sistema.

Varie sono le tipologie che si possono esaminare. Si può, per esempio, definire una

struttura nella quale tutti gli oggetti sono sullo stesso piano, oppure delineare una struttura

stratificata dove gli oggetti si trovano ad operare a diversi livelli. Infine si può scegliere di

operare con una struttura master slave dove un oggetto è a capo di tutti gli altri. Ogni

soluzione presenta sia vantaggi che svantaggi.

Un’architettura che pone tutti gli oggetti sullo stesso livello porta con sé il privilegio di

poterli fare comunicare liberamente fra di loro. Costituendo una struttura estremamente

complessa, veloce e compatta. Tuttavia si rischia di produrre un sistema impossibile da

comprendere e da manipolare. Questo perché le relazioni che intercorrono tra gli oggetti

possono diventare troppo complesse ed intricate da poterle seguire facilmente e in un attimo,

eliminando una relazione o modificandola, si può compromettere l’intero sistema. Questa

struttura porta anche un difetto più subdolo da analizzare, infatti rende inutile una

programmazione orientata agli oggetti e il paradigma di programmazione si sposta su una

programmazione procedurale. Infatti gli algoritmi contenuti negli oggetti vengono elaborati

uno dopo l’altro in sequenza rendendo vano lo sforzo di divedere i compiti su più oggetti, è

come se gli algoritmi fossero in sequenza temporale all’interno di un programma monolitico.

In più una tale soluzione entra in netto contrasto con un Sistema Operativo quale Windows

nel quale si adottano messaggi per la comunicazione tra oggetti che devono essere ben

definiti. Infine si genera un sistema nel quale uno sviluppatore non può estenderne una sola

parte senza capire come funziona l’intera struttura.

41

La stratificazione degli oggetti potrebbe essere una soluzione più conveniente, oltre a

rendere la struttura più semplice e intuitiva, permette agli oggetti di riacquistare il proprio

valore e inserirsi, grazie alle loro caratteristiche, a livelli ben definiti all’interno della

struttura. Tuttavia entra in gioco un’ulteriore domanda: quanti strati deve avere la struttura?

Infatti c’è il pericolo di sovrastimare il numero di strati e rendere il sistema troppo lento nel

rispondere agli stimoli esterni prodotti dall’utente, o per esempio, nella cattura e

riconoscimento dei movimenti. Se il sistema è obbligato a tradurre le operazione da svolgere

attraverso numerosi passaggi tra uno strato e l’altro, l’elaborazione può risultare complessa e

divenire lenta nella sua globalità.

L’ultima tipologia da discutere non è stata rilegata alla fine senza motivo, infatti questa è la

soluzione che si adotterà per il sistema. La struttura master slave risponde alla domanda di

poco fa sugli strati che deve possedere l’architettura, ogni oggetto che incapsula al suo interno

un algoritmo diviene schiavo di un oggetto che presiede al controllo e alla gestione di tutto il

sistema. In pratica la struttura si divide su due soli livelli, il livello più basso contiene

l’oggetto che controlla l’intero sistema, mentre al livello superiore risiedono gli oggetti che

contengono gli algoritmi per l’elaborazione dei dati. Un oggetto slave dello strato superiore,

per poter comunicare con un oggetto dello stesso livello deve chiamare il master al livello

inferiore informandolo sull’operazione che vuole svolgere, sarà quest’ultimo ad impartire gli

ordini all’oggetto servo destinatario.

Questo permette di trattare gli oggetti servi come delle scatole nere, utile nello sviluppo

futuro del sistema in quanto gruppi di sviluppatori diversi possono interessarsi solo ad alcuni

aspetti del sistema tralasciandone altri, senza entrare in conflitto. Per gestire le funzioni dello

strato master viene incaricato un oggetto che, oltre a regolare gli oggetti schiavi, pensa ad

organizzare e tradurre le azioni che l’utente fa sul sistema, visualizzando i risultati su schermo

grazie ad un rapporto stretto con le funzioni Windows.

Esiste un solo difetto che colpisce questa tipologia di struttura ed è rappresentato dalla

velocità. Si è già incontrato questo aggettivo quando si discutevano i vantaggi di una struttura

in cui tutti gli oggetti erano posti sullo stesso livello. Quindi potrebbe rivelarsi una strada

valida da percorrere, cercare di portare ed inserire nella struttura le qualità che promettono la

caratteristica della velocità. Per ottenere questo traguardo, si deve purtroppo infrangere una

regola importante che costituisce una struttura master slave, e cioè, permettere che gli oggetti

schiavi possano comunicare fra di loro eliminando il vincolo che li obbliga a parlare

solamente al master.

42

In questo caso, si privilegia la caratteristica di velocità a dispetto della coerenza nella

struttura. Tuttavia non si deve abusare di questa singolarità, essa è ammessa solo nei casi

strettamente legati al tempo di esecuzione, permettendo agli oggetti servi di interagire

direttamente senza l’ausilio del master.

A questo punto è possibile contemplare la struttura del sistema nella sua interezza. Essa

sarà gestita da un oggetto che farà le veci del master, quest’oggetto avrà il compito di

supervisore in tutte le operazioni che il sistema dovrà essere in grado di compiere. Gli oggetti

predisposti per interpretare i comandi decisi dall’utente dovranno comunicare al supervisore

quale operazione è stata richiesta, quest’ultimo impartirà gli ordini agli oggetti su cui ricade

quella operazione e i risultati ottenuti saranno visualizzati grazie ad altri oggetti sempre gestiti

dal supervisore.

È importante ancora discutere le qualità e definire le caratteristiche che ogni oggetto slave

deve possedere. Per far questo è possibile iniziare col pensare a come l’ausilio opererà. La

scala che permette di raggiungere il nostro obbiettivo è composta da numerosi scalini, ogni

scalino è composto da uno o più oggetti servi che ne definiscono la natura. Tutta la scala,

infine, viene tenuta insieme dal cemento costituito dal master o supervisore.

Ogni scalino, e quindi gli oggetti che lo costituiscono, deve essere pensato come il telaio

nel quale incastonare gli algoritmi che verranno presentati nel paragrafo successivo, sono

infatti quest’ultimi la vera risorsa del sistema, sono essi a permettere il funzionamento

dell’ausilio. Qui con la tipologia di struttura master slave prima e con l’analisi degli oggetti

fra poco, non si fa altro che porre le fondamenta per accogliere gli algoritmi che manipolano i

dati e che portano alla soluzione del problema.

Vengono ora presentati grazie a sezioni separate tutti gli scalini che permettono di arrivare

all’obiettivo.

43

4.1.1 Acquisizione immagini

Una delle qualità più importanti che il sistema deve possedere è la capacità di agganciare e

ottenere immagini dalle videocamere collegate al computer. Il sistema ha incaricato, per

questa potenzialità, un singolo oggetto al quale è addossata l’intera responsabilità di questa

operazione. Il suo compito non è solo quello di prelevare le immagini dalle videocamere ma

deve gestire tutto quello che è attinente alle camere stesse. Sotto l’interrogazione del master,

che d’ora in poi verrà chiamato Supervisore, deve fornire dei risultati attinenti alle domande

oppure rispondere indicando un errore se qualcosa non può essere fatto. Questo oggetto non

contiene al suo interno particolari algoritmi per l’elaborazione dei dati, è solo il tramite e il

mezzo che il sistema utilizza verso il mondo delle videocamere. I risultati più importanti che

l’oggetto deve essere in grado di ottenere sono l’aggancio delle camere nel sistema, la cattura

di singoli fotogrammi, la possibilità di modificare parametri quali la luce e il contrasto delle

immagini e infine poter rilasciare l’aggancio delle camere prima che il sistema venga

terminato.

La caratteristica del poter modificare alcuni importanti parametri relativi all’immagine si

ricollega al discorso intrapreso nei capitoli precedenti sul problema di rendere semplice

l’utilizzo del sistema da parte dell’utente e del suo assistente. Infatti, utilizzando questa

caratteristica è possibile modificare i parametri dell’immagine direttamente dall’ausilio senza

dover usare altri programmi forniti dalla casa costruttrice della videocamera. Questo permette

di velocizzare queste operazioni e rendere meno pesante il lavoro richiesto all’utente.

È importante notare che il sistema ha un forte bisogno di questo oggetto. Infatti grazie ad

esso non si deve preoccupare di come le camere vengono gestite. Questo implica che uno

44

sviluppatore a cui non interessa questa parte del sistema non è assolutamente obbligato a

capire come è implementato al suo interno quest’oggetto, ne deve soltanto richiedere le

funzionalità. Al contrario uno sviluppatore interessato al miglioramento di questa parte del

sistema può agire liberamente, con il solo vincolo di rispettare la coerenza sulle risposte che

l’oggetto può dare al Supervisore rispetto alle versioni precedenti.

4.1.2 Scelta del riferimento

In questa sezione si risponde ad un problema che era stato analizzato nel capitolo relativo

al contesto dell’ausilio. In quella sezione era stata trattata la differenza tra motion capture

intrusiva o non intrusiva elencando i pregi e i difetti di ciascuna soluzione. La nostra scelta

non può che ricadere su un sistema che utilizza motion capture intrusiva, in quanto si cerca di

privilegiare un sistema che introduca il minor numero di errori nell’elaborazione

dell’immagine. Questo può essere fatto solo utilizzando la tecnica intrusiva, non priva di

errori ovviamente, ma più sicura di quella non intrusiva.

Tuttavia non si deve dimenticare il fatto che, grazie alla programmazione orientata agli

oggetti è sempre possibile implementare una soluzione che incorpora la tecnica non intrusiva.

Anche se passare da una soluzione all’altra possa essere visto come riedificare una parte delle

fondamenta e quindi un operazione abbastanza complessa, tutto il lavoro si focalizza solo su

pochi oggetti i quali sono adibiti a questa elaborazione all’interno del sistema.

Ora grazie alla scelta caduta sulla motion capture intrusiva è possibile definire le

caratteristiche che deve possedere l’oggetto o gli oggetti a cui è demandato il compito di

intercettare il riferimento colorato nelle immagini catturate grazie all’oggetto descritto

precedentemente.

Gli oggetti che costituiscono questo passo, se interrogati dal Supervisore devono essere in

grado di indicare quali colori sono presenti all’interno delle immagini catturate e dare la

possibilità all’utente di scegliere il colore da intercettare, quest’ultimo sarà il colore che più si

avvicina al colore del riferimento fisso sul volto dell’utente.

Anche in questo caso il sistema non ha bisogno di conoscere come queste operazioni

vengono effettuate all’interno degli oggetti, il Supervisore non deve fare altro che ottenere il

colore scelto dall’utente e passarlo agli oggetti che permettono al sistema di proseguire nella

sua elaborazione. Questo, come è gia stato ribadito più volte è il grande vantaggio di poter

operare con gli oggetti.

45

4.1.3 Motion capture

Gli oggetti che appartengono a questa parte del sistema sono il cuore della struttura stessa,

è infatti in questo luogo che il sistema riesce a intuire il movimento del volto dell’utente

grazie al riferimento fisso sulla sua fronte. Gli algoritmi contenuti all’interno di questi oggetti

rendono possibile tutto questo e per questa ragione sono i più importanti di tutto il sistema.

Ottenuto il colore del riferimento dal Supervisore che li gestisce e comanda in tutte le

azioni che possono attuare, gli oggetti a cui è demandato quest’incarico devono essere in

grado di fornire al sistema un’indicazione, la più precisa possibile, di dove si trovi il

riferimento su ogni immagine acquisita dalle videocamere. A questo punto catturando

un’immagine dopo l’altra è possibile tenere traccia dei movimenti del volto dell’utente. È qui

che entra in gioco la singolarità che impone alla struttura di venir meno alle leggi imposte tra

master e slave. Infatti, il fattore velocità diventa una carta importante da giocare in questa

parte dell’elaborazione. Per privilegiare la rapidità si deve permettere agli oggetti che

contengono gli algoritmi che elaborano le immagini catturate di poter comunicare

direttamente con l’oggetto che è incaricato della cattura stessa. Scavalcando, in questo modo,

la mediazione costituita dal Supervisore. Il vantaggio che risiede nella velocità di

elaborazione supera di gran lunga lo svantaggio di destabilizzare la coerenza della struttura

stessa.

Infatti l’obbligo, da parte degli oggetti che costituiscono questa parte del sistema, di

comunicare solo con il Supervisore e chiedere a quest’ultimo di caricare ogni immagine porta

ad un overhead di operazioni troppo costoso e inutile. Inoltre lasciando liberi questi oggetti di

comunicare si arriva ad una implementazione più chiara da comprendere per i futuri

sviluppatori.

Ciò non toglie che solo in questo caso si può attuare questa politica, per tutto il resto del

sistema le regole stabilite da una struttura master slave devono essere strettamente rispettate,

qui si è agito in questo modo solo perché nella lista di priorità si è posta la velocità prima di

tutto il resto e quindi anche delle regole che determinano la struttura. Tuttavia nessun altra

eccezione può essere fatta in quanto subito dopo la prontezza nell’elaborazione la lista delle

priorità accoglie il dovere di rispettare diligentemente le regole che sanciscono la struttura.

4.1.4 Definizione campo visivo

A questo punto del cammino il Supervisore deve gestire degli oggetti che custodiscono al

loro interno gli algoritmi che permettono di associare ad una data posizione del riferimento la

46

direzione in cui è rivolto il viso dell’utente. Sono questi oggetti che contengono tutte le

tematiche che si sono analizzate mentre si parlava della calibrazione del sistema.

Una volta in grado di intercettare il movimento del riferimento nelle immagini catturate

dalle videocamere, in che modo si può tradurre questa informazione in un dato utile?

Il discorso già intrapreso nel capitolo attinente al contesto nel quale si parlava del

cablaggio ha introdotto un concetto interessante quale il sistema di riferimento assoluto. È

possibile infatti, definire un sistema di riferimento assoluto basato sull’immagine stessa

catturata. Essa nasce dal sistema ottico di una videocamera che per definizione è fissa nello

spazio. Quest’ultima viene appoggiata per esempio sul bordo superiore dello schermo e in

quella posizione resta per tutto il tempo in cui il sistema è al lavoro senza essere più toccata.

Ciò significa che sia lo schermo del Personal Computer sia la videocamera sono fermi uno

rispetto all’altro. Questa caratteristica viene sfruttata per ricavarne la constatazione che se

l’utente muove solo la testa e non tutto il corpo fissando il punto in alto a destra dello

schermo, il riferimento solidale al suo volto, si troverà in una posizione ben precisa nel

sistema assoluto dell’immagine. Tutte le volte che l’utente fisserà quel punto il riferimento si

troverà sempre nella stessa posizione.

Il trucco sta nel far fissare all’utente i quattro angoli dello schermo e da questi si ottiene

un’area rettangolare nel sistema di riferimento assoluto dell’immagine nel quale il riferimento

solidale al volto dell’utente ha libertà di movimento. Se, per esempio, l’utente rivolge il viso

verso il centro dello schermo, il riferimento si troverà al centro dell’area rettangolare definita

nel sistema di riferimento solidale con l’immagine. Mentre se quest’ultimo esce dall’area

appena determinata significa che l’utente non ha il viso rivolto verso lo schermo.

La determinazione di quest’area, scalata nel sistema di riferimento solidale con l’immagine

catturata dalla videocamera, è il compito a cui gli oggetti che risiedono a questo punto del

cammino verso il nostro obiettivo sono obbligati a compiere. O meglio, gli algoritmi

contenuti in questi oggetti devono portare al sistema questo risultato.

Quando si è discusso nei capitoli precedenti sulla disposizione delle videocamere, l’analisi

non si è spinta oltre l’esame dei pregi e difetti di ogni soluzione. Precedentemente si è indicata

la parte superiore dello schermo come una potenziale posizione della videocamera, ebbene

questa sarà la posizione definitiva che la camera principale occuperà nel sistema. È stata

scelta questa posizione per minimizzare al massimo i problemi di linearità insiti nella

geometria spaziale che incorpora schermo, utente e videocamera.

Infatti la soluzione che vedeva la camera posta al di sotto dello schermo del video porta

all’introduzione di trasformazioni curvilinee tra riferimento e punto fissato difficili da

47

compensare, per esempio il rettangolo dell’area definita dal cablaggio non è più un rettangolo

ma una famiglia di curve sempre più accentuate.

Questo è dovuto al fatto che l’asse tra la camera e la fronte dell’utente dove risiede il

riferimento è praticamente orizzontale nella soluzione nella quale la camera è sopra lo

schermo, mentre è obliquo se la videocamera è posta sotto lo schermo ed è questo fatto a

introdurre le non linearità nella relazione tra riferimento e punto fissato dall’utente.

4.1.5 Indicazione dello sguardo

Si è quasi raggiunta la soluzione del problema, ora il Supervisore non deve fare altro che

gestire un oggetto che ha il compito di riferire in quale direzione l’utente abbia rivolto il viso.

Questo oggetto ha il compito di elaborare dati che sono già stati filtrati e a cui si è già dato un

significato attraverso i numerosi oggetti precedentemente descritti. In pratica questo oggetto

non deve fare altro che controllare la posizione del riferimento in ogni immagine catturata,

confrontarla con l’area ottenuta nella calibrazione e, in base ad un algoritmo ben preciso,

avvertire il Supervisore del punto sullo schermo sul quale l’utente ha rivolto lo sguardo.

Entra ora in gioco l’analisi che è stata fatta sulla possibilità di connettere al sistema

applicazioni indipendenti. In quella discussione era emerso che sarebbe stata un’ottima

caratteristica poter inserire nel sistema applicazioni che non avevano nulla da condividere con

l’ausilio, se non sfruttare le sue potenzialità per i loro obiettivi privati. La struttura che si è

definita in queste pagine permette di risolvere questo problema in maniera egregia.

Le applicazioni vengono trattate dal sistema come oggetti che appartengono allo strato

superiore, dove risiedono tutti gli oggetti che sono stati definiti slave. In quest’ottica è

possibile suddividere questo strato in due insiemi separati di oggetti schiavi, da un lato ci sono

tutti gli oggetti che concorrono alla soluzione del problema, quindi tutti gli oggetti che si sono

descritto fino ad ora. Nell’altro insieme risiedono gli oggetti che contengono al loro interno

un’applicazione indipendente. Questo gruppo ha al suo interno solo un oggetto attivo alla

volta ed è quest’ultimo che contiene l’applicazione attiva, cioè quella che riceve

l’informazione sulla posizione del viso dell’utente.

L’unico compito aggiuntivo per uno sviluppatore di applicazioni, che incorporano al loro

interno le potenzialità dell’ausilio, sarà quello di iscrivere nella lista delle applicazioni tenuta

dal Supervisore la sua applicazione e informare quest’ultimo che, tra le tante applicazioni

esistenti all’interno del sistema, quella attiva è la sua. A questo punto il Supervisore, quando

otterrà il punto fissato dal volto dell’utente grazie all’ultimo oggetto nella scala

dell’elaborazione dei dati, avviserà con questa informazione l’applicazione che in quel dato

istante è attiva.

48

Questa è la parte più elegante di tutta la struttura ed è al fine di raggiungere questa

potenzialità che il sistema è stato concepito, questo è il fine ultimo dell’ausilio, tutto

all’interno di esso spinge per rendere attiva questa possibilità.

È per questo motivo che si è deciso di utilizzare questa tipologia di struttura in quanto se

tutti gli oggetti fossero stati posti sullo stesso piano, il compito di capire quale applicazione

indipendente era attiva sarebbe stato di difficile soluzione. Grazie al Supervisore, che tiene

traccia di tutte le applicazioni all’interno del sistema e conosce con precisione quale delle

tante sia in quell’istante attiva questo problema viene risolto in maniera elementare.

4.1.6 Gestione del sistema

Il Supervisore non ha solo il compito di controllare tutta la catena di acquisizione ed

elaborazione dati, nonché la gestione di tutte le applicazioni indipendenti iscritte nel sistema,

ma deve gestire anche degli oggetti che non concorrono direttamente alla soluzione finale.

Questi oggetti risiedono, come tutti gli altri, nello strato superiore descritto come slave.

Possono quasi essere visti come un terzo insieme tra quelli descritti in precedenza,

quest’ultimi si occupano di tutta la gestione indiretta del sistema, dalla gestione del menu, alla

gestione degli errori, alla visualizzazione delle finestre attive e infine alla gestione delle liste

che contengono le applicazioni. Possiamo vederli come oggetti che lavorano dietro le quinte e

che aiutano il Supervisore nei suoi compiti più pesanti ma che esulano dall’elaborazione più

importante.

Il loro compito principale è quello di rendere l’architettura semplice e ben ordinata, per

permettere una facile lettura del codice sorgente e per non affaticare in maniera pesante le

operazioni svolte dal Supervisore. Quest’ultimo demanda i compiti più pesanti e noiosi a

questi oggetti trattenendo per sé le elaborazioni più ricche di significato per il sistema.

Nel corso della trattazione è possibile riferirsi a questi oggetti in maniera superficiale, ma

solamente nelle appendici verranno esaminati nel loro complesso e nella loro totalità.

4.2 Algoritmi Ora, finalmente, non si cammina più su un terreno inesplorato. Ora esiste una base a cui si

è cercato di dare un’impostazione più solida possibile. Le fondamenta, descritte nel paragrafo

precedente, su cui nascerà tutto il resto sembrano robuste, ora non resta da fare altro che

definire degli algoritmi che sfruttino al massimo le potenzialità offerte dall’architettura del

sistema.

La parola algoritmo contiene al suo interno un concetto estremamente importante per il

mondo annesso all’informatica e all’elaborazione compiuta dal cosiddetto cervello

49

elettronico. Chi non ha mai avuto contatti con questo mondo reputa questa disciplina fredda e

a volte oscura, tanto da tenersi alla larga e a trattarla con diffidenza. Tuttavia nulla come

l’informatica dà prova delle potenzialità su cui può contare la mente umana. Questa disciplina

deve essere collocata al pari di molte altre, nelle conquiste che la razza umana ha ottenuto nei

milioni di anni combattuti per l’evoluzione. Il calore e l’emozione che può dare, per esempio,

l’arte musicale producendo una meravigliosa sonata devono essere percepiti anche nelle

creazioni imputabili alla nostra arte, infatti ambedue sono opere dell’intelletto umano e

contribuiscono a rendere particolare ciò che noi siamo. L’algoritmo, è per molti versi il soffio

di vita che percorre l’elemento inanimato costituito dal cervello elettronico, è la traccia

lasciata da un essere umano in un oggetto inorganico. È per questa ragione che non si deve

temere questa disciplina, in essa possiamo ritrovare solo il lavoro di noi stessi, come

qualunque altra cosa prodotta dall’uomo.

Gli algoritmi che vengono presentati in questo paragrafo non recano neppure lontanamente

l’ambizione descritta precedentemente, sono solo granelli di sabbia in una spiaggia

vastissima, ma nel loro piccolo cercano di migliorare la vita di qualche essere umano che può

trovare giovamento da questo umile ausilio.

A questo punto possiamo iniziare l’analisi dei vari algoritmi che si incontreranno negli

oggetti che costituiscono la struttura del sistema. Essi verranno divisi e studiati in varie

sezioni che si sovrappongono alle sezioni trattate nel paragrafo precedente.

Come già accennato quando si è parlato dell’oggetto candidato alla cattura delle immagini,

nessun algoritmo è contenuto al suo interno, questo è dovuto al fatto che il suo solo compito è

prelevare le immagini dalla camera. Il primo algoritmo che si incontrerà permette all’utente di

scegliere il colore del riferimento per le catture successive, poi verrà analizzato l’algoritmo

che permette di catturare il movimento del volto dell’utente. A questo punto si incontrerà

quello che aiuta a calibrare il sistema e infine verrà presentato l’algoritmo che risiederà

nell’oggetto, il cui incarico è dare l’informazione sul punto fissato sullo schermo dall’utente,

al Supervisore.

È di vitale importanza comprendere che in questo capitolo avviene l’analisi teorica degli

algoritmi, quindi si discute la catena logica di avvenimenti che rendono possibile

l’elaborazione dati. Ogni algoritmo viene discusso in maniera isolata dagli altri e l’unico

punto di contatto sono i risultati che l’algoritmo a monte dona alla procedura che sta a valle.

Tuttavia, come si vedrà nel prossimo capitolo l’implementazione è più complessa dell’analisi

teorica. In quanto, escluso l’algoritmo che permette all’utente di scegliere il riferimento, gli

altri sono estremamente correlati e potrebbero essere trattati quasi come un unico algoritmo.

50

La struttura che li organizza è ancora quella descritta in questo capitolo ma, alcuni oggetti

incapsulano più di un algoritmo al loro interno.

4.2.1 Scelta del riferimento

Il problema, che deve essere risolto da questo algoritmo, riguarda la possibilità, da parte

dell’utente, di scegliere il colore più conveniente al fine di riconoscere il riferimento fisso

sulla sua fronte.

La motion capture intrusiva ci permette di rendere relativamente semplice la risoluzione di

questo problema. Prima di tutto dobbiamo scegliere un colore, da applicare alla fronte

dell’utente, che sia particolare, cioè non troppo comune. Infatti esso deve risaltare

nell’immagine catturata dalla camera nella quale, presumibilmente, verranno visualizzati oltre

al volto dell’utente anche lo sfondo del luogo dove si trova e i capi di abbigliamento che

coprono le sue spalle. Per esempio si può scegliere un rosso acceso oppure un verde forte.

Tuttavia il sistema dovrebbe essere in grado di intercettare in modo corretto più colori per

risultare abbastanza versatile. Questa caratteristica è molto importante in quanto ci potremmo

ritrovare in situazioni strane, dove si cerca di intercettare un rosso in una stanza con la

tappezzeria rossa, quindi risulterebbe positivo, poter cambiare il colore del riferimento per

esempio in un verde ed evitare i problemi di intercettazione. Questo accade perché non siamo

in grado di prevedere dove il sistema verrà utilizzato e quindi non possiamo imporre dei

determinati colori allo sfondo e ai vestiti dell’utente.

A questo punto, affrontando l’analisi dell’algoritmo in maniera superficiale e senza introdurre

tutte le problematiche che entrano in gioco realmente, potremmo definire con facilità le

operazioni che esso deve compiere. La procedura non dovrebbe fare altro che, elencare i vari

colori che appaiono in un’immagine catturata presa come modello. Da questa lista l’utente

dovrebbe selezionare il colore che più si avvicina al colore del riferimento e l’algoritmo

dovrebbe concludere informando il Supervisore del colore scelto.

Purtroppo l’algoritmo, per ottenere un risultato valido e utilizzabile nelle elaborazioni

successive, non può essere così semplice. Ci sono fattori che ci costringono a produrre una

procedura più complessa di quanto possa sembrare. L’unico punto in comune tra la soluzione

appena descritta e l’algoritmo vero e proprio e il passo iniziale nel quale viene preso come

modello un’immagine catturata. Quest’ultima può essere vista come la prima immagine, del

mondo reale, catturata dal sistema, essa è il modello su cui si baseranno tutte le immagini

catturate successivamente. Infatti solo piccoli spostamenti del volto dell’utente faranno la

differenza tra questa e tutte le altre immagini.

51

Per cominciare, un fattore che preclude completamente la possibilità di costituire un algoritmo

semplice come è stato descritto è la definizione delle immagini che vengono elaborate dal

sistema. Nel nostro caso utilizziamo immagini a 24 bit che ammettono più di sedici milioni di

colori. Risulta ovvio che, sarebbe impossibile per l’utente poter eseguire una scelta sensata in

una lista così grande.

Inoltre, esiste un problema ancora più subdolo che è quasi paragonabile ad un errore. Se

catturiamo due immagini, anche vicine temporalmente, dello stesso oggetto, non otterremo

mai gli stessi colori dei pixel che lo definiscono. Questo è causato dall’illuminazione che può

variare, dall’angolazione differente dell’oggetto nelle due immagini rispetto alla sorgente di

luce, da imprecisioni dovute alla periferica di cattura ed errori nell’ottica di quest’ultima.

Quindi due immagini che l’occhio umano vede come pressoché identiche possono avere

differenze notevoli a livello di colore dei singoli pixel, basta una leggera sfumatura diversa

per identificare nell’elaborazione colori completamente differenti. Può anche capitare che

nell’immagine in alcune zone molto chiare o molto scure vengano catturati colori che non

hanno senso. L’algoritmo deve accorgersi di queste incongruenze e cercare di correggerle

oppure informarne l’utente.

Tenendo conto di questi fattori, che tentano di destabilizzare il nostro algoritmo, quest’ultimo

deve proporre delle contromisure valide per ottenere il risultato che ci prefiggiamo. Prima di

tutto la procedura non analizza più i colori singolarmente ma attua una sorta di filtro,

introducendo un concetto nuovo quale la sfumatura. Quest’ultima è una sorta di insieme di

colori che hanno una caratteristica in comune, sono cioè molto simili nella loro gradazione,

per esempio è una sfumatura il rosso scuro o il verde molto chiaro. Questo permette di

aggirare il problema della lista dei colori molto grande, infatti limitando il numero delle

sfumature che il sistema riconosce possiamo presentare all’utente un numero limitato di

opzioni nel quale può scegliere e rendere così più semplice il suo compito.

Per la definizione di un gruppo di colori in una sfumatura ci soccorre la discussione che

abbiamo intrapreso in un capitolo precedente riguardo ai colori e come essi sono interpretati

dall’elaboratore. Grazie alla scissione di un dato colore nei tre colori primari rosso,verde e

blu, siamo in grado, per esempio, di definire un rosso come un colore dove la componente

rossa è molto elevata mentre sia la componente verde che quella blu sono contenute. Quindi

possiamo definire una sfumatura, quell’insieme di colori che si discostano poco nel rapporto

tra le tre componenti.

Per riprendere l’esempio di poco fa, una sfumatura di rosso acceso conterrà tutti i colori che

hanno una componente di rosso prossima a 255 (ricordiamo che ogni componente in una

52

struttura RGB è definita da un byte che permette un range da 0 a 255), mentre avranno un

numero prossimo allo 0 nelle altre due componenti. Per questo motivo due colori

caratterizzati rispettivamente da 250,10,10 e 252,8,8 faranno parte entrambi della stessa

sfumatura in quanto possono essere entrambi definiti dei rossi accesi anche se il secondo è

leggermente più brillante del primo.

Per risolvere l’incongruenza prodotta dal secondo fattore viene introdotta un’ulteriore idea

che ci può aiutare. Ogni sfumatura porta con sé l’informazione del luogo nel quale è collocata

nell’immagine. Per fare questo si introduce un sistema di riferimento solidale all’immagine,

che reca l’origine nell’angolo in basso a sinistra della figura con gli assi diretti parallelamente

ai bordi della stessa, l’unità di misura è il pixel quindi le ascisse hanno un range che va da 0 al

numero di colonne dell’immagine, mentre le ordinate vanno da 0 al numero di righe. Con

questo metodo possiamo indicare e tenere traccia di tutti i punti che costituiscono l’immagine

stessa.

Grazie a questo sistema di riferimento possiamo indicare in che area è localizzata una certa

sfumatura. Per esempio se il nostro riferimento è di un rosso acceso, tra le molte sfumature

che il sistema può riconoscere ce ne sarà una che indica il rosso acceso e l’area nella quale

risiede combacerà sull’immagine nel punto esatto dove il riferimento si trova. Questo è stato

fatto per evitare che l’utente scelga sfumature che non sono corrette e che non intercettano il

riferimento in maniera corretta.

Può capitare infatti, che più di una sfumatura possa essere ricondotta al colore del riferimento.

Questo avviene perché il sistema non definisce una sola sfumatura per il colore ma ne indica

alcune. Per esempio il sistema riconosce varie sfumature di rosso dal più brillante al più scuro

e cupo al fine di permettere al sistema di funzionare sia con una luce perfetta sia con una

carenza nell’illuminazione dell’utente. Quindi non si deve fare altro che, scegliere la

sfumatura che ricalca più fedelmente il contorno del riferimento in quanto le altre potrebbero

essere guastate da colori che rientrano nella sfumatura ma che non hanno niente a che fare con

il riferimento. Si attua in pratica una specie di filtro che permette al sistema di individuare e

scartare gli errori nella colorazione dei pixel nell’immagine catturata.

Ora, grazie a questi concetti e alle idee analizzate fino a questo punto possiamo definire i

passi che l’algoritmo deve compiere per arrivare al suo completamento. Per prima cosa si

deve scorrere l’array che contiene l’informazione sul colore dei pixel scegliendo la sfumatura

adatta per ognuno. Tutte le volte che viene trovata una sfumatura nuova, che il sistema può

intercettare, si indica la zona che localizza la sfumatura come l’area che incorpora la

posizione di quel pixel. Se vengono, successivamente trovati altri pixel che appartengono a

53

quella sfumatura, l’area viene modificata per contenere anche quel punto. Tutte le sfumature

che vengono intercettate sono poste in una lista che praticamente indica una palette filtrata dei

colori che compongono l’immagine. Ovviamente la maggioranza dei colori che costituiscono

l’immagine deve essere scartata in quanto indicano zone della stessa di poco interesse, i colori

non bene identificabili come i grigi o colori molto scuri vengono inseriti in una sfumatura che

essenzialmente fa da cestino dei rifiuti. La nostra attenzione è focalizzata sulle sfumature che

il nostro riferimento può adottare, quindi sfumature di rosso, verde o blu.

A questo punto la procedura deve visualizzare questa lista di sfumature al fine di far scegliere

all’utente il colore migliore. Per aiutare quest’ultimo nella sua scelta il sistema deve indicare

l’area di competenza sull’immagine catturata di ogni sfumatura, si sceglierà quella che

possiede un’area che combacia meglio con i contorni del riferimento. L’algoritmo termina

come nella sua versione più semplice, informando il Supervisore della scelta attuata

dall’utente.

4.2.2 Cattura del movimento

Prima di analizzare come questa operazione possa essere attuata dall’algoritmo contenuto a

questo punto della catena di acquisizione ed elaborazione dati, dobbiamo discutere un

problema che a prima vista può sembrare estraneo al nostro scopo ma che invece riveste una

fondamentale importanza.

Dobbiamo descrivere le caratteristiche fisiche che deve possedere il riferimento. Questa

discussione è strettamente correlata alla capacità della camera di riprodurre fedelmente i

colori del mondo reale che si appresta a catturare.

A seconda di un riferimento lucido od opaco oppure con una superficie piana o curvilinea il

sistema si comporterà in maniera differente. Questa caratteristica è dovuta al modo di

riflettere il colore se il riferimento viene colpito dalla luce da diverse angolazioni.

Se, per esempio, utilizziamo un riferimento con una superficie piana c’è il rischio che, in

alcune angolazioni, la luce si rifletta come su uno specchio, inibendo la capacità della camera

di catturare il colore. Questo effetto può sembrare strano, ma non lo è, se si pensa che

abbiamo a che fare con camere che non sono molto potenti e che possono essere ingannate da

simili eventi. Mentre una superficie curvilinea è sempre in grado di avere almeno una zona

che colpisce la luce nella stessa angolatura e permette al riferimento di indicare lo stesso

colore anche se in maniera parziale sulla superficie.

Anche una superficie opaca può portare a problemi, infatti abbiamo bisogno di rendere il più

brillante possibile il riferimento, per sfruttare le situazioni di poca luce al meglio. Dunque un

54

riferimento opaco potrebbe essere difficile da intercettare quando ci troviamo ad operare con

un’illuminazione carente.

Potrebbe sembrare una soluzione valida adottare un riferimento dotato di luce propria come,

per esempio, un led colorato. Tuttavia c’è il pericolo, non del tutto remoto, che la camera

venga, per così dire, abbagliata dalla luce e indicare nell’immagine una zona bianca invece

che colorata.

Per i nostri scopi quindi, il riferimento dovrebbe avere una superficie lucida per riflettere il

più possibile il colore e non avere una superficie piana ma curvata per evitare la riflessione

totale del colore.

La natura del riferimento, unita ai fattori destabilizzanti introdotti nella sezione precedente

rendono questo algoritmo estremamente complesso anche se, la sua funzione di tenere traccia

degli spostamenti del volto dell’utente è un’operazione abbastanza semplice.

Questo è dovuto al fatto che, l’algoritmo utilizza, per intercettare il riferimento, la sfumatura

definita grazie alla procedure del passo precedente e non ha la possibilità di scegliere una

sfumatura differente per ogni immagine utilizzando sempre la migliore per una data

angolazione della luce. In tal modo si possono catturare immagini dove la sfumatura scelta

non ricalca fedelmente i contorni del riferimento ma ne intercetta solo alcuni, cambiando

forma ad ogni immagine. In più entrano in gioco anche gli errori nell’ottica della camera che

potrebbero inserire nell’immagine colori attinenti alla sfumatura cercata ma in zone che non

hanno niente a che vedere con il riferimento. Questa possibilità produce delle zone nel quale è

contenuta la sfumatura troppo grandi che non permettono di indicare con una certa precisione

il riferimento.

A questo punto, dopo aver delineato tutti i problemi che l’algoritmo deve cercare di evitare o

almeno ridimensionare, possiamo analizzare la procedura attuata per raggiungere il nostro

scopo. È logico pensare che, per avere la sensazione di movimento dobbiamo catturare

un’immagine dietro l’altra del volto dell’utente. L’algoritmo deve andare ad intercettare su

ogni figura la zona dove risiede la sfumatura scelta precedentemente che indica il riferimento.

Tuttavia non è possibile ottenere un risultato apprezzabile elaborando direttamente questa

informazione ma siamo costretti a utilizzare una specie di filtro per ottenere una soluzione

accettabile.

Questo viene realizzato attendendo la cattura di un certo numero di immagini che vengono

elaborate per presentare una soluzione. Precisamente sono le zone dove risiede la sfumatura

ad essere analizzate, scartando le zone che sembrano troppo grandi per contenere il

riferimento e mediando le altre per ottenere una zona che si avvicina il più possibile ai

55

contorni del riferimento. Scartare le zone più grandi serve per ridurre al minimo gli errori

descritti in precedenza sulle ottiche in quanto vengono scartate zone che comprendono gli

errori nei pixel e che non sono attinenti al riferimento. Mediare significa evitare i problemi

riguardo al riferimento stesso dove ci possono essere delle riflessioni che modificano troppo

velocemente la zona della sfumatura che sì, indica il riferimento, ma ne indica solo alcuni

contorni per volta. In pratica si devono mediare tanti piccoli rettangoli che indicano il punto

del riferimento ma che variano troppo la loro forma da una cattura all’altra, producendo in

questo caso un rettangolo che dovrebbe definire in maniera abbastanza corretta i contorni del

riferimento.

Questo metodo porta ovviamente dei ritardi nell’indicazione della posizione del riferimento,

tuttavia è essenziale per ottenere un risultato accettabile per le elaborazioni future. Agire sulla

velocità della cattura delle immagini può essere una valida alternativa per ottenere delle

indicazioni che si avvicinano all’elaborazione real time ed indicare più prontamente la

posizione del riferimento. Anche diminuire il numero di campioni può essere un fattore per

avvicinarsi all’elaborazione in tempo reale tuttavia ridurlo di molto inficia l’utilità del filtro.

Quest’ultimo metodo è utile se possiamo utilizzare il sistema con un’illuminazione ottima che

rende possibile definire zone precise sull’immagine della sfumatura del riferimento. Se

possiamo usufruire di questo vantaggio non siamo costretti a mediare o scartare molte

immagini e otteniamo un’elaborazione molto veloce e precisa.

Un ultima funzione delegata dal sistema all’algoritmo impone che quest’ultimo produca una

sorta di controllo sui dati che elabora. Se la zona che descrive la sfumatura diventa troppo

piccola nella sequenza di immagini catturate e contiene pochi pixel che hanno un colore

corretto, l’algoritmo deve informare il Supervisore di questo fatto. Questa operazione ha

l’utilità di avvertire l’utente che forse la sfumatura che ha scelto non è la più corretta ed è

conveniente ripetere la valutazione sul colore del riferimento.

Infine, l’algoritmo deve proporre delle soluzioni che permettano un’elaborazione più

efficiente dei dati a disposizione, questo può essere fatto sfruttando la natura del riferimento.

Ciò significa che il volto dell’utente non subisce spostamenti repentini e di grande ampiezza,

cosicché è possibile ricercare la zona della sfumatura, non su tutta l’immagine, ma su una

zona definibile grazie alla posizione del riferimento nell’immagine precedente. Questo

permette di diminuire in maniera significativa il lavoro del processore che può dedicare il suo

tempo a operazioni più importanti. Tuttavia questo implica che l’algoritmo possa perdere

l’aggancio col riferimento, se ciò avviene non resta che cercare di nuovo la sfumatura su tutta

l’immagine.

56

Il Supervisore deve venir informato anche se l’algoritmo perde l’aggancio con il riferimento e

non riesce più ad individuarlo nell’immagine. Questo può essere causato da uno spostamento

eccezionalmente grande dell’utente davanti alla camera che perde dal suo campo visivo

l’immagine del riferimento.

4.2.3 Analisi del campo visivo

È importante far notare che d’ora in poi si presume di lavorare con dati che non sono più

affetti da errori o almeno gli algoritmi vengono informati se il sistema non riesce a controllarli

in maniera corretta. Tra i due algoritmi presentati nelle sezioni precedenti e quelli che

verranno analizzati in questa e nella successiva si intravede una netta divisione sulla loro

natura. Mentre le procedure che sono state descritte avevano a che fare con il mondo reale e

dovevano essere in grado di elaborare immagini che da questo mondo provenivano, gli

algoritmi che ci apprestiamo a osservare lavorano su dei dati che sono, per così dire, un

modello matematico del mondo reale, ci troviamo ad un livello di astrazione superiore. Questa

caratteristica permette una più facile analisi delle procedure qui di seguito presentate in

quanto i dati che devono elaborare non sono più complessi come i dati che erano tenuti ad

elaborare gli algoritmi precedenti.

Guardando il cammino che dobbiamo ancora percorrere ed appoggiandoci a quanto detto nel

paragrafo precedente dove si analizzava l’architettura del sistema, ci troviamo di fronte

all’algoritmo che permette al sistema di definire il campo visivo che l’utente utilizza per

spostare lo sguardo sul terminale video del Personal Computer.

Il compito assegnato a questa procedura è quello di cablare il sistema ed ottenere dei dati

sufficienti per l’ultimo algoritmo che permette di indicare in che posizione l’utente ha rivolto

il viso.

Per ottenere questo risultato la procedura deve assicurarsi una specie di zona, sempre indicata

tramite il sistema di riferimento fisso sull’immagine, nel quale il riferimento ha libertà di

movimento ed ogni posizione occupata all’interno di quest’area è relazionabile ad una

posizione fissa del volto dell’utente. Come già descritto nel paragrafo precedente se, per

esempio, l’utente rivolge il viso verso il centro dello schermo, il riferimento si deve trovare al

centro della zona ottenuta con questo algoritmo.

È a questo punto che entra in gioco l’analisi che è stata fatta nel capitolo nel quale si

discutevano i vari problemi legati all’ausilio e particolarmente, sul problema di tenere

fortemente in considerazione le capacità residue dell’utente. Infatti, mentre il lavoro richiesto

all’utente dai precedenti algoritmi è praticamente nullo, è l’assistente a fare il grosso del

lavoro mentre l’utente non deve fare altro che indossare il riferimento e posizionarsi davanti

57

allo schermo del Personal Computer. In questo caso è l’utilizzatore che riveste un ruolo

importante ed è per questa ragione che all’algoritmo viene chiesta una caratteristica notevole

che sta nel rendere le richieste all’utente semplici e non gravose. Questo, come è già stato

analizzato nel capitolo riguardante il contesto, è dovuto al fatto che l’operazione di cablaggio,

per la natura dell’ausilio, deve essere riproposta ad ogni sessione di lavoro e quindi l’utente

deve ripetere ciò che l’algoritmo gli indica ogni volta che vuole utilizzare l’ausilio.

L’algoritmo ottiene il suo obiettivo richiedendo all’utente di fissare per alcuni istanti delle

zone ben definite dello schermo. Grazie alla posizione intercettata, dalla procedura descritta

precedentemente del riferimento, questo algoritmo permette di delineare un’area che contiene

al suo interno tutte queste zone. In pratica l’algoritmo chiede all’utente di rivolgere il viso sui

contorni dello schermo e in particolare negli angoli di quest’ultimo.

Tutte le volte che l’utente ha il desiderio di cablare il sistema per poterlo utilizzare richiede

all’ausilio le funzionalità attribuite a questo algoritmo. Quest’ultimo inizia avvertendo l’utente

che la fase di cablaggio è in corso dopodiché presenta un pallino colorato che l’utente deve

fissare con tutto il viso. Ogni volta che l’algoritmo è sicuro di aver intercettato ed

immagazzinato la posizione della sfumatura grazie al sistema di riferimento solidale con

l’immagine, il pallino colorato cambia posizione sullo schermo e percorre con questa tecnica

i punti che si ritengono più importanti per definire l’area dell’immagine che definisce i

contorni del video.

Una funzionalità che è estremamente importante è garantire l’informazione sulla stabilità

della cattura del riferimento da parte del sistema. Per ottenere questo l’algoritmo deve sempre

informare l’utente dell’aggancio sul riferimento, in quanto se si perde la traccia del

riferimento la procedura di cablaggio è incapace di portare a termine il suo compito.

È importante far notare, infine, che l’algoritmo analizzato nella sezione precedente continua a

lavorare anche quando la procedura qui descritta è in corso. Ciò significa che le informazioni

dell’algoritmo che permette l’intercettazione del riferimento vengono utilizzate per gli scopi

di cablaggio quindi se l’algoritmo che intercetta la sfumatura del riferimento avvisa il sistema

che l’aggancio è scarso oppure è stato perso, la procedura di cablaggio deve informare

l’utente di questo fatto.

Mentre il sistema veniva creato si è incappati in una caratteristica che a prima vista non era

stata analizzata ma che permette all’algoritmo di essere più versatile e rendere il lavoro

dell’utente estremamente semplice. Invece di obbligare l’utente a seguire il pallino per

ottenere il cablaggio possiamo ottenere lo stesso risultato permettendo all’utilizzatore di

fissare liberamente gli angoli del video. Questo ovviamente deve essere fatto da un utente che

58

ha già in mente i punti da fissare, se il sistema viene usato per la prima volta è consigliabile

eseguire il cablaggio seguito passo a passo dal sistema grazie al pallino colorato da fissare.

Se l’utente è esperto e sa già come il sistema funziona può utilizzare questa scorciatoia.

Avvisando il sistema che ha intenzione di cablarlo e poi definendo l’area che delimita il

campo d’azione del riferimento in modo manuale fissando i quattro angoli del video e

controllando l’effettivo ridimensionamento dell’area direttamente sull’immagine e sul sistema

di riferimento ad essa relativo.

In questo caso si parla di cablaggio manuale mentre la procedura di cablaggio gestita dal

sistema grazie al pallino colorato prende il nome di cablaggio automatico.

4.2.4 Definizione del punto fissato

A questo punto, nella catena di acquisizione ed elaborazione dei dati, siamo arrivati nel luogo

più alto e quindi più astratto dell’intero sistema. Questo significa che l’algoritmo presentato in

questa sezione si trova ad elaborare dati che rappresentano un modello matematico e

geometrico della realtà ma che con essa non hanno più niente a che vedere. Si presume che a

questo punto i dati che ci troviamo ad elaborare siano privi di errori o almeno subiscano in

modo lieve i danni di quest’ultimi.

Il fine ultimo di questa procedura è avvertire il Supervisore circa la zona dello schermo sul

quale l’utente ha rivolto il viso. Per fare questo utilizza i dati che i precedenti algoritmi hanno

prodotto. Grazie all’area che definisce il campo visivo e all’uso dell’algoritmo che intercetta il

riferimento questa procedura ricava il punto fissato.

Tutte le volte che la procedura che individua il riferimento fisso sul volto dell’utente produce

dei risultati stabili nei quali si sono filtrati gli errori, quindi è in grado di definire una zona ben

precisa nel sistema di riferimento solidale con l’immagine, entra in gioco questo algoritmo.

Le operazioni svolte non sono estremamente complesse, il suo compito sta nel prendere

questa precisa zona e correlarla con il campo visivo ottenuto dalla procedura precedente.

L’unica trappola sta nella specularità delle immagini catturate, ciò significa che la camera

cattura un’immagine che è paragonabile alla figura riflessa da uno specchio. Quindi

l’algoritmo deve compensare questo evento e produrre dei dati che abbiano senso. Per

esempio, se l’utente fissa il punto in alto a destra dello schermo, la procedura si ritroverà ad

elaborare una zona della sfumatura scelta che, nel sistema di riferimento solidale

all’immagine, si troverà nelle vicinanze dell’angolo in alto a sinistra della zona che definisce

il campo visivo. Quindi non deve fare altro che invertire il riferimento nella zona del campo

visivo per ottenere il punto corretto fissato. Fatta questa operazione l’algoritmo conclude

59

avvisando il Supervisore della zona in cui l’utente ha rivolto il viso, sarà quest’ultimo ad

avvisare l’applicazione che in quell’istante è attiva.

Prima di concludere questa sezione e con essa il capitolo dobbiamo analizzare una

caratteristica del sistema che non è ancora stata discussa. Per simulare il comportamento di un

mouse dobbiamo incorporare nel sistema un metodo per riprodurre il clic, questo per

permettere all’utente di attuare una sorta di selezione degli oggetti che fissa sullo schermo.

I metodi per attuare questa operazione sono strettamente legati alla posizione delle camere e

al loro numero. Principalmente due sono le possibilità, attuare un clic basandosi sul tempo in

cui l’utente rimane a fissare un certo oggetto, oppure utilizzare la palpebra abbassata come

indicatore del clic. Il primo metodo è attuabile se possiamo utilizzare una sola camera, in

quanto la scelta di posizionarla al di sopra dello schermo non permette di analizzare in

maniera corretta l’occhio. Questo perché, come abbiamo spiegato nel capitolo che analizzava

i problemi, se l’utente fissa un punto in basso dello schermo l’angolatura non permette alla

camera di vedere in maniera corretta l’occhio in quanto quest’ultimo è coperto dal sopracilio.

Tuttavia se c’e la possibilità di utilizzare due camere la seconda può essere posta sotto lo

schermo ed in questa posizione siamo in grado di avere sempre l’aggancio sulla zona

dell’occhio, potendo in questo modo attuare il secondo metodo per identificare i clic.

Il sistema è stato studiato per attuare entrambe le soluzioni tuttavia, a causa del collo di

bottiglia identificabile sul Bus dati, la maggior parte dei Personal Computer tuttora sul

mercato non permettono di utilizzare in concorrenza più di una camera. Per questo motivo il

sistema, per ora, simula il clic valutando il tempo che l’utente passa a fissare un certo punto

sullo schermo.

Questa operazione viene svolta dall’algoritmo analizzato in questa sezione. Esso tiene traccia

degli spostamenti del riferimento e se essi si concentrano in un punto per un determinato

valore del tempo la procedura informa il Supervisore che c’è stato un clic. Sarà quest’ultimo

ad informare l’applicazione attiva di questo evento.

60

61

5 Implementazione

Potrà sembrare strano e, sicuramente il lettore si sarà chiesto per quale motivo un

documento che dovrebbe studiare un'applicazione informatica non abbia ancora analizzato

una sola riga di codice. Questa domanda, seppur legittima, trova risposta nella volontà di

mettere il lettore nella posizione più comoda e proficua per comprendere appieno il contenuto

di questo capitolo. Il lavoro fin qui svolto ha un enorme significato, in quanto ha permesso di

comprendere in maniera completa l’analisi teorica degli algoritmi che costituiscono il sistema.

I precedenti capitoli hanno risposto alle domande sul perché è nata la necessità di tali

procedure e hanno fornito risposte adeguate a come gli algoritmi devono provvedere ai

compiti riservati a loro dal sistema.

Sono gli algoritmi la risorsa più importante contenuta nell’ausilio, è da ricercare in essi la

scintilla di ingegno che permette al sistema di portare a termine il suo compito. La struttura

che si trova in secondo piano e che ha il compito di incastonare le pietre preziose costituite

dalle procedure, assume contorni indefiniti negli scopi che si sono prefissi, per non

appesantire in maniera sproporzionata la discussione. Tuttavia la sua ombra è sempre presente

nello studio che si intraprenderà sull’implementazione dei vari algoritmi. Ignorare

l’implementazione della struttura nel contesto in cui ci si ritrova costa un caro prezzo in

62

quanto questo risultato è stato raggiunto con dei grandi sforzi paragonabili a quelli compiuti

per ottenere gli algoritmi. Quindi è con immensa pena che nel presente capitolo non viene

affrontata l’analisi dettagliata sulla gestione del sistema, mentre tutta l’attenzione si focalizza

su come le procedure sono state implementate.

A questo punto, prima di analizzare in maniera dettagliata l’implementazione degli

algoritmi, è importante riprendere un concetto che era stato solo introdotto nel capitolo

precedente. In quel contesto si era detto che, per semplificare l’analisi, la discussione sugli

algoritmi seguiva un filo logico che legava quest’ultimi come in una catena. I risultati ottenuti

da una procedura erano utilizzati come base di partenza per l’algoritmo che stava a valle.

Tuttavia, in realtà, nell’implementazione ciò avviene solo per il primo algoritmo, che permette

di scegliere la sfumatura del riferimento. Le restanti procedure sono talmente correlate tra di

loro che potrebbero essere analizzate insieme. Infatti quest’ultime sono implementate negli

stessi oggetti e rappresentano una sorta di algoritmo monolitico che è l’unione omogenea delle

tre procedure divise logicamente nel capitolo precedente.

Quindi sono due le strade che si possono percorrere per analizzare l’implementazione. Si

può enfatizzare la logica che regola i rapporti tra gli algoritmi oppure privilegiare

l’implementazione stessa. Nel primo caso l’analisi risulta semplice e lineare anche se si è

obbligati a descrivere separatamente il sorgente analizzando solo alcune parti di esso per ogni

algoritmo. Scegliendo la seconda via è possibile analizzare totalmente le procedure contenute

negli oggetti sminuendo il discorso logico fin qui fatto. La scelta non può che ricadere sul

primo metodo, infatti la logica che regola gli algoritmi è un punto di forza dell’intera

trattazione e non può essere abbandonata proprio a questo punto.

Resta ancora da affrontare una tematica che abbraccia nel complesso la trattazione di

questo capitolo. Di solito si è tentati di utilizzare tutte le potenzialità che ci vengono offerte

sia dal Sistema Operativo che dal linguaggio di programmazione. Tuttavia agire in questo

modo non sempre porta a risultati soddisfacenti. Non significa che se il linguaggio di

programmazione permette di utilizzare costrutti molto compatti ma estremamente complessi

per scrivere il sorgente essi debbano per forza essere utilizzati. Se lo stesso risultato può

essere raggiunto scrivendo un sorgente più semplice non c’è motivo nell’utilizzare il costrutto

più complesso. Per questa ragione molte potenzialità offerte dal C++ non sono state utilizzate.

Per esempio la caratteristica dell’ereditarietà, i template di funzione e classe e il sovraccarico

degli operatori non compaiono nel sorgente. Questo non è indice di incompetenza oppure di

inesperienza da parte di chi scrive il sorgente, ma rispecchia l’obbligo di rendere quest’ultimo

scorrevole e semplice da comprendere.

63

Nulla ora vieta la possibilità di analizzare efficacemente come gli algoritmi sono stati

implementati. Il capitolo avrà una struttura simile al precedente nel quale ogni paragrafo

studierà un algoritmo, conservando l’ordine imposto dalla logica di elaborazione dati descritta

in precedenza.

5.1 Scelta del riferimento Come analizzato dettagliatamente nel capitolo precedente questo algoritmo deve indicare

al sistema quale colore è stato scelto dall’utente per intercettare il riferimento. Per ottenere

questa informazione la procedura si divide in quattro passaggi intermedi. Per prima cosa il

sistema definisce i parametri essenziali dell’oggetto, che ha il compito di contenere le

procedure che implementano l’algoritmo, successivamente si compone la tavolozza di

sfumature intercettabili nell’immagine. Come terzo passo viene visualizzata sullo schermo

questa palette per informare l’utente dei colori disponibili. Infine si permette all’utente di

scegliere il colore e viene informato il sistema della scelta.

Prima di analizzare i quattro passi è importante descrivere una caratteristica alquanto

particolare insita nel codice scritto. Si potranno notare in esso dei nomi di variabili piuttosto

insolite, che potrebbero suonare strane. Questo è stato fatto per rendere il sorgente più

semplice da leggere. Per esempio, i compiti del Supervisore, che è stato descritto ampliamente

nel paragrafo sull’architettura, sono incapsulati in una classe definita grazie al C++ chiamata a

64

sua volta Supervisore. L’istanza di tale classe è un oggetto che prende il nome di Angelo. È

stato scelto questo nome per ricordare le caratteristiche del Supervisore, in quanto esso

presiede a tutte le operazioni eseguibili del sistema. Altri strani nomi verranno analizzati

quando ci imbatteremo in essi nel corso della discussione.

5.1.1 Impostazione

Come si è visto, l’obbligo di focalizzare la discussione solamente sull’implementazione

degli algoritmi, porta a dover analizzare punti nel codice che non sono ordinati

temporalmente. Ciò significa che il sistema, o meglio la struttura che incapsula gli algoritmi,

ha già svolto del lavoro ed è in grado di fornire alla procedura il materiale adatto per la sua

elaborazione. Per esempio, in questo caso, il sistema ha già ottenuto l’aggancio alla camera

per la cattura delle immagini grazie alla libreria Video For Windows e all’oggetto incaricato

di questa mansione. Inoltre la prima immagine, utilizzata come modello, è stata catturata e il

sistema la offre, grazie alla libreria Victor, alla procedura che la elaborerà.

Prima di passare all’elaborazione vera e propria il Supervisore informa gli oggetti che

incorporano le funzionalità dell’algoritmo che presto verrà richiesto il loro apporto e per

questo motivo chiede a quest’ultimi di definire i parametri e le strutture di elaborazione dati

che l’algoritmo utilizza per i suoi scopi. L’istanza della classe Palette chiamata Tavolozza si

serve di due liste doppiamente concatenate per contenere l’elaborazione della procedura.

65

La prima contiene l’insieme delle sfumature intercettabili sull’immagine catturata. Ogni

elemento è costituito dalla struttura seguente, LC significa Lista Complessa:

typedef struct CELLA_COLORE_LC{

COLORE Colore;SCOSTAMENTO Scostamento;UINT TipoColore;RECT Zona;int Bersagli;int Posizione;struct CELLA_COLORE_LC *Precedente,*Successivo;

};

Sono indicate molte informazioni, ci sono i contorni dove è localizzata la sfumatura nel

sistema di riferimento relativo all’immagine (Zona), il numero di pixel dell’immagine che

fanno parte della sfumatura (Bersagli) e i parametri che identificano il tipo di sfumatura

(Colore, TipoColore e Scostamento). Per le sfumature non intercettabili che includono tutti i

colori che appartengono all’immagine ma che non hanno valore per la nostra elaborazione

viene utilizzato il primo elemento della lista che rappresenta una sorta di cestino dei rifiuti.

La seconda è utilizzata come merce di scambio tra i vari oggetti che concorrono alla

riuscita dell’algoritmo e contiene solo i tipi di sfumature intercettati, con i valori dei tre colori

primari del colore identificativo della sfumatura, LS indica Lista Semplice.

typedef struct CELLA_COLORE_LS{

BYTE Blu,Verde,Rosso;UINT TipoColore;struct CELLA_COLORE_LS *Precedente,*Successivo;

};

Una volta definite queste strutture dati e generato i vari puntatori che permettono di

utilizzarle l’oggetto Tavolozza è pronto a partire con l’elaborazione vera e propria

dell’algoritmo. Esiste anche la possibilità di eliminare le liste se per caso sono già state

richieste le funzionalità dell’algoritmo e si decide di riproporlo con un’altra immagine presa

come modello.

5.1.2 Analisi immagine

È in questa sezione che viene analizzata la parte più importante della procedura per la

determinazione del colore del riferimento.

L’oggetto Tavolozza ha a sua disposizione il puntatore alla variabile Victor che contiene

l’immagine catturata. Non deve fare altro che scorrere il vettore dove sono contenuti i colori

primari di ogni pixel, si ricorda che vengono elaborate immagini a 24 bit che non utilizzano

una palette ma conservano direttamente il colore di ogni pixel. Grazie al ciclo:

66

i = j = 0;X = Y = 0;while (i < ((int) Immagine->bmh->biSizeImage)){

Colore.Blu = Immagine->ibuff[i];Colore.Verde = Immagine->ibuff[i+1];Colore.Rosso = Immagine->ibuff[i+2];this->In(Colore);i = i + 3;j++;X = j % 320;Y = j / 320;

}

si può scorrere tutto il vettore. Ad ogni passaggio viene conservato il colore di un pixel nella

struttura Colore, non altro che l’unione di tre byte. Dopodiché si lancia la procedura che

elabora le caratteristiche del colore, del quale identifica la sfumatura di appartenenza e la

inserisce nella lista se non è stata già incontrata.

Le variabili i e j servono per scorrere il vettore, mentre le variabili X e Y identificano il

pixel nel sistema di riferimento solidale con l’immagine e permettono di definire i contorni

della sfumatura, si utilizza il valore 320 perché le immagini elaborate sono 480X320.

Passando ora ad analizzare la procedura In() notiamo che essa rappresenta il cuore di tutta

l’implementazione dell’algoritmo:

void Palette::In(COLORE Colore){// Creo la Cella Colore Lista Complessa

TempLC = new(CELLA_COLORE_LC);TempLC->Colore.Blu = Colore.Blu;TempLC->Colore.Verde = Colore.Verde;TempLC->Colore.Rosso = Colore.Rosso;TempLC->Scostamento.BV = Colore.Blu - Colore.Verde;TempLC->Scostamento.VR = Colore.Verde - Colore.Rosso;TempLC->Scostamento.RB = Colore.Rosso - Colore.Blu;TempLC->TipoColore = TipoColore(TempLC);TempLC->Posizione = Elementi;if (!Trovato(TempLC)){// Inserisco la Cella in coda alla Lista Complessa

TempLC->Precedente = CodaLC;TempLC->Successivo = NULL;CodaLC->Successivo = TempLC;CodaLC = TempLC;

// Creo la Cella Colore Lista SempliceTempLS = new(CELLA_COLORE_LS);TempLS->Blu = Colore.Blu;TempLS->Verde = Colore.Verde;TempLS->Rosso = Colore.Rosso;TempLS->TipoColore = TempLC->TipoColore;

// Inserisco la Cella in coda alla Lista SempliceTempLS->Precedente = CodaLS;TempLS->Successivo = NULL;CodaLS->Successivo = TempLS;CodaLS = TempLS;

67

Elementi += 1;}else

delete TempLC;}

La procedura genera le due liste che contengono le informazioni sulle sfumature intercettabili

nell’immagine. Per produrre questo risultato riserva, nella memoria di massa, spazio per un

elemento della Lista Complessa dove verranno indicate tutte le qualità del colore passato

come parametro. Grazie alle procedure TipoColore() e Trovato() analizza rispettivamente in

che sfumatura risiede il colore e se è già stata incontrata questa sfumatura. Se è stata

intercettata una nuova sfumatura la procedura la inserisce in coda alla Lista Complessa e

genera un nuovo elemento della Lista Semplice (LS) inserendolo in coda a quest’ultima.

Mentre se abbiamo a che fare con un colore che appartiene ad una sfumatura già intercettata,

lo scartiamo e liberiamo, grazie alla delete, lo spazio in memoria riservato all’elemento della

Lista Complessa.

La procedura chiamata TipoColore() permette di determinare a che sfumatura appartiene il

colore del pixel. Grazie alla scomposizione nei tre colori primari si è in grado di definire

qualsiasi sfumatura ma la procedura identifica solo poche sfumature focalizzate sui colori che

hanno un nesso col colore del riferimento. Si hanno sei sfumature differenti di rosso, verde e

blu che vanno dal più brillante al più scuro. Il sistema è in grado di intercettare anche alcune

sfumature di colori composti come il giallo, l’azzurrino e il violetto, nonché diverse sfumature

di grigio.

La procedura Trovato() merita una discussione più dettagliata:

bool Palette::Trovato(CELLA_COLORE_LC *Cella){

IndiceLC = TestaLC;while(IndiceLC != NULL){

if(IndiceLC->TipoColore == Cella->TipoColore){

if(X < IndiceLC->Zona.left)IndiceLC->Zona.left = X;

if(Y > IndiceLC->Zona.top)IndiceLC->Zona.top = Y;

if(X > IndiceLC->Zona.right)IndiceLC->Zona.right = X;

if(Y < IndiceLC->Zona.bottom)IndiceLC->Zona.bottom = Y;

IndiceLC->Bersagli++;return true;

}IndiceLC = IndiceLC->Successivo;

}Cella->Zona.left = Cella->Zona.right = X;Cella->Zona.top = Cella->Zona.bottom = Y;Cella->Bersagli = 1;

68

return false;}

Il compito svolto da questa funzione è lievemente più complesso di quello svolto dalle

precedenti. Prima di tutto la procedura ricerca nella Lista Complessa un elemento che

possieda lo stesso TipoColore dell’elemento passato come parametro. Se questa ricerca va a

buon fine significa che la sfumatura era già stata precedentemente individuata dall’algoritmo.

A questo punto non resta che modificare i contorni della zona che indica la posizione della

sfumatura nel sistema di riferimento solidale con l’immagine e incrementare il contatore del

numero di pixel appartenenti alla sfumatura. Se si arriva alla fine della Lista Complessa senza

aver trovato la sfumatura significa che il sistema ha intercettato una nuova sfumatura e

l’elemento deve essere incluso nella lista. Vengono definiti i contorni della sfumatura come le

coordinate del pixel e il conto dei pixel relativi viene posto a 1. Quindi si avvisa la procedura

chiamante In() che l’elemento deve essere inserito in coda alla Lista Complessa.

5.1.3 Visualizzazione

Ora la parte di elaborazione può ritenersi conclusa. Non resta che visualizzare sul terminale

video i risultati e aspettare la scelta dell’utente. È a questo punto che entra in gioco la seconda

lista denominata Semplice. Quest’ultima viene prelevata dall’Angelo e indirizzata alla

window procedure della finestra preposta alla visualizzazione dei risultati di questo algoritmo.

Una dopo l’altra le varie sfumature intercettate nell’immagine, usata come modello, vengono

indicate sullo schermo sotto forma di piccoli rettangoli colorati posti in fila. Dopodiché la

procedura si pone in attesa della scelta dell’utente.

5.1.4 Scelta

Quando l’utente si posiziona sul piccolo rettangolo che contraddistingue la sfumatura e

preme il pulsante sinistro del mouse indica alla procedura che è stata selezionata una

sfumatura. Questo evento genera una serie di operazioni che avvengono in cascata. La

window procedure che gestisce la finestra dove vengono visualizzate le sfumature avvisa

l’Angelo della scelta. Quest’ultimo compie la sua mansione di supervisore chiedendo le

coordinate della zona nel quale risiede la sfumatura direttamente all’oggetto Tavolozza

istanza di Palette, grazie alla procedura:

RECT* Palette::GetZonaColore(UINT TipoColore){

IndiceLC = TestaLC;while(IndiceLC != NULL){

if(IndiceLC->TipoColore == TipoColore)return &IndiceLC->Zona;

69

IndiceLC = IndiceLC->Successivo;}return NULL;

}

Quindi invia un messaggio alla window procedure relativa alla finestra dove viene

visualizzata l’immagine. Questo messaggio porta con sé i contorni della zona della sfumatura

che vengono sovrapposti all’immagine stessa. Se la sfumatura scelta è adatta i suoi contorni

dovrebbero intercettare il riferimento.

È importante analizzare perché l’algoritmo si comporti in questo modo quando potrebbe

concludere indicando solamente all’Angelo la sfumatura scelta. Ci troviamo di fronte questo

comportamento per implementare ciò che è stato detto nel capitolo precedente. Si richiedeva

alla procedura di aiutare l’utente nella scelta del colore del riferimento, non solo per facilitare

il compito dell’utilizzatore ma far si che non venisse scelta una sfumatura che poteva

condurre all’errore. Grazie a questo accorgimento l’algoritmo è in grado di identificare la

zona dell’immagine dove risiede la sfumatura. Questo permette all’utente di scegliere quella

che più si adatta ai contorni del riferimento inquadrato nell’immagine, evitando le sfumature

simili ma che introducono gli errori elencati nel capitolo precedente. In pratica l’utente può

scegliere la sfumatura che meglio si adatta ai contorni del riferimento, provando a selezionare

le varie sfumature presentate grazie ai piccoli rettangoli colorati.

5.2 Cattura del movimento Si apre con questo paragrafo la discussione dell’algoritmo monolitico descritto in

precedenza. Quest’ultimo è costituito dalle tre procedure logiche analizzate nel capitolo

precedente che permettono la cattura del movimento, la definizione del campo visivo e

l’indicazione dello sguardo. La prima, discussa in questo contesto, è la più importante in

quanto costituisce la base sulla quale le altre due verranno costituite. Tuttavia essendo

estremamente correlata, in quanto parte dell’algoritmo monolitico, con le altre, alcune sue

parti non potranno essere analizzate in questo paragrafo ma si dovrà attendere il momento

propizio per farlo. Solo alla conclusione del capitolo si avrà sotto controllo il disegno generale

del sistema.

Prima di poter analizzare a fondo questa procedura è essenziale introdurre un’ulteriore

caratteristica che contraddistingue gli algoritmi. Un aspetto importante che è stato tralasciato

nella discussione fin qui svolta è il tempo che impiega la procedura per elaborare i dati iniziali

al fine di ottenere dei risultati. Per esempio, l’algoritmo che è stato appena discusso

impiegava un breve intervallo di tempo per portare a termine la sua elaborazione. Tuttavia

non sempre si ha a che fare con procedure di questo tipo. L’algoritmo che viene presentato in

70

questo paragrafo è di natura completamente differente. Non è stato pensato per offrire un

risultato singolo alla fine dell’elaborazione ma, il suo compito è fornire al sistema

informazioni continue sul mondo reale che lo circonda. Quindi non è possibile a priori

stabilire quanto tempo occorra alla sua elaborazione.

Questo porta ad una diversa struttura in grado di sorreggerlo. La funzione principale

affidata al Supervisore è la cattura e l’elaborazione in tempo utile di tutti i messaggi che

arrivano all’applicazione sia dal Sistema Operativo sia dall’utente stesso. Per esempio, la

scelta di una voce dal menu oppure il ridimensionamento della finestra che include

l’applicazione sono operazioni che non devono essere rallentate oppure posticipate ma, hanno

estrema urgenza di essere eseguite. Quindi, mentre il Supervisore poteva permettersi di

visionare direttamente gli algoritmi che promettevano tempi di elaborazione estremamente

compatti non può assolutamente addossarsi l’onere di controllare un tale algoritmo, dove

l’elaborazione si protrae per un tempo indeterminato.

Per questo motivo si utilizzano le potenzialità offerte dalla programmazione concorrente.

Si lascia libero il Supervisore di rispondere non appena arriva una richiesta, mentre il lavoro

attuato dall’algoritmo viene visionato da un thread che utilizza l’elaboratore in modo

concorrente al sistema principale. In questo modo al Supervisore restano poche e veloci

operazioni da compiere in quanto deve solo attivare, terminare il thread ed utilizzare le

informazioni che regolarmente quest’ultimo gli fornisce. Per analizzare in maniera adeguata

questa procedura, quindi, si devono abbandonare le vecchie convinzioni sugli algoritmi che

avevano un inizio ed una fine ma pensare che le operazioni fatte rappresentano un continuo.

Entra in gioco ora l’analisi svolta nel capitolo precedente sulle caratteristiche che doveva

possedere la procedura. Si era detto che non era possibile indicare ad ogni cattura la posizione

del riferimento, in quanto esistevano errori che potevano vanificare l’elaborazione. Era quindi

necessaria una specie di filtro che scartasse i dati incoerenti e producesse informazioni utili al

sistema, prive o almeno con un’attenuazione sugli errori. Questo viene implementato

introducendo il concetto di sequenza e sfruttando la natura del thread. Ogni cattura ed

elaborazione di un’immagine è inserita in una sequenza, che rappresenta le operazioni svolte

dalla procedura ogni volta che il thread ottiene dal Sistema Operativo l’accesso alla CPU.

Tuttavia in questa fase l’algoritmo non informa il sistema sulla posizione del riferimento ma

si limita a conservare le informazioni relative ai contorni dello stesso senza produrre nessun

risultato. L’informazione, che rappresenta lo scopo della procedura, viene costituita quando si

ottengono un certo numero di catture dunque quando si sono effettuati svariate sequenze. A

71

questo punto l’algoritmo attua il filtro e scarta i dati che ritiene inconsistenti mediando gli altri

per ottenere un risultato apprezzabile.

5.2.1 Definizione parametri funzionali

Sebbene la procedura qui descritta sia completamente differente da un punto di vista

concettuale rispetto a quella descritta in precedenza, entrambe utilizzano una prima fase dove

è possibile indicare ai vari oggetti che incapsulano l’algoritmo i parametri di funzionamento

utili all’elaborazione che si apprestano a compiere. È in questa fase che si istruisce l’algoritmo

su quale sfumatura deve essere intercettata, utilizzando i risultati ottenuti dalla procedura

precedente. Per far questo si utilizza una struttura atta a contenere tutte queste informazioni:

typedef struct LIMITI_COLORE{

bool ColoreAttivo;bool ColoreTrovato;UINT TipoColore;RECT Zona;RECT Dominio;RECT Focus;RECT Lento;RECT FocusEye;RECT ZonaEye;short RossoMin,RossoMax,VerdeMin,VerdeMax,BluMin,BluMax;

};

Per esempio, nel campo TipoColore viene inserita l’informazione ottenuta grazie alla scelta

dell’utente, nel campo Zona risiede l’indicazione dei contorni della sfumatura e nei campi

RossoMin, RossoMax ecc… ci sono i limiti espressi nei colori primari che identificano la

sfumatura. Gli altri campi sono utilizzati per l’elaborazione e verranno introdotti e analizzati

successivamente.

Questa fase si conclude con la definizione dei parametri che permettono al thread un

corretto funzionamento. Per ottenere questo risultato si utilizza una zona di memoria

condivisa dove il thread è in grado di reperire le informazioni che il sistema gli comunica. In

questo caso si utilizza una struttura composta in questo modo:

typedef struct{

UINT Tipo;HWND hwnd;Supervisore* Angelo;Intercetta* Mago;Decisore* Oz;imgdes* Immagine;imgdes* ImmagineEye;RETTANGOLI* Rettangoli;UINT Stato;

72

bool Ucciso;} PARAMS, *PPARAMS;

I due campi più importanti sono Ucciso, nel quale il Supervisore comunica al thread che è

conclusa la sua elaborazione e Angelo, che indica al thread la possibilità di comunicare con il

Supervisore. È importante notare come all’oggetto thread si offra la possibilità di utilizzare

direttamente gli altri oggetti (Mago e Oz) aggirando l’obbligo di comunicare solamente con

l’Angelo. Questo è da attribuirsi all’analisi fatta nei capitoli precedenti in cui era emerso che

il sistema, in alcuni casi, prediligeva la velocità di elaborazione ad una struttura rigida e

strettamente regolamentata.

5.2.2 Elaborazione svolta in una sequenza

Prima di poter analizzare in maniera capillare il codice che permette l’implementazione

dell’algoritmo è importante discutere la relazione che lega il thread al sistema. Entra in gioco

a questo punto il discorso fatto all’inizio del capitolo. Il thread fa parte dell’implementazione

globale dell’algoritmo monolitico e per questa ragione non deve essere solamente ricondotto

all’implementazione dell’algoritmo logico descritto in questo paragrafo. Questo significa che

tutte e tre le procedure logiche che costituiscono l’algoritmo globale utilizzano il thread per i

loro scopi. Infatti questa caratteristica è visibile nella struttura stessa del codice sorgente che

implementa il thread. Sia la natura unitaria sia l’elemento logico sono evidenti nella sua

struttura. Il thread è composto da un grande ciclo che rispecchia la natura dell’algoritmo

monolitico. Mentre l’impronta logica è data da cicli minori che permettono al thread di

svolgere svariati compiti utili agli algoritmi.

La natura monolitica è esaltata anche dal fatto che il Supervisore attiva il thread all’inizio

di questa procedura e lo disattiva solo quando l’utente decide di fermare l’algoritmo logico

che identifica lo sguardo. Questo significa che i tre algoritmi logici descritti in questo e nei

prossimi due paragrafi sono visti dal Supervisore come un’unica entità.

Il compito dei cicli minori è implementare una specie di clock per le funzioni che

descrivono i tre algoritmi. Il thread dà il ritmo e permette di creare la sensazione che gli

algoritmi elaborino in modo continuo le immagini del mondo reale. È il cuore pulsante ed il

motore per la vita delle procedure.

Ogni ciclo minore si compone di quattro passaggi fondamentali. Per prima cosa interroga

gli oggetti che incapsulano le funzioni che caratterizzano l’algoritmo, dopodiché analizza i

risultati e avvisa il sistema se ci sono dei cambiamenti o errori. Prima di rilasciare la CPU,

visualizza i dati ottenuti nella finestra dell’applicazione.

Il ciclo minore che permette l’elaborazione discussa in questo paragrafo è il seguente:

73

while ((!pparams->Ucciso) && (pparams->Stato == STATO_ZERO)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,

pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);

}

Il primo passo, fatto dal ciclo minore, è attivare le funzione che permette l’elaborazione

relativa all’algoritmo descritto in questo paragrafo. Dopodiché il thread controlla se

l’algoritmo non presenta errori come, per esempio, la perdita dell’aggancio sulla sfumatura

del riferimento. Infine, prima di “addormentarsi” e rilasciare la CPU avverte l’applicazione

che dei nuovi dati sono pronti per essere visualizzati.

Ad ogni battito del ciclo minore il thread richiama la procedura:

UINT Intercetta::GetPosizioneI(){// Carica una nuova immagine

Camera->GetFoto();Camera->GetFile();loadbmp(NomeFile,&Immagine);if(Colore.ColoreAttivo){

74

// Setto la zona per accogliere le hitColore.Zona.left = 320;Colore.Zona.right = 0;Colore.Zona.bottom = 240;Colore.Zona.top = 0;Hit[Indice].Bersagli = 0;

// Setto il Dominio e il Focus per questa chiamataif (Colore.ColoreTrovato)

Colore.ColoreTrovato = TrovaColoreFocus();else

Colore.ColoreTrovato = TrovaColoreDominio();// Metto la zona trovata nel vettore delle catture

CopyRect(&Hit[Indice].Zona,&Colore.Zona);// Dopo NUMERO_CATTURE dò dei risultati più stabili

if(Indice >= (NUMERO_AQUISIZIONI - 1))return SetLento();

elseIndice++;

}return StatoCattura;

}

Il compito riservato a questa funzione è quello di richiedere direttamente all’oggetto che si

occupa della camera di catturare una nuova immagine per l’elaborazione, anche in questo

caso gli oggetti comunicano direttamente fra di loro senza passare dall’Angelo, questo per

ottenere un’elaborazione estremamente più veloce.

Quando si attiva il thread per la prima volta e si inizia l’elaborazione di questa procedura

grazie al ciclo minore, la funzione GetPosizioneI() non lavora ancora a pieno regime. Infatti

per operare al massimo delle sue possibilità ha bisogno di dati che solo il prossimo algoritmo

può fornirgli. Questo implica che per ottenere una spiegazione esauriente si deve attendere il

paragrafo successivo. Nel contesto in cui ci si ritrova è sufficiente indicare che il compito

affidato all’algoritmo viene comunque portato a termine. Grazie al campo della struttura che

identifica la sfumatura, l’algoritmo tiene traccia della posizione del riferimento. Il campo

Zona indica i contorni della sfumatura in ogni immagine catturata. Per ottenere questo

risultato non si deve fare altro che ricercare nel vettore che identifica i pixel i colori che

rientrano nei parametri che delineano la sfumatura.

5.2.3 Filtro

L’implementazione svolta dell’algoritmo permette, a questo punto, di attuare in maniera

abbastanza semplice l’operazione di filtro sui dati ottenuti dall’elaborazione. Ogni volta che la

funzione GetPosizioneI() conclude la sua sequenza di operazioni e quindi intercetta i contorni

della sfumatura, quest’ultimi vengono salvati in un vettore nel quale ogni elemento è una

struttura del tipo:

typedef struct HIT{

75

int Bersagli;RECT Zona;RECT ZonaEye;

};

che conservano sia la zona indicata nel sistema di riferimento solidale con l’immagine sia il

numero di pixel che appartengono alla sfumatura.

Quando sono avvenute tante sequenze quanti sono gli elementi del vettore si attiva la

funzione che permette all’algoritmo di produrre dei dati più stabili da poter offrire al sistema

per le elaborazioni successive. Avviene quello che è stato chiamato filtro:

UINT Intercetta::SetLento(){short i,Miss,Less;int Lunghezza,Larghezza;short Cont = 0;RECT Sum;

Miss = Less = 0;Indice = 0;Sum.left = Sum.right = Sum.top = Sum.bottom = 0;Larghezza = Colore.Lento.right - Colore.Lento.left;Lunghezza = Colore.Lento.top - Colore.Lento.bottom;for(i=0; i<NUMERO_AQUISIZIONI ; i++){

if(((Hit[i].Zona.right - Hit[i].Zona.left) <= (Larghezza + 10))&&

((Hit[i].Zona.top - Hit[i].Zona.bottom) <= (Lunghezza + 10))&&(Hit[i].Zona.left < Hit[i].Zona.right))

{Sum.left = Sum.left + Hit[i].Zona.left;Sum.right = Sum.right + Hit[i].Zona.right;Sum.bottom = Sum.bottom + Hit[i].Zona.bottom;Sum.top = Sum.top + Hit[i].Zona.top;Cont++;

}if((Hit[i].Bersagli > 0) && (Hit[i].Bersagli < MASSIMO_LESS))

Less++;if(Hit[i].Bersagli == 0)

Miss++;}if(Cont > 0){

Colore.Lento.left = (Sum.left / Cont);Colore.Lento.right = (Sum.right / Cont);Colore.Lento.bottom = (Sum.bottom / Cont);Colore.Lento.top = (Sum.top / Cont);CopyRect(&Rettangoli.Lento,&Colore.Lento);

}if(Miss == NUMERO_AQUISIZIONI)

StatoCattura = STATO_CATTURA_ROSSO;if(((Miss > 2) && (Miss < NUMERO_AQUISIZIONI)) || (Less > 5))

StatoCattura = STATO_CATTURA_GIALLO;if((Miss <= 2) && (Less < 5))

StatoCattura = STATO_CATTURA_VERDE;return StatoCattura;

}

76

Il campo che contiene l’informazione filtrata è Lento. Per ottenere questo risultato si elabora il

vettore nel quale risiedono i contorni del riferimento acquisiti nelle varie sequenze proposte

dal ciclo minore. L’analisi compiuta nel capitolo precedente riguardo al filtro in questa

funzione trova la sua corretta implementazione. Prima di tutto si scartano i dati che

evidenziano dei contorni che si discostano troppo dalle dimensioni normali del riferimento.

Questo potrebbe essere dovuto al fatto che l’algoritmo ha intercettato un pixel che appartiene

alla sfumatura ma che è il risultato di un errore sulle ottiche della camera e che non ha niente

a che vedere con il riferimento. I dati che passano questo controllo vengono mediati per

ottenere un’indicazione più stabile sulla posizione del riferimento. Questo serve per

compensare gli effetti che può dare la luce riflessa sul riferimento. In quanto, se si utilizza

come base una superficie curvilinea per il colore la luce può cambiare repentinamente il

colore e ingannare l’algoritmo. Tuttavia i piccoli rettangoli definiti da Zona sono tutti molto

vicini fra loro sull’immagine e la loro media permette di intercettare in modo corretto il

riferimento.

Un’altro incarico importante implementato da questa funzione è quello di dare una sorta di

controllo sulla cattura ed elaborazione dell’immagine. Se il numero di pixel attinenti alla

sfumatura diminuisce in maniera notevole l’algoritmo deve informare il sistema che non è più

in grado di fornire dati corretti. Quest’ultimo indica all’utente che probabilmente la sfumatura

scelta non è valida e si dovrebbero ripetere le operazioni offerte dalla procedura descritta nel

paragrafo precedente.

In conclusione il sistema è ora in grado di avere sempre sotto controllo la posizione del

riferimento nel sistema di riferimento solidale con l’immagine. Ovviamente l’elaborazione per

ricavare dati attendibili non può essere in tempo reale, tuttavia il ritardo che apporta

l’algoritmo è accettabile e verrà ampliante discusso nel capitolo successivo.

5.3 Definizione campo visivo Questa procedura è sicuramente quella più dinamica tra le tre che costituiscono l’algoritmo

monolitico. Il suo compito è di ottenere la dimensione del campo visivo dell’utente. Questa

zona fissa nel sistema di riferimento solidale con l’immagine indica l’insieme di coordinate

dove il riferimento ha libertà di movimento. In pratica si indicano le posizioni del volto che

hanno un’attinenza con i punti del terminale video. Se il riferimento esce dai contorni definiti

dal campo visivo significa che l’utente non ha il viso rivolto verso lo schermo.

La procedura deve chiedere all’utente di fissare i lati dello schermo e definire in questo

modo, appoggiandosi all’algoritmo descritto nel paragrafo precedente, il campo visivo.

77

Per ottenere questo risultato l’algoritmo utilizza l’elaborazione di un’ulteriore thread che

lavorando in simbiosi con il precedente ricava i risultati voluti. La comunicazione tra i due

thread è demandata all’Angelo.

L’algoritmo si evolve per stati, ogni stato rappresenta una posizione sullo schermo che

l’utente deve fissare con il viso. Per rendere la fase di cablaggio il più veloce e semplice

possibile per l’utente, si sono ridotti al minimo i punti da fissare. Quest’ultimi sono disposti

sui lati dell’applicazione e sono rappresentati da un pallino colorato, quando la finestra

dell’applicazione è distesa su tutto lo schermo i punti combaciano con i lati del terminale

video e questo permette di ricavare il campo visivo. In ogni stato l’algoritmo deve essere in

grado di catturare un certo numero di immagini al fine di ottenere un’informazione adeguata e

filtrata dagli errori che si sono già discussi.

Il thread introdotto in questo paragrafo ha la funzione di scandire i vari stati e con essi le

posizioni che l’utente deve fissare. Quando il thread principale ha ottenuto un risultato

accettabile riguardo ad un punto fissato avvisa il Supervisore che è pronto a passare ad un

altro stato. Quest’ultimo fa in modo che il thread che scandisce gli stati passi al successivo e

l’elaborazione si ripete.

Un altro motivo per cui si deve attendere la cattura di più immagini per ogni stato è il

periodo di assestamento che il viso dell’utente ha nello spostarsi da un punto dello schermo

all’altro. Questo comporta che alcune immagini debbano essere scartate in quanto indicano

zone intermedie tra i due punti che identificano stati differenti.

L’analisi dettagliata non avviene per oggetti, ma si studierà l’intero procedimento che

definisce un singolo stato. Si inizierà con l’analizzare il thread che scandisce gli stati, le

richieste che questo fa all’Angelo, l’elaborazione fatta dal thread principale ed infine i

messaggi che permettono di passare allo stato successivo. Ovviamente l’elaborazione

completa dell’algoritmo è la somma dell’elaborazione avvenuta nei vari stati.

5.3.1 Introduzione di uno stato

L’algoritmo prende vita nella procedura che implementa il thread che scandisce i vari stati.

Quest’ultimo non ha altro compito che avvertire il sistema e gli oggetti che prendono parte

all’elaborazione dello stato in cui ci si ritrova:

void ThreadPosiziona(PVOID pvoid){UINT TemporaneoStato;PPARAMS_POSIZIONA pparams = (PPARAMS_POSIZIONA) pvoid;

Sleep(1000);while(!pparams->Ucciso){

78

InvalidateRect(pparams->hwnd,NULL,true);if(pparams->Stato <= STATO_NOVE){

TemporaneoStato = pparams->Stato;pparams->Angelo->HandShake(AN_ASK_INIZIO_STATO,

AN_THREAD_POSIZIONA,pparams->Stato);

while((!pparams->Ucciso) &&(pparams->Stato == TemporaneoStato))Sleep(200);

}else{

Sleep(2000);pparams->Angelo->HandShake(AN_ASK_FINE_SETTAGGIO,

AN_THREAD_POSIZIONA,0);pparams->Ucciso = true;

}}pparams->Stato = STATO_ZERO;_endthread();

}

Finché l’Angelo non lo avverte che lo stato si è concluso il thread, dopo aver inviato un

messaggio all’Angelo, si pone in attesa. La sottofinestra correlata col thread utilizza

l’informazione sullo stato raggiunto per indicare, tramite un pallino colorato, la posizione

sullo schermo su cui l’utente deve rivolgere il viso. La comunicazione tra l’Angelo e il thread

avviene grazie ad una struttura non dissimile da quella usata dal thread principale, nel quale il

parametro più importante è il campo Ucciso che permette al Supervisore di terminare il thread

e con esso questo algoritmo.

79

5.3.2 HandShake

Il messaggio che il thread invia all’Angelo fa partire l’elaborazione vera e propria che

contraddistingue uno stato. Quest’ultimo viene elaborato dal Supervisore grazie al codice:

case AN_ASK_INIZIO_STATO:SendMessage(Finestre>GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

WM_STATO_MUTATO,0,(long) Parametro);

break;

Il quale trasferisce al thread principale l’informazione sullo stato raggiunto. In questo

modo si rispettano in maniera completa le regole che sono state definite nei capitoli

precedenti. È il supervisore a gestire i rapporti fra gli oggetti. In quanto in questo caso non è

importante la velocità di esecuzione e allo stesso tempo l’elaborazione svolta dall’Angelo non

compromette il suo compito principale nel rispondere ai messaggi che provengono

dall’esterno.

5.3.3 Elaborazione del singolo stato

Torna qui di attualità l’analisi svolta nel paragrafo precedente sui cicli ed i cicli minori

contenuti nel codice sorgente del thread principale. Per asservire i compiti specifici di questa

procedura si utilizza un altro ciclo minore che imprime all’algoritmo la sensazione di

continuità e scandisce il ritmo delle catture delle immagini che vengono utilizzate per ricavare

l’informazione voluta.

if((pparams->Stato >= STATO_UNO) && (pparams->Stato <= STATO_NOVE)){

pparams->Mago->UnLock(pparams->Stato);TemporaneoStato = pparams->Stato;StatoProgresso = STATO_PROGRESSO_INIZIO;

// Ciclo Minorewhile ((!pparams->Ucciso) && (pparams->Stato == TemporaneoStato)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;pparams->Angelo>Messaggio(AN_ASK_STATO_CATTURA_MUTATO,

pparams->Tipo,StatoCattura);}TemporaneoStatoProgresso = pparams->Mago->GetProgresso();if(StatoProgresso != TemporaneoStatoProgresso){

StatoProgresso = TemporaneoStatoProgresso;if(StatoProgresso == STATO_PROGRESSO_FINE){

pparams->Angelo->HandShake(AN_ASK_FINE_STATO,AN_THREAD_INTERCETTA,pparams->Stato);

pparams->Mago->Lock();StatoProgresso = STATO_PROGRESSO_INIZIO;

80

}pparams->Angelo->Messaggio(AN_ASK_STATO_PROGRESSO_MUTATO,

pparams->Tipo,StatoProgresso);}Sleep(SLEEP_THREAD_FRONTALE);

}}

Anche in questo caso si possono trovare le fasi che contraddistinguono ogni ciclo minore. Per

prima cosa vengono interrogati gli oggetti che permettono la cattura e l’elaborazioni delle

immagini. Dopodiché si passa a controllare se ci sono errori oppure se lo stato è arrivato alla

sua conclusione, infine, prima di rilasciare la CPU, si avverte il Supervisore se è necessario

procedere all’elaborazione dello stadio successivo o si è giunti alla conclusione della

procedura.

La fase che permette l’interrogazione degli oggetti che contengono il sorgente atto

all’elaborazione svolta da questo algoritmo potrebbe risultare, a prima vista, molto simile a

quella discussa nel paragrafo precedente. Tuttavia in essa si nasconde una rilevante

differenza. Per comprendere appieno l’elaborazione svolta in questo contesto, si deve

introdurre il campo Dominio, contenuto nella struttura che identifica la sfumatura, che non è

stato ancora trattato. Nel Dominio risiede il rettangolo che descrive il campo visivo, nel

sistema di riferimento solidale con l’immagine. Una proprietà estremamente importante che

accompagna il Dominio è la possibilità di modificarlo solo quando il sistema permette questa

operazione, per il resto il Dominio rimane fisso ed è impossibile, per le procedure,

ridimensionarlo.

Nel ciclo minore preso qui in esame si può notare come, prima di iniziare la cattura e

l’elaborazione delle immagini, si dà la possibilità di modificare i contorni del Dominio grazie

alla funzione UnLock(). A questo punto la funzione GetPosizioneI() non si limita

all’elaborazione discussa nel paragrafo precedente ma modifica anche il Dominio e decide

quando l’analisi dello stato è conclusa bloccando il ridimensionamento del Dominio grazie

alla funzione Lock().

L’analisi dettagliata di tutte le funzioni che permettono le operazioni descritte

precedentemente e contenute negli oggetti che fanno da supporto per l’algoritmo non vengono

qui descritte. Questo perché la loro discussione potrebbe appesantire in modo estremo il

discorso fin qui fatto. Per un’analisi completa del sorgente si rimanda alle appendici.

L’unica caratteristica che merita una discussione approfondita e che completa il discorso

introdotto nel paragrafo precedente è il procedimento utilizzato da GetPosizioneI() per

elaborare le immagini. Si era detto che nell’algoritmo precedente la funzione non lavorava in

maniera completa ma solo parzialmente. Ora grazie al Dominio la funzione inizia a operare al

81

massimo regime. È di vitale importanza capire che ogni sequenza, che elabora un’immagine

catturata, non è isolata. Viene influenzata dalla precedente e influenza a sua volta la

successiva. Per ottimizzare la ricerca della sfumatura non si analizza tutta l’immagine ma solo

parte di essa. Grazie al campo Focus si riduce l’elaborazione ad un sottoinsieme di pixel. Nel

funzionamento normale, ogni volta che si richiama GetPosizioneI(), si ricerca la sfumatura

nella zona delimitata da Focus, tuttavia se, per esempio, l’utente cambia repentinamente la

posizione del volto c’è la possibilità di perdere l’aggancio con il riferimento. Se si avvera

questa possibilità la funzione non ricerca il riferimento su Focus ma sul Dominio ed in questo

modo si è sicuri che, se l’utente rivolge il viso allo schermo, sicuramente il riferimento si

troverà nella zona delineata dal Dominio. Quindi se una sequenza perde l’aggancio del

riferimento, avvisa la successiva che si deve ricercare il riferimento nel Dominio. Questo

comunque ottimizza ancora la ricerca in quanto il Dominio rappresenta solo una parte

dell’immagine completa.

5.3.4 Conclusione

Quando l’elaborazione di uno stato si conclude il ciclo minore avverte l’Angelo.

Quest’ultimo non deve fare altro che informare il thread che scandisce gli stati. Due sono le

possibilità che si incontrano a questo punto. La prima indica la conclusione di uno stato

intermedio, mentre l’altra indica la conclusione dell’ultimo stato che determina la fine

dell’algoritmo preso in esame in questo paragrafo. Se a concludere è uno stato intermedio la

procedura inizia un’elaborazione simile a quella descritta, con l’unica differenza che nella

finestra di applicazione il pallino colorato ha cambiato posizione. Se, invece, si è concluso

l’ultimo stato, l’Angelo termina il thread che scandisce gli stati e riporta il sistema nella

configurazione che aveva prima che l’utente attivasse questo algoritmo. L’unica differenza è

che a questo punto il Supervisore ha in mano un Dominio valido per le elaborazioni

successive.

5.3.5 Cablaggio manuale

Per facilitare il più possibile il compito dell’utente il sistema propone anche un metodo

alternativo per affrontare la fase di cablaggio. Invece di seguire passo a passo le istruzioni che

compaiono nella finestra e seguire il pallino colorato nelle varie posizioni sullo schermo, c’è

la possibilità di delineare in maniera manuale il campo visivo. Questo metodo, comunque, è

da preferire solamente se l’utente ha già utilizzato il sistema ed è in grado di prevenire le

richieste dell’applicazione.

82

Questa soluzione è da ricondurre all’analisi svolta nei capitoli precedenti, in quanto

permette di cablare il sistema in maniera più veloce e intuitiva, aiutando l’utente in un

compito che deve affrontare obbligatoriamente ogni volta che utilizza l’ausilio.

Anche in questo caso c’è un ciclo minore che dà vita alla procedura, nel thread principale:

if(pparams->Stato == STATO_MANUALE){

pparams->Mago->UnLock(pparams->Stato);while ((!pparams->Ucciso) && (pparams->Stato == STATO_MANUALE)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,

pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);

}}

Come si può notare, prima di iniziare il ciclo minore si dà la possibilità di modificare i

contorni del Dominio grazie alla già discussa funzione UnLock(). Dopodiché, anche in questo

caso, si evidenziano le quattro fasi che costituiscono il ciclo. Si richiama la funzione che

permette la cattura e l’elaborazione dell’immagine, si controlla che i dati ottenuti siano privi

d’errore ed infine, prima di rilasciare il controllo della CPU si informa il sistema dei risultati

conseguiti.

Una volta attivata la procedura manuale grazie al menu, l’utente non deve fare altro che

fissare per un breve periodo di tempo i quattro angoli dello schermo e controllare nelle

finestre dell’applicazione se effettivamente il rettangolo che delimita il Dominio modifica le

sue dimensioni.

Per concludere è necessario notare che questa procedura non termina automaticamente

come la precedente, quindi deve essere l’utente che ne decreta la conclusione utilizzando i

comandi che reperisce nel menu. A questo punto è il Supervisore che impedisce la possibilità

di modificare il Dominio con la funzione Lock().

5.4 Indicazione dello sguardo Questa procedura conclude sia l’analisi degli algoritmi descritti teoricamente nel capitolo

precedente sia lo studio dell’algoritmo che è stato chiamato monolitico. Tra quelle presentate

fino ad ora risulta essere la più semplice, in quanto si trova ad elaborare dati che

rappresentano sostanzialmente un modello matematico e geometrico della realtà che circonda

83

l’applicazione. Il suo compito è di indicare al sistema dove sia rivolto il viso dell’utente. Per

ottenere questo risultato relaziona la posizione del riferimento con il campo visivo ottenuto in

precedenza.

Considerando l’analisi che è stata svolta nel capitolo relativo all’architettura e agli

algoritmi è possibile discutere senza esitazioni l’implementazione pratica della procedura.

Come le precedenti, anche in questo caso l’algoritmo si basa su un ciclo minore contenuto nel

thread principale per elaborare le operazioni che fanno parte di una sequenza. Inoltre è in

grado di avvertire il sistema se l’utente fissa un punto sullo schermo per un certo periodo di

tempo al fine di simulare il clic del mouse.

5.4.1 Elaborazioni svolte in una sequenza

Anche in quest’ultimo caso la sensazione di continuità nell’elaborazione è data da un ciclo

minore contenuto nel thread principale:

while ((!pparams->Ucciso) && (pparams->Stato == STATO_LAVORO)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;

84

pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,pparams->Tipo,StatoCattura);

}switch (pparams->Oz->GetDecisione()){case AN_RET_NO_DECISIONE:

break;case AN_RET_SI_DECISIONE:

pparams->Angelo->Messaggio(AN_ASK_DECISIONE_AVVENUTA);break;

case AN_RET_CLICK_TEMPORALE:pparams->Angelo->Messaggio(AN_ASK_CLICK_TEMPORALE_AVVENUTA);break;

}Sleep(SLEEP_THREAD_FRONTALE);

}

L’unica fase del ciclo che si discosta dalle precedenti e merita un’analisi dettagliata è la parte

in cui si avvisa l’Angelo se avviene un evento degno di nota. L’elaborazione svolta in

GetDecisione() si basa sul campo Lento descritto in precedenza, quindi un risultato accettabile

non è pronto ad ogni sequenza ma si deve attendere che la procedura che intercetta il

movimento deliberi le informazioni contenute in quel campo. In questo modo solo dopo un

certo numero di sequenze viene inviato un messaggio al Supervisore.

La procedura GetDecisione():

UINT Decisore::GetDecisione(){

Contatore++;if(Contatore >= (NUMERO_AQUISIZIONI - 1)){

Contatore = 0;// Trovo il punto mediano di Lento:

X = (float)((Rettangoli->Lento.right + Rettangoli->Lento.left)/ 2);

Y = (float)((Rettangoli->Lento.top + Rettangoli->Lento.bottom)/ 2);// Controllo se il Punto mediano di Lento è nelle "vicinanze" del Dominio:

if((X > (Rettangoli->Dominio.left - BORDO_DOMINIO)) &&(X < Rettangoli->Dominio.left))

X = (float)Rettangoli->Dominio.left;if((X < (Rettangoli->Dominio.right + BORDO_DOMINIO)) &&

(X > Rettangoli->Dominio.right))X = (float)Rettangoli->Dominio.right;

if((Y > (Rettangoli->Dominio.bottom - BORDO_DOMINIO)) &&(Y < Rettangoli->Dominio.bottom))

Y = (float)Rettangoli->Dominio.bottom;if((Y < (Rettangoli->Dominio.top + BORDO_DOMINIO)) &&

(Y > Rettangoli->Dominio.top))Y = (float)Rettangoli->Dominio.top;

// Se il Punto mediano di Lento è contenuto nel Dominio:if((X >= Rettangoli->Dominio.left) &&

(X <= Rettangoli->Dominio.right) &&(Y >= Rettangoli->Dominio.bottom) &&(Y <= Rettangoli->Dominio.top))

{// Lo setto nel SdR del Dominio:

X = X - Rettangoli->Dominio.left;

85

Y = Y - Rettangoli->Dominio.bottom;// Lo traslo sulla Finestra Applicazione e lo metto nell'area publica:

Punto.X = (1 - (X / NumeroDiColonne));Punto.Y = (1 - (Y / NumeroDiLinee));

// Controllo se il punto è attinente al Click:if((X >= Click.Regione.left) &&

(X <= Click.Regione.right) &&(Y >= Click.Regione.bottom) &&(Y <= Click.Regione.top))

{Click.Attinenze++;if(Click.Attinenze > NUMERO_AQUISIZIONI_AREA_CLICK){

Click.Attinenze = 0;SetRect(&this->Click.Regione,

(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,

(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);

return AN_RET_CLICK_TEMPORALE;}

}else{

Click.Attinenze = 0;SetRect(&this->Click.Regione,

(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,

(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);

}return AN_RET_SI_DECISIONE;

}}return AN_RET_NO_DECISIONE;

}

Questa funzione oltre che a compensare l’effetto specchio delle immagini catturate dalla

camera decide se il riferimento è rimasto nello stesso luogo per un certo periodo di tempo. Se

questo avviene l’algoritmo avvisa l’Angelo che l’utente molto probabilmente vuole simulare

il clic.

Si parla di clic temporale in quanto si utilizza il tempo come parametro di decisione per

avvisare il sistema del clic. Per ora il sistema utilizza solo questo metodo per simulare il clic

ma ci sono svariate possibilità per attuare questa opzione. Se ci fosse la possibilità, in futuro,

di connettere più camere si potrebbe simulare il clic semplicemente abbassando la palpebra e

incaricare una camera di catturare questo movimento.

Per concludere questa sezione è importante notare che le coordinate del punto fissato sono

indicate con valori che vanno da 0 a 1. In questo modo, per esempio, l’angolo in basso a

destra è indicato dalla coppia 1, 1 e il punto centrale della finestra dell’applicazione viene

risolto con 0.5 e 0.5 e così via. Questo metodo permette di separare completamente gli oggetti

che descrivono questo algoritmo dagli oggetti che identificano le applicazioni indipendenti, in

86

quanto la procedura non deve sapere la dimensione effettiva dell’applicazione ma rilascia

valori assoluti che poi, verranno convertiti in coordinate relative alla finestra.

5.4.2 Messaggi alle applicazioni

Anche se questa discussione esula dall’algoritmo vero e proprio qui descritto è importante

descrivere i messaggi che le applicazioni indipendenti ricevono. Oltre ai messaggi che invia il

Sistema Operativo le window procedure devono elaborare altri messaggi che arrivano

direttamente dal sistema.

Per prima cosa il sistema invia all’applicazione il messaggio WM_CREATE_PUNTO che

permette all’applicazione indipendente di agganciare la zona di memoria dove può reperire

con facilità i dati appena elaborati dall’algoritmo, indicanti le coordinate del punto fissato.

Con il messaggio WM_NON_VALIDO si avverte l’applicazione indipendente che

l’algoritmo ha dei dati nuovi è più recenti sul punto fissato.

Infine, per quanto riguarda i messaggi relativi all’elaborazione di questa procedura il

Supervisore invia all’applicazione il messaggio WM_CLICK che indica il clic temporale.

Per concludere il sistema invia un’ulteriore messaggio che permette all’applicazione

indipendente di indicare all’utente se la cattura e l’aggancio sul riferimento sono elaborati

correttamente oppure se il sistema non è più in grado di intercettare il riferimento. Questo

avviene con l’invio del messaggio WM_STATO_CATTURA_MUTATO.

Il Supervisore si limita ad inviare questi messaggi, è compito dell’applicazione

indipendente elaborarli in maniera corretta. Il sistema è stato concepito in questo modo per

lasciare completa libertà d’azione agli sviluppatori delle applicazioni. Infatti varia da

applicazione ad applicazione la reazione richiesta per ogni messaggio. Un’applicazione può

persino scartare e non elaborare parte dei messaggi che il sistema gli propone, tutto dipende

dalle caratteristiche e dal fine che vuole ottenere l’applicazione indipendente.

87

6 Valutazioni sperimentali

L’analisi fin qui svolta non può ritenersi conclusa se non si discutono le prestazioni che il

sistema è in grado di fornire. È importante studiare e comprendere se gli algoritmi, descritti

nei capitoli precedenti, sono validi e portano a termine gli obiettivi che l’ausilio si proponeva

di conseguire. Nell’implementazione delle procedure si è cercata la soluzione migliore ad

ogni problema ma, solo l’analisi delle prestazioni può rispondere correttamente ai dubbi sulla

validità della soluzione.

Il fattore più importante che influenza le prestazioni in modo pressoché completo sfugge

purtroppo dal controllo del sistema. Gli algoritmi che compongono la catena di acquisizione

ed elaborazione dati entrano in contatto con il mondo reale. Questo implica che le immagini

catturate siano solo una rappresentazione della realtà. Questa rappresentazione è afflitta, come

è già stato descritto, da numerosi errori che vengono in qualche modo soppressi o attenuati dal

lavoro delle procedure. Tuttavia la caratteristica che da sola decide sul funzionamento del

sistema è l’illuminazione dell’utente quando si trova davanti al terminale video. Se questa non

è sufficiente, gli errori insiti nelle immagini vengono enfatizzati e risulta difficile se non

impossibile agli algoritmi compensare per ottenere un risultato utile. L’illuminazione non è un

elemento che il sistema possa controllare, quindi, le prestazioni del sistema sono legate

88

direttamente a questo fattore. Se l’illuminazione è ottima anche le prestazioni saranno ottime,

mentre se la luce che colpisce l’utente è mediocre, mediocri saranno le prestazioni del

sistema.

Non è possibile in questo contesto seguire la catena di passi utilizzata per i precedenti due

capitoli. Mentre non ha significato analizzare le prestazioni di ogni singolo algoritmo, è di

grande utilità focalizzare invece l’attenzione sull’algoritmo monolitico precedentemente

introdotto. Quest’ultimo rappresenta l’unità elaborativa centrale di tutto il sistema ed è grazie

ad essa che l’ausilio può portare a termine il suo compito.

Prima di poter studiare in profondità le prestazioni che questo algoritmo monolitico offre,

occorre tuttavia discutere ed analizzare una caratteristica estremamente importante che

accompagna il sistema. È rilevante notare che l’elaborazione delle immagini catturate dalla

camera non avviene in modo rigido e fissato ma, il sistema permette all’utente di modificare

l’elaborazione mentre questa si sta compiendo. I parametri di funzionamento dell’algoritmo

monolitico vengono controllati direttamente dall’utente grazie ad una sottofinestra visibile

nella finestra dell’applicazione. Agendo su questi controlli, l’utilizzatore è in grado di adattare

il sistema alle condizioni ambientali che lo circondano. Quindi è possibile migliorare

l’elaborazione anche se l’illuminazione risulta carente.

Solo dopo aver analizzato questi controlli, è possibile studiare le prestazioni che il sistema

può offrire. Per fare questo si è implementata una coppia di applicazioni indipendenti, il cui

unico scopo è testare il sistema. Infatti non esiste altro modo per verificare se l’ausilio

raggiunge gli obiettivi che si erano posti all’inizio.

6.1 I controlli La maggior parte dei sistemi, inclusi i sistemi informatici, devono possedere una sorta di

controllo per portare a termine i loro compiti. Non è possibile lasciare che l’elaborazione

avvenga in modo predefinito, senza modificarsi in base alle condizioni ambientali che

circondano il sistema. Solo se il sistema si trova a lavorare in un ambiente isolato non è

irragionevole, ma possibile tralasciare la caratteristica di controllo, in quanto non esistono

azioni esterne che possano turbare l’attività del sistema. In questo caso, al contrario, l’ausilio

è estremamente vincolato all’ambiente che lo circonda ed è impensabile non attuare una

specie di controllo. Se per assurdo, il sistema non attuasse un controllo ed elaborasse in

maniera fissa i dati che ottiene dall’ambiente circostante, i risultati che si otterrebbero

potrebbero essere completamente errati e privi di significato. È per questa ragione che si è

data all’utente la possibilità di modificare i parametri che definiscono il comportamento

89

dell’elaborazione dell’immagine. Grazie a semplici ed intuitivi controlli attivabili in una

sottofinestra dell’applicazione principale è possibile agire in maniera diretta sui parametri che

regolano l’algoritmo monolitico che rappresenta la mente di tutto il sistema.

È da considerarsi inoltre l’implementazione come prototipo per l’ausilio che si vuole

produrre. Questo comporta che molti controlli non sono automatici in quanto gli algoritmi che

lo compongono non sono così complessi da attuare una sorta di retroazione al loro interno.

Nel contesto in cui si trova ad operare il fine ultimo è ottenere una soluzione che possa

concretizzare gli obiettivi eletti dall’ausilio e non un’ottimizzazione degli stessi.

Prima di analizzare i vari controlli singolarmente è utile aggiungere che il sistema è stato

testato su un Personal Computer che monta una scheda madre Intel con processore Celeron da

2.0 Ghz. con 128 Mb di Ram e si sono utilizzate due WebCam 5 della Creative per la cattura

delle immagini, connesse alle porte USB dell’elaboratore.

6.1.1 Controllo sulla velocità di esecuzione del thread principale

È importante aprire una piccola parentesi sul thread e la funzione Sleep() da lui richiamata,

prima di passare alla discussione vera e propria del controllo.

Come è già stato studiato nel capitolo precedente il thread principale utilizza dei piccoli

cicli, detti cicli minori, per rappresentare le varie procedure che costituiscono l’algoritmo

monolitico. Ogni ciclo minore ha, per ultima fase, la chiamata alla funzione Sleep(). Questa

operazione è fortemente correlata alle operazioni svolte dal Sistema Operativo per assegnare

la CPU ai vari processi. Se il ciclo minore non utilizzasse la funzione Sleep() la CPU verrebbe

sopraffatta dall’elaborazione del thread. Infatti alla conclusione della sequenza il Sistema

Operativo porrebbe nella coda dei processi attivi il thread pronto per una sequenza successiva.

Se per caso, ma la probabilità è alta, non ci fossero altri processi in attesa, il thread otterrebbe

di nuovo l’utilizzo della CPU. Questo porterebbe alla saturazione completa della CPU per

l’elaborazione del thread e si avrebbe anche un rallentamento importante nelle altre funzioni

svolte dal Sistema Operativo, come l’apertura di finestre, menu e così via.

L’utilizzo della funzione Sleep() evita tutto questo. Il thread, quando conclude il ciclo

minore, avverte il Sistema Operativo che è pronto a rilasciare la CPU. Quando il Sistema

Operativo vede che il thread ha richiamato la funzione Sleep() non lo mette più nella coda

relativa ai processi attivi in attesa della CPU finché non è trascorso il tempo indicato nella

Sleep(). Solo quando, un timer impostato sul tempo di attesa avverte il Sistema Operativo

della scadenza, il thread viene di nuovo posto nella coda dei processi attivi è può così

riottenere la CPU.

90

Il controllo non fa altro che variare l’argomento della Sleep(). Cosicché è possibile, dalla

finestra di applicazione, modificare il tempo di esecuzione dell’algoritmo monolitico. La

gamma di possibilità va da 1 a 100 millisecondi. Più il tempo di attesa si porta verso il

millisecondo più le catture delle immagini e l’elaborazione delle stesse avvengono con un

ritmo accelerato. In un Personal Computer potente come quello su cui si è testato il sistema,

attese prossime al millisecondo ingannano l’occhio umano che non è più in grado di percepire

l’attesa tra un’immagine e l’altra, mentre la CPU non viene utilizzata più del 15%. Tuttavia in

elaboratori meno evoluti potrebbe non essere possibile arrivare a certi livelli, ed è per questo

motivo che si è mantenuta una gamma abbastanza cospicua per poter agire su questo

parametro.

Per concludere è importante notare che il tempo di attesa non definisce in modo completo

il tempo utilizzato da ogni ciclo minore per l’elaborazione. Infatti è possibile scomporre il

tempo utilizzato da un ciclo in tre parti distinte. Per prima cosa c’è il tempo che il ciclo

occupa a catturare l’immagine dalla camera, dopodiché c’è l’intervallo di tempo utilizzato per

l’elaborazione e solo a questo punto sopraggiunge il tempo di attesa implementato grazie alla

Sleep(). Solo gli ultimi due possono essere modificati e sono sotto il controllo del sistema. Il

tempo di attesa è direttamente modificabile dall’utente, mentre il tempo di elaborazione può

essere migliorato implementando algoritmi più complessi ed ottimizzati. In un elaboratore

recente il tempo di elaborazione non supera le frazioni del millisecondo in quanto le

operazioni svolte in una sequenza non sono molte. Detto questo si potrebbe pensare che

limitando il tempo di attesa al millisecondo si potrebbe arrivare ad elaborare un migliaio di

sequenze al secondo. Purtroppo ciò non accade perché si è tralasciato il tempo che il ciclo

minore passa per catturare e caricare l’immagine dalla camera. Questo ritardo non dipende dal

sistema ma e imputabile alla velocità del Bus che collega la porta USB alla scheda madre ed è

maggiore di alcuni ordini di grandezza rispetto agli altri due.

Precedentemente si è scritto che se il controllo della velocità di elaborazione del thread

viene posto al millisecondo, la CPU non ha un utilizzo maggiore del 15%. Questo perché

anche il processore deve attendere che l’immagine venga trasportata dal Bus prima di poterla

elaborare. C’è il cosiddetto collo di bottiglia che interessa il trasporto dell’immagine dalla

camera al processore. Questo implica che non si possano elaborare più di una decina di

immagini al secondo anche con il tempo di attesa che sfiora il millisecondo.

6.1.2 Numero di catture per attuare il filtro

Come si è già detto all’inizio di questo capitolo il sistema è fortemente legato all’ambiente

che lo circonda. Il controllo descritto in questa sezione non fa altro che assecondare

91

l’illuminazione che colpisce il volto dell’utente. Inoltre non si deve pensare ai controlli come

entità separate ma, l’azione su uno di essi deve essere bilanciata con l’utilizzo di altri

controlli. Tutti i controlli concorrono al miglioramento delle prestazioni del sistema. Se, per

esempio, l’illuminazione è scarsa occorre elaborare più immagini per ottenere un risultato

accettabile. Questo comporta che, se la velocità del thread resta invariata, si dovrà attendere

un tempo maggiore prima di vedere i risultati dell’elaborazione e quindi il sistema risulterà

più lento. Tuttavia se a fronte di un filtro che elabora più immagini si accelera l’elaborazione

del thread è possibile ottenere le stesse prestazioni anche se il sistema è obbligato, per via

dell’illuminazione, ad elaborare più immagini.

Stessa analisi si può effettuare se le immagini catturate presentano un’illuminazione

ottima. Se il riferimento viene intercettato dal sistema con facilità non è indicato attuare un

filtro con molte immagini. Il controllo in questo caso può essere utilizzato per diminuire le

catture utilizzate dal filtro per ottenere un’elaborazione più veloce ma sempre precisa. In

questo caso, se il processore è sovraccarico, è possibile diminuire la velocità di elaborazione

del thread senza ridurre le prestazioni.

Questo controllo utilizzato in simbiosi con il precedente assicura al sistema una reazione in

base alle condizioni ambientali presenti intorno all’utente ed al suo viso. Ovviamente nulla è

possibile fare se l’illuminazione è estremamente carente. Se il riferimento non è intercettabile

il filtro scarterà tutte le immagini e anche se il controllo imporrà al filtro l’elaborazione di 30

immagini, limite superiore del range, non si otterranno risultati utilizzabili.

Per concludere è importante descrivere una caratteristica piuttosto singolare del controllo.

Il suo range va da 1 a 30 immagini catturate per l’elaborazione del filtro. Se si sceglie solo

un’immagine catturata il filtro cessa la sua funzione ed il sistema lavora come se ogni

immagine portasse ad un rilevamento. Per utilizzare le variabili descritte nel capitolo

precedente si può dire che il Lento si va a sovrapporre al campo Zona.

6.1.3 Clic temporale

Per analizzare in modo approfondito questo controllo si deve riprendere la discussione fatta

nel capitolo precedente. In quel contesto si era studiata la tecnica utilizzata dal sistema per

rilevare, o meglio, simulare il clic. Dopo un certo numero ben definito di rilevamenti, attuati

dall’algoritmo monolitico, nello stesso punto del video, si era in grado di avvertire il sistema

che era avvenuto un clic temporale. Dall’esperienza maturata con l’implementazione

dell’ausilio si è posto a circa due secondi il tempo in cui l’utente deve fissare un punto per

ottenere il clic temporale. Si è scelto questo intervallo di tempo per rendere l’utilizzo del

sistema, da parte dell’utente, il più semplice e comodo possibile. Infatti un intervallo

92

maggiore potrebbe, alla lunga, affaticare l’utilizzatore, mentre una pausa più ristretta potrebbe

ingannare il sistema e rilevare un clic anche se l’utente sta solo ricercando le informazioni sul

video e rallenta gli spostamenti del volto.

Per queste ragioni entra in gioco il controllo qui descritto. Se vengono modificati, grazie ai

controlli precedentemente descritti, la velocità delle catture e il numero di rilevamenti per

unità di tempo è molto probabile che il limite dei due secondi per rilevare il clic non sia più

rispettato. Se, per esempio, viene aumentata la velocità delle catture anche il numero di

rilevamenti per secondo aumenta proporzionatamente. Questo implica che occorreranno meno

di due secondi per far indicare dal sistema all’applicazione indipendente l’evento del clic

temporale. Ecco che con l’aiuto di questo controllo è possibile ristabilire il margine di tempo

voluto, in base alla velocità delle catture e alla velocità di elaborazione del filtro. Questo è

fatto aumentando il numero di rilevamenti che devono avere le stesse coordinate nel sistema

di riferimento solidale con l’immagine. In questo modo si dilatano i tempi per decidere se si è

di fronte ad un clic temporale, riequilibrando nel contempo il sistema sui fatidici due secondi

per clic.

6.1.4 Regione relativa al clic

Nella sezione precedente non si è tenuto conto del fatto che il filtro, purtroppo, non è una

barriera insormontabile per tutti gli errori. Questo causa degli effetti collaterali che possono

alterare il funzionamento dell’ausilio. In particolare c’è un effetto che risulta estremamente

collegato all’ambiente che circonda il sistema. Più l’illuminazione è carente più si evidenzia

questo fenomeno. Nel funzionamento normale dell’ausilio si nota che il punto che indica sullo

schermo la zona fissata dall’utente oscilla. L’oscillazione è tanto più marcata tanto più

l’illuminazione del viso dell’utente è insufficiente. Se l’illuminazione risulta ottima

l’oscillazione è quasi impercettibile. Questo implica che, per definire un clic, non si possa

attuare un’elaborazione semplice.

Come è già stato discusso nel capitolo precedente si utilizza una zona che racchiude il

punto fissato, se il rilevamento fatto dal filtro nella sequenza successiva è contenuto ancora in

questa zona è alta la probabilità che l’utente stia effettuando un clic temporale. Se un numero

di rilevamenti successivi, pari al valore definito grazie al controllo precedentemente descritto,

è incluso in questa zona allora il sistema avverte l’applicazione indipendente che è avvenuto

un clic temporale.

Il controllo qui descritto permette di modificare le dimensioni della zona che aiuta il

sistema nel capire se l’utente sta fissando un punto preciso dello schermo. In questo modo si

possono assecondare le oscillazioni dovute agli errori non trattenuti dal filtro. Questo è fatto

93

perché se le oscillazioni sono troppo marcate e la zona ha dimensioni inferiori all’ampiezza

dell’oscillazione stessa, non sarà possibile al sistema intercettare i clic temporali. Mentre è

utile ridurre le dimensioni, se l’illuminazione è buona, per ottenere una definizione maggiore

dei punti fissati nell’applicazione indipendente. Questo porta ad avere un sistema che si

avvicina nelle prestazioni ad un mouse collegato al Personal Computer.

Il sistema, a questo punto dello sviluppo, riesce a intercettare, in condizione di luce buona,

oggetti dalle dimensioni di pochi centimetri (1.5-2.0) come per esempio le icone poste nel

DeskTop.

6.1.5 Focus

Questo controllo permette al sistema un’elaborazione più veloce. Si è già discusso di come

l’algoritmo monolitico utilizzi una tecnica particolare per attuare meno elaborazioni quando si

trova davanti una nuova immagine. Invece di cercare su tutta l’immagine la sfumatura da

intercettare la si cerca in un sottoinsieme di pixel indicato dal rettangolo Focus, elaborato

dalla sequenza precedente. In questo modo si accelera la ricerca e si è relativamente sicuri che

la sfumatura si troverà in questo rettangolo se l’utente non ha eseguito movimenti repentini

con il volto.

Grazie a questo controllo è possibile modificare la dimensione del Focus. Più si restringe

quest’area più l’elaborazione è veloce, viceversa se si aumentano le dimensioni, l’algoritmo

dovrà processare più pixel e l’elaborazione ne risulterà rallentata.

Dall’esperienza fatta è utile sovradimensionare i contorni del Focus nella fase di cablaggio

del sistema. In quanto l’algoritmo monolitico non lavora ancora a pieno regime e

l’intercettazione del riferimento si basa esclusivamente sul Focus. Questo implica che, se

l’utente ha un movimento repentino, c’è la possibilità che il sistema perda l’aggancio. Con un

rettangolo del Focus sovradimensionato questa possibilità diviene del tutto remota. Conclusa

la fase di cablaggio è possibile ridurre i contorni del Focus, in quanto, ora, l’algoritmo

monolitico funziona a pieno regime e se anche l’utente attua un movimento veloce il sistema

non perde l’aggancio con il riferimento. Se l’utente continua a rivolgere il viso allo schermo

l’algoritmo può ritrovare il riferimento grazie al rettangolo definito dal Dominio che identifica

il campo visivo.

In un elaboratore potente, questo artifizio può sembrare evitabile, infatti sono

relativamente poche le operazioni che vengono eluse. Non c’è grossa differenza tra

l’elaborare un’immagine completa o solo parte di essa. Tuttavia il fine del sistema è offrire

un’elaborazione ottimizzata e il più possibile veloce, quindi ogni metodo per processare meno

operazioni è ben accetto. Inoltre c’è la possibilità che l’ausilio venga eseguito su un Personal

94

Computer meno potente ed in questo caso ogni aiuto possibile può ripercuotersi sulle

prestazioni finali.

6.2 Le applicazioni A questo punto l’analisi si può spostare sulla discussione delle due applicazioni

implementate per testare le prestazioni del sistema. Entrambe hanno solamente un carattere

dimostrativo. Oltre a costituire una base di studio sulle potenzialità del sistema sono state

utilizzate da quest’ultimo per capire quali fossero i messaggi più convenienti per una

comunicazione adeguata. Grazie alla loro implementazione è stato possibile separare i compiti

svolti sui due fronti. Da un lato si è capito cosa dovesse fare il sistema e dall’altra parte si è

compreso quali fossero i doveri dell’applicazione indipendente.

Grazie al lavoro svolto si è definito l’insieme dei messaggi che sono stati discussi nel

capitolo precedente. Come si è avuto modo di vedere le operazioni svolte dal Supervisore

sono limitate all’invio dei messaggi e tutta l’elaborazione viene demandata all’applicazione.

Questo modello nel concepire la comunicazione fra Supervisore e applicazioni indipendenti è

dovuto a numerosi motivi. Prima di tutto, se parte dell’elaborazione fosse sotto il controllo del

Supervisore, gli sviluppatori di applicazioni indipendenti dovrebbero conoscere, oltre ai

messaggi scambiati, anche le operazioni svolte dal Supervisore sulle loro applicazioni. Questo

dissolverebbe una delle regole più importanti imposte dall’architettura del sistema e cioè la

netta separazione tra sistema di elaborazione delle immagini e applicazioni indipendenti.

Inoltre le ipotetiche operazioni svolte dal Supervisore sulle applicazioni potrebbero essere

valide per un certo tipo di applicazioni, mentre potrebbero rendere impossibile

l’implementazioni di altre. Infine agendo in questo modo gli sviluppatori hanno la possibilità

di implementare codice che può reagire in qualsiasi maniera ai messaggi che il Supervisore gli

spedisce.

6.2.1 Applicazione Uno: Addestramento

Tenendo ben presente lo studio fatto nel capitolo relativo al contesto, l’utente che si

appresta ad utilizzare il sistema è dotato di capacità limitate. Questa caratteristica amplifica

ancor di più il dovere, da parte del sistema, di offrire il maggior aiuto possibile all’utente.

Un’operazione importante non ancora discussa è la fase di apprendimento. Infatti, come per

qualsiasi utensile prodotto per l’uomo, anche in questo caso ci sarà una fase nel quale l’utente

dovrà prendere confidenza con il sistema. Solo grazie all’esperienza è possibile utilizzare

l’ausilio in maniera ottimizzata.

95

Questa applicazione è stata implementata al fine di servire l’operazione appena descritta.

Si dà la possibilità all’utente di imparare a gestire i movimenti del viso per assecondare e

prevenire le reazioni del sistema. Inoltre l’impostazione appresa grazie a questa applicazione

sarà utile in tutte le applicazioni che entreranno a far parte dell’ausilio in futuro. Tutto si

svolge come nel gioco del tiro a segno, nel quale si deve cercare di colpire l’area delimitata da

un rettangolo. Al fine di imparare a selezionare gli oggetti visualizzati nelle applicazioni si

deve fissare il rettangolo per alcuni istanti per indicare al sistema che un clic temporale è stato

eseguito. Solo se il clic è attinente all’area delimitata dal rettangolo è possibile procedere

all’intercettazione di un altro obiettivo.

La caratteristica che colpisce maggiormente è che, in un sistema appena creato, solo al

momento dell’utilizzo è possibile definire il comportamento da seguire per un buon utilizzo.

Grazie all’esperienza fatta con questa applicazione si è in grado di elencare alcuni

atteggiamenti che facilitano l’apprendimento sull’uso dell’ausilio.

Per prima cosa si deve pensare che la visualizzazione del punto fissato arriva con un certo

ritardo, modificabile grazie ai controlli descritti nel paragrafo precedente. A condizioni di luce

sufficiente, un’informazione più accurata è accompagnata da un ritardo maggiore

nell’elaborazione. Questo implica che i movimenti del viso dell’utente sono rappresentati sul

terminale video del Personal Computer con un ritardo dovuto all’elaborazione. Si consiglia,

per questo motivo di fare sempre movimenti fluidi, armonici e progressivi senza scatti

improvvisi.

Se, per esempio, si deve intercettare un rettangolo in alto a destra e il viso è rivolto verso il

basso a sinistra è possibile, muovere velocemente il viso verso la posizione in alto a destra.

Tuttavia tanto più ci si avvicina alla posizione del rettangolo tanto più i movimenti del viso

devono essere fluidi e contenuti. Si deve pensare al percorso che unisce i due punti e

percorrerlo con il movimento del volto, assecondando il punto che si vede sullo schermo,

lavorando in simbiosi con il sistema. Se si passa con un movimento repentino dal punto in

basso a sinistra a quello in alto a destra è possibile che si inneschino strane oscillazioni dovute

al ritardo attuato dall’elaborazione. In pratica si cerca di posizionare il riferimento all’interno

del rettangolo tramite movimenti veloci e a scatti che pregiudicano il funzionamento. Si vedrà

il punto che indica il riferimento oscillare sui contorni esterni del rettangolo senza poterlo

intercettare in modo preciso. E importante rallentare e compiere movimenti dolci e contenuti

non appena si arriva in prossimità dell’oggetto che si vuole selezionare, in questo caso il

rettangolo colorato.

96

Solo grazie all’esperienza l’utente sarà in grado di imparare ad usare il sistema

correttamente, tuttavia il tempo richiesto per questa fase non è estremamente lungo. Nel giro

di pochi minuti si può ottenere un ottimo feeling con l’ausilio e passare alle applicazioni più

complesse senza problemi.

6.2.2 Applicazione Due: Tastiera

L’implementazione di una applicazione indipendente più complessa rispetto alla

precedente, permette di analizzare altre caratteristiche importanti che possono essere rilevate

solo con una sperimentazione diretta.

Una proprietà al quale lo sviluppatore deve sempre porre grande attenzione è la capacità,

da parte dell’applicazione, di lavorare con qualsiasi condizione ambientale. Sia con

un’illuminazione ottima sia con una mediocre l’applicazione deve sempre fornire un servizio.

Non si può accettare il fatto che l’ausilio funzioni solo con condizioni ambientali ottime.

L’applicazione deve assecondare le prestazioni del sistema. Tanto più l’illuminazione risulta

mediocre, tanto più la possibilità di intercettare oggetti di dimensione ridotta si affievolisce.

Quindi è indispensabile tenere conto di questo fatto quando si implementano nuove

applicazioni indipendenti. È fondamentale attuare una certa flessibilità sulle dimensioni degli

oggetti selezionabili, al fine di rendere l’applicazione utile con qualsiasi condizione

ambientale. Ovviamente se le condizioni di luce sono talmente scarse da inibire addirittura il

funzionamento del sistema nulla può essere richiesto all’applicazione, semplicemente l’ausilio

non è utilizzabile.

L’applicazione discussa in questa sezione implementa una semplice tastiera sulla quale

l’utente può comporre delle frasi. Grazie al compito svolto da questa applicazione è possibile

analizzare il comportamento descritto precedentemente. La rappresentazione della tastiera non

è fissa ma l’utente, o meglio l’assistente, può scegliere diverse alternative per utilizzare al

meglio le prestazioni del sistema. Lo spazio riservato all’applicazione sullo schermo del

Personal Computer deve essere ottimizzato per contenere i pulsanti. Più caratteri possono

essere utilizzati per comporre le frasi, più piccole saranno le dimensioni di ogni pulsante che

rappresenta un carattere. L’applicazione permette di definire cinque tastiere differenti, ognuna

delle quali conta un diverso numero di pulsanti e quindi di caratteri. Si và da una tastiera che

contiene solo le lettere dell’alfabeto ad una tastiera che contiene tutti i possibili caratteri

reperibili su una tastiera collegata al Personal Computer. Se le prestazioni del sistema lo

permettono è possibile utilizzare la tastiera con più caratteri, in quanto l’utente è in grado di

selezionare i pulsanti, anche se la loro dimensione è ridotta. Tuttavia se le prestazioni sono

mediocri in quanto rispecchiano condizioni ambientali mediocri, l’applicazione può essere

97

ancora utilizzata selezionando una tastiera che contiene meno caratteri ma che è composta da

pulsanti di dimensioni maggiori. Il servizio minimo viene garantito anche se il sistema si

trova ad elaborare dati non buoni.

Questa è la caratteristica che differenzia un’applicazione scritta per essere collegata

all’ausilio da una normale applicazione Windows. Lo sviluppatore di applicazioni

indipendente deve prendere in considerazione questa peculiarità se vuole ottenere applicazioni

che rispondano in maniera versatile alle prestazioni del sistema e offrano anche in condizioni

limite un servizio minimo.

98

99

7 Sviluppi futuri

Tutto sembra semplice quando si conosce la strada che conduce nel luogo dove si vuole

andare, non esiste niente di più facile che seguire un itinerario che si è già percorso o si

compie il cammino in compagnia di qualcuno, che ha ben preciso in mente le strade da

percorrere. Purtroppo il compito si complica notevolmente se si ignora il modo di arrivare alla

meta. L’unica soluzione è partire e cercare in qualche modo di arrivare a destinazione,

lasciarsi andare e compiere il balzo nel vuoto che si apre davanti ai nostri piedi. Molte saranno

le strade possibili per arrivare a destinazione, indubbiamente esisteranno strade facili da

percorrere, scorciatoie oppure difficili valichi impraticabili. Tuttavia, in un modo o nell’altro,

si potranno ottenere dei risultati.

La soluzione proposta per questo ausilio non è altro che una fra le tante possibili.

L’informatica, d’altro canto, è caratterizzata dal fatto di offrire numerose soluzione per

qualsiasi problema. Come i percorsi per arrivare a destinazione le soluzioni potranno essere

facili, complesse oppure presentare delle scorciatoie che semplificano di molto

l’implementazione del sistema.

Il salto nel buio compiuto è visibile nel codice che implementa l’ausilio. Se lo si osserva

attentamente è facile notare che il percorso intrapreso non è lineare e preciso fino alla meta,

100

ma presenta itinerari appena accennati e non ulteriormente sviluppati che si discostano dal

cammino principale e si perdono nell’oscurità. Quest’ultimi rappresentano possibili soluzioni

che non sono state approfondite quando si sono trovate, sul percorso, due strade ugualmente

percorribili. Tuttavia ciò non vuol dire che la scelta scartata sia impraticabile o inutile ma

significa solamente che si sono fatte delle valutazioni su certi obiettivi da raggiungere a

discapito di altri. Questi sentieri appena accennati non sono stati eliminati completamente dal

sorgente perché rappresentano le porte dalle quali si potrà, in futuro, sviluppare nuove

potenzialità del sistema.

Gli obiettivi delineati all’inizio dell’analisi spingevano sulla ricerca di una soluzione, per

questo motivo non si sono studiate a fondo tutte le scelte possibili ma solo quelle che

promettevano dei risultati concreti. Tuttavia, a questo punto dello sviluppo, occorre

approfondire questi sentieri singolarmente e cercare di comprendere se possono portare ad un

miglioramento delle prestazioni del sistema.

7.1 Legami esterni Mai, come in questo caso, la frase “il fine giustifica i mezzi” è appropriata. Dimostrare la

possibilità di creare un ausilio che assecondasse tutti gli obiettivi descritti nei capitoli

precedenti vantava una priorità maggiore di qualsiasi obiettivo sull’ottimizzazione. Ora che

questo fine è stato raggiunto, è compito dello sviluppo mettere in primo piano

l’ottimizzazione.

Questo significa che, gli aiuti ricevuti dall’esterno, erano giustificati fintanto che il fine era

la creazione del sistema, tuttavia a questo punto dello sviluppo si deve pensare anche

all’ottimizzazione. Per questa ragione i legami che vincolano il sistema con l’esterno devono

essere recisi. Il sistema deve svilupparsi al punto di gestire al suo interno le operazioni che,

per ora, gli vengono fornite dalle librerie della Victor e dalla RamDisk implementata dalla

Cenatec.

Questo passo è imposto da svariate ragioni, prima fra tutte la semplificazione delle fasi di

installazione del sistema sul Personal Computer dell’utente. Non è ammissibile, infatti, che

l’utente, o meglio l’assistente, debba addossarsi un elevato numero di operazioni per rendere

operativo l’ausilio. In quanto già l’installazione del software relativo al funzionamento della

camera implica un notevole lavoro.

Un’altra ragione è puramente economica. Sia le librerie della Victor sia la RamDisk della

Cenatec vengono fornite a pagamento, quindi non è possibile addossare all’utente una spesa

che si aggiunge al costo della camera. Una soluzione di ripiego potrebbe essere quella di

101

utilizzare le versioni di valutazione gratuite reperibili in rete, tuttavia quest’ultime hanno una

scadenza che impone un periodico download.

Infine, un sistema che è autosufficiente rispecchia una soluzione più elegante rispetto ad un

sistema che per lavorare ha bisogno di aiuti esterni.

Per questi motivi, prima di sviluppare qualsiasi altra sezione del codice, è importante

rimuovere gli aiuti esterni, in quanto il loro compito ha perso d’importanza ed inoltre sono

diventati d’impaccio per il sistema stesso.

7.2 Struttura Come è già stato analizzato nel capitolo relativo all’architettura è importante creare una

base solida su cui costruire. Se le fondamenta sono salde anche la struttura, che su di esse

appoggerà, sarà robusta. Se il percorso fin qui fatto rappresenta solo una prima tappa di un

cammino molto più lungo e articolato, è necessario sviluppare e rafforzare ancor di più la

struttura che si trova alla base del sistema. Ciò non significa che il lavoro svolto fino a questo

punto sia da scartare ma, come per il resto del sistema, deve evolversi e migliorarsi. È

impensabile tralasciare lo sviluppo di questa parte del sistema per dedicarsi allo sviluppo della

struttura che su di essa poggia. L’architettura implementata a questo punto dello sviluppo è

più che ottima per gli obiettivi che si sono delineati all’inizio, tuttavia rappresenta solo il

modello da seguire nello sviluppo futuro.

Molto deve essere ancora fatto per rendere reale la soluzione teorica che è stata analizzata

nei capitoli precedenti. Per ora la divisione dei compiti tra oggetti differenti non è ancora

completa. Anche se concettualmente la separazione è evidente, purtroppo l’implementazione

non rispecchia a fondo questa caratteristica. Le operazioni svolte dai diversi oggetti sono

ancora legate dalle chiamate dirette di funzione delle varie procedure, il codice non è separato

come lo sono concettualmente gli oggetti. Lo stack unisce ancora le varie operazioni fatte dai

diversi oggetti. Quando, per esempio, il thread richiama le funzionalità del Supervisore,

quest’ultime vengono elaborate basandosi sullo stack del thread come in una normale

chiamata a funzione, inibendo la separazione che si vuole ottenere tra il codice del thread e

del Supervisore. Questo porta ad una struttura implementata abbastanza complessa che deve

essere semplificata se l’obiettivo e produrre un sistema più evoluto.

Una possibile soluzione potrebbe essere quella di utilizzare pesantemente le code di

messaggi che il Sistema Operativo Windows permette di implementare quando si definisce

una finestra. In pratica la separazione tra i vari oggetti utilizza le finestre come portale per la

comunicazione. Questo fa sì che anche a livello di codice le funzionalità racchiuse negli

102

oggetti vengano separate. Tuttavia questa non è altro che una congettura, una strada possibile

da percorrere, solo l’implementazione potrà verificare se è possibile intraprendere questo

cammino. Introducendo i messaggi e le rispettive code è importante testare se le operazioni

vengono ancora svolte in sequenza oppure si è obbligati ad introdurre una sorta di IPC per

regolare la comunicazione tra i vari oggetti.

Molteplici sono le soluzioni per superare questi problemi, tuttavia essi devono essere

superati per pensare di portare avanti il lavoro ed evolvere il sistema. La struttura che regola

ora il sistema è paragonabile alle solide fondamenta di una piccola casa, è impensabile

costruire su di esse un grattacielo.

7.3 Algoritmi Gli algoritmi, come è già stato detto, sono il cuore del sistema. Se supportati da una

struttura solida e ben regolamentata risulta possibile introdurre nuove potenzialità e, nello

stesso tempo, ottimizzare quelle già esistenti.

Ogni procedura descritta nei capitoli precedenti ha potenzialmente un ampio margine di

sviluppo. A questo stadio di maturazione si è cercato di trovare una implementazione che

permettesse di raggiungere gli obiettivi prefissati. Tuttavia non appena una soluzione per una

procedura si è rivelata valida, la ricerca si è spostata verso gli algoritmi successivi,

tralasciando lo sviluppo per conseguire le mete prestabilite. Il compito di migliorare

correttamente ogni algoritmo sarà affidato allo sviluppo futuro.

Per esempio, la prima procedura descritta nel capitolo dedicato all’implementazione può e

deve essere rivista per includere nuove potenzialità. Dallo studio attuato sul sistema fino ad

ora, è emerso che è restrittivo utilizzare una sola sfumatura per intercettare il riferimento fisso

sul volto dell’utente. In quanto, diverse angolazioni del volto rispetto alla fonte luminosa

enfatizzano diverse sfumature dello stesso colore. Il sistema dovrebbe valutare questa

caratteristica. Per far questo si dovrebbe fornire un’analisi più accurata sulla palette di colori

disponibile. Invece di studiare solo un’immagine si potrebbe stilare una casistica su più

catture ed indicare all’utente la scelta che si è rivelata più probante.

Anche la procedura che intercetta il movimento del riferimento non dovrebbe basare la sua

ricerca su una sola sfumatura ma attuare in parallelo l’intercettazione delle diverse sfumature

dello stesso colore. Verificando se durante la sessione di lavoro alcune di esse diventano più

efficaci di quella scelta all’inizio.

Addirittura la procedura che indica al sistema il punto fissato dall’utente sullo schermo del

Personal Computer potrebbe essere ottimizzata. Per ora la sua funzione si limita ad analizzare

103

i dati che ottiene dagli altri algoritmi ed indicare in coordinate assolute il punto. Tuttavia

nessuna compensazione viene attuata e l’elaborazione si basa su operazioni lineari. Questo

ostacola la libertà di scelta sulla posizione della camera nello spazio delineato intorno al

video. Le posizioni che introducono delle non linearità devono essere scartate. Inoltre

potrebbe essere possibile ottenere una sorta di previsione sul comportamento tenuto

dall’utente al fine di anticipare le sue scelte ed assecondare i suoi movimenti.

In conclusione i margini di sviluppo per ogni algoritmo sono enormi, tuttavia esiste una

regola importante che deve essere sempre rispettata. Le evoluzioni future del sistema devono

mantenere una coerenza nei messaggi che vengono inviati alle applicazioni indipendenti. È

impensabile che lo sviluppo più recente del sistema inibisca l’utilizzo delle applicazioni

scritte precedentemente per il solo fatto che i messaggi sono stati modificati. Se, in futuro, si

individueranno nuovi messaggi possibili, da inviare alle applicazioni, per accrescere le loro

potenzialità, il sistema dovrà comunque fornire un insieme di base che permette alle

applicazioni meno recenti di utilizzare ancora il sistema. Il pregio più importante che il

sistema possiede e che si sono divisi in maniera netta i compiti svolti dal sistema da quelli

svolti dalle applicazioni. Dal punto di vista dell’applicazione indipendente è ininfluente

l’elaborazione che sta dietro ad ogni messaggio, l’importante è che lo stesso messaggio venga

inviato da tutte le possibili versioni future del sistema.

7.4 Applicazioni Sullo sviluppo futuro delle applicazioni indipendenti poco si può prevedere, in quanto solo

l’estro e la fantasia degli sviluppatori è un sicuro limite alle loro prestazioni. Le applicazioni

non sono altro che programmi sviluppati per operare sotto i Sistemi Operativi della famiglia

Microsoft a cui vengono aggiunte delle potenzialità offerte dall’ausilio. Per questa ragione

sono imprevedibili le strade che possono essere percorse, in quanto qualsiasi problema può

essere risolto implementando una valida applicazione.

Pochi sono i miglioramenti che a questo punto dello sviluppo ha senso analizzare. Per

esempio, sfruttare le potenzialità che l’ausilio offre, non solo per le applicazioni ma anche per

il sistema stesso. La scelta dell’applicazione da attivare viene ora eseguita grazie al menu,

tuttavia questa operazione deve essere effettuata dall’assistente mentre potrebbe essere

positivo lasciare la scelta all’utente stesso. Per ottenere questo risultato, il sistema dovrebbe

visualizzare la finestra di un’applicazione, per così dire principale, dove vengono inserite

tutte le applicazioni, iscritte nel sistema, che l’utente può selezionare. Questo permetterebbe

104

all’utente di acquistare più autonomia e poter passare da una applicazione all’altra senza

l’aiuto dell’assistente.

Per attivare questa opzione è possibile intraprendere due strade differenti. Da un lato è

possibile implementare delle applicazioni indipendenti che comprendano la volontà

dell’utente di passare ad un’altra applicazione ed informino, di conseguenza, il sistema.

Dall’altro addossare al sistema stesso l’onere di intercettare le scelte dell’utente.

La prima soluzione non è percorribile perché infrange la regola base che stabilisce il

rapporto tra sistema e applicazioni indipendenti. Solo il sistema deve sapere quali sono le

applicazioni iscritte, mentre le applicazioni non devono e non possono comunicare con il

Supervisore ma solo elaborare i messaggi spediti da quest’ultimo.

La seconda soluzione, oltre ad essere l’unica percorribile, apre nuove strade nello sviluppo

non solo relativo alle applicazioni ma, come si è già detto, dell’intero sistema. Il sistema deve

elaborare parallelamente all’applicazione le scelte fatte dall’utente. Per ottenere questo è

possibile utilizzare una specie di banner sempre visibile, anche quando il sistema visualizza la

finestra dell’applicazione attiva. Invece di dimensionare la finestra dell’applicazione a pieno

schermo potrebbe essere utile sacrificare un po’ di spazio, per esempio in basso, per una

speciale finestra di controllo gestita direttamente dal sistema. Lo spazio offerto dallo schermo

del Personal Computer viene così condiviso sia dall’applicazione attiva, che ne occupa la

maggior parte, sia dal sistema stesso. L’algoritmo incaricato di decidere dove l’utente stia

fissando può filtrare l’informazione e inviare i messaggi all’applicazione solo se lo sguardo è

relativo allo spazio occupato dall’applicazione. Nell’altro caso è possibile elaborare

direttamente la posizione del riferimento per identificare i comandi che l’utente vuole

impartire al sistema stesso.

Il banner potrebbe implementare una serie di pulsanti di scelta rapida che permettono

all’utente di ritornare alla finestra principale dove è possibile riformulare la scelta

dell’applicazione che si vuole utilizzare. Oppure ancora, fornire la possibilità di modificare

direttamente i controlli descritti nel capitolo precedente senza l’aiuto dell’assistente. In questo

modo l’utente potrebbe gestire in prima persona tutte le potenzialità offerte dall’ausilio ed

essere in questo modo completamente indipendente per tutta la sessione di lavoro svolta con il

sistema.

Infine, lo sviluppo deve toccare anche la struttura che regola i rapporti tra sistema e

applicazioni indipendenti. Per ora sono molti i punti del sorgente che lo sviluppatore deve

manipolare per poter agganciare al sistema la sua creazione. Un miglioramento in questo

senso potrebbe ridurre al minimo queste zone di collegamento. Tuttavia ci si potrebbe

105

chiedere fino a che punto è possibile diminuire le zone del sorgente dove è possibile ancorare

le applicazioni indipendenti. La scelta del numero di punti di ancoraggio è un fattore talmente

importante che tutta la struttura può risentirne. Due sono le possibili soluzioni, da una parte si

continua a mantenere uno o più agganci, mentre dall’altra si eliminano completamente le zone

di ancoraggio.

La prima soluzione rispecchia un’evoluzione della struttura presente, in quanto devono

essere migliorate solamente le potenzialità offerte dal Supervisore. Rendere automatico

l’aggancio, agendo in una sola sezione del codice. Obbligare il Supervisore ad addossarsi tutte

le operazioni che devono essere fatte per permettere all’utente di poter scegliere ed utilizzare

l’applicazione che preferisce. La struttura in questo caso non subisce modifiche, le regole

definite nei capitoli precedenti restano invariate e continuano a valere.

Il sistema ha il pieno controllo delle applicazioni e in ogni istante conosce quale è attiva.

Tuttavia, solo una applicazione può essere attiva in un certo istante. Questo porta a

domandarsi se sia limitativo permettere all’utente di utilizzare solo un’applicazione alla volta.

La risposta può essere ricercata nella natura stessa dell’ausilio. Le applicazioni occupano, nel

loro funzionamento, tutto lo schermo del Personal Computer. Quindi è irrilevante se altre

applicazioni sono attive contemporaneamente in quanto solo una alla volta potrà occupare

tutto le spazio a disposizione. Se per alcune elaborazioni particolari, si dovranno utilizzare le

potenzialità di due distinte applicazioni è sempre possibile fonderle in una sola e utilizzare

due sottofinestre per simulare i diversi compiti. Sarà l’applicazione stessa ad elaborare ed

istradare i comandi alle due sottofinestre.

Dall’altra parte, eliminare totalmente l’iscrizione delle applicazioni indipendenti nel

sistema, permette di utilizzare più applicazioni attive nello stesso istante. I messaggi che

devono essere spediti alle applicazioni dovranno essere inoltrati al Sistema Operativo, il quale

avrà il compito di notificarli alle applicazioni indipendenti attive in quel momento. Questo

concetto rivoluziona totalmente la struttura del sistema. Si divide completamente il sistema

dalle applicazioni indipendenti, l’ausilio è composto solamente dalla parte che elabora le

immagini catturate dalla camera e inoltra i messaggi al Sistema Operativo. L’ausilio lavora,

per così dire, in background mentre le applicazioni lavorano in foreground o in primo piano

elaborando i messaggi che il Sistema Operativo gli inoltra. Questa tecnica tuttavia introduce

più svantaggi che vantaggi. Infatti diventa complessa la comunicazione tra Supervisore e

applicazioni in quanto devono essere utilizzate zone condivise di memoria. Anche i messaggi

non sono più leggeri da elaborare come nel caso precedente ma comportano una complessità

maggiore sia da parte del Supervisore sia da parte delle applicazioni. Inoltre anche il lavoro

106

degli sviluppatori di applicazioni diventa complesso in quanto non esiste modo di debaggare

in maniera semplice il sorgente scritto. Gli ambienti di sviluppo, per ora, non possono

analizzare più di un programma alla volta. Tuttavia separando in maniera completa il sistema

dalle applicazioni è possibile compilare in maniera indipendente il codice. Ogni volta che una

nuova applicazione viene prodotta non si è obbligati a ricompilare il codice relativo al sistema

ma la compilazione riguarda solamente il sorgente relativo all’applicazione.

7.5 Conclusioni Purtroppo nei libri che cercano di insegnare l’informatica e la programmazione si tralascia

un aspetto importante che deve caratterizzare il codice sorgente. Questo perché forse non è

semplice trattarlo oppure è troppo intuitivo e solo l’esperienza nella programmazione può

farlo intravedere. Sfortunatamente è difficile anche trovare un nome che possa definirlo,

quello che più si avvicina al concetto che si vuole analizzare forse è massa critica. Con il

termine massa critica, o meglio limite della massa critica, si intende il punto in cui non è più

possibile toccare il sorgente per poter aggiungere nuove potenzialità. Il sorgente è talmente

complesso (o disorganizzato) che ogni operazione fatta su di esso può compromettere il

funzionamento dell’intero sistema. Più il limite è basso, prima si raggiungerà la massa critica,

ciò significa che anche un programma descritto da poche decine di Kbyte di sorgente può

diventare impossibile da gestire e da sviluppare. Invece un sistema in cui il limite viene alzato

a dismisura ha ampi margini di sviluppo prima di raggiungere la massa critica.

Il concetto di massa critica è inoltre relativo al soggetto che si pone davanti al codice

sorgente. Per il creatore il livello sarà sempre più alto rispetto ad un estraneo che legge il

sorgente per la prima volta. Questo è dovuto al fatto che chi ha sviluppato dall’inizio il

sorgente ha ben preciso in mente quello che ha fatto, mentre l’estraneo non può immaginare

quello che è passato per la mente del creatore.

Il limite che più interessa è ha utilità a fini pratici può essere chiamato limite assoluto di

massa critica, questo limite si trova tra i due relativi descritti in precedenza. È più alto del

limite visto dall’estraneo in quanto quest’ultimo può studiare il sorgente e a mano a mano

capirlo sempre più in profondità. Mentre è più basso del limite visto dal creatore perché

quest’ultimo non tiene in considerazione la fase di apprendimento che l’estraneo deve fare sul

suo sorgente.

Più il sorgente è stato scritto con cura e con attenzione, più il limite assoluto si avvicinerà

al limite relativo del creatore, in quanto l’estraneo potrà capire, con lo studio, le tecniche usate

dal creatore. Più il codice sarà scritto male e disorganizzato più il limite assoluto si avvicinerà

107

a quello dell’estraneo, perché quest’ultimo non potrà capire, anche analizzandoli, i concetti

che ha utilizzato il creatore.

Il limite assoluto può essere modificato grazie alle operazioni svolte dallo sviluppatore.

Uno studio dettagliato sull’architettura, la scrittura del sorgente con tecniche e impostazioni

classiche, l’introduzione dei commenti e l’utilizzo di algoritmi semplici possono innalzare i

limiti relativi e di conseguenza l’assoluto, producendo un sorgente che allontana l’ombra della

massa critica. Al contrario, una superficialità diffusa nella creazione del sorgente, l’utilizzo di

numerose chiamate a funzione, obiettivi annebbiati e non ben delineati, una scrittura che non

segue le impostazioni classiche, una mancanza di commenti e una esasperazione

sull’ottimizzazione del sistema porta velocemente il sorgente alla massa critica, abbassando di

conseguenza tutti i limiti.

Questi concetti hanno accompagnato tutto il lavoro fin qui svolto, ora tocca ai futuri

sviluppatori tenere bene a mente queste caratteristiche se si vuole produrre un sistema che

abbia un futuro. Infatti il sistema, oltre ad offrire il suo supporto all’utente, deve promettere

ampi margini di miglioramento che solo un limite assoluto della massa critica estremamente

elevato possono assicurare.

Quindi l’unico consiglio che si può dare ai futuri sviluppatori è di evitare le fughe in

avanti. Il compito principale che deve essere sempre tenuto in considerazione consiste nel far

crescere in maniera omogenea l’intera struttura. Pensando nel contempo che il lavoro svolto

sarà la base di partenza per un altro sviluppatore, esclusi ovviamente coloro che sviluppano

nuove applicazioni indipendenti.

108

109

8 Conclusioni

Nulla è più possibile aggiungere. L’utilità dell’ausilio è in mano a coloro i quali avranno,

in futuro, il potere di svilupparlo e donarlo a chi ne ha più bisogno. Il lavoro fin qui fatto è

solo il primo piccolo passo verso un aiuto concreto alle persone disabili a cui questo sistema

si rivolge.

Gli obiettivi posti all’inizio dovrebbero essere stati raggiunti. L’ausilio cercava di dare un

aiuto alle persone con gravi problemi motori e per questo il sistema permette di selezionare

oggetti sul video del Personal Computer con il solo movimento del volto. L’ausilio doveva

utilizzare tecnologie economiche e per questo motivo il sistema utilizza solamente una

periferica di acquisizione video estremamente modesta come può esserlo una WebCam. Infine

si è cercato di rendere la sessione di lavoro il più confortevole possibile all’utente che deve

solo indossare un piccolo oggetto che permette al sistema di intercettare il movimento.

Ovviamente ulteriori miglioramenti saranno possibili anche grazie alla collaborazione

diretta di persone disabili che, con le loro sensazioni, potranno dirigere il lavoro futuro.

1

Appendice A Elementi principali

Main #include <windows.h>#include <process.h>#include "resource.h"

// Prima parte delle inclusioni private:

#include "Dichiarazioni.h"#include "Messaggi.h"#include "Supervisore.h"#include "Cattura.h"#include "Intercetta.h"#include "Palette.h"#include "Decisore.h"#include "Menu.h"#include "Errore.h"#include "Finestre.h"#include "MenuFinestre.h"#include "MenuCattura.h"#include "MenuIntercetta.h"#include "MenuPosiziona.h"#include "MenuDemo.h"#include "ProprietaDlg.h"

TCHAR szAppName[] = TEXT ("Eye"),szAppImmagineFrontale[] = TEXT ("ImmagineFrontale"),szAppImmagineLaterale[] = TEXT ("ImmagineLaterale"),szAppPaletteFrontale[] = TEXT ("PaletteFrontale"),szAppPaletteLaterale[] = TEXT ("PaletteLaterale"),szAppWebUno[] = TEXT ("WebUno"),szAppWebDue[] = TEXT ("WebDue"),szAppControlli[] = TEXT ("Controlli"),szAppPosiziona[] = TEXT ("Posiziona");

// Nome delle Applicazioni:

TCHAR szAppDemoUno[] = TEXT ("DemoUno"),szAppDemoDue[] = TEXT ("DemoDue"),szAppDemoTre[] = TEXT ("DemoTre");//...

Supervisore Angelo;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)

{// Handler per le Finestre di Eye:

HWND FinestraPrincipale,FinestraImmagineFrontale,FinestraImmagineLaterale,FinestraPaletteFrontale,FinestraPaletteLaterale,FinestraWebUno,FinestraWebDue,FinestraControlli,

// Handler per le finestre di Applicazione:HWND FinestraDemoUno,

FinestraDemoDue,FinestraDemoTre;// ...

MSG msg;WNDCLASS wndVirtuale;HMENU MenuPrincipale;Cattura CameraUno,

CameraDue;Intercetta MagoFrontale,MagoLaterale;Palette TavolozzaFrontale,TavolozzaLaterale;Decisore Oz;GestoreMenu Menu;GestoreErrore Errore;

2

GestoreFinestre Finestre;// Parametri standard per tutte le classi delle finestre

wndVirtuale.style = CS_HREDRAW | CS_VREDRAW;wndVirtuale.cbClsExtra = 0;wndVirtuale.cbWndExtra = 0;wndVirtuale.hInstance = hInstance;wndVirtuale.lpszMenuName = NULL;wndVirtuale.hCursor = LoadCursor (NULL, IDC_ARROW);wndVirtuale.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);

// Parametri per definire la classe della Finestra Principale e registrazionewndVirtuale.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICONA_PRINCIPALE));wndVirtuale.lpfnWndProc = WndProc;wndVirtuale.lpszClassName = szAppName;if (!RegisterClass (&wndVirtuale)){

MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR);

return 0 ;}

// Parametri per definire le Finestre Secondarie:// Parametri per definire la classe della Finestra ImmagineFrontale e registrazione

wndVirtuale.hIcon = NULL; // diventa parametro standardwndVirtuale.lpfnWndProc = ImmagineFrontaleProc;wndVirtuale.lpszClassName = szAppImmagineFrontale;RegisterClass(&wndVirtuale);

// Parametri per definire la classe della Finestra ImmagineLaterale e registrazionewndVirtuale.lpfnWndProc = ImmagineLateraleProc;wndVirtuale.lpszClassName = szAppImmagineLaterale;RegisterClass(&wndVirtuale);

// Parametri per definire la classe della Finestra PaletteFrontale e registrazionewndVirtuale.lpfnWndProc = PaletteFrontaleProc;wndVirtuale.lpszClassName = szAppPaletteFrontale;RegisterClass(&wndVirtuale);

// Parametri per definire la classe della Finestra PaletteLaterale e registrazionewndVirtuale.lpfnWndProc = PaletteLateraleProc;wndVirtuale.lpszClassName = szAppPaletteLaterale;RegisterClass(&wndVirtuale);

// Parametri per definire la classe della Finestra Web Uno e registrazionewndVirtuale.lpfnWndProc = WebUnoProc;wndVirtuale.lpszClassName = szAppWebUno;RegisterClass(&wndVirtuale);

// Parametri per definire la classe della Finestra Web Due e registrazionewndVirtuale.lpfnWndProc = WebDueProc;wndVirtuale.lpszClassName = szAppWebDue;RegisterClass(&wndVirtuale);

// Parametri per definire la classe della Finestra Controlli e registrazionewndVirtuale.lpfnWndProc = ControlliProc;wndVirtuale.lpszClassName = szAppControlli;RegisterClass(&wndVirtuale);

// Parametri per definire la classe della Finestra Posiziona e registrazionewndVirtuale.lpfnWndProc = PosizionaProc;wndVirtuale.lpszClassName = szAppPosiziona;RegisterClass(&wndVirtuale);

// Parametri per definire le Finestre di Applicazione:// Demo Uno:

wndVirtuale.lpfnWndProc = DemoUnoProc;wndVirtuale.lpszClassName = szAppDemoUno;RegisterClass(&wndVirtuale);

// Demo Due:wndVirtuale.lpfnWndProc = DemoDueProc;wndVirtuale.lpszClassName = szAppDemoDue;RegisterClass(&wndVirtuale);

// Demo Tre:wndVirtuale.lpfnWndProc = DemoTreProc;wndVirtuale.lpszClassName = szAppDemoTre;RegisterClass(&wndVirtuale);

// Successive Demo...

// Creazione dell'aggancio al Menù PrincipaleMenuPrincipale = LoadMenu(hInstance,MAKEINTRESOURCE (IDR_MENUPRINCIPALE)) ;

// Creazione della Finestra PrincipaleFinestraPrincipale = CreateWindow (szAppName, // nome della classe

TEXT ("Eye_6_1"), // titoloWS_OVERLAPPEDWINDOW, // stile della finestra150, // posizione iniziale x50, // posizione iniziale y700, // larghezza650, // lunghezzaNULL, // finestra genitoreMenuPrincipale, // menuhInstance, // instanza del programmaNULL) ; // parametri di creazione

3

// Settaggio dell'Angelo e delle sottoFinestre attiveAngelo.Set(FinestraPrincipale, // Finestra Principale

MenuPrincipale, // Menu Principale(char*) szAppName, // Nome della Classe&CameraUno,&CameraDue, // Oggetti Cattura&MagoFrontale,&MagoLaterale, // Oggetti Intercetta&TavolozzaFrontale,&TavolozzaLaterale, // Oggetti Palette&Oz, // Oggetto Decisore&Menu, // Oggetto GestoreMenu&Errore, // Oggetto GestoreErrore&Finestre); // Oggetto GestoreFinestre

// Creazione delle Finestre Secondarie// Finestra Immagine Frontale:

FinestraImmagineFrontale = CreateWindow(szAppImmagineFrontale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE,FinestraImmagineFrontale);// Finestra Immagine Laterale:

FinestraImmagineLaterale = CreateWindow(szAppImmagineLaterale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE,FinestraImmagineLaterale);// Finestra Palette Frontale:

FinestraPaletteFrontale = CreateWindow(szAppPaletteFrontale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE,FinestraPaletteFrontale);// Finestra Palette Laterale:

FinestraPaletteLaterale = CreateWindow(szAppPaletteLaterale,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_PALETTE_LATERALE,FinestraPaletteLaterale);// Finestra Web Uno:

FinestraWebUno = CreateWindow(szAppWebUno,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_WEB_UNO,FinestraWebUno);// Finestra Web Due:

FinestraWebDue = CreateWindow(szAppWebDue,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_WEB_DUE,FinestraWebDue);// Finestra Controlli:

FinestraControlli = CreateWindow(szAppControlli,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_CONTROLLI,FinestraControlli);// Finestra Posiziona:

FinestraPosiziona = CreateWindow(szAppPosiziona,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreSecondarie(FINESTRA_POSIZIONA,FinestraPosiziona);

// Iscrizione nell'Angelo delle Applicazioni:// Demo Uno:

FinestraDemoUno = CreateWindow(szAppDemoUno,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO,FinestraDemoUno,true);// Demo Due:

FinestraDemoDue = CreateWindow(szAppDemoDue,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE,FinestraDemoDue,false);// Demo Tre:

FinestraDemoTre = CreateWindow(szAppDemoTre,NULL,WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE,0,0,0,0,FinestraPrincipale,NULL,hInstance,NULL);

Angelo.SetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE,FinestraDemoTre,false);// Successive Demo...

// Visualizzazione e creazione coda dei messaggi per la Finestra PrincipaleShowWindow(FinestraPrincipale,iCmdShow);UpdateWindow(FinestraPrincipale);

while (GetMessage(&msg,NULL,0,0)){

TranslateMessage(&msg);DispatchMessage(&msg);

}return msg.wParam ;

}

4

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){

static HINSTANCE hInstance;HMENU hMenu;int cxClient,cyClient;

switch (message){case WM_CREATE:

hInstance = ((LPCREATESTRUCT) lParam)->hInstance;Angelo.SethInstance(hInstance);return 0;

case WM_SIZE:cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);

// Gestione delle dimensioni delle Finestre Secondarie:

if(Angelo.VisibileSecondarie(FINESTRA_POSIZIONA))MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_POSIZIONA),

0,0,cxClient,cyClient,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_POSIZIONA),

0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE))

MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),0,50,cxClient/2,(cyClient/2)-50,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_IMMAGINE_LATERALE))

MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),cxClient/2,50,cxClient/2,(cyClient/2)-50,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),

0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_PALETTE_FRONTALE))

MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),0,0,cxClient/2,50,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),

0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_PALETTE_LATERALE))

MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),cxClient/2,0,cxClient/2,50,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),

0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_CONTROLLI))

MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_CONTROLLI),0,cyClient/2,cxClient,100,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_CONTROLLI),

0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_WEB_UNO))

MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_UNO),0,(cyClient/2)+100,cxClient/2,(cyClient/2)-100,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_UNO),

0,0,0,0,true);if(Angelo.VisibileSecondarie(FINESTRA_WEB_DUE))

MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_DUE),cxClient/2,(cyClient/2)+100,cxClient/2,(cyClient/2)-100,true);

elseMoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_DUE),

0,0,0,0,true);

// Gestione delle dimensioi delle Finestre di Applicazione:

5

if(Angelo.VisibileApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO))MoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO),

0,0,cxClient,cyClient,true);

elseMoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO),

0,0,0,0,true);if(Angelo.VisibileApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE))

MoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE),0,0,cxClient,cyClient,true);

elseMoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE),

0,0,0,0,true);if(Angelo.VisibileApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE))

MoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE),0,0,cxClient,cyClient,true);

elseMoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE),

0,0,0,0,true);// ...return 0;

case WM_COMMAND:hMenu = GetMenu (hwnd) ;

switch (LOWORD (wParam)){case ID_FINESTRE_FINESTRAIMMAGINEFRONTALE:case ID_FINESTRE_FINESTRAIMMAGINELATERALE:case ID_FINESTRE_FINESTRAPALETTEFRONTALE:case ID_FINESTRE_FINESTRAPALETTELATERALE:case ID_FINESTRE_FINESTRACONTROLLI:case ID_FINESTRE_FINESTRAWEBUNO:case ID_FINESTRE_FINESTRAWEBDUE:case ID_FINESTRE_FINESTRAPOSIZIONA:

MenuFinestre(wParam,&Angelo);return 0;

case ID_CATTURA_AGGANCIO:case ID_CATTURA_FINESTRA_FORMATO:case ID_CATTURA_FINESTRA_SORGENTE:case ID_CATTURA_FOTO:case ID_CATTURA_RILASCIO:case ID_CATTURA_SETTAGGI_OVERLAY:case ID_CATTURA_SETTAGGI_INFORMAZIONI:case ID_CATTURA_ONOFF_2WEB:case ID_CATTURA_TARGET_FILE:case ID_CATTURA_PROPRIETA_PROPRIETAUNO:case ID_CATTURA_PROPRIETA_PROPRIETADUE:

MenuAggancio(wParam,&Angelo);return 0;

case ID_INTERCETTA_PALETTE_FRONTALE:case ID_INTERCETTA_PALETTE_LATERALE:case ID_INTERCETTA_VISUALIZZA_FRONTALE:case ID_INTERCETTA_START_FRONTALE:case ID_INTERCETTA_STOP_FRONTALE:case ID_INTERCETTA_VISUALIZZA_LATERALE:case ID_INTERCETTA_START_LATERALE:case ID_INTERCETTA_STOP_LATERALE:case ID_INTERCETTA_VISUALIZZA_FRONTALE_II:case ID_INTERCETTA_START_FRONTALE_II:case ID_INTERCETTA_STOP_FRONTALE_II:

MenuIntercetta(wParam,&Angelo);return 0 ;

case ID_POSIZIONA_AUTOMATICO:case ID_POSIZIONA_INTERROMPI:case ID_POSIZIONA_MANUALE:case ID_POSIZIONA_STOP:case ID_POSIZIONA_OK:case ID_POSIZIONA_RITORNA:

MenuPosiziona(wParam,&Angelo);return 0;

case ID_APPLICAZIONI_DEMOUNO:case ID_APPLICAZIONI_DEMODUE:case ID_APPLICAZIONI_DEMOTRE:// ...

MenuDemo(wParam,&Angelo);return 0;

}case WM_DESTROY:

6

PostQuitMessage (0) ;return 0 ;

}return DefWindowProc (hwnd, message, wParam, lParam) ;

}// Seconda parte delle inclusioni private:#include "ThreadIntercettaFrontale.h"#include "ThreadIntercettaLaterale.h"#include "ThreadIntercettaII.h"#include "ThreadPosiziona.h"#include "FinestreCamereProc.h"#include "PaletteFrontaleProc.h"#include "PaletteLateraleProc.h"#include "ImmagineFrontaleProc.h"#include "ImmagineLateraleProc.h"#include "ControlliProc.h"#include "PosizionaProc.h"

// Inclusioni per le applicazioni:

#include "DemoUnoProc.h"#include "DemoDueProc.h"#include "DemoTreProc.h"//...

Dichiarazioni #include <vfw.h>#include "Vicdefs.h"

// Dichiarazione delle WinProc per Eye:

LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK WebUnoProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK WebDueProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK PaletteFrontaleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK PaletteLateraleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK ImmagineFrontaleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK ImmagineLateraleProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK ControlliProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK PosizionaProc (HWND,UINT,WPARAM,LPARAM);BOOL CALLBACK ProprietaDlgProc (HWND,UINT,WPARAM,LPARAM);

// Dichiarazioni delle WinProc per le Applicazioni:

LRESULT CALLBACK DemoUnoProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK DemoDueProc (HWND,UINT,WPARAM,LPARAM);LRESULT CALLBACK DemoTreProc (HWND,UINT,WPARAM,LPARAM);//...

// Dichiarazione delle Classi

class Cattura;class Intercetta;class Supervisore;class GestoreMenu;class GestoreErrore;class GestoreFinestre;class Palette;class Decisore;

// Dichiarazioni delle Costanti

short SLEEP_THREAD_FRONTALE = 10;short SLEEP_THREAD_LATERALE = 10;short NUMERO_AQUISIZIONI = 10;short NUMERO_AQUISIZIONI_AREA_CLICK = 4;short BORDO_CLICK = 2;short BORDO_FOCUS = 20;

const short BORDO_DOMINIO = 10;const short FOCUS_EYE = 52; // deve essere un multiplo di 4const short MASSIMO_LESS = 5;const short LIMITE_BERSAGLI_MINIMO = 0;const short LUMINOSITA = 70;const short NUMERO_AQUISIZIONI_PROGRESSO = 6;

// Dichiarazioni delle Strutture

typedef struct CELLA_COLORE_LS{

7

BYTE Blu,Verde,Rosso;UINT TipoColore;struct CELLA_COLORE_LS *Precedente,*Successivo;

} CELLA_COLORE_LS;typedef struct RETTANGOLI{

RECT Zona;RECT Focus;RECT Dominio;RECT Lento;RECT FocusEye;

} RETTANGOLI;typedef struct CELLA_POSIZIONE{

RECT Posizione;UINT Stato;struct CELLA_POSIZIONE *Successivo,*Precedente;

} CELLA_POSIZIONE;typedef struct{

BYTE Blu,Verde,Rosso;} COLORE;typedef struct{

short BV,VR,RB;} SCOSTAMENTO;typedef struct{

UINT Camera;int ImageWidth,

ImageHeight;char* NomeDriver;bool LiveWindow,

OverlayWindow,Scale,UsingDefaultPalette,AudioHardware,CapFileExists,CapturingNow,HasOverlay,HasDlgVideoSource,HasDlgVideoFormat,HasDlgVideoDisplay,CaptureInitialized,DriverSuppliesPalettes;

} PROPRIETA;typedef struct{

UINT Tipo;HWND hwnd;Supervisore* Angelo;Intercetta* Mago;Decisore* Oz;imgdes* Immagine;imgdes* ImmagineEye;RETTANGOLI* Rettangoli;UINT Stato;bool Ucciso;

} PARAMS, *PPARAMS;typedef struct{

HWND hwnd;Supervisore* Angelo;UINT Stato;bool Ucciso;

} PARAMS_POSIZIONA, *PPARAMS_POSIZIONA;typedef struct{

bool DueWeb;bool Segnale;bool Aggancio;RECT Dimensioni;

} CUBE,*PCUBE;typedef struct{

float X,Y;} PUNTO;

// Classi

class Supervisore // Angelo{public:

8

inline Supervisore() {}void Set(HWND,

HMENU,char*,Cattura*,Cattura*,Intercetta*,Intercetta*,Palette*,Palette*,Decisore*,GestoreMenu*,GestoreErrore*,GestoreFinestre*);

void SethInstance(HINSTANCE);bool GetDueWeb();

// Setto l'handler delle Finestre Secondarie e d'Applicazione:

void SetFinestreSecondarie(UINT,HWND);void SetFinestreApplicazione(UINT,HWND,bool);

// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:

HWND GetFinestreSecondarie(UINT);HWND GetFinestreApplicazione(UINT);

// Controllo se sono visibili le Finestre Secondarie e d'Applicazione:

bool VisibileSecondarie(UINT);bool VisibileApplicazione(UINT);

// Funzione che definisce l'Applicazione prescelta:

void Prescelta(UINT);

// Funzioni che gestiscono i messaggi:

void Messaggio(UINT);void Messaggio(UINT,UINT);void Messaggio(UINT,UINT,UINT);

// Funzione che permette l'handshake tra i Thread:

void HandShake(UINT,UINT,UINT);

public:CUBE Cube;

private:void Scossa();

private:HWND FinestraDlgSettaggi;

HINSTANCE hInstance;Cattura *CameraUno,

*CameraDue;Intercetta *MagoFrontale,

*MagoLaterale;Palette *TavolozzaFrontale,

*TavolozzaLaterale;Decisore *Oz;GestoreMenu *Menu;GestoreErrore *Errore;GestoreFinestre *Finestre;static char NomeFileUno[20],

NomeFileDue[20];};

class GestoreMenu // Menu{public:

void Set(HMENU,PCUBE,GestoreFinestre*);void Messaggio(UINT);

private:HMENU MenuPrincipale;GestoreFinestre *Finestre;PCUBE Cube; // devono essere solo letti

};

class GestoreErrore // Errore{public:

void Set(HWND,char*);void Messaggio(UINT);

private:void SetMessageBox(char*);HWND FinestraPrincipale;

9

char* szAppName;};

class GestoreFinestre // Finestre{public:

// Settaggio dell'Oggetto:

void Set(HWND);

// Setto l'handler delle Finestre Secondarie e d'Applicazione:

void SetFinestreSecondarie(UINT,HWND);void SetFinestreApplicazione(UINT,HWND,bool);

// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:

HWND GetFinestreSecondarie(UINT);HWND GetFinestreApplicazione(UINT);HWND GetFinestreApplicazione();

// Verifico la visibilità delle Finestre Secondarie e d'Applicazione:

bool GetVisibileSecondarie(UINT);bool GetVisibileApplicazione(UINT);

// Setto la Visibilità delle Finestre Secondarie:

void SetVisibileSecondarie(UINT,bool);

// Setto l'Applicazione prescelta:

void SetPresceltaApplicazione(UINT);

// Verifico se l'Applicazione è prescelta:

bool GetPresceltaApplicazione(UINT);

// Gestore dei Messaggi:

void Messaggio(UINT);

private:typedef struct CELLA_FINESTRA{

UINT Tipo;HWND hwndFinestra;bool Visibile;bool Ripristino;struct CELLA_FINESTRA *Successivo;

};typedef struct CELLA_APPLICAZIONE{

UINT Tipo;HWND hwndApplicazione;bool Prescelta;bool Visibile;struct CELLA_APPLICAZIONE *Successivo;

};private:

HWND FinestraPrincipale;struct CELLA_FINESTRA *Testa,*Coda,*Indice,*Temp;struct CELLA_APPLICAZIONE *TestaLA,*CodaLA,*IndiceLA,*TempLA;

};class Cattura // CameraUno,CameraDue{public:

inline Cattura() {}bool Disconnetti();UINT Set(HWND,UINT,char*,int,int,int,int);UINT GetFormato();UINT GetSorgente();

// bool GetInformazioni();bool GetFoto();bool GetFile();void GetSettaggi(PROPRIETA*);HWND GetFinestraCattura();

// UINT OverLay();private:

HWND FinestraPadre,FinestraCattura;

CAPTUREPARMS ParametriCattura;

10

CAPDRIVERCAPS CapacitaDriver;CAPSTATUS StatoCattura;char cNomeDriver[80];int x,y,larghezza,lunghezza;UINT Indice;char Nome[80];char Versione[80];char* NomeFile;int Width;

};

class Intercetta // Mago{public:

inline Intercetta() {}UINT GetPosizioneI();void GetPosizioneEye();bool GetPosizioneII();imgdes* GetImmagine();imgdes* GetImmagineEye();bool GetAttivo();void SetAttivo(bool);UINT Set(Cattura*,char*);RETTANGOLI* SetColoreAttivo(UINT,RECT*);RETTANGOLI* GetRettangoli();CELLA_POSIZIONE* GetPosizioni();void Lock();void UnLock(UINT);UINT GetProgresso();void Free();

public:imgdes Immagine;imgdes ImmagineEye;RETTANGOLI Rettangoli;CELLA_POSIZIONE *Posizioni;

private:typedef struct LIMITI_COLORE{

bool ColoreAttivo;bool ColoreTrovato;UINT TipoColore;RECT Zona;RECT Dominio;RECT Focus;RECT Lento;RECT FocusEye;RECT ZonaEye;short RossoMin,RossoMax,

VerdeMin,VerdeMax,BluMin,BluMax;

};typedef struct HIT{

int Bersagli;RECT Zona;RECT ZonaEye;

};typedef struct PROGRESSO{

RECT Lento;};

private:bool TrovaColoreFocus();bool TrovaColoreDominio();void ConfrontaColore(COLORE,int,int);void Dominio();void Focus();UINT SetLento();

private:bool Attivo;bool DominioAttivo;Cattura* Camera;HWND FinestraSorgente;char* NomeFile;struct LIMITI_COLORE Colore;struct HIT Hit[100];struct PROGRESSO Progresso[NUMERO_AQUISIZIONI_PROGRESSO];short Indice;short IndiceProgresso;UINT StatoCattura;UINT StatoPosiziona;UINT StatoProgresso;CELLA_POSIZIONE *Testa,*Coda,*Temp;

11

int Width,Height,BitCount;};

class Palette // Tavolozza{public:

inline Palette() {}void Set();void SetAttivo(bool);bool GetAttivo();void Pulisci();void Colora(imgdes*);CELLA_COLORE_LS* GetListaColore();RECT* GetZonaColore(UINT);

public:struct CELLA_COLORE_LS *TestaLS,*CodaLS,*IndiceLS,*TempLS; // Lista Semplice

private:typedef struct CELLA_COLORE_LC{

COLORE Colore;SCOSTAMENTO Scostamento;UINT TipoColore;RECT Zona;int Bersagli;int Posizione;struct CELLA_COLORE_LC *Precedente,*Successivo;

};struct CELLA_COLORE_LC *TestaLC,*IndiceLC,*CodaLC,*TempLC; // Lista Complessaint Elementi;int X,Y;bool Attivo;

private:bool Trovato(CELLA_COLORE_LC*);void In(COLORE);UINT TipoColore(CELLA_COLORE_LC*);

};

class Decisore // Oz{public:

inline Decisore() {}void Set(RETTANGOLI*,CELLA_POSIZIONE*,int,int);void SetAttivo(bool);void SetDimensioniFinestra(int,int);bool GetAttivo();UINT GetDecisione();void Pulisci();PUNTO *GetPunto();

public:PUNTO Punto;

private:typedef struct SCOSTAMENTO{

short x,y;} SCOSTAMENTO;typedef struct CELLA_LINEA{

short NumeroLinea;SCOSTAMENTO *Linea;struct CELLA_LINEA *Precedente,*Successivo;

};typedef struct CLICK{

RECT Regione;short Attinenze;

};struct CELLA_LINEA *Testa,*Coda,*Indice,*Temp;struct CLICK Click;RETTANGOLI *Rettangoli;int NumeroDiLinee,NumeroDiColonne;short Contatore;float X,Y;SCOSTAMENTO *Scostamento;bool Attivo;

private:SCOSTAMENTO* GetScostamento(long,long);

};

Messaggi // Identificativi

12

#define AN_WEB_FRONTALE 1#define AN_WEB_LATERALE 2#define AN_THREAD_POSIZIONA 3#define AN_THREAD_INTERCETTA 4

#define STATO_CATTURA_INIZIO 11#define STATO_CATTURA_VERDE 12#define STATO_CATTURA_GIALLO 13#define STATO_CATTURA_ROSSO 14

#define STATO_ZERO 100#define STATO_UNO 101#define STATO_DUE 102#define STATO_TRE 103#define STATO_QUATTRO 104#define STATO_CINQUE 105#define STATO_SEI 106#define STATO_SETTE 107#define STATO_OTTO 108#define STATO_NOVE 109#define STATO_FINE 110#define STATO_MANUALE 111#define STATO_LAVORO 112

#define STATO_PROGRESSO_INIZIO 200#define STATO_PROGRESSO_UNO 201#define STATO_PROGRESSO_DUE 202#define STATO_PROGRESSO_TRE 203#define STATO_PROGRESSO_FINE 204

#define FINESTRE_ATTIVE 300#define FINESTRA_PRINCIPALE 301#define FINESTRA_IMMAGINE_FRONTALE 302#define FINESTRA_IMMAGINE_LATERALE 303#define FINESTRA_PALETTE_FRONTALE 304#define FINESTRA_PALETTE_LATERALE 305#define FINESTRA_WEB_UNO 306#define FINESTRA_WEB_DUE 307#define FINESTRA_CONTROLLI 308#define FINESTRA_POSIZIONA 309#define FINESTRA_APPLICAZIONE 310#define FINESTRE_SECONDARIE 311

#define FINESTRA_APPLICAZIONE_DEMO_UNO 400#define FINESTRA_APPLICAZIONE_DEMO_DUE 401#define FINESTRA_APPLICAZIONE_DEMO_TRE 402//...

// I messaggi successivi sono usati nelle WinProc e possono creare conflitto ,se// li definisco con una mia metrica privata con i messaggi di Windows ,usando// WM_USER sono sicuro che essi sono unici

// Messaggi per le WinProc

#define WM_START_THREAD_I (WM_USER + 1) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_STOP_THREAD_I (WM_USER + 2) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_SINGOLA_IMMAGINE_I (WM_USER + 3) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_START_THREAD_II (WM_USER + 4) // da Supervisore per ImmagineFrontaleProc#define WM_STOP_THREAD_II (WM_USER + 5) // da Supervisore per ImmagineFrontaleProc#define WM_SINGOLA_IMMAGINE_II(WM_USER + 6) // da Supervisore per ImmagineFrontaleProc#define WM_TAVOLOZZA_PRONTA (WM_USER + 7) // da Supervisore per PaletteProc#define WM_COLORE_PRONTO (WM_USER + 8) // da Supervisore per ImmagineFrontale\LateraleProc#define WM_TAVOLOZZA_ATTIVA (WM_USER + 9) // da Supervisore per PaletteProc#define WM_STATO_CATTURA_MUTATO_FRONTALE (WM_USER + 10) // da Supervisore per SemaforiProc#define WM_STATO_CATTURA_MUTATO_LATERALE (WM_USER + 11) // da Supervisore per SemaforiProc#define WM_STATO_CATTURA_MUTATO (WM_USER + 12) // da Supervisore per ApplicazioneProc#define WM_START_POSIZIONA (WM_USER + 13) // da Supervisore per PosizionaProc#define WM_STATO_MUTATO (WM_USER + 14) // da Supervisore per ImmagineFrontaleProc#define WM_STOP_POSIZIONA (WM_USER + 15) // da Supervisore per PosizionaProc#define WM_STATO_PROGRESSO_MUTATO (WM_USER + 16) // da Supervisore per PosizionaProc#define WM_POSIZIONI_PRONTE (WM_USER + 17) // da Supervisore per ImmagineFrontale#define WM_CREATE_PUNTO (WM_USER + 18) // da Supervisore per ApplicazioneProc#define WM_CLICK (WM_USER + 19) // da Supervisore per ApplicazioneProc#define WM_NON_VALIDO (WM_USER + 20) // da Supervisore per ApplicazioneProc

// I messaggi successivi sono usati nelle mie procedure dunque non creano conflitto// e posso definirli con una mia metrica

// Messaggi di operazioni avvenute correttamente

#define AN_OK_AGGANCIO 1001 // da Supervisore per GestoreMenu#define AN_OK_RILASCIO 1002 // da Supervisore per GestoreMenu#define AN_OK_START_FRONTALE 1003 // da Supervisore per GestoreMenu

13

#define AN_OK_STOP_FRONTALE 1004 // da Supervisore per GestoreMenu#define AN_OK_START_LATERALE 1005 // da Supervisore per GestoreMenu#define AN_OK_STOP_LATERALE 1006 // da Supervisore per GestoreMenu#define AN_OK_START_FRONTALE_II 1007 // da Supervisore per GestoreMenu#define AN_OK_STOP_FRONTALE_II 1008 // da Supervisore per GestoreMenu#define AN_OK_DUE_WEB_NO 1009 // da Supervisore per GestoreMenu#define AN_OK_DUE_WEB_SI 1010 // da Supervisore per GestoreMenu#define AN_OK_VISUALIZZA_FRONTALE 1025 // da Supervisore per GestoreMenu#define AN_OK_VISUALIZZA_LATERALE 1026 // da Supervisore per GestoreMenu#define AN_OK_COLORE_SCELTO_FRONTALE 1027 // da Supervisore per GestoreMenu#define AN_OK_COLORE_SCELTO_LATERALE 1028 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_NO 1029 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_SI 1030 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_NO 1031 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_SI 1032 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_NO 1033 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_SI 1034 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_NO 1035 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_SI 1036 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_CONTROLLI_VISIBILE_NO 1037 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_CONTROLLI_VISIBILE_SI 1038 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_UNO_VISIBILE_NO 1039 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_UNO_VISIBILE_SI 1040 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_DUE_VISIBILE_NO 1041 // da Supervisore per GestoreMenu#define AN_OK_FINESTRA_WEB_DUE_VISIBILE_SI 1042 // da Supervisore per GestoreMenu#define AN_OK_START_POSIZIONA 1043 // da Supervisore per GestoreMenu#define AN_OK_STOP_POSIZIONA 1044 // da Supervisore per GestoreMenu#define AN_OK_VISUALIZZA_FRONTALE_II 1045 // da Supervisore per GestoreMenu#define AN_OK_START_MANUALE 1046 // da Supervisore per GestoreMenu#define AN_OK_STOP_MANUALE 1047 // da Supervisore per GestoreMenu#define AN_OK_OK 1048 // da supervisore per GestoreMenu#define AN_OK_RITORNA 1049 // da Supervisore per GestoreMenu#define AN_OK_APPLICAZIONE_PRESCELTA 1050 // da Supervisore per GestoreMenu

// Messaggi di operazioni fallite

#define AN_NO_AGGANCIO 2001 // da Supervisore per GestoreErrore#define AN_NO_DATI 2002 // da Supervisore per GestoreErrore#define AN_NO_FORMATO_ERRORE 2003 // da Supervisore per GestoreErrore#define AN_NO_FINESTRA_FORMATO 2004 // da Supervisore per GestoreErrore#define AN_NO_SORGENTE_ERRORE 2005 // da Supervisore per GestoreErrore#define AN_NO_FINESTRA_SORGENTE 2006 // da Supervisore per GestoreErrore#define AN_NO_FOTO 2007 // da Supervisore per GestoreErrore#define AN_NO_RILASCIO_WEB_UNO 2008 // da Supervisore per GestoreErrore#define AN_NO_FILE 2009 // da Supervisore per GestoreErrore#define AN_NO_ALLOCIMAGE 2010 // da Supervisore per GestoreErrore#define AN_NO_ELABORAZIONE 2011 // da Supervisore per GestoreErrore#define AN_NO_RILASCIO_WEB_DUE 2012 // da Supervisore per GestoreErrore

// Messaggi di OnOff

#define AN_ONOFF_DUE_WEB 3001 // da MenuCattura per Supervisore

// Messaggi di richiesta

#define AN_ASK_AGGANCIO 4001 // da MenuCattura per Supervisore#define AN_ASK_FINESTRA_FORMATO 4002 // da MenuCattura per Supervisore#define AN_ASK_FINESTRA_SORGENTE 4003 // da MenuCattura per Supervisore#define AN_ASK_FOTO 4004 // da MenuCattura per Supervisore#define AN_ASK_RILASCIO 4005 // da MenuCattura per Supervisore#define AN_ASK_DLG_PROPRIETA 4006 // da MenuCattura per Supervisore#define AN_ASK_FILE 4007 // da MenuCattura per Supervisore#define AN_ASK_VISUALIZZA 4008 // da MenuIntercetta per Supervisore#define AN_ASK_START 4009 // da MenuIntercetta per Supervisore#define AN_ASK_STOP 4010 // da MenuIntercetta per Supervisore#define AN_ASK_VISUALIZZA_II 4011 // da MenuIntercetta per Supervisore#define AN_ASK_START_II 4012 // da MenuIntercetta per Supervisore#define AN_ASK_STOP_II 4013 // da MenuIntercetta per Supervisore#define AN_ASK_PALETTE 4014 // da MenuIntercetta per Supervisore#define AN_ASK_INTERCETTA_COLORE 4016 // da PaletteProc per Supervisore;#define AN_ASK_STATO_CATTURA_MUTATO 4017 // da ThreadIntercettaFrontale/Laterale per Supervisore#define AN_ASK_START_POSIZIONA 4018 // da MenuPosiziona per Supervisore#define AN_ASK_FINESTRA_IMMAGINE_FRONTALE 4019 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_IMMAGINE_LATERALE 4020 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_PALETTE_FRONTALE 4021 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_PALETTE_LATERALE 4022 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_CONTROLLI 4023 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_WEB_UNO 4024 // da MenuSet per Supervisore#define AN_ASK_FINESTRA_WEB_DUE 4025 // da MenuSet per Supervisore#define AN_ASK_INIZIO_STATO 4026 // da ThreadPosiziona per Supervisore#define AN_ASK_FINE_STATO 4027 // da ThreadIntercettaFrontale per Supervisore#define AN_ASK_FINE_SETTAGGIO 4028 // da ThreadPosiziona per Supervisore#define AN_ASK_STATO_COMPLETATO 4029 // da ThreadIntercettaFrontale per Supervisore

14

#define AN_ASK_STOP_POSIZIONA 4030 // da MenuPosiziona per Supervisore#define AN_ASK_STATO_PROGRESSO_MUTATO 4031 // da ThreadIntercettaFrontale per Supervisore#define AN_ASK_START_MANUALE 4032 // da MenuPosiziona per Supervisore#define AN_ASK_STOP_MANUALE 4033 // da MenuPosiziona per Supervisore#define AN_ASK_OK 4034 // da MenuPosiziona per Supervisore#define AN_ASK_RITORNA 4035 // da MenuPosiziona per Supervisore#define AN_ASK_DECISIONE_AVVENUTA 4036 // da ThreadIntercettaFrontale per Supervisore#define AN_ASK_CLICK_TEMPORALE_AVVENUTA 4037 // da ThreadIntercettaFrontale per Supervisore

// Codici di ritorno delle varie funzioni

#define AN_RET_OK 6001 // da Cattura per Supervisore#define AN_RET_AGGANCIO_NO 6002 // da Cattura per Supervisore#define AN_RET_DATI_NO 6003 // da Cattura per Supervisore#define AN_RET_FORMATO_ERRORE 6004 // da Cattura per Supervisore#define AN_RET_FORMATO_NO 6005 // da Cattura per Supervisore#define AN_RET_SORGENTE_ERRORE 6006 // da Cattura per Supervisore#define AN_RET_SORGENTE_NO 6007 // da Cattura per Supervisore#define AN_RET_OVERLAY_ERRORE 6008 // da Cattura per Supervisore#define AN_RET_OVERLAY_NO 6009 // da Cattura per Supervisore#define AN_RET_ALLOC_NO 6010 // da Intercetta per Supervisore#define AN_RET_NO_DECISIONE 6011 // da Decisore per ThreadIntercettaFrontale#define AN_RET_SI_DECISIONE 6012 // da Decisore per ThreadIntercettaFrontale#define AN_RET_CLICK_TEMPORALE 6013 // da Decisore per ThreadIntercettaFrontale

// Codici per i colori nella Tavolozza

#define GRIGIO_1 7001 // da TrovaGrigio (Palette)#define GRIGIO_2 7002 // da TrovaGrigio (Palette)#define GRIGIO_3 7003 // da TrovaGrigio (Palette)#define GRIGIO_4 7004 // da TrovaGrigio (Palette)#define GRIGIO_5 7005 // da TrovaGrigio (Palette)#define ROSSO_1 7006 // da TrovaColore (Palette)#define ROSSO_2 7007 // da TrovaColore (Palette)#define ROSSO_3 7008 // da TrovaColore (Palette)#define ROSSO_4 7009 // da TrovaColore (Palette)#define ROSSO_5 7010 // da TrovaColore (Palette)#define ROSSO_6 7011 // da TrovaColore (Palette)#define VERDE_1 7012 // da TrovaColore (Palette)#define VERDE_2 7013 // da TrovaColore (Palette)#define VERDE_3 7014 // da TrovaColore (Palette)#define VERDE_4 7015 // da TrovaColore (Palette)#define VERDE_5 7016 // da TrovaColore (Palette)#define VERDE_6 7017 // da TrovaColore (Palette)#define BLU_1 7018 // da TrovaColore (Palette)#define BLU_2 7019 // da TrovaColore (Palette)#define BLU_3 7020 // da TrovaColore (Palette)#define BLU_4 7021 // da TrovaColore (Palette)#define BLU_5 7022 // da TrovaColore (Palette)#define BLU_6 7023 // da TrovaColore (Palette)#define GIALLO_1 7024 // da TrovaColore (Palette)#define GIALLO_2 7025 // da TrovaColore (Palette)#define GIALLO_3 7026 // da TrovaColore (Palette)#define CYANO_1 7027 // da TrovaColore (Palette)#define CYANO_2 7028 // da TrovaColore (Palette)#define CYANO_3 7029 // da TrovaColore (Palette)#define VIOLETTO_1 7030 // da TrovaColore (Palette)#define VIOLETTO_2 7031 // da TrovaColore (Palette)#define VIOLETTO_3 7032 // da TrovaColore (Palette)#define INDEFINITO 7033 // da TrovaColore (Palette)

Supervisore // Definizioni membri statici:

char Supervisore::NomeFileUno[20] = "Z:\\FileUno.bmp";char Supervisore::NomeFileDue[20] = "Z:\\FileDue.bmp";

// Definizioni funzioni membro:void Supervisore::SethInstance(HINSTANCE hInstance) { this->hInstance=hInstance; }bool Supervisore::GetDueWeb() { return Cube.DueWeb; }

// Setto l'handler delle Finestre Secondarie e d'Applicazione:void Supervisore::SetFinestreSecondarie(UINT Tipo,HWND Finestra)

{ Finestre->SetFinestreSecondarie(Tipo,Finestra); }void Supervisore::SetFinestreApplicazione(UINT Tipo,HWND Finestra,bool Prescelta)

{ Finestre->SetFinestreApplicazione(Tipo,Finestra,Prescelta); }

// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:HWND Supervisore::GetFinestreSecondarie(UINT Tipo){ return Finestre->GetFinestreSecondarie(Tipo); }HWND Supervisore::GetFinestreApplicazione(UINT Tipo)

{ return Finestre->GetFinestreApplicazione(Tipo); }

15

// Controllo la visibilità delle Finestre Secondarie e d'Applicazione:bool Supervisore::VisibileSecondarie(UINT Tipo){ return Finestre->GetVisibileSecondarie(Tipo); }bool Supervisore::VisibileApplicazione(UINT Tipo){ return Finestre->GetVisibileApplicazione(Tipo); }

// Definisco l'Applicazione prescelta:void Supervisore::Prescelta(UINT Tipo){

Finestre->SetPresceltaApplicazione(Tipo);Menu->Messaggio(AN_OK_APPLICAZIONE_PRESCELTA);

}

// Settaggio dell'Angelo:void Supervisore::Set( HWND FinestraPrincipale,

HMENU MenuPrincipale,char* szAppName,Cattura* CameraUno,Cattura* CameraDue,Intercetta* MagoFrontale,Intercetta* MagoLaterale,Palette* TavolozzaFrontale,Palette* TavolozzaLaterale,Decisore* Oz,GestoreMenu* Menu,GestoreErrore* Errore,GestoreFinestre* Finestre)

{this->CameraUno = CameraUno;this->CameraDue = CameraDue;this->MagoFrontale = MagoFrontale;this->MagoLaterale = MagoLaterale;this->TavolozzaFrontale = TavolozzaFrontale;this->TavolozzaLaterale = TavolozzaLaterale;this->Oz = Oz;this->Menu = Menu;this->Errore = Errore;this->Finestre = Finestre;this->Menu->Set(MenuPrincipale,&Cube,Finestre);this->Errore->Set(FinestraPrincipale,szAppName);this->Finestre->Set(FinestraPrincipale);Cube.DueWeb = false;Cube.Aggancio = false;GetWindowRect(FinestraPrincipale,&Cube.Dimensioni);MagoFrontale->SetAttivo(false);MagoLaterale->SetAttivo(false);TavolozzaFrontale->SetAttivo(false);TavolozzaLaterale->SetAttivo(false);Oz->SetAttivo(false);

}void Supervisore::Scossa(){

GetWindowRect(Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),&Cube.Dimensioni);MoveWindow(Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),

Cube.Dimensioni.left,Cube.Dimensioni.top,Cube.Dimensioni.right-Cube.Dimensioni.left,Cube.Dimensioni.bottom-Cube.Dimensioni.top-1,true);

MoveWindow(Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),Cube.Dimensioni.left,Cube.Dimensioni.top,Cube.Dimensioni.right-Cube.Dimensioni.left,Cube.Dimensioni.bottom-Cube.Dimensioni.top,true);

}void Supervisore::Messaggio(UINT Messaggio){

switch (Messaggio){

// Gestione di OnOff:// Due Web:

case AN_ONOFF_DUE_WEB:if(Cube.DueWeb){

Cube.DueWeb = false;Menu->Messaggio(AN_OK_DUE_WEB_NO);

}else{

Cube.DueWeb = true;Menu->Messaggio(AN_OK_DUE_WEB_SI);

}InvalidateRect(Finestre->GetFinestreSecondarie(FINESTRA_CONTROLLI),NULL,true);break;

16

// Gestione delle finestre Visibili://Finestra Immagine Frontale:

case AN_ASK_FINESTRA_IMMAGINE_FRONTALE:if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE)){

Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE,false);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_NO);

}else{

Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE,true);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_SI);

}Scossa();break;

// Finestra Immagine Laterale:case AN_ASK_FINESTRA_IMMAGINE_LATERALE:

if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE)){

Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE,false);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_NO);

}else{

Finestre->SetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE,true);Menu->Messaggio(AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_SI);

}Scossa();break;

// Finestra Palette Frontale:case AN_ASK_FINESTRA_PALETTE_FRONTALE:

if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE)){

Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE,false);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_NO);

}else{

Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE,true);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_SI);

}Scossa();break;

// Finestra Palette Laterale:case AN_ASK_FINESTRA_PALETTE_LATERALE:

if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_LATERALE)){

Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_LATERALE,false);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_NO);

}else{

Finestre->SetVisibileSecondarie(FINESTRA_PALETTE_LATERALE,true);Menu->Messaggio(AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_SI);

}Scossa();break;

// Finestra Semafori:case AN_ASK_FINESTRA_CONTROLLI:

if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI)){

Finestre->SetVisibileSecondarie(FINESTRA_CONTROLLI,false);Menu->Messaggio(AN_OK_FINESTRA_CONTROLLI_VISIBILE_NO);

}else{

Finestre->SetVisibileSecondarie(FINESTRA_CONTROLLI,true);Menu->Messaggio(AN_OK_FINESTRA_CONTROLLI_VISIBILE_SI);

}Scossa();break;

// Finestra Web Uno:case AN_ASK_FINESTRA_WEB_UNO:

if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_UNO)){

Finestre->SetVisibileSecondarie(FINESTRA_WEB_UNO,false);Menu->Messaggio(AN_OK_FINESTRA_WEB_UNO_VISIBILE_NO);

}

17

else{

Finestre->SetVisibileSecondarie(FINESTRA_WEB_UNO,true);Menu->Messaggio(AN_OK_FINESTRA_WEB_UNO_VISIBILE_SI);

}Scossa();break;

// Finestra Web Due:case AN_ASK_FINESTRA_WEB_DUE:

if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_DUE)){

Finestre->SetVisibileSecondarie(FINESTRA_WEB_DUE,false);Menu->Messaggio(AN_OK_FINESTRA_WEB_DUE_VISIBILE_NO);

}else{

Finestre->SetVisibileSecondarie(FINESTRA_WEB_DUE,true);Menu->Messaggio(AN_OK_FINESTRA_WEB_DUE_VISIBILE_SI);

}Scossa();break;

// Richiesta di posizionamento automatico:case AN_ASK_START_POSIZIONA:

Finestre->Messaggio(FINESTRA_POSIZIONA);Menu->Messaggio(AN_OK_START_POSIZIONA);Scossa();Cube.Segnale = false;SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),

WM_START_POSIZIONA,0,0);break;

case AN_ASK_STOP_POSIZIONA:SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

WM_STATO_MUTATO,0,(long) STATO_ZERO);if(Cube.DueWeb == true)

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),WM_STATO_MUTATO,0,(long) STATO_ZERO);

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STOP_POSIZIONA,0,0);

Finestre->Messaggio(FINESTRE_ATTIVE);Menu->Messaggio(AN_OK_STOP_POSIZIONA);Scossa();break;

// Richiesta di posizionamento manuale:case AN_ASK_START_MANUALE:

Menu->Messaggio(AN_OK_START_MANUALE);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

WM_STATO_MUTATO,0,(long) STATO_MANUALE);break;

case AN_ASK_STOP_MANUALE:RETTANGOLI *Rettangoli;

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) STATO_ZERO);

Menu->Messaggio(AN_OK_STOP_MANUALE);Rettangoli = MagoFrontale->GetRettangoli();if(Oz->GetAttivo())

Oz->Pulisci();Oz->Set(Rettangoli,NULL,

(Cube.Dimensioni.right - Cube.Dimensioni.left),(Cube.Dimensioni.bottom - Cube.Dimensioni.top));

break;

// Richiesta per il lancio dell'Applicazione:case AN_ASK_OK:PUNTO *Punto;

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) STATO_LAVORO);

Menu->Messaggio(AN_OK_OK);Finestre->Messaggio(FINESTRA_APPLICAZIONE);Punto = Oz->GetPunto();SendMessage(Finestre->GetFinestreApplicazione(),

WM_CREATE_PUNTO,0,(long) Punto);SendMessage(Finestre->GetFinestreApplicazione(),

WM_STATO_CATTURA_MUTATO,0,(long) STATO_CATTURA_VERDE);Scossa();break;

case AN_ASK_RITORNA:SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

18

WM_STATO_MUTATO,0,(long) STATO_ZERO);Menu->Messaggio(AN_OK_RITORNA);Finestre->Messaggio(FINESTRE_SECONDARIE);Scossa();break;

// Richiesta di decisione avvenuta:case AN_ASK_DECISIONE_AVVENUTA:

SendMessage(Finestre->GetFinestreApplicazione(),WM_NON_VALIDO,0,0);break;

// richiesta di click temporale:case AN_ASK_CLICK_TEMPORALE_AVVENUTA:

SendMessage(Finestre->GetFinestreApplicazione(),WM_CLICK,0,0);break;

}}void Supervisore::Messaggio(UINT Messaggio,UINT Generatore){

switch (Generatore){

// Gestione richieste da web:case AN_WEB_FRONTALE:

switch (Messaggio){

// Agganciocase AN_ASK_AGGANCIO:

switch (CameraUno->Set(Finestre->GetFinestreSecondarie(FINESTRA_WEB_UNO),AN_WEB_FRONTALE,(char*) &NomeFileUno,0,0,400,400))

{case AN_RET_AGGANCIO_NO:

Errore->Messaggio(AN_NO_AGGANCIO);break;

case AN_RET_DATI_NO:Errore->Messaggio(AN_NO_DATI);break;

case AN_RET_OK:Menu->Messaggio(AN_OK_AGGANCIO);Cube.Aggancio = true;break;

}break;

// Finestra Formatocase AN_ASK_FINESTRA_FORMATO:

switch (CameraUno->GetFormato()){case AN_RET_FORMATO_ERRORE:

Errore->Messaggio(AN_NO_FORMATO_ERRORE);break;

case AN_RET_FORMATO_NO:Errore->Messaggio(AN_NO_FINESTRA_FORMATO);break;

case AN_RET_OK:break;

}break;

// Finestra Sorgentecase AN_ASK_FINESTRA_SORGENTE:

switch (CameraUno->GetSorgente()){case AN_RET_SORGENTE_ERRORE:

Errore->Messaggio(AN_NO_SORGENTE_ERRORE);break;

case AN_RET_SORGENTE_NO:Errore->Messaggio(AN_NO_FINESTRA_SORGENTE);break;

case AN_RET_OK:break;

}break;

// Fotocase AN_ASK_FOTO:

if(!CameraUno->GetFoto())Errore->Messaggio(AN_NO_FOTO);

break;// Rilascio

case AN_ASK_RILASCIO:if(!CameraUno->Disconnetti())

Errore->Messaggio(AN_NO_RILASCIO_WEB_UNO);else

Menu->Messaggio(AN_OK_RILASCIO);Cube.Aggancio = false;

19

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_I,0,0);

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_II,0,0);

break;// File

case AN_ASK_FILE:if(!CameraUno->GetFile())

Errore->Messaggio(AN_NO_FILE);break;

// Palettecase AN_ASK_PALETTE:

if(TavolozzaFrontale->GetAttivo())TavolozzaFrontale->Pulisci();

TavolozzaFrontale->Set();TavolozzaFrontale->Colora(MagoFrontale->GetImmagine());SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),

WM_TAVOLOZZA_PRONTA,0,(long) TavolozzaFrontale->GetListaColore());

break;// Visualizza

case AN_ASK_VISUALIZZA:if(MagoFrontale->GetAttivo())

MagoFrontale->Free();switch (MagoFrontale->Set(CameraUno,(char*) &NomeFileUno)){case AN_RET_ALLOC_NO:

Errore->Messaggio(AN_NO_ALLOCIMAGE);break;

case AN_RET_OK:break;

}MagoFrontale->GetPosizioneI();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

WM_SINGOLA_IMMAGINE_I,0,(long) MagoFrontale->GetImmagine());Menu->Messaggio(AN_OK_VISUALIZZA_FRONTALE);break;

// Startcase AN_ASK_START:

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_START_THREAD_I,(long) Oz,(long) MagoFrontale);

Menu->Messaggio(AN_OK_START_FRONTALE);break;

// Stopcase AN_ASK_STOP:

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_I,0,0);

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_FRONTALE),WM_STOP_THREAD_I,0,0);

Menu->Messaggio(AN_OK_STOP_FRONTALE);break;

// Visualizza IIcase AN_ASK_VISUALIZZA_II:

if(MagoFrontale->GetAttivo())MagoFrontale->Free();

switch (MagoFrontale->Set(CameraUno,(char*) &NomeFileUno)){case AN_RET_ALLOC_NO:

Errore->Messaggio(AN_NO_ALLOCIMAGE);break;

case AN_RET_OK:break;

}if(!MagoFrontale->GetPosizioneII())

Errore->Messaggio(AN_NO_ELABORAZIONE);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

WM_SINGOLA_IMMAGINE_II,0,(long) MagoFrontale->GetImmagine());Menu->Messaggio(AN_OK_VISUALIZZA_FRONTALE_II);break;

// Start IIcase AN_ASK_START_II:

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_START_THREAD_II,0,(long) MagoFrontale);

Menu->Messaggio(AN_OK_START_FRONTALE_II);break;

// Stop IIcase AN_ASK_STOP_II:

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STOP_THREAD_II,0,0);

Menu->Messaggio(AN_OK_STOP_FRONTALE_II);break;

20

// DLG Proprietàcase AN_ASK_DLG_PROPRIETA:

PROPRIETA SettaggiCamera = { 0,0,0,NULL, false,false,false,false,false,false,false,false,false,false,false,false,false };

SettaggiCamera.Camera=AN_WEB_FRONTALE;CameraUno->GetSettaggi(&SettaggiCamera);DialogBoxParam(hInstance,TEXT ("PROPRIETADLG"),

Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),ProprietaDlgProc,(long) &SettaggiCamera);

break;}break;

case AN_WEB_LATERALE:switch (Messaggio){

// Agganciocase AN_ASK_AGGANCIO:

switch (CameraDue->Set(Finestre->GetFinestreSecondarie(FINESTRA_WEB_DUE),AN_WEB_LATERALE,(char*) &NomeFileDue,0,0,400,400))

{case AN_RET_AGGANCIO_NO:

Errore->Messaggio(AN_NO_AGGANCIO);break;

case AN_RET_DATI_NO:Errore->Messaggio(AN_NO_DATI);break;

case AN_RET_OK:Menu->Messaggio(AN_OK_AGGANCIO);break;

}break;

// Finestra Formatocase AN_ASK_FINESTRA_FORMATO:

switch (CameraDue->GetFormato()){case AN_RET_FORMATO_ERRORE:

Errore->Messaggio(AN_NO_FORMATO_ERRORE);break;

case AN_RET_FORMATO_NO:Errore->Messaggio(AN_NO_FINESTRA_FORMATO);break;

case AN_RET_OK:break;

}break;

// Finestra Sorgentecase AN_ASK_FINESTRA_SORGENTE:

switch (CameraDue->GetSorgente()){case AN_RET_SORGENTE_ERRORE:

Errore->Messaggio(AN_NO_SORGENTE_ERRORE);break;

case AN_RET_SORGENTE_NO:Errore->Messaggio(AN_NO_FINESTRA_SORGENTE);break;

case AN_RET_OK:break;

}break;

// Fotocase AN_ASK_FOTO:

if(!CameraDue->GetFoto())Errore->Messaggio(AN_NO_FOTO);

break;// Rilascio

case AN_ASK_RILASCIO:if(!CameraDue->Disconnetti())

Errore->Messaggio(AN_NO_RILASCIO_WEB_DUE);else

Menu->Messaggio(AN_OK_RILASCIO);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),

WM_STOP_THREAD_I,0,0);break;

// Filecase AN_ASK_FILE:

if(!CameraDue->GetFile())Errore->Messaggio(AN_NO_FILE);

break;// Palette

case AN_ASK_PALETTE:if(TavolozzaLaterale->GetAttivo())

TavolozzaLaterale->Pulisci();TavolozzaLaterale->Set();

21

TavolozzaLaterale->Colora(MagoLaterale->GetImmagine());SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),

WM_TAVOLOZZA_PRONTA,0,(long) TavolozzaLaterale->GetListaColore());break;

// Visualizzacase AN_ASK_VISUALIZZA:

if(MagoLaterale->GetAttivo())MagoLaterale->Free();

switch (MagoLaterale->Set(CameraDue,(char*) &NomeFileDue)){case AN_RET_ALLOC_NO:

Errore->Messaggio(AN_NO_ALLOCIMAGE);break;

case AN_RET_OK:break;

}MagoLaterale->GetPosizioneI();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),

WM_SINGOLA_IMMAGINE_I,0,(long) MagoLaterale->GetImmagine());Menu->Messaggio(AN_OK_VISUALIZZA_LATERALE);break;

// Startcase AN_ASK_START:

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),WM_START_THREAD_I,(long) Oz,(long) MagoLaterale);

Menu->Messaggio(AN_OK_START_LATERALE);break;

// Stopcase AN_ASK_STOP:

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),WM_STOP_THREAD_I,0,0);

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),WM_STOP_THREAD_I,0,0);

Menu->Messaggio(AN_OK_STOP_LATERALE);break;

// DLG Proprietàcase AN_ASK_DLG_PROPRIETA:

PROPRIETA Settaggi = { 0,0,0,NULL,false,false,false,false,false,false,false,false,false,false,false,false,false };

Settaggi.Camera=AN_WEB_LATERALE;CameraDue->GetSettaggi(&Settaggi);DialogBoxParam(hInstance,TEXT ("PROPRIETADLG"),

Finestre->GetFinestreSecondarie(FINESTRA_PRINCIPALE),ProprietaDlgProc,(long) &Settaggi);

break;}break;

}}void Supervisore::Messaggio(UINT Messaggio,UINT Generatore,UINT Parametro){

switch (Generatore){case AN_WEB_FRONTALE:

switch (Messaggio){

// Intercetta Colorecase AN_ASK_INTERCETTA_COLORE:RECT Zona;RETTANGOLI* Rettangoli;

CopyRect(&Zona,TavolozzaFrontale->GetZonaColore(Parametro));Rettangoli = MagoFrontale->SetColoreAttivo(Parametro,&Zona);SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

WM_COLORE_PRONTO,0,(long) Rettangoli);Menu->Messaggio(AN_OK_COLORE_SCELTO_FRONTALE);break;

case AN_ASK_STATO_CATTURA_MUTATO:if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI)){

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_CONTROLLI),WM_STATO_CATTURA_MUTATO_FRONTALE,0,(long) Parametro);

break;}if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA)){

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STATO_CATTURA_MUTATO_FRONTALE,0,(long) Parametro);

break;}SendMessage(Finestre->GetFinestreApplicazione(),

WM_STATO_CATTURA_MUTATO,0,(long) Parametro);break;

case AN_ASK_STATO_PROGRESSO_MUTATO:

22

if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA))SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),

WM_STATO_PROGRESSO_MUTATO,0,(long) Parametro);break;

}break;

case AN_WEB_LATERALE:switch (Messaggio){case AN_ASK_INTERCETTA_COLORE:RECT Zona;RETTANGOLI* Rettangoli;

CopyRect(&Zona,TavolozzaLaterale->GetZonaColore(Parametro));Rettangoli = MagoLaterale->SetColoreAttivo(Parametro,&Zona);MagoLaterale->GetPosizioneEye();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),

WM_COLORE_PRONTO,(long) MagoFrontale->GetImmagineEye(),(long) Rettangoli);

Menu->Messaggio(AN_OK_COLORE_SCELTO_LATERALE);break;

case AN_ASK_STATO_CATTURA_MUTATO:if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI))

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_CONTROLLI),WM_STATO_CATTURA_MUTATO_LATERALE,0,(long) Parametro);

if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA))SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),

WM_STATO_CATTURA_MUTATO_LATERALE,0,(long) Parametro);break;

/* case AN_ASK_STATO_PROGRESSO_MUTATO:if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA))

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STATO_PROGRESSO_MUTATO,0,(long) Parametro);

break;*/ }

break;}

}void Supervisore::HandShake(UINT Messaggio,UINT Generatore,UINT Parametro){

switch (Generatore){case AN_THREAD_POSIZIONA:

switch (Messaggio){case AN_ASK_INIZIO_STATO:

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) Parametro);

if(Cube.DueWeb)SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),

WM_STATO_MUTATO,0,(long) Parametro);break;

case AN_ASK_FINE_SETTAGGIO:CELLA_POSIZIONE *Posizioni;RETTANGOLI *Rettangoli;

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),WM_STATO_MUTATO,0,(long) STATO_ZERO);

Posizioni = MagoFrontale->GetPosizioni();SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),

WM_POSIZIONI_PRONTE,0,(long) Posizioni);Rettangoli = MagoFrontale->GetRettangoli();if(Oz->GetAttivo())

Oz->Pulisci();Oz->Set(Rettangoli,Posizioni,

(Cube.Dimensioni.right - Cube.Dimensioni.left),(Cube.Dimensioni.bottom - Cube.Dimensioni.top));

if(Cube.DueWeb)SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),

WM_STATO_MUTATO,0,(long) STATO_ZERO);Finestre->Messaggio(FINESTRE_ATTIVE);Menu->Messaggio(AN_OK_STOP_POSIZIONA);Scossa();break;

}break;

case AN_THREAD_INTERCETTA:switch (Messaggio){case AN_ASK_FINE_STATO:

if(!Cube.DueWeb)SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),

WM_STATO_MUTATO,0,(long) Parametro+1);else{

23

if(!Cube.Segnale)Cube.Segnale = true;

else{

SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),WM_STATO_MUTATO,0,(long) Parametro+1);

Cube.Segnale = false;}

}break;

}break;

}}

24

25

Appendice B Oggetti principali

Cattura HWND Cattura::GetFinestraCattura() { return FinestraCattura; }

UINT Cattura::Set(HWND FinestraPadre,UINT Indice,char* NomeFile,int x,int y,int larghezza,int lunghezza)

{// Settaggi

this->FinestraPadre = FinestraPadre;this->Indice = Indice;this->NomeFile = NomeFile;this->x = x;this->y = y;this->larghezza = larghezza;this->lunghezza = lunghezza;

// Cerco di agganciare la Web alla Finestra di CatturaFinestraCattura=capCreateCaptureWindow(TEXT ("Finestra di cattura"),

WS_CHILD | WS_VISIBLE,x,y,larghezza,lunghezza,FinestraPadre,0);

if(!capDriverConnect(FinestraCattura,0))return AN_RET_AGGANCIO_NO;

if(!(capCaptureGetSetup(FinestraCattura,&ParametriCattura,sizeof ParametriCattura)) ||!(capDriverGetCaps(FinestraCattura,&CapacitaDriver,sizeof CapacitaDriver)) ||!(capDriverGetName(FinestraCattura,&cNomeDriver,sizeof cNomeDriver)) ||!(capGetStatus(FinestraCattura,&StatoCattura,sizeof StatoCattura)))

return AN_RET_DATI_NO;return AN_RET_OK;

}bool Cattura::Disconnetti(){

if(capDriverDisconnect(FinestraCattura))return true;

return false;}UINT Cattura::GetFormato(){

if(CapacitaDriver.fHasDlgVideoFormat == 1){

if(!capDlgVideoFormat(FinestraCattura))return AN_RET_FORMATO_ERRORE;

return AN_RET_OK;}else

return AN_RET_FORMATO_NO;}UINT Cattura::GetSorgente(){

if(CapacitaDriver.fHasDlgVideoSource == 1){

if(!capDlgVideoSource(FinestraCattura))return AN_RET_SORGENTE_ERRORE;

return AN_RET_OK;}else

return AN_RET_SORGENTE_NO;}bool Cattura::GetFoto(){

if(capGrabFrame(FinestraCattura)) return true;return false;

}bool Cattura::GetFile(){

if(capFileSaveDIB(FinestraCattura,NomeFile)) return true;return false;

}void Cattura::GetSettaggi(PROPRIETA *Settaggi){

Settaggi->ImageWidth = StatoCattura.uiImageWidth;Settaggi->ImageHeight = StatoCattura.uiImageHeight;

26

Settaggi->NomeDriver = (char*) &cNomeDriver;if(StatoCattura.fLiveWindow == 1) Settaggi->LiveWindow = true;if(StatoCattura.fOverlayWindow == 1) Settaggi->OverlayWindow = true;if(StatoCattura.fScale == 1) Settaggi->Scale = true;if(StatoCattura.fUsingDefaultPalette == 1) Settaggi->UsingDefaultPalette = true;if(StatoCattura.fAudioHardware == 1) Settaggi->AudioHardware = true;if(StatoCattura.fCapFileExists == 1) Settaggi->CapFileExists = true;if(StatoCattura.fCapturingNow == 1) Settaggi->CapturingNow = true;if(CapacitaDriver.fHasOverlay == 1) Settaggi->HasOverlay = true;if(CapacitaDriver.fHasDlgVideoSource == 1) Settaggi->HasDlgVideoSource = true;if(CapacitaDriver.fHasDlgVideoFormat == 1) Settaggi->HasDlgVideoFormat = true;if(CapacitaDriver.fHasDlgVideoDisplay == 1) Settaggi->HasDlgVideoDisplay = true;if(CapacitaDriver.fCaptureInitialized == 1) Settaggi->CaptureInitialized = true;if(CapacitaDriver.fDriverSuppliesPalettes == 1) Settaggi->DriverSuppliesPalettes = true;

}

Palette CELLA_COLORE_LS* Palette::GetListaColore() { return this->TestaLS; }void Palette::SetAttivo(bool Attivo) { this->Attivo = Attivo;}bool Palette::GetAttivo() { return this->Attivo; }

void Palette::Set(){

TestaLS = CodaLS = IndiceLS = NULL;TestaLC = CodaLC = IndiceLC = NULL;Elementi = 0;

// Creo la Cella Colore Lista Complessa che servirà da rifiuto per tutti i colori indefinitiTempLC = new(CELLA_COLORE_LC);TempLC->Colore.Blu = 0;TempLC->Colore.Verde = 0;TempLC->Colore.Rosso = 0;TempLC->Scostamento.BV = 0;TempLC->Scostamento.VR = 0;TempLC->Scostamento.RB = 0;TempLC->TipoColore = INDEFINITO;TempLC->Zona.left = 0;TempLC->Zona.top = 240;TempLC->Zona.right = 320;TempLC->Zona.bottom = 0;TempLC->Bersagli = 0;TempLC->Posizione = Elementi;

// Inserisco la Cella in testa (primo elemento)TempLC->Precedente = NULL;TempLC->Successivo = NULL;TestaLC = CodaLC = TempLC;

// Creo la Cella Colore Lista SempliceTempLS = new(CELLA_COLORE_LS);TempLS->Blu = 0;TempLS->Verde = 0;TempLS->Rosso = 0;TempLS->TipoColore = INDEFINITO;

// Inserisco la Cella in testa (primo elemento)TempLS->Precedente = NULL;TempLS->Successivo = NULL;TestaLS = CodaLS = TempLS;Elementi += 1;this->Attivo = true;

}void Palette::Pulisci(){

// Elimino gli elementi della lista ComplessaIndiceLC = CodaLC->Precedente;delete CodaLC;while(IndiceLC != NULL){

CodaLC = IndiceLC;IndiceLC = CodaLC->Precedente;delete CodaLC;

}TestaLC = CodaLC = IndiceLC = TempLC = NULL;// Elimino gli elementi della lista SempliceIndiceLS = CodaLS->Precedente;delete CodaLS;while(IndiceLS != NULL){

CodaLS = IndiceLS;IndiceLS = CodaLS->Precedente;delete CodaLS;

}TestaLS = CodaLS = IndiceLS = TempLS = NULL;// Setto il numero di elementi a 0

27

Elementi = 0;this->Attivo = false;

}RECT* Palette::GetZonaColore(UINT TipoColore){

IndiceLC = TestaLC;while(IndiceLC != NULL){

if(IndiceLC->TipoColore == TipoColore)return &IndiceLC->Zona;

IndiceLC = IndiceLC->Successivo;}return NULL;

}bool Palette::Trovato(CELLA_COLORE_LC *Cella){

IndiceLC = TestaLC;while(IndiceLC != NULL){

if(IndiceLC->TipoColore == Cella->TipoColore){

if(X < IndiceLC->Zona.left) IndiceLC->Zona.left = X;if(Y > IndiceLC->Zona.top) IndiceLC->Zona.top = Y;if(X > IndiceLC->Zona.right) IndiceLC->Zona.right = X;if(Y < IndiceLC->Zona.bottom) IndiceLC->Zona.bottom = Y;IndiceLC->Bersagli++;return true;

}IndiceLC = IndiceLC->Successivo;

}Cella->Zona.left = Cella->Zona.right = X;Cella->Zona.top = Cella->Zona.bottom = Y;Cella->Bersagli = 1;return false;

}void Palette::Colora(imgdes* Immagine){int i = 0; // Contatore di BYTEint j = 0; // Contatore di pixelCOLORE Colore;

X = Y = 0;while (i < ((int) Immagine->bmh->biSizeImage)){

Colore.Blu = Immagine->ibuff[i];Colore.Verde = Immagine->ibuff[i+1];Colore.Rosso = Immagine->ibuff[i+2];this->In(Colore);i = i + 3;j++;X = j % 320;Y = j / 320;

}}void Palette::In(COLORE Colore){// Creo la Cella Colore Lista Complessa

TempLC = new(CELLA_COLORE_LC);TempLC->Colore.Blu = Colore.Blu;TempLC->Colore.Verde = Colore.Verde;TempLC->Colore.Rosso = Colore.Rosso;TempLC->Scostamento.BV = Colore.Blu - Colore.Verde;TempLC->Scostamento.VR = Colore.Verde - Colore.Rosso;TempLC->Scostamento.RB = Colore.Rosso - Colore.Blu;TempLC->TipoColore = TipoColore(TempLC);TempLC->Posizione = Elementi;if (!Trovato(TempLC)){

// Inserisco la Cella in coda alla Lista ComplessaTempLC->Precedente = CodaLC;TempLC->Successivo = NULL;CodaLC->Successivo = TempLC;CodaLC = TempLC;

// Creo la Cella Colore Lista SempliceTempLS = new(CELLA_COLORE_LS);TempLS->Blu = Colore.Blu;TempLS->Verde = Colore.Verde;TempLS->Rosso = Colore.Rosso;TempLS->TipoColore = TipoColore(TempLC); // da rivedere

// Inserisco la Cella in coda alla Lista SempliceTempLS->Precedente = CodaLS;TempLS->Successivo = NULL;CodaLS->Successivo = TempLS;CodaLS = TempLS;

28

Elementi += 1;}else

delete TempLC;}UINT Palette::TipoColore(CELLA_COLORE_LC *Cella){// Controllo se il colore è un grigio

if((Cella->Scostamento.BV <= 10) && (Cella->Scostamento.BV >= -10) &&(Cella->Scostamento.VR <= 10) && (Cella->Scostamento.VR >= -10) &&(Cella->Scostamento.RB <= 10) && (Cella->Scostamento.RB >= -10))

{// Definisco il tipo di grigio da molto scuro (1) a bianco (5)

if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 120)return GRIGIO_1;

if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 300)return GRIGIO_2;

if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 480)return GRIGIO_3;

if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 690)return GRIGIO_4;

if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso) <= 765)return GRIGIO_5;

}// Controllo se il colore è un rosso

if((Cella->Colore.Rosso <= 255) && (Cella->Colore.Rosso > 220) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_1;

if((Cella->Colore.Rosso <= 220) && (Cella->Colore.Rosso > 180) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_2;

if((Cella->Colore.Rosso <= 180) && (Cella->Colore.Rosso > 150) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_3;

if((Cella->Colore.Rosso <= 150) && (Cella->Colore.Rosso > 120) &&(Cella->Colore.Verde <= 100) && (Cella->Colore.Blu <= 100))return ROSSO_4;

if((Cella->Colore.Rosso <= 120) && (Cella->Colore.Rosso > 100) &&(Cella->Colore.Verde <= 90) && (Cella->Colore.Blu <= 90))return ROSSO_5;

if((Cella->Colore.Rosso <= 100) && (Cella->Colore.Rosso > 90) &&(Cella->Colore.Verde <= 80) && (Cella->Colore.Blu <= 80))return ROSSO_6;

// Controllo se il colore è un verdeif((Cella->Colore.Verde <= 255) && (Cella->Colore.Verde > 220) &&

(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_1;

if((Cella->Colore.Verde <= 220) && (Cella->Colore.Verde > 180) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_2;

if((Cella->Colore.Verde <= 180) && (Cella->Colore.Verde > 150) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_3;

if((Cella->Colore.Verde <= 150) && (Cella->Colore.Verde > 120) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Blu <= 100))return VERDE_4;

if((Cella->Colore.Verde <= 120) && (Cella->Colore.Verde > 100) &&(Cella->Colore.Rosso <= 90) && (Cella->Colore.Blu <= 90))return VERDE_5;

if((Cella->Colore.Verde <= 100) && (Cella->Colore.Verde > 90) &&(Cella->Colore.Rosso <= 80) && (Cella->Colore.Blu <= 80))return VERDE_6;

// Controllo se il colore è un bluif((Cella->Colore.Blu <= 255) && (Cella->Colore.Blu > 220) &&

(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_1;

if((Cella->Colore.Blu <= 220) && (Cella->Colore.Blu > 180) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_2;

if((Cella->Colore.Blu <= 180) && (Cella->Colore.Blu > 150) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_3;

if((Cella->Colore.Blu <= 150) && (Cella->Colore.Blu > 120) &&(Cella->Colore.Rosso <= 100) && (Cella->Colore.Verde <= 100))return BLU_4;

if((Cella->Colore.Blu <= 120) && (Cella->Colore.Blu > 100) &&(Cella->Colore.Rosso <= 90) && (Cella->Colore.Verde <= 90))return BLU_5;

if((Cella->Colore.Blu <= 100) && (Cella->Colore.Blu > 90) &&(Cella->Colore.Rosso <= 80) && (Cella->Colore.Verde <= 80))return BLU_6;

// Controllo se il colore è un gialloif((Cella->Colore.Rosso <= 255) && (Cella->Colore.Rosso > 220) &&

29

(Cella->Colore.Verde <= 255) && (Cella->Colore.Verde > 220) &&(Cella->Colore.Blu <= 50))return GIALLO_1;

if((Cella->Colore.Rosso <= 220) && (Cella->Colore.Rosso > 180) &&(Cella->Colore.Verde <= 220) && (Cella->Colore.Verde > 180) &&(Cella->Colore.Blu <= 50))return GIALLO_2;

if((Cella->Colore.Rosso <= 180) && (Cella->Colore.Rosso > 150) &&(Cella->Colore.Verde <= 180) && (Cella->Colore.Verde > 150) &&(Cella->Colore.Blu <= 50))return GIALLO_3;

// Controllo se il colore è un cyanoif((Cella->Colore.Blu <= 255) && (Cella->Colore.Blu > 220) &&

(Cella->Colore.Verde <= 255) && (Cella->Colore.Verde > 220) &&(Cella->Colore.Rosso <= 50))return CYANO_1;

if((Cella->Colore.Blu <= 220) && (Cella->Colore.Blu > 180) &&(Cella->Colore.Verde <= 220) && (Cella->Colore.Verde > 180) &&(Cella->Colore.Rosso <= 50))return CYANO_2;

if((Cella->Colore.Blu <= 180) && (Cella->Colore.Blu > 150) &&(Cella->Colore.Verde <= 180) && (Cella->Colore.Verde > 150) &&(Cella->Colore.Rosso <= 50))return CYANO_3;

// Controllo se il colore è un violettoif((Cella->Colore.Blu <= 255) && (Cella->Colore.Blu > 220) &&

(Cella->Colore.Rosso <= 255) && (Cella->Colore.Rosso > 220) &&(Cella->Colore.Verde <= 50))return VIOLETTO_1;

if((Cella->Colore.Blu <= 220) && (Cella->Colore.Blu > 180) &&(Cella->Colore.Rosso <= 220) && (Cella->Colore.Rosso > 180) &&(Cella->Colore.Verde <= 50))return VIOLETTO_2;

if((Cella->Colore.Blu <= 180) && (Cella->Colore.Blu > 150) &&(Cella->Colore.Rosso <= 180) && (Cella->Colore.Rosso > 150) &&(Cella->Colore.Verde <= 50))return VIOLETTO_3;

return INDEFINITO;}

Intercetta imgdes* Intercetta::GetImmagine() { return &Immagine; }imgdes* Intercetta::GetImmagineEye() { return &ImmagineEye; }bool Intercetta::GetAttivo() { return Attivo; }CELLA_POSIZIONE* Intercetta::GetPosizioni() { return Posizioni; }UINT Intercetta::GetProgresso() { return StatoProgresso; }void Intercetta::SetAttivo(bool Attivo) { this->Attivo = Attivo; }void Intercetta::Lock(){

this->DominioAttivo = false;this->StatoProgresso = STATO_PROGRESSO_INIZIO;

}void Intercetta::UnLock(UINT StatoPosiziona){

this->DominioAttivo = true;this->StatoPosiziona = StatoPosiziona;this->IndiceProgresso = 0;

}UINT Intercetta::Set(Cattura* Camera,char* NomeFile){int errore;BITMAPINFOHEADER Info;

this->Camera = Camera;this->NomeFile = NomeFile;this->FinestraSorgente = Camera->GetFinestraCattura();errore = bmpinfo(NomeFile,&Info);this->Width = Info.biWidth;this->Height = Info.biHeight;this->BitCount = Info.biBitCount;if(allocimage(&Immagine,Info.biWidth,Info.biHeight,Info.biBitCount) != NO_ERROR)

return AN_RET_ALLOC_NO;if(allocimage(&ImmagineEye,FOCUS_EYE,FOCUS_EYE,24) != NO_ERROR)

return AN_RET_ALLOC_NO;this->Attivo = true;this->Colore.ColoreAttivo = false;this->DominioAttivo = true;this->StatoCattura = STATO_CATTURA_INIZIO;this->StatoPosiziona = STATO_ZERO;this->StatoProgresso = STATO_PROGRESSO_INIZIO;

// Creo la lista delle posizioni:

30

Temp = new(CELLA_POSIZIONE);Temp->Precedente = Temp->Successivo = NULL;Testa = Coda = Posizioni = Temp;IndiceProgresso = 0;return AN_RET_OK;

}RETTANGOLI* Intercetta::GetRettangoli(){

CopyRect(&Rettangoli.Zona,&Colore.Zona);CopyRect(&Rettangoli.Focus,&Colore.Focus);CopyRect(&Rettangoli.Dominio,&Colore.Dominio);CopyRect(&Rettangoli.Lento,&Colore.Lento);CopyRect(&Rettangoli.FocusEye,&Colore.FocusEye);return &this->Rettangoli;

}void Intercetta::Free(){

freeimage(&Immagine);freeimage(&ImmagineEye);

// Cancello la lista delle posizioni:Temp = Coda->Precedente;delete Coda;while(Temp != NULL){

Coda = Temp;Temp = Coda->Precedente;delete Coda;

}Testa = Coda = Temp = NULL;IndiceProgresso = 0;this->Attivo = false;

}RETTANGOLI* Intercetta::SetColoreAttivo(UINT TipoColore,RECT* Zona){

Colore.TipoColore = TipoColore;// Posizionamento della Zona sull'Immagine

CopyRect(&Colore.Zona,Zona);// Posizionamento del Dominio sull'Immagine

CopyRect(&Colore.Dominio,&Colore.Zona);// Posizionamento del Lento sull'Immagine

CopyRect(&Colore.Lento,&Colore.Zona);// Posizionamento del Focus sull'Immagine

if(Colore.Zona.left > BORDO_FOCUS)Colore.Focus.left = (Colore.Zona.left - BORDO_FOCUS);

elseColore.Focus.left = 0;

if(Colore.Zona.top < (Height - BORDO_FOCUS))Colore.Focus.top = (Colore.Zona.top + BORDO_FOCUS);

elseColore.Focus.top = (Height - 1);

if(Colore.Zona.right < (Width - BORDO_FOCUS))Colore.Focus.right = (Colore.Zona.right + BORDO_FOCUS);

elseColore.Focus.right = (Width - 1);

if(Colore.Zona.bottom > 10)Colore.Focus.bottom = (Colore.Zona.bottom - BORDO_FOCUS);

elseColore.Focus.bottom = 0;

// Settaggio dei limiti del coloreswitch (TipoColore){case GRIGIO_1: // non entrerò mai in questi case li ho messi solocase GRIGIO_2: // per evitare errori.case GRIGIO_3:case GRIGIO_4:case GRIGIO_5:

Colore.RossoMin = Colore.VerdeMin = Colore.BluMin = 0;Colore.RossoMax = Colore.VerdeMax = Colore.BluMax = 0;break;

case ROSSO_1:Colore.RossoMin = 221;Colore.RossoMax = 255;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;

case ROSSO_2:Colore.RossoMin = 181;Colore.RossoMax = 220;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;

case ROSSO_3:Colore.RossoMin = 151;

31

Colore.RossoMax = 180;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;

case ROSSO_4:Colore.RossoMin = 121;Colore.RossoMax = 150;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 100;break;

case ROSSO_5:Colore.RossoMin = 101;Colore.RossoMax = 120;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 90;break;

case ROSSO_6:Colore.RossoMin = 91;Colore.RossoMax = 100;Colore.VerdeMin = Colore.BluMin = 0;Colore.VerdeMax = Colore.BluMax = 80;break;

case VERDE_1:Colore.VerdeMin = 221;Colore.VerdeMax = 255;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;

case VERDE_2:Colore.VerdeMin = 181;Colore.VerdeMax = 220;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;

case VERDE_3:Colore.VerdeMin = 151;Colore.VerdeMax = 180;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;

case VERDE_4:Colore.VerdeMin = 121;Colore.VerdeMax = 150;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 100;break;

case VERDE_5:Colore.VerdeMin = 101;Colore.VerdeMax = 120;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 90;break;

case VERDE_6:Colore.VerdeMin = 91;Colore.VerdeMax = 100;Colore.RossoMin = Colore.BluMin = 0;Colore.RossoMax = Colore.BluMax = 80;break;

case BLU_1:Colore.BluMin = 221;Colore.BluMax = 255;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;

case BLU_2:Colore.BluMin = 181;Colore.BluMax = 220;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;

case BLU_3:Colore.BluMin = 151;Colore.BluMax = 180;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;

case BLU_4:Colore.BluMin = 121;Colore.BluMax = 150;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 100;break;

case BLU_5:

32

Colore.BluMin = 101;Colore.BluMax = 120;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 90;break;

case BLU_6:Colore.BluMin = 91;Colore.BluMax = 100;Colore.RossoMin = Colore.VerdeMin = 0;Colore.RossoMax = Colore.VerdeMax = 80;break;

}// Settaggio iniziale per la struttura RETTANGOLI

CopyRect(&Rettangoli.Zona,&Colore.Zona);CopyRect(&Rettangoli.Focus,&Colore.Focus);CopyRect(&Rettangoli.Dominio,&Colore.Dominio);CopyRect(&Rettangoli.Lento,&Colore.Lento);CopyRect(&Rettangoli.FocusEye,&Colore.FocusEye);

// Settaggio iniziale per il vettore di strutture HITHit[0].Bersagli = 0;CopyRect(&Hit[0].Zona,&Colore.Zona);Indice = 1;

// Il colore è stato sceltoColore.ColoreAttivo = true;Colore.ColoreTrovato = true;return &Rettangoli;

}void Intercetta::GetPosizioneEye(){int i,j,k;short IndiceTemp;// Posizionamento del FocusEye sull'Immagine

if(Indice >= 1)IndiceTemp = (Indice - 1);

elseIndiceTemp = (NUMERO_AQUISIZIONI - 1);

if(Hit[IndiceTemp].Zona.left > FOCUS_EYE)Colore.FocusEye.left = (Hit[IndiceTemp].Zona.left - FOCUS_EYE);

elseColore.FocusEye.left = 0;

if(Hit[IndiceTemp].Zona.bottom > FOCUS_EYE)Colore.FocusEye.bottom = (Hit[IndiceTemp].Zona.bottom - FOCUS_EYE);

elseColore.FocusEye.bottom = 0;

Colore.FocusEye.right = Hit[IndiceTemp].Zona.left;Colore.FocusEye.top = Hit[IndiceTemp].Zona.bottom;k = 0;i = this->Colore.FocusEye.bottom * (320*3);while (i < (this->Colore.FocusEye.top * (320*3))){

j = (this->Colore.FocusEye.left * 3);while (j < (this->Colore.FocusEye.right * 3)){

if((Immagine.ibuff[(i+j)+0] > LUMINOSITA) &&(Immagine.ibuff[(i+j)+1] > LUMINOSITA) &&(Immagine.ibuff[(i+j)+2] > LUMINOSITA))

ImmagineEye.ibuff[k+0] = ImmagineEye.ibuff[k+1] =ImmagineEye.ibuff[k+2] = 255;

elseImmagineEye.ibuff[k+0] = ImmagineEye.ibuff[k+1] =

ImmagineEye.ibuff[k+2] = 0;j = j + 3;k = k + 3;

}i = i + (320*3);

}// Cerco il contorno dell'occhio e setto la ZonaEye

}UINT Intercetta::GetPosizioneI(){// Carica una nuova immagine

Camera->GetFoto();Camera->GetFile();loadbmp(NomeFile,&Immagine);if(Colore.ColoreAttivo){

// Setto la zona per accogliere le hitColore.Zona.left = 320;Colore.Zona.right = 0;Colore.Zona.bottom = 240;Colore.Zona.top = 0;Hit[Indice].Bersagli = 0;

33

// Setto il Dominio e il Focus per questa chiamataif (Colore.ColoreTrovato)

Colore.ColoreTrovato = TrovaColoreFocus();else

Colore.ColoreTrovato = TrovaColoreDominio();// Metto la zona trovata nel vettore delle catture

CopyRect(&Hit[Indice].Zona,&Colore.Zona);// Dopo NUMERO_CATTURE dò dei risultati più stabili

if(Indice >= (NUMERO_AQUISIZIONI - 1))return SetLento();

elseIndice++;

}return StatoCattura;

}UINT Intercetta::SetLento(){short i,Miss,Less;int Lunghezza,Larghezza;short Cont = 0;RECT Sum;

Miss = Less = 0;Indice = 0;Sum.left = Sum.right = Sum.top = Sum.bottom = 0;Larghezza = Colore.Lento.right - Colore.Lento.left;Lunghezza = Colore.Lento.top - Colore.Lento.bottom;for(i=0; i<NUMERO_AQUISIZIONI ; i++){

if(((Hit[i].Zona.right - Hit[i].Zona.left) <= (Larghezza + 10)) &&((Hit[i].Zona.top - Hit[i].Zona.bottom) <= (Lunghezza + 10)) &&(Hit[i].Zona.left < Hit[i].Zona.right))

{Sum.left = Sum.left + Hit[i].Zona.left;Sum.right = Sum.right + Hit[i].Zona.right;Sum.bottom = Sum.bottom + Hit[i].Zona.bottom;Sum.top = Sum.top + Hit[i].Zona.top;Cont++;

}if((Hit[i].Bersagli > 0) && (Hit[i].Bersagli < MASSIMO_LESS))

Less++;if(Hit[i].Bersagli == 0)

Miss++;}if(Cont > 0){

Colore.Lento.left = (Sum.left / Cont);Colore.Lento.right = (Sum.right / Cont);Colore.Lento.bottom = (Sum.bottom / Cont);Colore.Lento.top = (Sum.top / Cont);CopyRect(&Rettangoli.Lento,&Colore.Lento);

}if(Miss == NUMERO_AQUISIZIONI)

StatoCattura = STATO_CATTURA_ROSSO;if(((Miss > 2) && (Miss < NUMERO_AQUISIZIONI)) || (Less > 5))

StatoCattura = STATO_CATTURA_GIALLO;if((Miss <= 2) && (Less < 5))

StatoCattura = STATO_CATTURA_VERDE;return StatoCattura;

}bool Intercetta::TrovaColoreFocus(){int i,j;int X,Y;COLORE Colore;

Y = this->Colore.Focus.bottom;i = this->Colore.Focus.bottom * (320*3);while (i <= (this->Colore.Focus.top * (320*3))){

X = this->Colore.Focus.left;j = (this->Colore.Focus.left * 3);while (j <= (this->Colore.Focus.right * 3)){

Colore.Blu = Immagine.ibuff[(i+j)+0];Colore.Verde = Immagine.ibuff[(i+j)+1];Colore.Rosso = Immagine.ibuff[(i+j)+2];ConfrontaColore(Colore,X,Y);j = j + 3;X++;

}i = i + (320*3);Y++;

}if(Hit[Indice].Bersagli > LIMITE_BERSAGLI_MINIMO)

34

{Dominio();Focus();return true;

}return false;

}bool Intercetta::TrovaColoreDominio(){int i,j;int X,Y;COLORE Colore;

Y = this->Colore.Dominio.bottom;

i = this->Colore.Dominio.bottom * (320*3);while (i <= (this->Colore.Dominio.top * (320*3))){

X = this->Colore.Dominio.left;j = (this->Colore.Dominio.left * 3);while (j <= (this->Colore.Dominio.right * 3)){

Colore.Blu = Immagine.ibuff[(i+j)+0];Colore.Verde = Immagine.ibuff[(i+j)+1];Colore.Rosso = Immagine.ibuff[(i+j)+2];ConfrontaColore(Colore,X,Y);j = j + 3;X++;

}i = i + (320*3);Y++;

}if(Hit[Indice].Bersagli > LIMITE_BERSAGLI_MINIMO){

Focus();return true;

}return false;

}void Intercetta::ConfrontaColore(COLORE Colore,int X,int Y){

if((Colore.Verde >= this->Colore.VerdeMin) && (Colore.Verde <= this->Colore.VerdeMax) &&(Colore.Rosso >= this->Colore.RossoMin) && (Colore.Rosso <= this->Colore.RossoMax) &&(Colore.Blu >= this->Colore.BluMin) && (Colore.Blu <= this->Colore.BluMax))

{// Setto la Zona

if(X < this->Colore.Zona.left) this->Colore.Zona.left = X;if(Y > this->Colore.Zona.top) this->Colore.Zona.top = Y;if(X > this->Colore.Zona.right) this->Colore.Zona.right = X;if(Y < this->Colore.Zona.bottom) this->Colore.Zona.bottom = Y;

// Setto la HitHit[Indice].Bersagli++;

}}void Intercetta::Dominio(){short i;RECT Sum;

if(this->DominioAttivo){

// Setto il Dominioif(this->Colore.Lento.left < this->Colore.Dominio.left)

this->Colore.Dominio.left = this->Colore.Lento.left;if(this->Colore.Lento.top > this->Colore.Dominio.top)

this->Colore.Dominio.top = this->Colore.Lento.top;if(this->Colore.Lento.right > this->Colore.Dominio.right)

this->Colore.Dominio.right = this->Colore.Lento.right;if(this->Colore.Lento.bottom < this->Colore.Dominio.bottom)

this->Colore.Dominio.bottom = this->Colore.Lento.bottom;// Setto il progresso

if((Indice == 0) && (StatoCattura == STATO_CATTURA_VERDE)){

CopyRect(&Progresso[IndiceProgresso].Lento,&Colore.Lento);IndiceProgresso++;

}if(IndiceProgresso < 2)

this->StatoProgresso = STATO_PROGRESSO_INIZIO;if((IndiceProgresso >= 2) && (IndiceProgresso < 4))

this->StatoProgresso = STATO_PROGRESSO_UNO;if((IndiceProgresso >= 4) && (IndiceProgresso < 5))

this->StatoProgresso = STATO_PROGRESSO_DUE;if((IndiceProgresso >= 5) && (IndiceProgresso < NUMERO_AQUISIZIONI_PROGRESSO))

this->StatoProgresso = STATO_PROGRESSO_TRE;if(IndiceProgresso == NUMERO_AQUISIZIONI_PROGRESSO)

35

{Sum.left = Sum.right = Sum.top = Sum.bottom = 0;for(i=2; i<NUMERO_AQUISIZIONI_PROGRESSO ; i++){

Sum.left = Sum.left + Progresso[i].Lento.left;Sum.right = Sum.right + Progresso[i].Lento.right;Sum.bottom = Sum.bottom + Progresso[i].Lento.bottom;Sum.top = Sum.top + Progresso[i].Lento.top;

}Sum.left = Sum.left / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);Sum.right = Sum.right / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);Sum.bottom = Sum.bottom / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);Sum.top = Sum.top / ((NUMERO_AQUISIZIONI_PROGRESSO)-2);

// setto una nuova cella della lista delle posizioniTemp = new(CELLA_POSIZIONE);CopyRect(&Temp->Posizione,&Sum);Temp->Stato = this->StatoPosiziona;Temp->Successivo = NULL;Temp->Precedente = Coda;Coda->Successivo = Temp;Coda = Temp;IndiceProgresso = 0;this->StatoProgresso = STATO_PROGRESSO_FINE;

}}

}

void Intercetta::Focus(){// Setto il Focus

if(this->Colore.Zona.left > BORDO_FOCUS)this->Colore.Focus.left = (this->Colore.Zona.left - BORDO_FOCUS);

elsethis->Colore.Focus.left = 0;

if(this->Colore.Zona.top < (this->Height - BORDO_FOCUS))this->Colore.Focus.top = (this->Colore.Zona.top + BORDO_FOCUS);

elsethis->Colore.Focus.top = (this->Height - 1);

if(this->Colore.Zona.right < (this->Width - BORDO_FOCUS))this->Colore.Focus.right = (this->Colore.Zona.right + BORDO_FOCUS);

elsethis->Colore.Focus.right = (this->Width - 1);

if(this->Colore.Zona.bottom > BORDO_FOCUS)this->Colore.Focus.bottom = (this->Colore.Zona.bottom - BORDO_FOCUS);

elsethis->Colore.Focus.bottom = 0;

}bool Intercetta::GetPosizioneII(){int errore;imgdes Temp;

Camera->GetFoto();errore = clienttoimage(FinestraSorgente,&Temp);errore = copyimage(&Temp,&Immagine);freeimage(&Temp);return true;

}

Decisore void Decisore::SetAttivo(bool Attivo) { this->Attivo = Attivo; }bool Decisore::GetAttivo() { return this->Attivo; }PUNTO *Decisore::GetPunto() { return &this->Punto; }

void Decisore::Set(RETTANGOLI *Rettangoli,CELLA_POSIZIONE *Posizione,int X,int Y){int i;

this->Rettangoli = Rettangoli;this->Contatore = 0;this->NumeroDiLinee = Rettangoli->Dominio.top - Rettangoli->Dominio.bottom;this->NumeroDiColonne = Rettangoli->Dominio.right - Rettangoli->Dominio.left;this->Click.Attinenze = 0;this->Punto.X = this->Punto.Y = 1;SetRect(&this->Click.Regione,0,0,0,0);Temp = new(CELLA_LINEA);Temp->NumeroLinea = NumeroDiLinee;;Temp->Linea = new SCOSTAMENTO[NumeroDiColonne];Temp->Precedente = NULL;Temp->Successivo = NULL;Testa = Coda = Temp;i = NumeroDiLinee;while(i > 0)

36

{Temp = new(CELLA_LINEA);Temp->NumeroLinea = i - 1;Temp->Linea = new SCOSTAMENTO[NumeroDiColonne];Temp->Precedente = Coda;Temp->Successivo = NULL;Coda->Successivo = Temp;Coda = Temp;i--;

}this->Attivo = true;

}void Decisore::Pulisci(){// Elimino gli elementi della lista e con essi gli array associati:

Indice = Coda->Precedente;delete[] Coda->Linea;delete Coda;while(Indice != NULL){

Coda = Indice;Indice = Coda->Precedente;delete[] Coda->Linea;delete Coda;

}Testa = Coda = Indice = Temp = NULL;

}UINT Decisore::GetDecisione(){

Contatore++;if(Contatore >= (NUMERO_AQUISIZIONI - 1)){

Contatore = 0;// Trovo il punto mediano di Lento:

X = (float)((Rettangoli->Lento.right + Rettangoli->Lento.left) / 2);Y = (float)((Rettangoli->Lento.top + Rettangoli->Lento.bottom) / 2);

// Controllo se il Punto mediano di Lento è nelle "vicinanze" del Dominio:if((X > (Rettangoli->Dominio.left - BORDO_DOMINIO)) &&

(X < Rettangoli->Dominio.left))X = (float)Rettangoli->Dominio.left;

if((X < (Rettangoli->Dominio.right + BORDO_DOMINIO)) &&(X > Rettangoli->Dominio.right))

X = (float)Rettangoli->Dominio.right;if((Y > (Rettangoli->Dominio.bottom - BORDO_DOMINIO)) &&

(Y < Rettangoli->Dominio.bottom))Y = (float)Rettangoli->Dominio.bottom;

if((Y < (Rettangoli->Dominio.top + BORDO_DOMINIO)) &&(Y > Rettangoli->Dominio.top))

Y = (float)Rettangoli->Dominio.top;// Se il Punto mediano di Lento è contenuto nel Dominio:

if((X >= Rettangoli->Dominio.left) &&(X <= Rettangoli->Dominio.right) &&(Y >= Rettangoli->Dominio.bottom) &&(Y <= Rettangoli->Dominio.top))

{// Lo setto nel SdR del Dominio:

X = X - Rettangoli->Dominio.left;Y = Y - Rettangoli->Dominio.bottom;

// Gli aggiungo lo Scostamento relativo:/* if((Scostamento = GetScostamento(X,Y)) != NULL)

{X = X + Scostamento->x;Y = Y + Scostamento->y;

}*/// Lo traslo sulla Finestra Applicazione e lo metto nell'area publica:

Punto.X = (1 - (X / NumeroDiColonne));Punto.Y = (1 - (Y / NumeroDiLinee));

// Controllo se il punto è attinente al Click:if((X >= Click.Regione.left) &&

(X <= Click.Regione.right) &&(Y >= Click.Regione.bottom) &&(Y <= Click.Regione.top))

{Click.Attinenze++;if(Click.Attinenze > NUMERO_AQUISIZIONI_AREA_CLICK){

Click.Attinenze = 0;SetRect(&this->Click.Regione,(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,

(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);return AN_RET_CLICK_TEMPORALE;

}}else

37

{Click.Attinenze = 0;SetRect(&this->Click.Regione,(int)X-BORDO_CLICK,(int)Y+BORDO_CLICK,

(int)X+BORDO_CLICK,(int)Y-BORDO_CLICK);}return AN_RET_SI_DECISIONE;

}}return AN_RET_NO_DECISIONE;

}Decisore::SCOSTAMENTO*Decisore::GetScostamento(long X,long Y){

Indice = Testa;while(Indice != NULL){

if(Temp->NumeroLinea == Y) return &Temp->Linea[X];Temp = Temp->Successivo;

}return NULL;

}

Thread principale void ThreadIntercettaFrontale(PVOID pvoid){UINT StatoCattura;UINT StatoProgresso;UINT TemporaneoCattura;UINT TemporaneoStato;UINT TemporaneoStatoProgresso;PPARAMS pparams = (PPARAMS) pvoid;

while (!pparams->Ucciso){

StatoCattura = STATO_CATTURA_INIZIO;if(pparams->Stato == STATO_ZERO){

pparams->Mago->Lock();while ((!pparams->Ucciso) && (pparams->Stato == STATO_ZERO)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,

pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();

InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);

}}if((pparams->Stato >= STATO_UNO) && (pparams->Stato <= STATO_NOVE)){

pparams->Mago->UnLock(pparams->Stato);TemporaneoStato = pparams->Stato;StatoProgresso = STATO_PROGRESSO_INIZIO;while ((!pparams->Ucciso) && (pparams->Stato == TemporaneoStato)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,

pparams->Tipo,StatoCattura);}TemporaneoStatoProgresso = pparams->Mago->GetProgresso();if(StatoProgresso != TemporaneoStatoProgresso){

StatoProgresso = TemporaneoStatoProgresso;if(StatoProgresso == STATO_PROGRESSO_FINE){

pparams->Angelo->HandShake(AN_ASK_FINE_STATO,AN_THREAD_INTERCETTA,pparams->Stato);

pparams->Mago->Lock();StatoProgresso = STATO_PROGRESSO_INIZIO;

}pparams->Angelo->Messaggio(AN_ASK_STATO_PROGRESSO_MUTATO,

pparams->Tipo,StatoProgresso);}Sleep(SLEEP_THREAD_FRONTALE);

}

38

}if(pparams->Stato == STATO_MANUALE){

pparams->Mago->UnLock(pparams->Stato);while ((!pparams->Ucciso) && (pparams->Stato == STATO_MANUALE)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,

pparams->Tipo,StatoCattura);}pparams->Rettangoli = pparams->Mago->GetRettangoli();InvalidateRect(pparams->hwnd,NULL,false);Sleep(SLEEP_THREAD_FRONTALE);

}}if(pparams->Stato == STATO_LAVORO){

pparams->Mago->Lock();while ((!pparams->Ucciso) && (pparams->Stato == STATO_LAVORO)){

TemporaneoCattura = pparams->Mago->GetPosizioneI();if(StatoCattura != TemporaneoCattura){

StatoCattura = TemporaneoCattura;pparams->Angelo->Messaggio(AN_ASK_STATO_CATTURA_MUTATO,

pparams->Tipo,StatoCattura);}switch (pparams->Oz->GetDecisione()){case AN_RET_NO_DECISIONE:

break;case AN_RET_SI_DECISIONE:

pparams->Angelo->Messaggio(AN_ASK_DECISIONE_AVVENUTA);break;

case AN_RET_CLICK_TEMPORALE:pparams->Angelo->Messaggio(AN_ASK_CLICK_TEMPORALE_AVVENUTA);break;

}Sleep(SLEEP_THREAD_FRONTALE);

}}

}pparams->Mago->Free();_endthread();

}

Thread di calibrazione void ThreadPosiziona(PVOID pvoid){UINT TemporaneoStato;PPARAMS_POSIZIONA pparams = (PPARAMS_POSIZIONA) pvoid;

Sleep(1000);while(!pparams->Ucciso){

InvalidateRect(pparams->hwnd,NULL,true);if(pparams->Stato <= STATO_NOVE){

TemporaneoStato = pparams->Stato;pparams->Angelo->HandShake(AN_ASK_INIZIO_STATO,AN_THREAD_POSIZIONA,

pparams->Stato);while((!pparams->Ucciso) && (pparams->Stato == TemporaneoStato))

Sleep(200);}else{

Sleep(2000);pparams->Angelo->HandShake(AN_ASK_FINE_SETTAGGIO,AN_THREAD_POSIZIONA,0);pparams->Ucciso = true;

}}pparams->Stato = STATO_ZERO;_endthread();

}

39

Appendice C Windows procedure delle sottofinestre

Tavolozza LRESULT CALLBACK PaletteFrontaleProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){HDC hdc;PAINTSTRUCT ps;static HPEN PennaContorno;static COLORE ColoreScelto;static CELLA_COLORE_LS *Testa,*Indice;static int Elementi;static bool TavolozzaAttiva;int i;

switch (message){

case WM_CREATE:PennaContorno = CreatePen(PS_SOLID,1,RGB(0,0,0));ColoreScelto.Blu = ColoreScelto.Verde = ColoreScelto.Rosso = 255;return 0;

case WM_PAINT:hdc = BeginPaint(hwnd,&ps);

if(TavolozzaAttiva){

SelectObject(hdc,PennaContorno);SelectObject(hdc,CreateSolidBrush

(RGB(ColoreScelto.Rosso,ColoreScelto.Verde,ColoreScelto.Blu)));

Rectangle(hdc,10,10,40,40);i = 0;Elementi = 0;for(Indice = Testa; Indice != NULL; Indice = Indice->Successivo){

SelectObject(hdc,CreateSolidBrush(RGB(Indice->Rosso,Indice->Verde,Indice->Blu)));

Rectangle(hdc,50+i,10,61+i,40);i = i + 10;Elementi++;DeleteObject(SelectObject(hdc,GetStockObject(WHITE_BRUSH)));

}}DeleteObject(SelectObject(hdc,GetStockObject(WHITE_BRUSH)));EndPaint(hwnd,&ps);return 0;

case WM_STOP_THREAD_I:TavolozzaAttiva = false;InvalidateRect(hwnd,NULL,true);return 0;

case WM_TAVOLOZZA_PRONTA:Testa = (CELLA_COLORE_LS*) lParam;TavolozzaAttiva = true;InvalidateRect(hwnd,NULL,true);return 0;

case WM_LBUTTONDOWN:int x,y ;x = LOWORD(lParam);y = HIWORD(lParam);if(TavolozzaAttiva){

Indice = Testa;if((y >= 10) && (y < 40) && (x >= 50) && (x < ((Elementi*10)+50)))

{for(i = 0; i < ((x-50)/10); i++)

Indice = Indice->Successivo;ColoreScelto.Blu = Indice->Blu;ColoreScelto.Verde = Indice->Verde;ColoreScelto.Rosso = Indice->Rosso;

Angelo.Messaggio(AN_ASK_INTERCETTA_COLORE,AN_WEB_FRONTALE,Indice->TipoColore);InvalidateRect(hwnd,NULL,true);

}}return 0;

case WM_DESTROY:

40

DeleteObject(PennaContorno);return 0;

}return DefWindowProc (hwnd, message, wParam, lParam) ;

}

Immagine LRESULT CALLBACK ImmagineFrontaleProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){

HDC hdc;PAINTSTRUCT ps;

static PARAMS params;static CELLA_POSIZIONE *Testa,*Temp;static bool ImmagineAttiva,ColoreAttivo,PosizioniAttive;static HPEN PennaZona,PennaFocus,PennaDominio,PennaLento,PennaPosizioni;

switch (message){

case WM_CREATE:PennaZona = CreatePen(PS_SOLID,1,RGB(0,255,0));PennaDominio = CreatePen(PS_SOLID,1,RGB(255,0,0));PennaFocus = CreatePen(PS_SOLID,1,RGB(0,0,255));PennaLento = CreatePen(PS_SOLID,1,RGB(255,255,0));PennaPosizioni = CreatePen(PS_SOLID,1,RGB(255,0,255));ImmagineAttiva = false;ColoreAttivo = false;PosizioniAttive = false;params.Stato = STATO_ZERO;params.Tipo = AN_WEB_FRONTALE;params.hwnd = hwnd;params.Angelo = &Angelo;return 0;

case WM_PAINT:hdc = BeginPaint (hwnd, &ps);

if(ImmagineAttiva){imgdes* Immagine;

Immagine = params.Immagine;SetDIBitsToDevice(hdc,0,0, // X,Y Posizioni del DeviceContext(unsigned)Immagine->bmh->biWidth, // Larghezza Immagine(unsigned)Immagine->bmh->biHeight, // Altezza Immagine0,0, // Posizione di partenza nell'immagine0, // linea di partenza(unsigned)Immagine->bmh->biHeight, // Numero di linee da visualizzareImmagine->ibuff, // Indirizzo del Ibuffer(BITMAPINFO far *)Immagine->bmh, // Indirizzo del BITMAPINFODIB_RGB_COLORS); // Tipo di colore usato

}if(ColoreAttivo){SelectObject(hdc,PennaZona);MoveToEx(hdc,params.Rettangoli->Zona.left,(240-params.Rettangoli->Zona.top),NULL);LineTo(hdc,params.Rettangoli->Zona.left,(240-params.Rettangoli->Zona.bottom));LineTo(hdc,params.Rettangoli->Zona.right,(240-params.Rettangoli->Zona.bottom));LineTo(hdc,params.Rettangoli->Zona.right,(240-params.Rettangoli->Zona.top));LineTo(hdc,params.Rettangoli->Zona.left,(240-params.Rettangoli->Zona.top));SelectObject(hdc,PennaDominio);MoveToEx(hdc,params.Rettangoli->Dominio.left,

(240-params.Rettangoli->Dominio.top),NULL);LineTo(hdc,params.Rettangoli->Dominio.left,

(240-params.Rettangoli->Dominio.bottom));LineTo(hdc,params.Rettangoli->Dominio.right,

(240-params.Rettangoli->Dominio.bottom));LineTo(hdc,params.Rettangoli->Dominio.right,(240-params.Rettangoli->Dominio.top));LineTo(hdc,params.Rettangoli->Dominio.left,(240-params.Rettangoli->Dominio.top));SelectObject(hdc,PennaFocus);MoveToEx(hdc,params.Rettangoli->Focus.left,(240-params.Rettangoli->Focus.top),NULL);LineTo(hdc,params.Rettangoli->Focus.left,(240-params.Rettangoli->Focus.bottom));LineTo(hdc,params.Rettangoli->Focus.right,(240-params.Rettangoli->Focus.bottom));LineTo(hdc,params.Rettangoli->Focus.right,(240-params.Rettangoli->Focus.top));LineTo(hdc,params.Rettangoli->Focus.left,(240-params.Rettangoli->Focus.top));SelectObject(hdc,PennaLento);MoveToEx(hdc,params.Rettangoli->Lento.left,(240-params.Rettangoli->Lento.top),NULL);LineTo(hdc,params.Rettangoli->Lento.left,(240-params.Rettangoli->Lento.bottom));LineTo(hdc,params.Rettangoli->Lento.right,(240-params.Rettangoli->Lento.bottom));LineTo(hdc,params.Rettangoli->Lento.right,(240-params.Rettangoli->Lento.top));LineTo(hdc,params.Rettangoli->Lento.left,(240-params.Rettangoli->Lento.top));}if(PosizioniAttive){

SelectObject(hdc,PennaPosizioni);Temp = Testa;

41

while(Temp != NULL){

MoveToEx(hdc,Temp->Posizione.left,(240-Temp->Posizione.top),NULL);LineTo(hdc,Temp->Posizione.left,(240-Temp->Posizione.bottom));LineTo(hdc,Temp->Posizione.right,(240-Temp->Posizione.bottom));LineTo(hdc,Temp->Posizione.right,(240-Temp->Posizione.top));LineTo(hdc,Temp->Posizione.left,(240-Temp->Posizione.top));Temp = Temp->Successivo;

}}

EndPaint (hwnd, &ps);return 0 ;case WM_SINGOLA_IMMAGINE_I:

params.Immagine = (imgdes*) lParam;ImmagineAttiva = true;InvalidateRect(hwnd,NULL,true);return 0;

case WM_COLORE_PRONTO:params.Rettangoli = (RETTANGOLI*) lParam;ColoreAttivo = true;InvalidateRect(hwnd,NULL,true);return 0;

case WM_POSIZIONI_PRONTE:Testa = ((CELLA_POSIZIONE*) lParam)->Successivo;PosizioniAttive = true;return 0;

case WM_START_THREAD_I:params.Oz = (Decisore*) wParam;params.Mago = (Intercetta*) lParam;params.Ucciso = false;ColoreAttivo = true;ImmagineAttiva = true;_beginthread(ThreadIntercettaFrontale,0,&params);return 0;

case WM_STATO_MUTATO:params.Stato = (UINT) lParam;break;

case WM_STOP_THREAD_I:params.Ucciso = true;ImmagineAttiva = false;ColoreAttivo = false;PosizioniAttive = false;InvalidateRect(hwnd,NULL,true);return 0;

case WM_SINGOLA_IMMAGINE_II:params.Immagine = (imgdes*) lParam;ImmagineAttiva = true;InvalidateRect(hwnd,NULL,true);return 0;

case WM_START_THREAD_II:params.Mago = (Intercetta*) lParam;params.Ucciso = false;ImmagineAttiva = true;_beginthread(ThreadIntercettaII,0,&params);return 0;

case WM_STOP_THREAD_II:params.Ucciso = true;ImmagineAttiva = false;return 0;

case WM_DESTROY:DeleteObject(PennaZona);DeleteObject(PennaFocus);DeleteObject(PennaDominio);DeleteObject(PennaLento);DeleteObject(PennaPosizioni);params.Ucciso = true;return 0;

}return DefWindowProc(hwnd,message,wParam,lParam);

}

Controlli LRESULT CALLBACK ControlliProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){const short VELOCITA_THREAD_FRONTALE = 1;const short VELOCITA_THREAD_LATERALE = 4;const short AQUISIZIONI = 7;const short CLICK_TEMPORALE = 10;const short BORDO_CLICH_TEMPORALE = 13;const short FOCUS = 16;HDC hdc;

42

PAINTSTRUCT ps;HINSTANCE hInstance;TCHAR Buffer[4];static RECT Invalido;static UINT StatoFrontale,StatoLaterale;static HPEN PennaContorno;static HBRUSH PennelloSfondo,PennelloNero,PennelloStatoCatturaRosso,

PennelloStatoCatturaGiallo,PennelloStatoCatturaVerde,PennelloStatoCatturaInizio,PennelloRossoSpento,PennelloGialloSpento,PennelloVerdeSpento;

static HWND SleepThreadFrontale,LabelSleepThreadFrontale,ValueSleepThreadFrontale,SleepThreadLaterale,LabelSleepThreadLaterale,ValueSleepThreadLaterale,NumeroAquisizioni,LabelNumeroAquisizioni,ValueNumeroAquisizioni,ClickTemporale,LabelClickTemporale,ValueClickTemporale,BordoClickTemporale,LabelBordoClickTemporale,ValueBordoClickTemporale,Focus,LabelFocus,ValueFocus;

static bool Hit;static int cxClient,cyClient;

switch (message){

case WM_CREATE:hInstance = (HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE);PennaContorno = CreatePen(PS_SOLID,1,RGB(0,0,0));PennelloSfondo = CreateSolidBrush(RGB(170,170,170));PennelloNero = CreateSolidBrush(RGB(100,100,100));PennelloStatoCatturaRosso = CreateSolidBrush(RGB(255,0,0));PennelloStatoCatturaGiallo = CreateSolidBrush(RGB(255,255,0));PennelloStatoCatturaVerde = CreateSolidBrush(RGB(0,255,0));PennelloStatoCatturaInizio = CreateSolidBrush(RGB(255,255,255));PennelloRossoSpento = CreateSolidBrush(RGB(100,0,0));PennelloGialloSpento = CreateSolidBrush(RGB(100,100,0));PennelloVerdeSpento = CreateSolidBrush(RGB(0,100,0));SleepThreadFrontale = CreateWindow(TEXT ("scrollbar"),NULL,

WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 1,hInstance,NULL);

LabelSleepThreadFrontale = CreateWindow(TEXT ("static"),TEXT ("Velocità thread Frontale"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 2,hInstance,NULL);

ValueSleepThreadFrontale = CreateWindow(TEXT ("static"),TEXT ("10"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 3,hInstance,NULL);

SetScrollRange(SleepThreadFrontale,SB_CTL,1,100,false);SetScrollPos(SleepThreadFrontale,SB_CTL,10,false);SleepThreadLaterale = CreateWindow(TEXT ("scrollbar"),NULL,

WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 4,hInstance,NULL);

LabelSleepThreadLaterale = CreateWindow(TEXT ("static"),TEXT ("Velocità thread Laterale"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 5,hInstance,NULL);

ValueSleepThreadLaterale = CreateWindow(TEXT ("static"),TEXT ("10"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 6,hInstance,NULL);

SetScrollRange(SleepThreadLaterale,SB_CTL,1,100,false);SetScrollPos(SleepThreadLaterale,SB_CTL,10,false);NumeroAquisizioni = CreateWindow(TEXT ("scrollbar"),NULL,

WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 7,hInstance,NULL);

LabelNumeroAquisizioni = CreateWindow(TEXT ("static"),TEXT ("Aquisizioni Lento"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 8,hInstance,NULL);

ValueNumeroAquisizioni = CreateWindow(TEXT ("static"),TEXT ("10"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 9,hInstance,NULL);

SetScrollRange(NumeroAquisizioni,SB_CTL,1,30,false);SetScrollPos(NumeroAquisizioni,SB_CTL,10,false);ClickTemporale = CreateWindow(TEXT ("scrollbar"),NULL,

WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 10,hInstance,NULL);

LabelClickTemporale = CreateWindow(TEXT ("static"),TEXT ("Aquisizioni Click"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 11,hInstance,NULL);

ValueClickTemporale = CreateWindow(TEXT ("static"),TEXT ("4"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 12,hInstance,NULL);

SetScrollRange(ClickTemporale,SB_CTL,1,20,false);SetScrollPos(ClickTemporale,SB_CTL,4,false);BordoClickTemporale = CreateWindow(TEXT ("scrollbar"),NULL,

WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 13,hInstance,NULL);

43

LabelBordoClickTemporale = CreateWindow(TEXT ("static"),TEXT ("Dimensione Click"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 14,hInstance,NULL);

ValueBordoClickTemporale = CreateWindow(TEXT ("static"),TEXT ("2"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 15,hInstance,NULL);

SetScrollRange(BordoClickTemporale,SB_CTL,1,10,false);SetScrollPos(BordoClickTemporale,SB_CTL,2,false);Focus = CreateWindow(TEXT ("scrollbar"),NULL,

WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 16,hInstance,NULL);

LabelFocus = CreateWindow(TEXT ("static"),TEXT ("Dimensione Focus"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 17,hInstance,NULL);

ValueFocus = CreateWindow(TEXT ("static"),TEXT ("20"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 18,hInstance,NULL);

SetScrollRange(Focus,SB_CTL,1,30,false);SetScrollPos(Focus,SB_CTL,20,false);StatoFrontale = STATO_CATTURA_INIZIO;StatoLaterale = STATO_CATTURA_INIZIO;return 0;

case WM_SIZE:cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);return 0;

case WM_PAINT:MoveWindow(SleepThreadFrontale,(cxClient*3/17),

(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelSleepThreadFrontale,(cxClient*3/17),

(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueSleepThreadFrontale,(cxClient*5/17),

(cyClient - 70),(cxClient*1/17),20,true);if(Angelo.GetDueWeb()){

MoveWindow(SleepThreadLaterale,(cxClient*3/17),(cyClient - 30),(cxClient*2/17),20,true);

MoveWindow(LabelSleepThreadLaterale,(cxClient*3/17),(cyClient - 50),(cxClient*3/17),20,true);

MoveWindow(ValueSleepThreadLaterale,(cxClient*5/17),(cyClient - 30),(cxClient*1/17),20,true);

}else{

MoveWindow(SleepThreadLaterale,0,0,0,0,true);MoveWindow(LabelSleepThreadLaterale,0,0,0,0,true);MoveWindow(ValueSleepThreadLaterale,0,0,0,0,true);

}MoveWindow(NumeroAquisizioni,(cxClient*6/17),

(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelNumeroAquisizioni,(cxClient*6/17),

(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueNumeroAquisizioni,(cxClient*8/17),

(cyClient - 70),(cxClient*1/17),20,true);MoveWindow(ClickTemporale,(cxClient*9/17),(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelClickTemporale,(cxClient*9/17),

(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueClickTemporale,(cxClient*11/17),

(cyClient - 70),(cxClient*1/17),20,true);MoveWindow(BordoClickTemporale,(cxClient*9/17),

(cyClient - 30),(cxClient*2/17),20,true);MoveWindow(LabelBordoClickTemporale,(cxClient*9/17),

(cyClient - 50),(cxClient*3/17),20,true);MoveWindow(ValueBordoClickTemporale,(cxClient*11/17),

(cyClient - 30),(cxClient*1/17),20,true);MoveWindow(Focus,(cxClient*12/17),(cyClient - 70),(cxClient*2/17),20,true);MoveWindow(LabelFocus,(cxClient*12/17),(cyClient - 90),(cxClient*3/17),20,true);MoveWindow(ValueFocus,(cxClient*14/17),(cyClient - 70),(cxClient*1/17),20,true);hdc = BeginPaint(hwnd,&ps);SelectObject(hdc,PennelloSfondo);Rectangle(hdc,0,0,cxClient,100);SelectObject(hdc,PennaContorno);SelectObject(hdc,PennelloNero);RoundRect(hdc,(cxClient*1/17),10,(cxClient*1/17)+20,70,5,5);SelectObject(hdc,PennelloRossoSpento);RoundRect(hdc,(cxClient*1/17),10,(cxClient*1/17)+20,30,20,20);SelectObject(hdc,PennelloGialloSpento);RoundRect(hdc,(cxClient*1/17),30,(cxClient*1/17)+20,50,20,20);SelectObject(hdc,PennelloVerdeSpento);RoundRect(hdc,(cxClient*1/17),50,(cxClient*1/17)+20,70,20,20);if(Angelo.GetDueWeb())

44

{SelectObject(hdc,PennelloNero);RoundRect(hdc,(cxClient*2/17),10,(cxClient*2/17)+20,70,5,5);SelectObject(hdc,PennelloRossoSpento);RoundRect(hdc,(cxClient*2/17),10,(cxClient*2/17)+20,30,20,20);SelectObject(hdc,PennelloGialloSpento);RoundRect(hdc,(cxClient*2/17),30,(cxClient*2/17)+20,50,20,20);SelectObject(hdc,PennelloVerdeSpento);RoundRect(hdc,(cxClient*2/17),50,(cxClient*2/17)+20,70,20,20);

}switch (StatoFrontale){case STATO_CATTURA_INIZIO:

break;case STATO_CATTURA_VERDE:

SelectObject(hdc,PennelloStatoCatturaVerde);RoundRect(hdc,(cxClient*1/17),50,(cxClient*1/17)+20,70,20,20);break;

case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);RoundRect(hdc,(cxClient*1/17),30,(cxClient*1/17)+20,50,20,20);break;

case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);RoundRect(hdc,(cxClient*1/17),10,(cxClient*1/17)+20,30,20,20);break;

}if(Angelo.GetDueWeb()){

switch (StatoLaterale){case STATO_CATTURA_INIZIO:

break;case STATO_CATTURA_VERDE:

SelectObject(hdc,PennelloStatoCatturaVerde);RoundRect(hdc,(cxClient*2/17),50,(cxClient*2/17)+20,70,20,20);break;

case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);RoundRect(hdc,(cxClient*2/17),30,(cxClient*2/17)+20,50,20,20);break;

case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);RoundRect(hdc,(cxClient*2/17),10,(cxClient*2/17)+20,30,20,20);break;

}}

EndPaint (hwnd, &ps);return 0;

case WM_STATO_CATTURA_MUTATO_FRONTALE:StatoFrontale = (UINT) lParam;SetRect(&Invalido,(cxClient*1/17),10,(cxClient*1/17)+20,70);InvalidateRect(hwnd,&Invalido,true);return 0;

case WM_STATO_CATTURA_MUTATO_LATERALE:StatoLaterale = (UINT) lParam;SetRect(&Invalido,(cxClient*2/17),10,(cxClient*2/17)+20,70);InvalidateRect(hwnd,&Invalido,true);return 0;

case WM_HSCROLL:Hit = false;switch(GetWindowLong((HWND) lParam,GWL_ID)){case VELOCITA_THREAD_FRONTALE:

switch(LOWORD(wParam)){case SB_LINEUP:

SLEEP_THREAD_FRONTALE = max(1,SLEEP_THREAD_FRONTALE - 1);Hit = true;break;

case SB_LINEDOWN:SLEEP_THREAD_FRONTALE = min(100,SLEEP_THREAD_FRONTALE + 1);Hit = true;break;

case SB_THUMBTRACK:SLEEP_THREAD_FRONTALE = HIWORD(wParam);Hit = true;break;

case SB_PAGEDOWN:SLEEP_THREAD_FRONTALE = min(100,SLEEP_THREAD_FRONTALE + 10);Hit = true;break;

45

case SB_PAGEUP:SLEEP_THREAD_FRONTALE = max(1,SLEEP_THREAD_FRONTALE - 10);Hit = true;break;

}if(Hit){

SetScrollPos(SleepThreadFrontale,SB_CTL,SLEEP_THREAD_FRONTALE,true);wsprintf(Buffer,TEXT("%i"),SLEEP_THREAD_FRONTALE);SetWindowText(ValueSleepThreadFrontale,Buffer);SetRect(&Invalido,(cxClient*5/17),(cyClient - 70),

(cxClient*6/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);

}break;

case VELOCITA_THREAD_LATERALE:switch(LOWORD(wParam)){case SB_LINEUP:

SLEEP_THREAD_LATERALE = max(1,SLEEP_THREAD_LATERALE - 1);Hit = true;break;

case SB_LINEDOWN:SLEEP_THREAD_LATERALE = min(100,SLEEP_THREAD_LATERALE + 1);Hit = true;break;

case SB_THUMBTRACK:SLEEP_THREAD_LATERALE = HIWORD(wParam);Hit = true;break;

case SB_PAGEDOWN:SLEEP_THREAD_LATERALE = min(100,SLEEP_THREAD_LATERALE + 10);Hit = true;break;

case SB_PAGEUP:SLEEP_THREAD_LATERALE = max(1,SLEEP_THREAD_LATERALE - 10);Hit = true;break;

}if(Hit){

SetScrollPos(SleepThreadLaterale,SB_CTL,SLEEP_THREAD_LATERALE,true);wsprintf(Buffer,TEXT("%i"),SLEEP_THREAD_LATERALE);SetWindowText(ValueSleepThreadLaterale,Buffer);SetRect(&Invalido,(cxClient*5/17),

(cyClient - 30),(cxClient*6/17),(cyClient - 10));InvalidateRect(hwnd,&Invalido,true);

}break;

case AQUISIZIONI:switch(LOWORD(wParam)){case SB_LINEUP:

NUMERO_AQUISIZIONI = max(1,NUMERO_AQUISIZIONI - 1);Hit = true;break;

case SB_LINEDOWN:NUMERO_AQUISIZIONI = min(30,NUMERO_AQUISIZIONI + 1);Hit = true;break;

case SB_THUMBTRACK:NUMERO_AQUISIZIONI = HIWORD(wParam);Hit = true;break;

case SB_PAGEDOWN:NUMERO_AQUISIZIONI = min(30,NUMERO_AQUISIZIONI + 10);Hit = true;break;

case SB_PAGEUP:NUMERO_AQUISIZIONI = max(1,NUMERO_AQUISIZIONI - 10);Hit = true;break;

}if(Hit){

SetScrollPos(NumeroAquisizioni,SB_CTL,NUMERO_AQUISIZIONI,true);wsprintf(Buffer,TEXT("%i"),NUMERO_AQUISIZIONI);SetWindowText(ValueNumeroAquisizioni,Buffer);SetRect(&Invalido,(cxClient*8/17),(cyClient - 70),

(cxClient*9/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);

}break;

46

case CLICK_TEMPORALE:switch(LOWORD(wParam)){case SB_LINEUP:NUMERO_AQUISIZIONI_AREA_CLICK = max(1,NUMERO_AQUISIZIONI_AREA_CLICK - 1);

Hit = true;break;

case SB_LINEDOWN:NUMERO_AQUISIZIONI_AREA_CLICK = min(20,NUMERO_AQUISIZIONI_AREA_CLICK + 1);

Hit = true;break;

case SB_THUMBTRACK:NUMERO_AQUISIZIONI_AREA_CLICK = HIWORD(wParam);Hit = true;break;

case SB_PAGEDOWN:NUMERO_AQUISIZIONI_AREA_CLICK = min(20,NUMERO_AQUISIZIONI_AREA_CLICK + 10);

Hit = true;break;

case SB_PAGEUP:NUMERO_AQUISIZIONI_AREA_CLICK = max(1,NUMERO_AQUISIZIONI_AREA_CLICK - 10);

Hit = true;break;

}if(Hit){

SetScrollPos(ClickTemporale,SB_CTL,NUMERO_AQUISIZIONI_AREA_CLICK,true);wsprintf(Buffer,TEXT("%i"),NUMERO_AQUISIZIONI_AREA_CLICK);SetWindowText(ValueClickTemporale,Buffer);SetRect(&Invalido,(cxClient*11/17),(cyClient - 70),

(cxClient*12/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);

}break;

case BORDO_CLICH_TEMPORALE:switch(LOWORD(wParam)){case SB_LINEUP:

BORDO_CLICK = max(1,BORDO_CLICK - 1);Hit = true;break;

case SB_LINEDOWN:BORDO_CLICK = min(10,BORDO_CLICK + 1);Hit = true;break;

case SB_THUMBTRACK:BORDO_CLICK = HIWORD(wParam);Hit = true;break;

case SB_PAGEDOWN:BORDO_CLICK = min(10,BORDO_CLICK + 2);Hit = true;break;

case SB_PAGEUP:BORDO_CLICK = max(1,BORDO_CLICK - 2);Hit = true;break;

}if(Hit){

SetScrollPos(BordoClickTemporale,SB_CTL,BORDO_CLICK,true);wsprintf(Buffer,TEXT("%i"),BORDO_CLICK);SetWindowText(ValueBordoClickTemporale,Buffer);SetRect(&Invalido,(cxClient*11/17),(cyClient - 30),

(cxClient*12/17),(cyClient - 10));InvalidateRect(hwnd,&Invalido,true);

}break;

case FOCUS:switch(LOWORD(wParam)){case SB_LINEUP:

BORDO_FOCUS = max(1,BORDO_FOCUS - 1);Hit = true;break;

case SB_LINEDOWN:BORDO_FOCUS = min(30,BORDO_FOCUS + 1);Hit = true;break;

case SB_THUMBTRACK:BORDO_FOCUS = HIWORD(wParam);Hit = true;break;

47

case SB_PAGEDOWN:BORDO_FOCUS = min(30,BORDO_FOCUS + 5);Hit = true;break;

case SB_PAGEUP:BORDO_FOCUS = max(1,BORDO_FOCUS - 5);Hit = true;break;

}if(Hit){

SetScrollPos(Focus,SB_CTL,BORDO_FOCUS,true);wsprintf(Buffer,TEXT("%i"),BORDO_FOCUS);SetWindowText(ValueFocus,Buffer);SetRect(&Invalido,(cxClient*14/17),(cyClient - 70),

(cxClient*15/17),(cyClient - 50));InvalidateRect(hwnd,&Invalido,true);

}break;

}return 0;

case WM_DESTROY:DeleteObject(PennaContorno);DeleteObject(PennelloSfondo);DeleteObject(PennelloNero);DeleteObject(PennelloStatoCatturaRosso);DeleteObject(PennelloStatoCatturaGiallo);DeleteObject(PennelloStatoCatturaVerde);DeleteObject(PennelloStatoCatturaInizio);DeleteObject(PennelloRossoSpento);DeleteObject(PennelloGialloSpento);DeleteObject(PennelloVerdeSpento);return 0;

}return DefWindowProc (hwnd, message, wParam, lParam) ;

}

Posiziona LRESULT CALLBACK PosizionaProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){

HDC hdc;PAINTSTRUCT ps;

static PARAMS_POSIZIONA params;static HPEN PennaPuntoRosso,PennaPuntoGiallo,PennaPuntoVerde,PennaPuntoInizio,

PennaBackRosso,PennaBackGiallo,PennaBackVerde,PennaBackInizio;RECT Rect;

static UINT StatoCatturaFrontale,StatoCatturaLaterale,StatoProgresso;switch (message){case WM_CREATE:

PennaPuntoRosso = CreatePen(PS_SOLID,18,RGB(255,0,0));PennaPuntoGiallo = CreatePen(PS_SOLID,18,RGB(255,255,0));PennaPuntoVerde = CreatePen(PS_SOLID,18,RGB(0,255,0));PennaPuntoInizio = CreatePen(PS_SOLID,18,RGB(0,255,0));PennaBackRosso = CreatePen(PS_SOLID,20,RGB(255,0,0));PennaBackGiallo = CreatePen(PS_SOLID,20,RGB(255,255,0));PennaBackVerde = CreatePen(PS_SOLID,20,RGB(0,255,0));PennaBackInizio = CreatePen(PS_SOLID,20,RGB(255,255,255));params.hwnd = hwnd;params.Angelo = &Angelo;params.Stato = STATO_ZERO;StatoCatturaFrontale = STATO_CATTURA_INIZIO;StatoCatturaLaterale = STATO_CATTURA_INIZIO;StatoProgresso = STATO_PROGRESSO_INIZIO;return 0;

case WM_PAINT:hdc = BeginPaint(hwnd,&ps);

switch (StatoProgresso){case STATO_PROGRESSO_INIZIO:

SelectObject(hdc,PennaBackInizio);break;

case STATO_PROGRESSO_UNO:SelectObject(hdc,PennaBackRosso);break;

case STATO_PROGRESSO_DUE:SelectObject(hdc,PennaBackGiallo);break;

case STATO_PROGRESSO_TRE:SelectObject(hdc,PennaBackVerde);break;

48

}GetClientRect(hwnd,&Rect);switch (params.Stato){case STATO_UNO:

DrawText(hdc,TEXT ("Segui il pallino !"),-1,&Rect,DT_SINGLELINE | DT_CENTER );

MoveToEx(hdc,Rect.right/2,Rect.bottom/2,NULL);LineTo(hdc,Rect.right/2,Rect.bottom/2);break;

case STATO_DUE:MoveToEx(hdc,10,Rect.bottom/2,NULL);LineTo(hdc,10,Rect.bottom/2);break;

case STATO_TRE:MoveToEx(hdc,10,Rect.bottom-10,NULL);LineTo(hdc,10,Rect.bottom-10);break;

case STATO_QUATTRO:MoveToEx(hdc,Rect.right/2,Rect.bottom-10,NULL);LineTo(hdc,Rect.right/2,Rect.bottom-10);break;

case STATO_CINQUE:MoveToEx(hdc,Rect.right-10,Rect.bottom-10,NULL);LineTo(hdc,Rect.right-10,Rect.bottom-10);break;

case STATO_SEI:MoveToEx(hdc,Rect.right-10,Rect.bottom/2,NULL);LineTo(hdc,Rect.right-10,Rect.bottom/2);break;

case STATO_SETTE:MoveToEx(hdc,Rect.right-10,10,NULL);LineTo(hdc,Rect.right-10,10);break;

case STATO_OTTO:MoveToEx(hdc,Rect.right/2,10,NULL);LineTo(hdc,Rect.right/2,10);break;

case STATO_NOVE:MoveToEx(hdc,10,10,NULL);LineTo(hdc,10,10);break;

case STATO_FINE:DrawText(hdc,TEXT ("Settaggio concluso !"),-1,

&Rect,DT_SINGLELINE | DT_CENTER );break;

}switch (StatoCatturaFrontale){case STATO_CATTURA_INIZIO:case STATO_CATTURA_VERDE:

switch (StatoCatturaLaterale){case STATO_CATTURA_INIZIO:case STATO_CATTURA_VERDE:

SelectObject(hdc,PennaPuntoVerde);break;

case STATO_CATTURA_GIALLO:SelectObject(hdc,PennaPuntoGiallo);break;

case STATO_CATTURA_ROSSO:SelectObject(hdc,PennaPuntoRosso);break;

}break;

case STATO_CATTURA_GIALLO:switch (StatoCatturaLaterale){case STATO_CATTURA_INIZIO:case STATO_CATTURA_VERDE:case STATO_CATTURA_GIALLO:

SelectObject(hdc,PennaPuntoGiallo);break;

case STATO_CATTURA_ROSSO:SelectObject(hdc,PennaPuntoRosso);break;

}break;

case STATO_CATTURA_ROSSO:SelectObject(hdc,PennaPuntoRosso);break;

}switch (params.Stato)

49

{case STATO_UNO:

MoveToEx(hdc,Rect.right/2,Rect.bottom/2,NULL);LineTo(hdc,Rect.right/2,Rect.bottom/2);break;

case STATO_DUE:MoveToEx(hdc,10,Rect.bottom/2,NULL);LineTo(hdc,10,Rect.bottom/2);break;

case STATO_TRE:MoveToEx(hdc,10,Rect.bottom-10,NULL);LineTo(hdc,10,Rect.bottom-10);break;

case STATO_QUATTRO:MoveToEx(hdc,Rect.right/2,Rect.bottom-10,NULL);LineTo(hdc,Rect.right/2,Rect.bottom-10);break;

case STATO_CINQUE:MoveToEx(hdc,Rect.right-10,Rect.bottom-10,NULL);LineTo(hdc,Rect.right-10,Rect.bottom-10);break;

case STATO_SEI:MoveToEx(hdc,Rect.right-10,Rect.bottom/2,NULL);LineTo(hdc,Rect.right-10,Rect.bottom/2);break;

case STATO_SETTE:MoveToEx(hdc,Rect.right-10,10,NULL);LineTo(hdc,Rect.right-10,10);break;

case STATO_OTTO:MoveToEx(hdc,Rect.right/2,10,NULL);LineTo(hdc,Rect.right/2,10);break;

case STATO_NOVE:MoveToEx(hdc,10,10,NULL);LineTo(hdc,10,10);break;

case STATO_FINE:DrawText(hdc,TEXT ("Settaggio concluso !"),-1,&Rect,

DT_SINGLELINE | DT_CENTER );break;

}EndPaint(hwnd,&ps);return 0 ;case WM_STATO_CATTURA_MUTATO_FRONTALE:

StatoCatturaFrontale = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;

case WM_STATO_CATTURA_MUTATO_LATERALE:StatoCatturaLaterale = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;

case WM_STATO_PROGRESSO_MUTATO:StatoProgresso = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;

case WM_STATO_MUTATO:params.Stato = (UINT) lParam;return 0;

case WM_START_POSIZIONA:params.Stato = STATO_UNO;StatoCatturaFrontale = STATO_CATTURA_INIZIO;StatoCatturaLaterale = STATO_CATTURA_INIZIO;params.Ucciso= false;_beginthread(ThreadPosiziona,0,&params);return 0;

case WM_STOP_POSIZIONA:params.Ucciso = true;return 0;

case WM_DESTROY:params.Ucciso = true;DeleteObject(PennaPuntoRosso);DeleteObject(PennaPuntoGiallo);DeleteObject(PennaPuntoVerde);DeleteObject(PennaPuntoInizio);DeleteObject(PennaBackRosso);DeleteObject(PennaBackGiallo);DeleteObject(PennaBackVerde);DeleteObject(PennaBackInizio);return 0;

}return DefWindowProc(hwnd,message,wParam,lParam) ;

}

50

51

Appendice D Windows procedure delle applicazioni indipendenti

Applicazione Uno: addestramento LRESULT CALLBACK DemoUnoProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){const short DIMENSIONE_BERSAGLIO = 40;const short BORDO_INVALIDO = 5;

HDC hdc;PAINTSTRUCT ps;

static PUNTO *Punto,Precedente;static bool Click,PuntoAttivo;static UINT StatoCattura;static HPEN PennaPunto,PennaBersaglio,PennaContorno;static HBRUSH PennelloStatoCatturaRosso,PennelloStatoCatturaGiallo,

PennelloStatoCatturaVerde,PennelloBersaglio;static RECT Bersaglio,Invalido;static int cxClient,cyClient;

switch (message){case WM_CREATE:

PennaPunto = CreatePen(PS_SOLID,10,RGB(0,0,255));PennaBersaglio = CreatePen(PS_SOLID,2,RGB(255,0,0));PennaContorno = CreatePen(PS_SOLID,1,RGB(0,0,0));PennelloStatoCatturaRosso = CreateSolidBrush(RGB(255,0,0));PennelloStatoCatturaGiallo = CreateSolidBrush(RGB(255,255,0));PennelloStatoCatturaVerde = CreateSolidBrush(RGB(0,255,0));PennelloBersaglio = CreateSolidBrush(RGB(255,255,255));PuntoAttivo = false;Click = false;StatoCattura = STATO_CATTURA_INIZIO;return 0;

case WM_SIZE:cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);Bersaglio.left = (rand() % (cxClient - DIMENSIONE_BERSAGLIO));Bersaglio.top = (rand() % (cyClient - DIMENSIONE_BERSAGLIO));Bersaglio.right = Bersaglio.left + DIMENSIONE_BERSAGLIO;Bersaglio.bottom = Bersaglio.top + DIMENSIONE_BERSAGLIO;return 0;

case WM_CREATE_PUNTO:Punto = (PUNTO*) lParam;Precedente.X = Punto->X;Precedente.Y = Punto->Y;PuntoAttivo = true;return 0;

case WM_NON_VALIDO:if(Precedente.X >= Punto->X){

Invalido.left = (int)(Punto->X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Precedente.X * cxClient) + BORDO_INVALIDO;

}else{

Invalido.left = (int)(Precedente.X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Punto->X * cxClient) + BORDO_INVALIDO;

}if(Precedente.Y >= Punto->Y)

{Invalido.top = (int)(Punto->Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Precedente.Y * cyClient) + BORDO_INVALIDO;

}else{

Invalido.top = (int)(Precedente.Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Punto->Y * cyClient) + BORDO_INVALIDO;

}InvalidateRect(hwnd,&Invalido,true);return 0;

case WM_CLICK:Click = true;InvalidateRect(hwnd,NULL,true);return 0;

52

case WM_STATO_CATTURA_MUTATO:StatoCattura = (UINT) lParam;InvalidateRect(hwnd,NULL,true);return 0;

case WM_PAINT:hdc = BeginPaint(hwnd,&ps);switch (StatoCattura){case STATO_CATTURA_VERDE:

SelectObject(hdc,PennelloStatoCatturaVerde);break;

case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);break;

case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);break;

}SelectObject(hdc,PennaContorno);RoundRect(hdc,10,10,30,50,10,10);SelectObject(hdc,PennaBersaglio);SelectObject(hdc,PennelloBersaglio);Rectangle(hdc,Bersaglio.left,Bersaglio.top,Bersaglio.right,Bersaglio.bottom);if(PuntoAttivo){

if(Click){

Click = false;if(((int)(Punto->X * cxClient) >= Bersaglio.left) &&

((int)(Punto->X * cxClient) <= Bersaglio.right) &&((int)(Punto->Y * cyClient) >= Bersaglio.top) &&((int)(Punto->Y * cyClient) <= Bersaglio.bottom))

{Bersaglio.left = (rand() % (cxClient - DIMENSIONE_BERSAGLIO));Bersaglio.top = (rand() % (cyClient - DIMENSIONE_BERSAGLIO));Bersaglio.right = Bersaglio.left + DIMENSIONE_BERSAGLIO;Bersaglio.bottom = Bersaglio.top + DIMENSIONE_BERSAGLIO;InvalidateRect(hwnd,NULL,true);

}}SelectObject(hdc,PennaPunto);MoveToEx(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient),NULL);LineTo(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient));Precedente.X = Punto->X;Precedente.Y = Punto->Y;

}EndPaint(hwnd,&ps);return 0;

case WM_DESTROY:DeleteObject(PennaPunto);DeleteObject(PennaBersaglio);DeleteObject(PennaContorno);DeleteObject(PennelloStatoCatturaRosso);DeleteObject(PennelloStatoCatturaGiallo);DeleteObject(PennelloStatoCatturaVerde);DeleteObject(PennelloBersaglio);return 0;

}return DefWindowProc (hwnd,message,wParam,lParam) ;

}

Applicazione Due: tastiera #include "Spaziatore.h"

LRESULT CALLBACK DemoDueProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){const short BORDO_INVALIDO = 5;

HDC hdc;PAINTSTRUCT ps;TCHAR Buffer[3];

static PUNTO *Punto,Precedente;static bool Click,PuntoAttivo;static RECT Invalido;static HPEN PennaPunto,PennaCella,PennaFrase,PennaClick;static HBRUSH PennelloStatoCatturaRosso,PennelloStatoCatturaGiallo,

PennelloStatoCatturaVerde;static UINT StatoCattura;static HWND Scroll,Label,Value,Frase;static Spaziatore Griglia;HINSTANCE hInstance;static int Definizione,cxClient,cyClient;

53

bool Hit;switch (message){case WM_CREATE:

hInstance = (HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE);PennaPunto = CreatePen(PS_SOLID,10,RGB(255,0,0));PennaClick = CreatePen(PS_SOLID,10,RGB(0,255,0));PennaCella = CreatePen(PS_SOLID,2,RGB(40,190,230));PennaFrase = CreatePen(PS_SOLID,1,RGB(0,0,0));PennelloStatoCatturaRosso = CreateSolidBrush(RGB(255,0,0));PennelloStatoCatturaGiallo = CreateSolidBrush(RGB(255,255,0));PennelloStatoCatturaVerde = CreateSolidBrush(RGB(0,255,0));StatoCattura = STATO_CATTURA_VERDE;PuntoAttivo = false;Click = false;Definizione = 1;Scroll = CreateWindow(TEXT ("scrollbar"),NULL,

WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_HORZ,0,0,0,0,hwnd,(HMENU) 1,hInstance,NULL) ;

SetScrollRange(Scroll,SB_CTL,1,5,FALSE);SetScrollPos(Scroll,SB_CTL,1,FALSE);Label = CreateWindow(TEXT ("static"),TEXT ("Dimensione Tasti"),

WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 2,hInstance,NULL);

Value = CreateWindow(TEXT ("static"),TEXT ("1"),WS_CHILD | WS_VISIBLE | SS_CENTER,0,0,0,0,hwnd,(HMENU) 3,hInstance,NULL);

return 0;case WM_SIZE:

cxClient = LOWORD (lParam);cyClient = HIWORD (lParam);Griglia.Set(cxClient,(cyClient - 80),Definizione);MoveWindow(Scroll,(cxClient-140),(cyClient - 40),100,20,true);MoveWindow(Label,(cxClient-200),(cyClient - 60),160,20,true);MoveWindow(Value,(cxClient-200),(cyClient - 40),60,20,true);return 0;

case WM_CREATE_PUNTO:Punto = (PUNTO*) lParam;Precedente.X = Punto->X;Precedente.Y = Punto->Y;PuntoAttivo = true;return 0;

case WM_NON_VALIDO:if(Precedente.X >= Punto->X){

Invalido.left = (int)(Punto->X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Precedente.X * cxClient) + BORDO_INVALIDO;

}else{

Invalido.left = (int)(Precedente.X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Punto->X * cxClient) + BORDO_INVALIDO;

}if(Precedente.Y >= Punto->Y){

Invalido.top = (int)(Punto->Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Precedente.Y * cyClient) + BORDO_INVALIDO;

}else{

Invalido.top = (int)(Precedente.Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Punto->Y * cyClient) + BORDO_INVALIDO;

}InvalidateRect(hwnd,&Invalido,true);return 0;

case WM_CLICK:Griglia.GetFrase((int)(Punto->X * cxClient),(int)(Punto->Y * cyClient));Click = true;if(Precedente.X >= Punto->X){

Invalido.left = (int)(Punto->X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Precedente.X * cxClient) + BORDO_INVALIDO;

}else{

Invalido.left = (int)(Precedente.X * cxClient) - BORDO_INVALIDO;Invalido.right = (int)(Punto->X * cxClient) + BORDO_INVALIDO;

}if(Precedente.Y >= Punto->Y){

Invalido.top = (int)(Punto->Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Precedente.Y * cyClient) + BORDO_INVALIDO;

}

54

else{

Invalido.top = (int)(Precedente.Y * cyClient) - BORDO_INVALIDO;Invalido.bottom = (int)(Punto->Y * cyClient) + BORDO_INVALIDO;

}InvalidateRect(hwnd,&Invalido,true);SetRect(&Invalido,10,(cyClient - 60),(cxClient - 200),(cyClient - 20));InvalidateRect(hwnd,&Invalido,true);return 0;

case WM_STATO_CATTURA_MUTATO:StatoCattura = (UINT) lParam;SetRect(&Invalido,(cxClient-30),(cyClient-60),(cxClient-10),(cyClient-20));InvalidateRect(hwnd,NULL,true);return 0;

case WM_PAINT:hdc = BeginPaint (hwnd, &ps);SelectObject(hdc,PennaCella);MoveToEx(hdc,0,0,NULL);while(Griglia.Get()){

MoveToEx(hdc,Griglia.Cella.left,Griglia.Cella.top,NULL);LineTo(hdc,Griglia.Cella.left,Griglia.Cella.bottom);LineTo(hdc,Griglia.Cella.right,Griglia.Cella.bottom);LineTo(hdc,Griglia.Cella.right,Griglia.Cella.top);LineTo(hdc,Griglia.Cella.left,Griglia.Cella.top);TextOut(hdc,(Griglia.Cella.left + 5),(Griglia.Cella.top +5),

&Griglia.Lettera,1);}TextOut(hdc,5,5,TEXT ("Canc"),4);SelectObject(hdc,PennaFrase);SelectObject(hdc,WHITE_BRUSH);RoundRect(hdc,10,(cyClient - 60),(cxClient - 220),(cyClient - 20),10,10);TextOut(hdc,15,(cyClient - 50),(char*)&Griglia.Frase,Griglia.ContatoreFrase);switch (StatoCattura){case STATO_CATTURA_VERDE:

SelectObject(hdc,PennelloStatoCatturaVerde);break;

case STATO_CATTURA_GIALLO:SelectObject(hdc,PennelloStatoCatturaGiallo);break;

case STATO_CATTURA_ROSSO:SelectObject(hdc,PennelloStatoCatturaRosso);break;

}RoundRect(hdc,(cxClient - 30),(cyClient - 60),(cxClient - 10),(cyClient - 20),10,10);if(PuntoAttivo){

if(Click){

SelectObject(hdc,PennaClick);Click = false;

}else

SelectObject(hdc,PennaPunto);MoveToEx(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient),NULL);LineTo(hdc,(int)(Punto->X * cxClient),(int)(Punto->Y * cyClient));Precedente.X = Punto->X;Precedente.Y = Punto->Y;

}EndPaint (hwnd, &ps);return 0;

case WM_HSCROLL:Hit = false;switch(LOWORD(wParam)){case SB_LINEUP:

Definizione = max(1,Definizione - 1);Hit = true;break;

case SB_LINEDOWN:Definizione = min(5,Definizione + 1);Hit = true;break;

case SB_THUMBTRACK:Definizione = HIWORD(wParam);Hit = true;break;

case SB_PAGEDOWN:Definizione = min(5,Definizione + 2);Hit = true;break;

case SB_PAGEUP:

55

Definizione = max(1,Definizione - 2);Hit = true;break;

}if(Hit){

Griglia.Set(cxClient,(cyClient - 80),Definizione);SetScrollPos(Scroll,SB_CTL,Definizione,true);wsprintf(Buffer,TEXT("%i"),Definizione);SetWindowText(Value,Buffer);InvalidateRect(hwnd,NULL,true);

}return 0;

case WM_DESTROY:DeleteObject(PennaPunto);DeleteObject(PennaCella);DeleteObject(PennaFrase);DeleteObject(PennelloStatoCatturaRosso);DeleteObject(PennelloStatoCatturaGiallo);DeleteObject(PennelloStatoCatturaVerde);return 0;

}return DefWindowProc (hwnd,message,wParam,lParam) ;

}

Spaziatore #include <math.h>

class Spaziatore{public:

RECT Cella;char Lettera;char Frase[100];short ContatoreFrase;

public:inline Spaziatore() {}void Set(int,int,short);bool Get();void GetFrase(int,int);

private:int A,B,X,Y;short Definizione;short NumeroCelle,NumeroCelleX,NumeroCelleY,

ContatoreX,ContatoreY,ContatoreLettere;static char Celle1[25],Celle2[31],Celle3[41],Celle4[51],Celle5[81];char* Lettere;

};

char Spaziatore::Celle1[25] = " abcdefghilmnopqrstuvz'";char Spaziatore::Celle2[31] = " abcdefghijklmnopqrstuvwxyz'\"";char Spaziatore::Celle3[41] = " abcdefghijklmnopqrstuvwxyz'\"0123456789";char Spaziatore::Celle4[51] = " abcdefghijklmnopqrstuvwxyz'\"0123456789,.;:*+-<>_";char Spaziatore::Celle5[81] = " abcdefghijklmnopqrstuvwxyz'\"0123456789àèéìòù^°ç§\\|£$%&()[],.-

;:_<>@#!+*/ ";

void Spaziatore::Set(int A,int B,short Definizione){

this->A = A;this->B = B;switch (Definizione){case 1:

NumeroCelle = 24;NumeroCelleX = 6;NumeroCelleY = 4;Lettere = (char*)&Celle1;break;

case 2:NumeroCelle = 30;NumeroCelleX = 6;NumeroCelleY = 5;Lettere = (char*)&Celle2;break;

case 3:NumeroCelle = 40;NumeroCelleX = 8;NumeroCelleY = 5;Lettere = (char*)&Celle3;break;

case 4:

56

NumeroCelle = 50;NumeroCelleX = 10;NumeroCelleY = 5;Lettere = (char*)&Celle4;break;

case 5:NumeroCelle = 80;NumeroCelleX = 10;NumeroCelleY = 8;Lettere = (char*)&Celle5;break;

}if((A > 0) && (B > 0)){

X = A / NumeroCelleX;Y = B / NumeroCelleY;

}for(ContatoreFrase = 0; ContatoreFrase < 100; ContatoreFrase++)

Frase[ContatoreFrase] = ' ';ContatoreFrase = ContatoreLettere = ContatoreX = ContatoreY = 0;

}bool Spaziatore::Get(){

if(ContatoreY < NumeroCelleY){

SetRect(&Cella,ContatoreX * X,ContatoreY * Y,(ContatoreX + 1) * X,(ContatoreY + 1) * Y);

Lettera = Lettere[ContatoreLettere];if(ContatoreX < (NumeroCelleX - 1))

ContatoreX++;else{

ContatoreX = 0;ContatoreY++;

}ContatoreLettere++;return true;

}ContatoreLettere = ContatoreY = 0;return false;

}void Spaziatore::GetFrase(int x,int y){short PosizioneX,PosizioneY;

if((x <= (NumeroCelleX * X)) && (y <= (NumeroCelleY * Y))){

PosizioneX = x / X;PosizioneY = y / Y;if(ContatoreFrase == 100){

for(ContatoreFrase = 0; ContatoreFrase < 100; ContatoreFrase++)Frase[ContatoreFrase] = ' ';

ContatoreFrase = 0;}if((PosizioneX == 0) && (PosizioneY == 0)){

if(ContatoreFrase > 0){

ContatoreFrase--;Frase[ContatoreFrase] = ' ';

}}else{

Frase[ContatoreFrase] = Lettere[(NumeroCelleX * PosizioneY) + PosizioneX];ContatoreFrase++;

}}

}

Applicazione Tre LRESULT CALLBACK DemoTreProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){

HDC hdc;PAINTSTRUCT ps;

static PUNTO *Punto;static HPEN PennaPunto;static bool PuntoAttivo;static RECT Rect;

switch (message){

57

case WM_CREATE:PennaPunto = CreatePen(PS_SOLID,10,RGB(0,255,0));PuntoAttivo = false;return 0;

case WM_SIZE:GetClientRect(hwnd,&Rect);return 0;

case WM_CREATE_PUNTO:Punto = (PUNTO*) lParam;PuntoAttivo = true;return 0;

case WM_NON_VALIDO:InvalidateRect(hwnd,NULL,true);return 0;

case WM_PAINT:hdc = BeginPaint (hwnd, &ps);SelectObject(hdc,PennaPunto);if (PuntoAttivo){

MoveToEx(hdc,(int)(Punto->X * Rect.right),(int)(Punto->Y * Rect.bottom),NULL);

LineTo(hdc,(int)(Punto->X * Rect.right),(int)(Punto->Y * Rect.bottom));}EndPaint (hwnd, &ps);return 0;

case WM_DESTROY:DeleteObject(PennaPunto);return 0;

}return DefWindowProc (hwnd,message,wParam,lParam) ;

}

58

59

Appendice E Messaggi definiti da Windows

Resource #define IDR_MENUPRINCIPALE 101#define IDI_ICONA_PRINCIPALE 102#define IDD_OLE_PROPPAGE_LARGE 105#define IDC_OK 1034#define IDC_STATIC_VAR_NOME 1036#define IDC_VAR_IMAGEWIDTH 1036#define IDC_STATIC_NOME 1037#define IDC_VAR_IMAGEHEIGHT 1039#define IDC_VAR_LIVEWINDOW 1041#define IDC_VAR_OVERLAYWINDOW 1043#define IDC_VAR_SCALE 1045#define IDC_VAR_USINGDEFAULTPALETTE 1047#define IDC_VAR_AUDIOHARDWARE 1049#define IDC_VAR_CAPFILEEXISTS 1051#define IDC_VAR_CAPTURINGNOW 1053#define IDC_STATIC_STATO 1054#define IDC_STATIC_CAPACITA 1055#define IDC_VAR_HASOVERLAY 1057#define IDC_VAR_HASDLGVIDEOSOURCE 1059#define IDC_VAR_HASDLGVIDEOFORMAT 1061#define IDC_VAR_HASDLGVIDEODISPLAY 1063#define IDC_VAR_NOMEDRIVER 1066#define IDC_OK_PROPRIETA 1069#define ID_CATTURA_AGGANCIO 40001#define ID_CATTURA_DATI 40002#define ID_CATTURA_RILASCIO 40003#define ID_CATTURA_FOTO 40004#define ID_CATTURA_FINESTRA_FORMATO 40005#define ID_CATTURA_FINESTRA_SORGENTE 40006#define ID_CATTURA_SETTAGGI_OVERLAY 40008#define ID_CATTURA_SETTAGGI_INFORMAZIONI 40009#define ID_CATTURA_ONOFF_2WEB 40012#define ID_CATTURA_TARGET_FILE 40015#define ID_CATTURA_SETTAGGI_SETTAGGIUNO 40016#define ID_CATTURA_PROPRIETA_PROPRIETAUNO 40016#define ID_CATTURA_SETTAGGI_SETTAGGIDUE 40017#define ID_CATTURA_PROPRIETA_PROPRIETADUE 40017#define ID_INTERCETTA_VISUALIZZA_FRONTALE 40018#define ID_INTERCETTA_START_FRONTALE 40020#define ID_INTERCETTA_STOP_FRONTALE 40021#define ID_INTERCETTA_VISUALIZZA_FRONTALE_II 40022#define ID_INTERCETTA_START_FRONTALE_II 40023#define ID_INTERCETTA_STOP_FRONTALE_II 40024#define ID_INTERCETTA_PALETTE_FRONTALE 40025#define ID_INTERCETTA_VISUALIZZA_LATERALE 40031#define ID_INTERCETTA_START_LATERALE 40032#define ID_INTERCETTA_STOP_LATERALE 40033#define ID_INTERCETTA_PALETTE_LATERALE 40034#define ID_POSIZIONA_VAI 40052#define ID_POSIZIONA_AUTOMATICO 40052#define ID_FINESTRE_FINESTRAIMMAGINEFRONTALE 40053#define ID_FINESTRE_FINESTRAIMMAGINELATERALE 40054#define ID_FINESTRE_FINESTRAPALETTEFRONTALE 40055#define ID_FINESTRE_FINESTRAPALETTELATERALE 40056#define ID_FINESTRE_FINESTRACONTROLLI 40057#define ID_FINESTRE_FINESTRAWEBUNO 40058#define ID_FINESTRE_FINESTRAWEBDUE 40059#define ID_FINESTRE_FINESTRAPOSIZIONA 40060#define ID_POSIZIONA_INTERROMPI 40061#define ID_POSIZIONA_MANUALE 40062#define ID_POSIZIONA_STOP 40063#define ID_APPLICAZIONI_DEMOUNO 40065#define ID_POSIZIONA_OK 40066#define ID_APPLICAZIONI_DEMODUE 40067#define ID_APPLICAZIONI_DEMOTRE 40068#define ID_POSIZIONA_RITORNA 40069

// Next default values for new objects//#ifdef APSTUDIO_INVOKED

60

#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE 103#define _APS_NEXT_COMMAND_VALUE 40070#define _APS_NEXT_CONTROL_VALUE 1070#define _APS_NEXT_SYMED_VALUE 101#endif#endif

Oggetti relativi alla gestione

Menu void GestoreMenu::Set(HMENU MenuPrincipale,PCUBE Cube,GestoreFinestre *Finestre){

this->MenuPrincipale = MenuPrincipale;this->Finestre = Finestre;this->Cube = Cube;

}void GestoreMenu::Messaggio(UINT Messaggio){

switch (Messaggio){

// Gestione dei comandi di Cattura:case AN_OK_AGGANCIO:

EnableMenuItem(MenuPrincipale,ID_CATTURA_AGGANCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_FORMATO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_SORGENTE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FOTO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_OVERLAY,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_INFORMAZIONI,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_TARGET_FILE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIUNO,MF_ENABLED);if(Cube->DueWeb){

EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIDUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_ENABLED);

}EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_ENABLED);break;

case AN_OK_RILASCIO:EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_FORMATO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FINESTRA_SORGENTE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_FOTO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_OVERLAY,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_INFORMAZIONI,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_TARGET_FILE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_SETTAGGI_SETTAGGIDUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II ,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_AGGANCIO,MF_ENABLED);break;

// Gestione dei comandi di posiziona:

case AN_OK_START_POSIZIONA:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_CHECKED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_GRAYED);

61

EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_INTERROMPI,MF_ENABLED);break;

case AN_OK_STOP_POSIZIONA:if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_FRONTALE))

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_IMMAGINE_LATERALE))

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_FRONTALE))

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_PALETTE_LATERALE))

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_CONTROLLI))

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_UNO))

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_CHECKED);if(Finestre->GetVisibileSecondarie(FINESTRA_WEB_DUE))

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_CHECKED);CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_UNCHECKED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPOSIZIONA,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_INTERROMPI,MF_GRAYED);break;

case AN_OK_START_MANUALE:EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_STOP,MF_ENABLED);break;

case AN_OK_STOP_MANUALE:EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_STOP,MF_GRAYED);break;

case AN_OK_OK:EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_GRAYED);//...EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE ,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE ,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_RITORNA,MF_ENABLED);break;

case AN_OK_RITORNA:EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_ENABLED);//...EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE ,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE ,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_ENABLED);

62

EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_OK,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_RITORNA,MF_GRAYED);break;

// Gestione sottomenu Applicazioni:

case AN_OK_APPLICAZIONE_PRESCELTA:if(Finestre->GetPresceltaApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO)){

CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_CHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_UNCHECKED);//...

}if(Finestre->GetPresceltaApplicazione(FINESTRA_APPLICAZIONE_DEMO_DUE)){

CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_CHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_UNCHECKED);//...

}if(Finestre->GetPresceltaApplicazione(FINESTRA_APPLICAZIONE_DEMO_TRE)){

CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOUNO,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMODUE,MF_UNCHECKED);CheckMenuItem(MenuPrincipale,ID_APPLICAZIONI_DEMOTRE,MF_CHECKED);//...

}//...break;

// Gestione dei comandi per l'Intercettazione manuale:

case AN_OK_VISUALIZZA_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);break;

case AN_OK_VISUALIZZA_FRONTALE_II:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);break;

case AN_OK_VISUALIZZA_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_LATERALE,MF_ENABLED);break;

case AN_OK_COLORE_SCELTO_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_ENABLED);break;

case AN_OK_COLORE_SCELTO_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_LATERALE,MF_ENABLED);break;

// Thread Frontale:

case AN_OK_START_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_ENABLED);break;

case AN_OK_STOP_FRONTALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_AUTOMATICO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_POSIZIONA_MANUALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);break;

// Thread Laterale:

63

case AN_OK_START_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_ENABLED);break;

case AN_OK_STOP_LATERALE:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_LATERALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_LATERALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);break;

// Thread Frontale II:

case AN_OK_START_FRONTALE_II:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_START_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_PALETTE_FRONTALE,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE_II,MF_ENABLED);break;

case AN_OK_STOP_FRONTALE_II:EnableMenuItem(MenuPrincipale,ID_INTERCETTA_STOP_FRONTALE_II,MF_GRAYED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_INTERCETTA_VISUALIZZA_FRONTALE_II,MF_ENABLED);EnableMenuItem(MenuPrincipale,ID_CATTURA_RILASCIO,MF_ENABLED);break;

// Gestione Due Web:

case AN_OK_DUE_WEB_NO:CheckMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_UNCHECKED);break;

case AN_OK_DUE_WEB_SI:CheckMenuItem(MenuPrincipale,ID_CATTURA_ONOFF_2WEB,MF_CHECKED);break;

// Gestione Finestra Immagine Frontale:

case AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_UNCHECKED);break;

case AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINEFRONTALE,MF_CHECKED);break;

// Gestione Finestra Immagine Laterale:

case AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_UNCHECKED);break;

case AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAIMMAGINELATERALE,MF_CHECKED);break;

// Gestione Finestra Palette Frontale:

case AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_UNCHECKED);break;

case AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTEFRONTALE,MF_CHECKED);break;

// Gestione Finestra Palette Laterale:

case AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_UNCHECKED);break;

case AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAPALETTELATERALE,MF_CHECKED);break;

// Gestione Finestra Semafori:

case AN_OK_FINESTRA_CONTROLLI_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_UNCHECKED);break;

case AN_OK_FINESTRA_CONTROLLI_VISIBILE_SI:

64

CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRACONTROLLI,MF_CHECKED);break;

// Gestione Finestra Web Uno:

case AN_OK_FINESTRA_WEB_UNO_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_UNCHECKED);break;

case AN_OK_FINESTRA_WEB_UNO_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBUNO,MF_CHECKED);break;

// Gestione Finestra Web Due:

case AN_OK_FINESTRA_WEB_DUE_VISIBILE_NO:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_UNCHECKED);break;

case AN_OK_FINESTRA_WEB_DUE_VISIBILE_SI:CheckMenuItem(MenuPrincipale,ID_FINESTRE_FINESTRAWEBDUE,MF_CHECKED);break;

}}

Errore void GestoreErrore::SetMessageBox(char* Testo){ MessageBox(FinestraPrincipale,TEXT (Testo),szAppName,MB_ICONINFORMATION | MB_OK);}void GestoreErrore::Set(HWND FinestraPrincipale,char* szAppName){

this->FinestraPrincipale = FinestraPrincipale;this->szAppName = szAppName;

}void GestoreErrore::Messaggio(UINT Messaggio){

switch (Messaggio){case AN_NO_AGGANCIO:

SetMessageBox("Non riesco ad agganciarmi alla finestra, Aggancio fallito!");break;

case AN_NO_DATI:SetMessageBox("Non riesco ad agganciare i dati della cattura, Aggancio fallito!");break;

case AN_NO_FINESTRA_FORMATO:SetMessageBox("Non esiste finestra di dialogo per il formato!");break;

case AN_NO_FORMATO_ERRORE:SetMessageBox("Errore nel visualizzare la finestra di dialogo per il formato!");break;

case AN_NO_FINESTRA_SORGENTE:SetMessageBox("Non posso aprire finestra del sorgente!");break;

case AN_NO_SORGENTE_ERRORE:SetMessageBox("Errore nel visualizzare la finestra di dialogo per il sorgente!");break;

case AN_NO_FOTO:SetMessageBox("Non riesco a fare una cattura!");

break;case AN_NO_FILE:

SetMessageBox("Problemi a salvare su file");break;

case AN_NO_ELABORAZIONE:SetMessageBox("Problemi a elaborare l'immagine");break;

case AN_NO_RILASCIO_WEB_UNO:SetMessageBox("Non riesco a disconnettere Camera1!");break;

case AN_NO_RILASCIO_WEB_DUE:SetMessageBox("Non riesco a disconnettere Camera2!");break;

case AN_NO_ALLOCIMAGE:SetMessageBox("Non Riesco a riservare memoria per l'immagine!");break;

}}

Finestre void GestoreFinestre::Set(HWND FinestraPrincipale){short i;

this->FinestraPrincipale = FinestraPrincipale;

65

// Creazione della lista delle Finestre Secondarie:

Testa = Coda = Indice = NULL;// Cella della Finestra Principale:

Temp = new(CELLA_FINESTRA);Temp->hwndFinestra = FinestraPrincipale;Temp->Tipo = FINESTRA_PRINCIPALE;Temp->Visibile = true; // IninfluenteTemp->Successivo = NULL;Testa = Coda = Temp;

for(i=FINESTRA_IMMAGINE_FRONTALE; i <= FINESTRA_CONTROLLI; i++){

Temp = new(CELLA_FINESTRA);Temp->hwndFinestra = NULL;Temp->Tipo = i;Temp->Visibile = true;Temp->Successivo = NULL;Coda->Successivo = Temp;Coda = Temp;

}// Cella della Finestra Posiziona:

Temp = new(CELLA_FINESTRA);Temp->hwndFinestra = NULL;Temp->Tipo = FINESTRA_POSIZIONA;Temp->Visibile = false;Temp->Successivo = NULL;Coda->Successivo = Temp;Coda = Temp;

// Creazione della lista delle Finestre di Applicazione:

TestaLA = CodaLA = IndiceLA = NULL;}

// Setto l'handler delle Finestre Secondarie e d'Applicazione:

void GestoreFinestre::SetFinestreSecondarie(UINT Tipo,HWND hwndFinestra){

Temp = Testa;while(Temp != NULL){

if(Temp->Tipo == Tipo)Temp->hwndFinestra = hwndFinestra;

Temp = Temp->Successivo;}

}void GestoreFinestre::SetFinestreApplicazione(UINT Tipo,HWND Finestra,bool Prescelta){

if(TestaLA == NULL){

TempLA = new (CELLA_APPLICAZIONE);TempLA->Tipo = Tipo;TempLA->hwndApplicazione = Finestra;TempLA->Visibile = false;TempLA->Prescelta = Prescelta;TempLA->Successivo = NULL;TestaLA = CodaLA = TempLA;

}else{

TempLA = new (CELLA_APPLICAZIONE);TempLA->Tipo = Tipo;TempLA->hwndApplicazione = Finestra;TempLA->Visibile = false;TempLA->Prescelta = Prescelta;TempLA->Successivo = NULL;CodaLA->Successivo = TempLA;CodaLA = TempLA;

}}

// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:

HWND GestoreFinestre::GetFinestreSecondarie(UINT Tipo){

Temp = Testa;while(Temp != NULL){

if(Temp->Tipo == Tipo)return Temp->hwndFinestra;

Temp = Temp->Successivo;

66

}return NULL;

}HWND GestoreFinestre::GetFinestreApplicazione(UINT Tipo){

TempLA = TestaLA;while(TempLA != NULL){

if(TempLA->Tipo == Tipo)return TempLA->hwndApplicazione;

TempLA = TempLA->Successivo;}return NULL;

}HWND GestoreFinestre::GetFinestreApplicazione(){

TempLA = TestaLA;while(TempLA != NULL){

if(TempLA->Prescelta)return TempLA->hwndApplicazione;

TempLA = TempLA->Successivo;}return NULL;

}// Controllo la visibilità delle Finestre Secondarie e d'Applicazione:

bool GestoreFinestre::GetVisibileSecondarie(UINT Tipo){

Temp = Testa;while(Temp != NULL){

if(Temp->Tipo == Tipo)return Temp->Visibile;

Temp = Temp->Successivo;}return false;

}bool GestoreFinestre::GetVisibileApplicazione(UINT Tipo){

TempLA = TestaLA;while(TempLA != NULL){

if(TempLA->Tipo == Tipo)return TempLA->Visibile;

TempLA = TempLA->Successivo;}return false;

}

// Setto la visibilità delle Finestre Secondarie:

void GestoreFinestre::SetVisibileSecondarie(UINT Tipo,bool Visibile){

Temp = Testa;while(Temp != NULL){

if(Temp->Tipo == Tipo)Temp->Visibile = Visibile;

Temp = Temp->Successivo;}

}

// Setto l'Applicazione prescelta:

void GestoreFinestre::SetPresceltaApplicazione(UINT Tipo){

TempLA = TestaLA;while(TempLA != NULL){

if(TempLA->Tipo == Tipo)TempLA->Prescelta = true;

elseTempLA->Prescelta = false;

TempLA = TempLA->Successivo;}

}

// Verifico se l'Applicazione è prescelta:

bool GestoreFinestre::GetPresceltaApplicazione(UINT Tipo){

TempLA = TestaLA;

67

while(TempLA != NULL){

if(TempLA->Tipo == Tipo)return TempLA->Prescelta;

TempLA = TempLA->Successivo;}return false;

}

// Gestore dei Messaggi:

void GestoreFinestre::Messaggio(UINT Messaggio){

switch (Messaggio){case FINESTRA_POSIZIONA:

Temp = Testa;while(Temp != NULL){

if(Temp->Tipo != FINESTRA_POSIZIONA){

Temp->Ripristino = Temp->Visibile;Temp->Visibile = false;

}else

Temp->Visibile = true;Temp = Temp->Successivo;

}break;

case FINESTRE_ATTIVE:Temp = Testa;while(Temp != NULL){

if(Temp->Tipo != FINESTRA_POSIZIONA)Temp->Visibile = Temp->Ripristino;

elseTemp->Visibile = false;

Temp = Temp->Successivo;}break;

case FINESTRA_APPLICAZIONE:Temp = Testa;while(Temp != NULL){

Temp->Ripristino = Temp->Visibile;Temp->Visibile = false;Temp = Temp->Successivo;

}TempLA = TestaLA;while(TempLA != NULL){

if(TempLA->Prescelta){

TempLA->Visibile = true;break;

}TempLA = TempLA->Successivo;

}break;

case FINESTRE_SECONDARIE:Temp = Testa;while(Temp != NULL){

Temp->Visibile = Temp->Ripristino;Temp = Temp->Successivo;

}TempLA = TestaLA;while(TempLA != NULL){

if(TempLA->Prescelta){

TempLA->Visibile = false;break;

}TempLA = TempLA->Successivo;

}break;

}}

68

Procedure di selezione comandi

Menu Cattura void MenuAggancio(WPARAM wParam,Supervisore* Angelo){

switch (LOWORD (wParam)){case ID_CATTURA_AGGANCIO:

Angelo->Messaggio(AN_ASK_AGGANCIO,AN_WEB_FRONTALE);break;

case ID_CATTURA_FINESTRA_FORMATO:Angelo->Messaggio(AN_ASK_FINESTRA_FORMATO,AN_WEB_FRONTALE);break;

case ID_CATTURA_FINESTRA_SORGENTE:Angelo->Messaggio(AN_ASK_FINESTRA_SORGENTE,AN_WEB_FRONTALE);break;

case ID_CATTURA_FOTO:Angelo->Messaggio(AN_ASK_FOTO,AN_WEB_FRONTALE);break;

case ID_CATTURA_RILASCIO:Angelo->Messaggio(AN_ASK_RILASCIO,AN_WEB_FRONTALE);break;

/* case ID_CATTURA_SETTAGGI_OVERLAY:if(!CameraUno->OverLay())

Angelo->SetMessageBox("Problemi sull'overlay");break;

*/ case ID_CATTURA_SETTAGGI_SETTAGGIUNO:Angelo->Messaggio(AN_ASK_DLG_PROPRIETA,AN_WEB_FRONTALE);break;

case ID_CATTURA_ONOFF_2WEB:Angelo->Messaggio(AN_ONOFF_DUE_WEB);break;

case ID_CATTURA_TARGET_FILE:Angelo->Messaggio(AN_ASK_FILE,AN_WEB_FRONTALE);break;

}if (Angelo->GetDueWeb()){

switch (LOWORD (wParam)){case ID_CATTURA_AGGANCIO:

Angelo->Messaggio(AN_ASK_AGGANCIO,AN_WEB_LATERALE);break;

case ID_CATTURA_FINESTRA_FORMATO:Angelo->Messaggio(AN_ASK_FINESTRA_FORMATO,AN_WEB_LATERALE);break;

case ID_CATTURA_FINESTRA_SORGENTE:Angelo->Messaggio(AN_ASK_FINESTRA_SORGENTE,AN_WEB_LATERALE);break;

case ID_CATTURA_FOTO:Angelo->Messaggio(AN_ASK_FOTO,AN_WEB_LATERALE);break;

case ID_CATTURA_RILASCIO:Angelo->Messaggio(AN_ASK_RILASCIO,AN_WEB_LATERALE);break;

case ID_CATTURA_SETTAGGI_SETTAGGIDUE:Angelo->Messaggio(AN_ASK_DLG_PROPRIETA,AN_WEB_LATERALE);break;

case ID_CATTURA_TARGET_FILE:Angelo->Messaggio(AN_ASK_FILE,AN_WEB_LATERALE);break;}

}}

Menu Intercetta void MenuIntercetta(WPARAM wParam,Supervisore* Angelo){

switch (LOWORD (wParam)){case ID_INTERCETTA_PALETTE_FRONTALE:

Angelo->Messaggio(AN_ASK_PALETTE,AN_WEB_FRONTALE);break;

case ID_INTERCETTA_PALETTE_LATERALE:Angelo->Messaggio(AN_ASK_PALETTE,AN_WEB_LATERALE);break;

case ID_INTERCETTA_VISUALIZZA_FRONTALE:Angelo->Messaggio(AN_ASK_VISUALIZZA,AN_WEB_FRONTALE);

69

break;case ID_INTERCETTA_START_FRONTALE:

Angelo->Messaggio(AN_ASK_START,AN_WEB_FRONTALE);break;

case ID_INTERCETTA_STOP_FRONTALE:Angelo->Messaggio(AN_ASK_STOP,AN_WEB_FRONTALE);break;

case ID_INTERCETTA_VISUALIZZA_LATERALE:Angelo->Messaggio(AN_ASK_VISUALIZZA,AN_WEB_LATERALE);break;

case ID_INTERCETTA_START_LATERALE:Angelo->Messaggio(AN_ASK_START,AN_WEB_LATERALE);break;

case ID_INTERCETTA_STOP_LATERALE:Angelo->Messaggio(AN_ASK_STOP,AN_WEB_LATERALE);break;

case ID_INTERCETTA_VISUALIZZA_FRONTALE_II:Angelo->Messaggio(AN_ASK_VISUALIZZA_II,AN_WEB_FRONTALE);break;

case ID_INTERCETTA_START_FRONTALE_II:Angelo->Messaggio(AN_ASK_START_II,AN_WEB_FRONTALE);break;

case ID_INTERCETTA_STOP_FRONTALE_II:Angelo->Messaggio(AN_ASK_STOP_II,AN_WEB_FRONTALE);break;

}}

Menu Posiziona void MenuPosiziona(WPARAM wParam,Supervisore* Angelo){

switch (LOWORD (wParam)){case ID_POSIZIONA_AUTOMATICO:

Angelo->Messaggio(AN_ASK_START_POSIZIONA);break;

case ID_POSIZIONA_INTERROMPI:Angelo->Messaggio(AN_ASK_STOP_POSIZIONA);break;

case ID_POSIZIONA_MANUALE:Angelo->Messaggio(AN_ASK_START_MANUALE);break;

case ID_POSIZIONA_STOP:Angelo->Messaggio(AN_ASK_STOP_MANUALE);break;

case ID_POSIZIONA_OK:Angelo->Messaggio(AN_ASK_OK);break;

case ID_POSIZIONA_RITORNA:Angelo->Messaggio(AN_ASK_RITORNA);break;

}}

Menu Applicazioni void MenuDemo(WPARAM wParam,Supervisore* Angelo){

switch (LOWORD (wParam)){case ID_APPLICAZIONI_DEMOUNO:

Angelo->Prescelta(FINESTRA_APPLICAZIONE_DEMO_UNO);break;

case ID_APPLICAZIONI_DEMODUE:Angelo->Prescelta(FINESTRA_APPLICAZIONE_DEMO_DUE);break;

case ID_APPLICAZIONI_DEMOTRE:Angelo->Prescelta(FINESTRA_APPLICAZIONE_DEMO_TRE);break;

//...}

}

Menu finestre void MenuFinestre(WPARAM wParam,Supervisore* Angelo){

switch (LOWORD (wParam))

70

{case ID_FINESTRE_FINESTRAIMMAGINEFRONTALE:

Angelo->Messaggio(AN_ASK_FINESTRA_IMMAGINE_FRONTALE);break;

case ID_FINESTRE_FINESTRAIMMAGINELATERALE:Angelo->Messaggio(AN_ASK_FINESTRA_IMMAGINE_LATERALE);break;

case ID_FINESTRE_FINESTRAPALETTEFRONTALE:Angelo->Messaggio(AN_ASK_FINESTRA_PALETTE_FRONTALE);break;

case ID_FINESTRE_FINESTRAPALETTELATERALE:Angelo->Messaggio(AN_ASK_FINESTRA_PALETTE_LATERALE);break;

case ID_FINESTRE_FINESTRACONTROLLI:Angelo->Messaggio(AN_ASK_FINESTRA_CONTROLLI);break;

case ID_FINESTRE_FINESTRAWEBUNO:Angelo->Messaggio(AN_ASK_FINESTRA_WEB_UNO);break;

case ID_FINESTRE_FINESTRAWEBDUE:Angelo->Messaggio(AN_ASK_FINESTRA_WEB_DUE);break;

case ID_FINESTRE_FINESTRAPOSIZIONA:break;

}}