e-Lite - Politecnico di Torino

Transcript

e-Lite - Politecnico di Torino
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
2 Motivazione e obiettivi
2.1 Ausili
2.1.1 Sensori
2.1.2 Tastiere
2.1.3 Ausili per la comunicazione
2.1.4 Dispositivi di puntamento
2.1.5 Sistemi di puntamento oculare
2.2 Contesto
2.2.1 Definizione degli obiettivi
2.3 Analisi dei problemi
2.3.1 Ambiente di lavoro
2.3.2 Architettura del sistema
2.3.3 Acquisizione ed elaborazione dati
2.3.4 Integrazione nel sistema di applicazioni indipendenti
2.3.5 Calibrazione
3 BackGround
3.1 Paradigma di programmazione
3.1.1 Programmazione procedurale
3.1.2 Programmazione basata sugli oggetti
3.1.3 Programmazione orientata agli oggetti
3.2 Windows e i messaggi
3.2.1 Registrazione della window class
3.2.2 Creazione e visualizzazione della finestra
3.2.3 Ciclo dei messaggi
3.2.4 Window procedure
3.3 Multitasking e multithreading
3.4 Victor e le immagini
3.4.1 Codifica delle immagini
3.4.2 DIB
3.4.3 Victor
4 Architettura e algoritmi
4.1 Architettura
4.1.1 Acquisizione immagini
4.1.2 Scelta del riferimento
4.1.3 Motion capture
4.1.4 Definizione campo visivo
4.1.5 Indicazione dello sguardo
4.1.6 Gestione del sistema
1
5
6
6
7
8
9
10
12
13
14
15
17
18
22
23
27
28
28
28
29
29
30
31
31
31
32
33
34
36
36
39
40
43
44
45
45
47
48
4.2 Algoritmi
4.2.1 Scelta del riferimento
4.2.2 Cattura del movimento
4.2.3 Analisi del campo visivo
4.2.4 Definizione del punto fissato
5 Implementazione
5.1 Scelta del riferimento
5.1.1 Impostazione
5.1.2 Analisi immagine
5.1.3 Visualizzazione
5.1.4 Scelta
5.2 Cattura del movimento
5.2.1 Definizione parametri funzionali
5.2.2 Elaborazione svolta in una sequenza
5.2.3 Filtro
5.3 Definizione campo visivo
5.3.1 Introduzione di uno stato
5.3.2 HandShake
5.3.3 Elaborazione del singolo stato
5.3.4 Conclusione
5.3.5 Cablaggio manuale
5.4 Indicazione dello sguardo
5.4.1 Elaborazioni svolte in una sequenza
5.4.2 Messaggi alle applicazioni
6 Valutazioni sperimentali
6.1 I controlli
6.1.1 Controllo sulla velocità di esecuzione
del thread principale
6.1.2 Numero di catture per attuare il filtro
6.1.3 Clic temporale
6.1.4 Regione relativa al clic
6.1.5 Focus
6.2 Le applicazioni
6.2.1 Applicazione Uno: Addestramento
6.2.2 Applicazione Due: Tastiera
7 Sviluppi futuri
7.1 Legami esterni
7.2 Struttura
7.3 Algoritmi
7.4 Applicazioni
7.5 Conclusioni
8 Conclusioni
48
50
53
56
58
61
63
64
65
68
68
69
71
72
74
76
77
79
79
81
81
82
83
86
87
88
89
90
91
92
93
94
94
96
99
100
101
102
103
106
109
Appendice A
Elementi principali
Main
Dichiarazioni
Messaggi
Supervisore
Appendice B
Oggetti principali
Cattura
Palette
Intercetta
Decisore
Thread principale
Thread di calibrazione
Appendice C
Windows procedure delle sottofinestre
Tavolozza
Immagine
Controlli
Posiziona
Appendice D
Windows procedure delle applicazioni indipendenti
Applicazione Uno: addestramento
Applicazione Due: tastiera
Spaziatore
Applicazione Tre
Appendice E
Messaggi definiti da Windows
Resource
Oggetti relativi alla gestione
Menu
Errore
Finestre
Procedure di selezione comandi
Menu Cattura
Menu Intercetta
Menu Posiziona
Menu Applicazioni
Menu Finestre
1
1
1
6
11
14
25
25
25
26
29
35
37
38
39
39
39
40
41
47
51
51
51
52
55
56
59
59
59
60
60
64
64
68
68
68
69
69
69
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
1
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.
2
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
3
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.
4
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
5
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
6
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
7
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
8
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.
9
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.
10
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.
11
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.
12
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.
13
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 Eyetracking.
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
14
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.
15
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
16
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.
17
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,
18
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.
19
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
20
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 eyetracking. 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
21
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.
22
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
23
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
24
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.
25
26
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 :
27
“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
28
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.
29
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.
30
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
31
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
32
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
33
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.
34
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.
35
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.
36
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.
37
38
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
39
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.
40
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.
41
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.
42
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
43
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.
44
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
45
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
46
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.
47
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
48
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.
49
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.
50
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
51
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
52
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
53
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
54
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.
55
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
56
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
57
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
58
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.
59
60
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
61
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.
62
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
63
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.
64
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:
65
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 Semplice
TempLS = 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 Semplice
TempLS->Precedente
= CodaLS;
TempLS->Successivo
= NULL;
CodaLS->Successivo
= TempLS;
CodaLS
= TempLS;
66
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;
67
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;
68
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
69
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
70
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
bool
UINT
RECT
RECT
RECT
RECT
RECT
RECT
short
ColoreAttivo;
ColoreTrovato;
TipoColore;
Zona;
Dominio;
Focus;
Lento;
FocusEye;
ZonaEye;
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
HWND
Supervisore*
Intercetta*
Decisore*
imgdes*
imgdes*
RETTANGOLI*
UINT
Tipo;
hwnd;
Angelo;
Mago;
Oz;
Immagine;
ImmagineEye;
Rettangoli;
Stato;
71
bool
} PARAMS, *PPARAMS;
Ucciso;
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:
72
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)
{
73
// Setto la zona per accogliere le hit
Colore.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 chiamata
if (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();
else
Indice++;
}
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
{
74
int
RECT
RECT
Bersagli;
Zona;
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;
}
75
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.
76
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)
{
77
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.
78
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 Minore
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;
79
}
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
80
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.
81
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
82
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;
83
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;
84
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
85
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.
86
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
87
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
88
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.
89
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
90
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
91
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
92
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
93
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.
94
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.
95
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
96
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.
97
98
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,
99
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
100
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
101
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
102
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
103
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
104
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
105
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à
106
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.
107
108
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.
109
Appendice A
Elementi principali
Main
#include <windows.h>
#include <process.h>
#include "resource.h"
// Prima parte delle inclusioni private:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
TCHAR
"Dichiarazioni.h"
"Messaggi.h"
"Supervisore.h"
"Cattura.h"
"Intercetta.h"
"Palette.h"
"Decisore.h"
"Menu.h"
"Errore.h"
"Finestre.h"
"MenuFinestre.h"
"MenuCattura.h"
"MenuIntercetta.h"
"MenuPosiziona.h"
"MenuDemo.h"
"ProprietaDlg.h"
szAppName[]
szAppImmagineFrontale[]
szAppImmagineLaterale[]
szAppPaletteFrontale[]
szAppPaletteLaterale[]
szAppWebUno[]
szAppWebDue[]
szAppControlli[]
szAppPosiziona[]
=
=
=
=
=
=
=
=
=
TEXT
TEXT
TEXT
TEXT
TEXT
TEXT
TEXT
TEXT
TEXT
("Eye"),
("ImmagineFrontale"),
("ImmagineLaterale"),
("PaletteFrontale"),
("PaletteLaterale"),
("WebUno"),
("WebDue"),
("Controlli"),
("Posiziona");
// Nome delle Applicazioni:
TCHAR
szAppDemoUno[]
szAppDemoDue[]
szAppDemoTre[]
//...
Supervisore
= TEXT ("DemoUno"),
= TEXT ("DemoDue"),
= TEXT ("DemoTre");
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;
1
//
//
//
//
//
//
//
//
//
//
//
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 registrazione
wndVirtuale.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 standard
wndVirtuale.lpfnWndProc
= ImmagineFrontaleProc;
wndVirtuale.lpszClassName
= szAppImmagineFrontale;
RegisterClass(&wndVirtuale);
Parametri per definire la classe della Finestra ImmagineLaterale e registrazione
wndVirtuale.lpfnWndProc
= ImmagineLateraleProc;
wndVirtuale.lpszClassName
= szAppImmagineLaterale;
RegisterClass(&wndVirtuale);
Parametri per definire la classe della Finestra PaletteFrontale e registrazione
wndVirtuale.lpfnWndProc
= PaletteFrontaleProc;
wndVirtuale.lpszClassName
= szAppPaletteFrontale;
RegisterClass(&wndVirtuale);
Parametri per definire la classe della Finestra PaletteLaterale e registrazione
wndVirtuale.lpfnWndProc
= PaletteLateraleProc;
wndVirtuale.lpszClassName
= szAppPaletteLaterale;
RegisterClass(&wndVirtuale);
Parametri per definire la classe della Finestra Web Uno e registrazione
wndVirtuale.lpfnWndProc
= WebUnoProc;
wndVirtuale.lpszClassName
= szAppWebUno;
RegisterClass(&wndVirtuale);
Parametri per definire la classe della Finestra Web Due e registrazione
wndVirtuale.lpfnWndProc
= WebDueProc;
wndVirtuale.lpszClassName
= szAppWebDue;
RegisterClass(&wndVirtuale);
Parametri per definire la classe della Finestra Controlli e registrazione
wndVirtuale.lpfnWndProc
= ControlliProc;
wndVirtuale.lpszClassName
= szAppControlli;
RegisterClass(&wndVirtuale);
Parametri per definire la classe della Finestra Posiziona e registrazione
wndVirtuale.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ù Principale
MenuPrincipale = LoadMenu(hInstance,MAKEINTRESOURCE (IDR_MENUPRINCIPALE)) ;
// Creazione della Finestra Principale
FinestraPrincipale = CreateWindow (szAppName,
// nome della classe
TEXT ("Eye_6_1"),
// titolo
WS_OVERLAPPEDWINDOW,
// stile della finestra
150,
// posizione iniziale x
50,
// posizione iniziale y
700,
// larghezza
650,
// lunghezza
NULL,
// finestra genitore
MenuPrincipale,
// menu
hInstance,
// instanza del programma
NULL) ;
// parametri di creazione
2
// Settaggio dell'Angelo e delle sottoFinestre attive
Angelo.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 Principale
ShowWindow(FinestraPrincipale,iCmdShow);
UpdateWindow(FinestraPrincipale);
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam ;
}
3
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);
else
MoveWindow(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);
else
MoveWindow(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);
else
MoveWindow(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);
else
MoveWindow(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);
else
MoveWindow(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);
else
MoveWindow(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);
else
MoveWindow(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);
else
MoveWindow(Angelo.GetFinestreSecondarie(FINESTRA_WEB_DUE),
0,0,0,0,true);
// Gestione delle dimensioi delle Finestre di Applicazione:
4
if(Angelo.VisibileApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO))
MoveWindow(Angelo.GetFinestreApplicazione(FINESTRA_APPLICAZIONE_DEMO_UNO),
0,
0,
cxClient,cyClient,true);
else
MoveWindow(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);
else
MoveWindow(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);
else
MoveWindow(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:
5
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
LRESULT CALLBACK WebUnoProc
LRESULT CALLBACK WebDueProc
LRESULT CALLBACK PaletteFrontaleProc
LRESULT CALLBACK PaletteLateraleProc
LRESULT CALLBACK ImmagineFrontaleProc
LRESULT CALLBACK ImmagineLateraleProc
LRESULT CALLBACK ControlliProc
LRESULT CALLBACK PosizionaProc
BOOL
CALLBACK ProprietaDlgProc
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(HWND,UINT,WPARAM,LPARAM);
(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
class
class
class
class
class
class
class
Cattura;
Intercetta;
Supervisore;
GestoreMenu;
GestoreErrore;
GestoreFinestre;
Palette;
Decisore;
// Dichiarazioni delle Costanti
short
short
short
short
short
short
SLEEP_THREAD_FRONTALE
SLEEP_THREAD_LATERALE
NUMERO_AQUISIZIONI
NUMERO_AQUISIZIONI_AREA_CLICK
BORDO_CLICK
BORDO_FOCUS
=
=
=
=
=
=
10;
10;
10;
4;
2;
20;
const
const
const
const
const
const
short
short
short
short
short
short
=
=
=
=
=
=
10;
52;
5;
0;
70;
6;
BORDO_DOMINIO
FOCUS_EYE
MASSIMO_LESS
LIMITE_BERSAGLI_MINIMO
LUMINOSITA
NUMERO_AQUISIZIONI_PROGRESSO
// deve essere un multiplo di 4
// Dichiarazioni delle Strutture
typedef struct CELLA_COLORE_LS
{
6
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
{
public:
// Angelo
7
inline
void
Supervisore() {}
Set(HWND,
HMENU,
char*,
Cattura*,Cattura*,
Intercetta*,Intercetta*,
Palette*,Palette*,
Decisore*,
GestoreMenu*,
GestoreErrore*,
GestoreFinestre*);
SethInstance(HINSTANCE);
GetDueWeb();
void
bool
// Setto l'handler delle Finestre Secondarie e d'Applicazione:
void
void
SetFinestreSecondarie(UINT,HWND);
SetFinestreApplicazione(UINT,HWND,bool);
// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:
HWND
HWND
GetFinestreSecondarie(UINT);
GetFinestreApplicazione(UINT);
// Controllo se sono visibili le Finestre Secondarie e d'Applicazione:
bool
bool
VisibileSecondarie(UINT);
VisibileApplicazione(UINT);
// Funzione che definisce l'Applicazione prescelta:
void
Prescelta(UINT);
// Funzioni che gestiscono i messaggi:
void
void
void
Messaggio(UINT);
Messaggio(UINT,UINT);
Messaggio(UINT,UINT,UINT);
// Funzione che permette l'handshake tra i Thread:
void
HandShake(UINT,UINT,UINT);
public:
CUBE
private:
Cube;
void
Scossa();
private:
HWND
HINSTANCE
Cattura
Intercetta
Palette
Decisore
GestoreMenu
GestoreErrore
GestoreFinestre
static char
FinestraDlgSettaggi;
hInstance;
*CameraUno,
*CameraDue;
*MagoFrontale,
*MagoLaterale;
*TavolozzaFrontale,
*TavolozzaLaterale;
*Oz;
*Menu;
*Errore;
*Finestre;
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
{
public:
void
void
private:
void
HWND
// Errore
Set(HWND,char*);
Messaggio(UINT);
SetMessageBox(char*);
FinestraPrincipale;
8
char*
szAppName;
};
class GestoreFinestre // Finestre
{
public:
// Settaggio dell'Oggetto:
void
Set(HWND);
// Setto l'handler delle Finestre Secondarie e d'Applicazione:
void
void
SetFinestreSecondarie(UINT,HWND);
SetFinestreApplicazione(UINT,HWND,bool);
// Ottengo l'handler delle Finestre Secondarie e d'Applicazione:
HWND
HWND
HWND
GetFinestreSecondarie(UINT);
GetFinestreApplicazione(UINT);
GetFinestreApplicazione();
// Verifico la visibilità delle Finestre Secondarie e d'Applicazione:
bool
bool
GetVisibileSecondarie(UINT);
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;
9
CAPDRIVERCAPS
CAPSTATUS
char
int
UINT
char
char
char*
int
CapacitaDriver;
StatoCattura;
cNomeDriver[80];
x,y,larghezza,lunghezza;
Indice;
Nome[80];
Versione[80];
NomeFile;
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;
10
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;
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;
int
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
11
// Lista Semplice
// Lista Complessa
#define
#define
#define
#define
AN_WEB_FRONTALE
AN_WEB_LATERALE
AN_THREAD_POSIZIONA
AN_THREAD_INTERCETTA
1
2
3
4
#define
#define
#define
#define
STATO_CATTURA_INIZIO
STATO_CATTURA_VERDE
STATO_CATTURA_GIALLO
STATO_CATTURA_ROSSO
11
12
13
14
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
STATO_ZERO
STATO_UNO
STATO_DUE
STATO_TRE
STATO_QUATTRO
STATO_CINQUE
STATO_SEI
STATO_SETTE
STATO_OTTO
STATO_NOVE
STATO_FINE
STATO_MANUALE
STATO_LAVORO
100
101
102
103
104
105
106
107
108
109
110
111
#define
#define
#define
#define
#define
STATO_PROGRESSO_INIZIO
STATO_PROGRESSO_UNO
STATO_PROGRESSO_DUE
STATO_PROGRESSO_TRE
STATO_PROGRESSO_FINE
112
200
201
202
203
204
#define FINESTRE_ATTIVE
#define FINESTRA_PRINCIPALE
#define FINESTRA_IMMAGINE_FRONTALE
#define FINESTRA_IMMAGINE_LATERALE
#define FINESTRA_PALETTE_FRONTALE
#define FINESTRA_PALETTE_LATERALE
#define FINESTRA_WEB_UNO
#define FINESTRA_WEB_DUE
#define FINESTRA_CONTROLLI
#define FINESTRA_POSIZIONA
#define FINESTRA_APPLICAZIONE
#define FINESTRE_SECONDARIE
300
301
302
303
304
305
306
307
308
309
310
311
#define FINESTRA_APPLICAZIONE_DEMO_UNO
#define FINESTRA_APPLICAZIONE_DEMO_DUE
#define FINESTRA_APPLICAZIONE_DEMO_TRE
//...
400
401
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
#define AN_OK_RILASCIO
#define AN_OK_START_FRONTALE
1001
1002
1003
//
//
//
12
da Supervisore per GestoreMenu
da Supervisore per GestoreMenu
da Supervisore per GestoreMenu
#define AN_OK_STOP_FRONTALE
1004
// da
#define AN_OK_START_LATERALE
1005
// da
#define AN_OK_STOP_LATERALE
1006
// da
#define AN_OK_START_FRONTALE_II
1007
// da
#define AN_OK_STOP_FRONTALE_II
1008
// da
#define AN_OK_DUE_WEB_NO
1009
// da
#define AN_OK_DUE_WEB_SI
1010
// da
#define AN_OK_VISUALIZZA_FRONTALE
1025
// da
#define AN_OK_VISUALIZZA_LATERALE
1026
// da
#define AN_OK_COLORE_SCELTO_FRONTALE
1027
// da
#define AN_OK_COLORE_SCELTO_LATERALE
1028
// da
#define AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_NO
#define AN_OK_FINESTRA_IMMAGINE_FRONTALE_VISIBILE_SI
#define AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_NO
#define AN_OK_FINESTRA_IMMAGINE_LATERALE_VISIBILE_SI
#define AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_NO
#define AN_OK_FINESTRA_PALETTE_FRONTALE_VISIBILE_SI
#define AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_NO
#define AN_OK_FINESTRA_PALETTE_LATERALE_VISIBILE_SI
#define AN_OK_FINESTRA_CONTROLLI_VISIBILE_NO
#define AN_OK_FINESTRA_CONTROLLI_VISIBILE_SI
#define AN_OK_FINESTRA_WEB_UNO_VISIBILE_NO
#define AN_OK_FINESTRA_WEB_UNO_VISIBILE_SI
#define AN_OK_FINESTRA_WEB_DUE_VISIBILE_NO
#define AN_OK_FINESTRA_WEB_DUE_VISIBILE_SI
1042
#define AN_OK_START_POSIZIONA
1043
#define AN_OK_STOP_POSIZIONA
1044
#define AN_OK_VISUALIZZA_FRONTALE_II
1045
#define AN_OK_START_MANUALE
1046
#define AN_OK_STOP_MANUALE
1047
#define AN_OK_OK
1048
#define AN_OK_RITORNA
1049
#define AN_OK_APPLICAZIONE_PRESCELTA
1050
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
Supervisore per GestoreMenu
1029
// da Supervisore per GestoreMenu
1030
// da Supervisore per GestoreMenu
1031
// da Supervisore per GestoreMenu
1032
// da Supervisore per GestoreMenu
1033
// da Supervisore per GestoreMenu
1034
// da Supervisore per GestoreMenu
1035
// da Supervisore per GestoreMenu
1036
// da Supervisore per GestoreMenu
1037
// da Supervisore per GestoreMenu
1038
// da Supervisore per GestoreMenu
1039
// da Supervisore per GestoreMenu
1040
// da Supervisore per GestoreMenu
1041
// da Supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// da supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// da Supervisore per GestoreMenu
// Messaggi di operazioni fallite
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
AN_NO_AGGANCIO
AN_NO_DATI
AN_NO_FORMATO_ERRORE
AN_NO_FINESTRA_FORMATO
AN_NO_SORGENTE_ERRORE
AN_NO_FINESTRA_SORGENTE
AN_NO_FOTO
AN_NO_RILASCIO_WEB_UNO
AN_NO_FILE
AN_NO_ALLOCIMAGE
AN_NO_ELABORAZIONE
AN_NO_RILASCIO_WEB_DUE
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
//
//
//
//
//
//
//
//
//
//
//
//
da
da
da
da
da
da
da
da
da
da
da
da
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
per
per
per
per
per
per
per
per
per
per
per
per
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
GestoreErrore
3001
//
da MenuCattura per Supervisore
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
// da
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
MenuCattura per Supervisore
MenuCattura per Supervisore
MenuCattura per Supervisore
MenuCattura per Supervisore
MenuCattura per Supervisore
MenuCattura per Supervisore
MenuCattura per Supervisore
MenuIntercetta per Supervisore
MenuIntercetta per Supervisore
MenuIntercetta per Supervisore
MenuIntercetta per Supervisore
MenuIntercetta per Supervisore
MenuIntercetta per Supervisore
MenuIntercetta per Supervisore
PaletteProc per Supervisore;
ThreadIntercettaFrontale/Laterale per Supervisore
MenuPosiziona per Supervisore
// da MenuSet per Supervisore
// da MenuSet per Supervisore
// da MenuSet per Supervisore
// da MenuSet per Supervisore
// da MenuSet per Supervisore
// da MenuSet per Supervisore
// da MenuSet per Supervisore
// da ThreadPosiziona per Supervisore
// da ThreadIntercettaFrontale per Supervisore
// da ThreadPosiziona per Supervisore
// da ThreadIntercettaFrontale per Supervisore
// Messaggi di OnOff
#define AN_ONOFF_DUE_WEB
// Messaggi di richiesta
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
AN_ASK_AGGANCIO
4001
AN_ASK_FINESTRA_FORMATO
4002
AN_ASK_FINESTRA_SORGENTE
4003
AN_ASK_FOTO
4004
AN_ASK_RILASCIO
4005
AN_ASK_DLG_PROPRIETA
4006
AN_ASK_FILE
4007
AN_ASK_VISUALIZZA
4008
AN_ASK_START
4009
AN_ASK_STOP
4010
AN_ASK_VISUALIZZA_II
4011
AN_ASK_START_II
4012
AN_ASK_STOP_II
4013
AN_ASK_PALETTE
4014
AN_ASK_INTERCETTA_COLORE
4016
AN_ASK_STATO_CATTURA_MUTATO 4017
AN_ASK_START_POSIZIONA
4018
AN_ASK_FINESTRA_IMMAGINE_FRONTALE
AN_ASK_FINESTRA_IMMAGINE_LATERALE
AN_ASK_FINESTRA_PALETTE_FRONTALE
AN_ASK_FINESTRA_PALETTE_LATERALE
AN_ASK_FINESTRA_CONTROLLI
AN_ASK_FINESTRA_WEB_UNO
AN_ASK_FINESTRA_WEB_DUE
AN_ASK_INIZIO_STATO
AN_ASK_FINE_STATO
AN_ASK_FINE_SETTAGGIO
AN_ASK_STATO_COMPLETATO
13
#define
#define
#define
#define
#define
#define
#define
#define
AN_ASK_STOP_POSIZIONA
AN_ASK_STATO_PROGRESSO_MUTATO
AN_ASK_START_MANUALE
AN_ASK_STOP_MANUALE
AN_ASK_OK
AN_ASK_RITORNA
AN_ASK_DECISIONE_AVVENUTA
AN_ASK_CLICK_TEMPORALE_AVVENUTA
4030
4031
4032
4033
4034
4035
4036
4037
//
//
//
//
//
//
//
//
da
da
da
da
da
da
da
da
MenuPosiziona per Supervisore
ThreadIntercettaFrontale per Supervisore
MenuPosiziona per Supervisore
MenuPosiziona per Supervisore
MenuPosiziona per Supervisore
MenuPosiziona per Supervisore
ThreadIntercettaFrontale per Supervisore
ThreadIntercettaFrontale per Supervisore
//
//
//
//
//
//
//
//
//
//
//
//
//
Cattura
Cattura
Cattura
Cattura
Cattura
Cattura
Cattura
Cattura
Cattura
Intercetta
Decisore
Decisore
Decisore
// Codici di ritorno delle varie funzioni
#define AN_RET_OK
#define AN_RET_AGGANCIO_NO
#define AN_RET_DATI_NO
#define AN_RET_FORMATO_ERRORE
#define AN_RET_FORMATO_NO
#define AN_RET_SORGENTE_ERRORE
#define AN_RET_SORGENTE_NO
#define AN_RET_OVERLAY_ERRORE
#define AN_RET_OVERLAY_NO
#define AN_RET_ALLOC_NO
#define AN_RET_NO_DECISIONE
#define AN_RET_SI_DECISIONE
#define AN_RET_CLICK_TEMPORALE
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
da
da
da
da
da
da
da
da
da
da
da
da
da
per
per
per
per
per
per
per
per
per
per
per
per
per
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
Supervisore
ThreadIntercettaFrontale
ThreadIntercettaFrontale
ThreadIntercettaFrontale
// Codici per i colori nella Tavolozza
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
GRIGIO_1
GRIGIO_2
GRIGIO_3
GRIGIO_4
GRIGIO_5
ROSSO_1
ROSSO_2
ROSSO_3
ROSSO_4
ROSSO_5
ROSSO_6
VERDE_1
VERDE_2
VERDE_3
VERDE_4
VERDE_5
VERDE_6
BLU_1
BLU_2
BLU_3
BLU_4
BLU_5
BLU_6
GIALLO_1
GIALLO_2
GIALLO_3
CYANO_1
CYANO_2
CYANO_3
VIOLETTO_1
VIOLETTO_2
VIOLETTO_3
INDEFINITO
7001
7002
7003
7004
7005
7006
7007
7008
7009
7010
7011
7012
7013
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
da
TrovaGrigio
TrovaGrigio
TrovaGrigio
TrovaGrigio
TrovaGrigio
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
TrovaColore
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(Palette)
(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); }
14
// 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
HMENU
char*
Cattura*
Cattura*
Intercetta*
Intercetta*
Palette*
Palette*
Decisore*
GestoreMenu*
GestoreErrore*
GestoreFinestre*
FinestraPrincipale,
MenuPrincipale,
szAppName,
CameraUno,
CameraDue,
MagoFrontale,
MagoLaterale,
TavolozzaFrontale,
TavolozzaLaterale,
Oz,
Menu,
Errore,
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;
15
// 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);
}
16
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),
17
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)
{
// Aggancio
case 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 Formato
case 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 Sorgente
case 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;
// Foto
case 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;
18
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;
// Palette
case 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;
// Start
case AN_ASK_START:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_START_THREAD_I,(long) Oz,(long) MagoFrontale);
Menu->Messaggio(AN_OK_START_FRONTALE);
break;
// Stop
case 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 II
case 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 II
case 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 II
case AN_ASK_STOP_II:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_FRONTALE),
WM_STOP_THREAD_II,0,0);
Menu->Messaggio(AN_OK_STOP_FRONTALE_II);
break;
19
// 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)
{
// Aggancio
case 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 Formato
case 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 Sorgente
case 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;
// Foto
case 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;
// File
case AN_ASK_FILE:
if(!CameraDue->GetFile())
Errore->Messaggio(AN_NO_FILE);
break;
// Palette
case AN_ASK_PALETTE:
if(TavolozzaLaterale->GetAttivo())
TavolozzaLaterale->Pulisci();
TavolozzaLaterale->Set();
20
TavolozzaLaterale->Colora(MagoLaterale->GetImmagine());
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_PALETTE_LATERALE),
WM_TAVOLOZZA_PRONTA,0,(long) TavolozzaLaterale->GetListaColore());
break;
// Visualizza
case 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;
// Start
case AN_ASK_START:
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_IMMAGINE_LATERALE),
WM_START_THREAD_I,(long) Oz,(long) MagoLaterale);
Menu->Messaggio(AN_OK_START_LATERALE);
break;
// Stop
case 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 Colore
case 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:
21
if(Finestre->GetVisibileSecondarie(FINESTRA_POSIZIONA))
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),
WM_STATO_PROGRESSO_MUTATO,0,(long) Parametro);
break;
/*
*/
}
void
{
}
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;
}
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
{
22
if(!Cube.Segnale)
Cube.Segnale = true;
else
{
SendMessage(Finestre->GetFinestreSecondarie(FINESTRA_POSIZIONA),
WM_STATO_MUTATO,0,(long) Parametro+1);
Cube.Segnale = false;
}
}
break;
}
break;
}
}
23
24
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 Cattura
FinestraCattura=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;
25
Settaggi->NomeDriver = (char*) &cNomeDriver;
if(StatoCattura.fLiveWindow == 1)
if(StatoCattura.fOverlayWindow == 1)
if(StatoCattura.fScale == 1)
if(StatoCattura.fUsingDefaultPalette == 1)
if(StatoCattura.fAudioHardware == 1)
if(StatoCattura.fCapFileExists == 1)
if(StatoCattura.fCapturingNow == 1)
if(CapacitaDriver.fHasOverlay == 1)
if(CapacitaDriver.fHasDlgVideoSource == 1)
if(CapacitaDriver.fHasDlgVideoFormat == 1)
if(CapacitaDriver.fHasDlgVideoDisplay == 1)
if(CapacitaDriver.fCaptureInitialized == 1)
if(CapacitaDriver.fDriverSuppliesPalettes == 1)
Settaggi->LiveWindow = true;
Settaggi->OverlayWindow = true;
Settaggi->Scale = true;
Settaggi->UsingDefaultPalette = true;
Settaggi->AudioHardware = true;
Settaggi->CapFileExists = true;
Settaggi->CapturingNow = true;
Settaggi->HasOverlay = true;
Settaggi->HasDlgVideoSource = true;
Settaggi->HasDlgVideoFormat = true;
Settaggi->HasDlgVideoDisplay = true;
Settaggi->CaptureInitialized = true;
Settaggi->DriverSuppliesPalettes = true;
}
Palette
CELLA_COLORE_LS*
void
bool
Palette::GetListaColore()
Palette::SetAttivo(bool Attivo)
Palette::GetAttivo()
{ return this->TestaLS; }
{ this->Attivo = Attivo;}
{ 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 indefiniti
TempLC = 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 Semplice
TempLS = 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 Complessa
IndiceLC = CodaLC->Precedente;
delete CodaLC;
while(IndiceLC != NULL)
{
CodaLC = IndiceLC;
IndiceLC = CodaLC->Precedente;
delete CodaLC;
}
TestaLC = CodaLC = IndiceLC = TempLC = NULL;
// Elimino gli elementi della lista Semplice
IndiceLS = 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
26
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 BYTE
int
j = 0; // Contatore di pixel
COLORE 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 Complessa
TempLC->Precedente
= CodaLC;
TempLC->Successivo
= NULL;
CodaLC->Successivo
= TempLC;
CodaLC
= TempLC;
// Creo la Cella Colore Lista Semplice
TempLS = 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 Semplice
TempLS->Precedente
= CodaLS;
TempLS->Successivo
= NULL;
CodaLS->Successivo
= TempLS;
CodaLS
= TempLS;
27
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)
return GRIGIO_1;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso)
return GRIGIO_2;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso)
return GRIGIO_3;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso)
return GRIGIO_4;
if((Cella->Colore.Blu + Cella->Colore.Verde + Cella->Colore.Rosso)
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 verde
if((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 blu
if((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 giallo
if((Cella->Colore.Rosso <= 255) && (Cella->Colore.Rosso > 220) &&
28
<= 120)
<= 300)
<= 480)
<= 690)
<= 765)
(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 cyano
if((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 violetto
if((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()
imgdes*
Intercetta::GetImmagineEye()
bool
Intercetta::GetAttivo()
CELLA_POSIZIONE*
Intercetta::GetPosizioni()
UINT
Intercetta::GetProgresso()
void
Intercetta::SetAttivo(bool 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;
{
{
{
{
{
{
return &Immagine; }
return &ImmagineEye; }
return Attivo; }
return Posizioni; }
return StatoProgresso; }
this->Attivo = Attivo; }
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:
29
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);
else
Colore.Focus.left = 0;
if(Colore.Zona.top < (Height - BORDO_FOCUS))
Colore.Focus.top = (Colore.Zona.top + BORDO_FOCUS);
else
Colore.Focus.top = (Height - 1);
if(Colore.Zona.right < (Width - BORDO_FOCUS))
Colore.Focus.right = (Colore.Zona.right + BORDO_FOCUS);
else
Colore.Focus.right = (Width - 1);
if(Colore.Zona.bottom > 10)
Colore.Focus.bottom = (Colore.Zona.bottom - BORDO_FOCUS);
else
Colore.Focus.bottom = 0;
// Settaggio dei limiti del colore
switch (TipoColore)
{
case GRIGIO_1:
// non entrerò mai in questi case li ho messi solo
case 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;
30
case
case
case
case
case
case
case
case
case
case
case
case
case
case
Colore.RossoMax
Colore.VerdeMin
Colore.VerdeMax
break;
ROSSO_4:
Colore.RossoMin
Colore.RossoMax
Colore.VerdeMin
Colore.VerdeMax
break;
ROSSO_5:
Colore.RossoMin
Colore.RossoMax
Colore.VerdeMin
Colore.VerdeMax
break;
ROSSO_6:
Colore.RossoMin
Colore.RossoMax
Colore.VerdeMin
Colore.VerdeMax
break;
VERDE_1:
Colore.VerdeMin
Colore.VerdeMax
Colore.RossoMin
Colore.RossoMax
break;
VERDE_2:
Colore.VerdeMin
Colore.VerdeMax
Colore.RossoMin
Colore.RossoMax
break;
VERDE_3:
Colore.VerdeMin
Colore.VerdeMax
Colore.RossoMin
Colore.RossoMax
break;
VERDE_4:
Colore.VerdeMin
Colore.VerdeMax
Colore.RossoMin
Colore.RossoMax
break;
VERDE_5:
Colore.VerdeMin
Colore.VerdeMax
Colore.RossoMin
Colore.RossoMax
break;
VERDE_6:
Colore.VerdeMin
Colore.VerdeMax
Colore.RossoMin
Colore.RossoMax
break;
BLU_1:
Colore.BluMin
Colore.BluMax
Colore.RossoMin
Colore.RossoMax
break;
BLU_2:
Colore.BluMin
Colore.BluMax
Colore.RossoMin
Colore.RossoMax
break;
BLU_3:
Colore.BluMin
Colore.BluMax
Colore.RossoMin
Colore.RossoMax
break;
BLU_4:
Colore.BluMin
Colore.BluMax
Colore.RossoMin
Colore.RossoMax
break;
BLU_5:
= 180;
= Colore.BluMin = 0;
= Colore.BluMax = 100;
=
=
=
=
121;
150;
Colore.BluMin = 0;
Colore.BluMax = 100;
=
=
=
=
101;
120;
Colore.BluMin = 0;
Colore.BluMax = 90;
=
=
=
=
91;
100;
Colore.BluMin = 0;
Colore.BluMax = 80;
=
=
=
=
221;
255;
Colore.BluMin = 0;
Colore.BluMax = 100;
=
=
=
=
181;
220;
Colore.BluMin = 0;
Colore.BluMax = 100;
=
=
=
=
151;
180;
Colore.BluMin = 0;
Colore.BluMax = 100;
=
=
=
=
121;
150;
Colore.BluMin = 0;
Colore.BluMax = 100;
=
=
=
=
101;
120;
Colore.BluMin = 0;
Colore.BluMax = 90;
=
=
=
=
91;
100;
Colore.BluMin = 0;
Colore.BluMax = 80;
=
=
=
=
221;
255;
Colore.VerdeMin = 0;
Colore.VerdeMax = 100;
=
=
=
=
181;
220;
Colore.VerdeMin = 0;
Colore.VerdeMax = 100;
=
=
=
=
151;
180;
Colore.VerdeMin = 0;
Colore.VerdeMax = 100;
=
=
=
=
121;
150;
Colore.VerdeMin = 0;
Colore.VerdeMax = 100;
31
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 HIT
Hit[0].Bersagli
= 0;
CopyRect(&Hit[0].Zona,&Colore.Zona);
Indice
= 1;
// Il colore è stato scelto
Colore.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);
else
IndiceTemp = (NUMERO_AQUISIZIONI - 1);
if(Hit[IndiceTemp].Zona.left > FOCUS_EYE)
Colore.FocusEye.left = (Hit[IndiceTemp].Zona.left - FOCUS_EYE);
else
Colore.FocusEye.left = 0;
if(Hit[IndiceTemp].Zona.bottom > FOCUS_EYE)
Colore.FocusEye.bottom = (Hit[IndiceTemp].Zona.bottom - FOCUS_EYE);
else
Colore.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;
else
ImmagineEye.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 hit
Colore.Zona.left
Colore.Zona.right
Colore.Zona.bottom
Colore.Zona.top
Hit[Indice].Bersagli = 0;
=
=
=
=
320;
0;
240;
0;
32
// Setto il Dominio e il Focus per questa chiamata
if (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();
else
Indice++;
}
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)
33
{
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 Hit
Hit[Indice].Bersagli++;
}
}
void Intercetta::Dominio()
{
short i;
RECT
Sum;
if(this->DominioAttivo)
{
// Setto il Dominio
if(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)
34
{
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 posizioni
Temp
= 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);
else
this->Colore.Focus.left = 0;
if(this->Colore.Zona.top < (this->Height - BORDO_FOCUS))
this->Colore.Focus.top = (this->Colore.Zona.top + BORDO_FOCUS);
else
this->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);
else
this->Colore.Focus.right = (this->Width - 1);
if(this->Colore.Zona.bottom > BORDO_FOCUS)
this->Colore.Focus.bottom = (this->Colore.Zona.bottom - BORDO_FOCUS);
else
this->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
bool
PUNTO
Decisore::SetAttivo(bool Attivo)
Decisore::GetAttivo()
*Decisore::GetPunto()
{ this->Attivo = Attivo; }
{ return this->Attivo; }
{ 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)
35
{
Temp
Temp->NumeroLinea
Temp->Linea
Temp->Precedente
Temp->Successivo
Coda->Successivo
Coda
i--;
=
=
=
=
=
=
=
new(CELLA_LINEA);
i - 1;
new SCOSTAMENTO[NumeroDiColonne];
Coda;
NULL;
Temp;
Temp;
}
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
36
{
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);
}
37
}
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();
}
38
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:
39
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 Immagine
0,0,
// Posizione di partenza nell'immagine
0,
// linea di partenza
(unsigned)Immagine->bmh->biHeight,
// Numero di linee da visualizzare
Immagine->ibuff,
// Indirizzo del Ibuffer
(BITMAPINFO far *)Immagine->bmh,
// Indirizzo del BITMAPINFO
DIB_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;
40
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
{
const short
VELOCITA_THREAD_FRONTALE
=
const short
VELOCITA_THREAD_LATERALE
=
const short
AQUISIZIONI
=
const short
CLICK_TEMPORALE
=
const short
BORDO_CLICH_TEMPORALE
=
const short
FOCUS
=
HDC
hdc;
message,WPARAM wParam,LPARAM lParam)
1;
4;
7;
10;
13;
16;
41
PAINTSTRUCT
HINSTANCE
TCHAR
static RECT
static UINT
static HPEN
static HBRUSH
static HWND
ps;
hInstance;
Buffer[4];
Invalido;
StatoFrontale,StatoLaterale;
PennaContorno;
PennelloSfondo,PennelloNero,PennelloStatoCatturaRosso,
PennelloStatoCatturaGiallo,PennelloStatoCatturaVerde,
PennelloStatoCatturaInizio,PennelloRossoSpento,PennelloGialloSpento,
PennelloVerdeSpento;
SleepThreadFrontale,LabelSleepThreadFrontale,ValueSleepThreadFrontale,
SleepThreadLaterale,LabelSleepThreadLaterale,ValueSleepThreadLaterale,
NumeroAquisizioni,LabelNumeroAquisizioni,ValueNumeroAquisizioni,
ClickTemporale,LabelClickTemporale,ValueClickTemporale,
BordoClickTemporale,LabelBordoClickTemporale,ValueBordoClickTemporale,
Focus,LabelFocus,ValueFocus;
Hit;
cxClient,cyClient;
static bool
static int
switch (message)
{
case WM_CREATE:
hInstance
PennaContorno
PennelloSfondo
PennelloNero
PennelloStatoCatturaRosso
PennelloStatoCatturaGiallo
PennelloStatoCatturaVerde
PennelloStatoCatturaInizio
PennelloRossoSpento
PennelloGialloSpento
PennelloVerdeSpento
SleepThreadFrontale
=
=
=
=
=
=
=
=
=
=
=
=
(HINSTANCE) GetWindowLong(hwnd,GWL_HINSTANCE);
CreatePen(PS_SOLID,1,RGB(0,0,0));
CreateSolidBrush(RGB(170,170,170));
CreateSolidBrush(RGB(100,100,100));
CreateSolidBrush(RGB(255,0,0));
CreateSolidBrush(RGB(255,255,0));
CreateSolidBrush(RGB(0,255,0));
CreateSolidBrush(RGB(255,255,255));
CreateSolidBrush(RGB(100,0,0));
CreateSolidBrush(RGB(100,100,0));
CreateSolidBrush(RGB(0,100,0));
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);
42
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())
43
{
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;
44
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;
45
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;
46
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;
47
}
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)
48
{
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) ;
}
49
50
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;
51
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;
52
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;
}
53
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:
54
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
char
char
char
char
Spaziatore::Celle1[25]
Spaziatore::Celle2[31]
Spaziatore::Celle3[41]
Spaziatore::Celle4[51]
Spaziatore::Celle5[81]
=
=
=
=
=
"
"
"
"
"
abcdefghilmnopqrstuvz'";
abcdefghijklmnopqrstuvwxyz'\"";
abcdefghijklmnopqrstuvwxyz'\"0123456789";
abcdefghijklmnopqrstuvwxyz'\"0123456789,.;:*+-<>_";
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:
55
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
{
HDC
PAINTSTRUCT
static PUNTO
static HPEN
static bool
static RECT
switch (message)
{
(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
hdc;
ps;
*Punto;
PennaPunto;
PuntoAttivo;
Rect;
56
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
}
EndPaint (hwnd, &ps);
return 0;
case WM_DESTROY:
DeleteObject(PennaPunto);
return 0;
}
return DefWindowProc (hwnd,message,wParam,lParam) ;
}
57
* Rect.bottom));
58
Appendice E
Messaggi definiti da Windows
Resource
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
IDR_MENUPRINCIPALE
IDI_ICONA_PRINCIPALE
IDD_OLE_PROPPAGE_LARGE
IDC_OK
IDC_STATIC_VAR_NOME
IDC_VAR_IMAGEWIDTH
IDC_STATIC_NOME
IDC_VAR_IMAGEHEIGHT
IDC_VAR_LIVEWINDOW
IDC_VAR_OVERLAYWINDOW
IDC_VAR_SCALE
IDC_VAR_USINGDEFAULTPALETTE
IDC_VAR_AUDIOHARDWARE
IDC_VAR_CAPFILEEXISTS
IDC_VAR_CAPTURINGNOW
IDC_STATIC_STATO
IDC_STATIC_CAPACITA
IDC_VAR_HASOVERLAY
IDC_VAR_HASDLGVIDEOSOURCE
IDC_VAR_HASDLGVIDEOFORMAT
IDC_VAR_HASDLGVIDEODISPLAY
IDC_VAR_NOMEDRIVER
IDC_OK_PROPRIETA
ID_CATTURA_AGGANCIO
ID_CATTURA_DATI
ID_CATTURA_RILASCIO
ID_CATTURA_FOTO
ID_CATTURA_FINESTRA_FORMATO
ID_CATTURA_FINESTRA_SORGENTE
ID_CATTURA_SETTAGGI_OVERLAY
ID_CATTURA_SETTAGGI_INFORMAZIONI
ID_CATTURA_ONOFF_2WEB
ID_CATTURA_TARGET_FILE
ID_CATTURA_SETTAGGI_SETTAGGIUNO
ID_CATTURA_PROPRIETA_PROPRIETAUNO
ID_CATTURA_SETTAGGI_SETTAGGIDUE
ID_CATTURA_PROPRIETA_PROPRIETADUE
ID_INTERCETTA_VISUALIZZA_FRONTALE
ID_INTERCETTA_START_FRONTALE
ID_INTERCETTA_STOP_FRONTALE
ID_INTERCETTA_VISUALIZZA_FRONTALE_II
ID_INTERCETTA_START_FRONTALE_II
ID_INTERCETTA_STOP_FRONTALE_II
ID_INTERCETTA_PALETTE_FRONTALE
ID_INTERCETTA_VISUALIZZA_LATERALE
ID_INTERCETTA_START_LATERALE
ID_INTERCETTA_STOP_LATERALE
ID_INTERCETTA_PALETTE_LATERALE
ID_POSIZIONA_VAI
ID_POSIZIONA_AUTOMATICO
ID_FINESTRE_FINESTRAIMMAGINEFRONTALE
ID_FINESTRE_FINESTRAIMMAGINELATERALE
ID_FINESTRE_FINESTRAPALETTEFRONTALE
ID_FINESTRE_FINESTRAPALETTELATERALE
ID_FINESTRE_FINESTRACONTROLLI
ID_FINESTRE_FINESTRAWEBUNO
ID_FINESTRE_FINESTRAWEBDUE
ID_FINESTRE_FINESTRAPOSIZIONA
ID_POSIZIONA_INTERROMPI
ID_POSIZIONA_MANUALE
ID_POSIZIONA_STOP
ID_APPLICAZIONI_DEMOUNO
ID_POSIZIONA_OK
ID_APPLICAZIONI_DEMODUE
ID_APPLICAZIONI_DEMOTRE
ID_POSIZIONA_RITORNA
101
102
105
1034
1036
1036
1037
1039
1041
1043
1045
1047
1049
1051
1053
1054
1055
1057
1059
1061
1063
1066
1069
40001
40002
40003
40004
40005
40006
40008
40009
40012
40015
40016
40016
40017
40017
40018
40020
40021
40022
40023
40024
40025
40031
40032
40033
40034
40052
40052
40053
40054
40055
40056
40057
40058
40059
40060
40061
40062
40063
40065
40066
40067
40068
40069
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
59
#ifndef
#define
#define
#define
#define
#endif
#endif
APSTUDIO_READONLY_SYMBOLS
_APS_NEXT_RESOURCE_VALUE
_APS_NEXT_COMMAND_VALUE
_APS_NEXT_CONTROL_VALUE
_APS_NEXT_SYMED_VALUE
103
40070
1070
101
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);
60
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);
61
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:
62
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:
63
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;
64
// 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;
// Ininfluente
Temp->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;
65
}
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;
else
TempLA->Prescelta = false;
TempLA = TempLA->Successivo;
}
}
// Verifico se l'Applicazione è prescelta:
bool GestoreFinestre::GetPresceltaApplicazione(UINT Tipo)
{
TempLA = TestaLA;
66
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;
else
Temp->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;
}
}
67
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);
68
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))
69
{
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;
}
}
70