Programmazione grafica: l’Interazione Daniele Marini 1 Generalità • L’interazione dipende dalle caratteristiche dei dispositivi fisici e del sistema operativo • OpenGL si appoggia sul window manager (XWindow, Microsoft Windows, Mac, ..) • Per interfacciare OpenGL al window manager faremo uso di GLUT, indipendente dallo specifico WM 2 Dispositivi di input Dispositivi logici e dispositivi fisici: analogo al rapporto indotto da funzioni come: printf, scanf, getchar, putchar: si puo’ leggere o scrivere su file, stampante, display, tastiera ecc. Nella grafica è più complesso: non sono solo caratteri! 3 Dispositivi di input • Dispositivi di puntamento: per indicare una posizione su uno schermo, comprende bottoni per inviare interrupt o segnali • • • • • • Mouse Trackball Tablet Light pen Joystick Spaceball • Tastiera: invia caratteri 4 Mouse/trackball • Non trasmettono posizione ma velocità • I due valori rilevati dal movimento della sferetta sono incrementi relativi, posti in relazione all’intervallo di tempo danno luogo al calcolo di una velocità, la cui integrazione restituisce una posizione relativa • Permette di ottenere movimenti lenti o veloci del cursore 5 tablet • Fornisce posizione assoluta • La posizione viene rilevata con interazione elettromagnetica della punta di una penna speciale con una maglia di fili annegati nella superficie • Touch screen ha funzioni analoghe 6 lightpen • Un sensore ottico rileva la presenza di luce dal display, invia un interrupt all’host, che ricava la posizione conoscendo l’istante di rilevazione dell’interrupt e il tempo di scansione della figura; identifica l’oggetto grafico indicato conoscendo l’intera struttura dati e il modo di attraversamento della struttura • Limiti dovuti alla complessità e alla soglia di illuminazione: se punta lo sfondo non rileva luce! 7 Joystick • Anche il joystick rileva velocità, che vengono integrate per calcolare posizioni relative • Può avere componenti meccaniche che simulano effetti di resistenza con stimolazione “cinestesica” 8 Spaceball • Dotata di sensori di pressione, ha sei gradi di libertà ed è adatta ad input 3D: • Front-back left-right top-bottom • Rotazioni su tre assi 9 Dispositivi logici - classificazione GKS e PHIGS • Secondo lo standard GKS e PHIGS sei logical devices: • String - es. tastiera • Locator - fornisce coordinate mondo (WC) es. mouse con conversione da SC (ccordinate schermo) a WC • Pick - restituice l’identificatore di un oggetto • Choice - seleziona una opzione • Dial - input analogico (potenziometri) • Stroke - array di locazioni 10 Logical devices - OpenGL • string - supportata dal Window Manager (WM), OGL non distingue tra dispositivo fisico e logico (tastiera) • locator - OGL converte da SC a WC con dati da mouse ecc. • Pick - OGL usa il processo selection • choice - OGL usa widgets (gadget: congegno, dispositivo; aggeggio, arnese) del WM (menu, scrollbar, bottoni ecc.) • dial - OGL usa widgets del WM (sliders) • stroke - OGL simula l’invio di sequenze di valori attivando e terminando lo stream ad es. col bottone del mouse 11 Misura e trigger • L’input di un dispositivi fisico è caratterizzato da misura e trigger – Misura: è il valore restituito dal dispositivo – Trigger: è un input fisico con cui l’utente invia un segnale al sistema • String: measure = caratteri battuti, trigger = CR • Locator: measure = posizione, trigger = click su bottone • Si può anche considerare un valore di stato (il cursore è fuori dalla window …) 12 Modi di input • Request mode – La misura viene ritornata al sistema quando il trigger viene attivato request_locator(device_id, &measure) • Sample mode – Input immediato, non c’è trigger sample_locator(device_id, &measure) 13 • Request mode - tipico di programmi non grafici, es. input da tastiera con scanf – Oppure per posizionare il cursore in WM e selezionare con click • Sample mode e request mode non sono adatti ad applicazioni in cui l’utente determina il flusso del programma, è piuttosto il programma che guida l’utente (es. scelte di menu, word processing) • In un simulatore di volo possono esserci input da moltissimi dispositivi 14 Event mode • Permette di trattare situazioni complesse • In ogni istante ogni dispositivo può generare un evento • Le measures del dispositivo, con identificatore associato, sono poste in una coda di eventi (event queue) indipendentemente da ciò che sta eseguendo il programma - il programma esamina la coda e decide che fare, se la coda è vuota resta idle 15 Event mode • Alternativamente si associa a ogni tipo di evento una callback • L’approccio con callback è il più usato nei sistemi di WM e in OGL, e si presta a situazioni client-server 16 Client-server • Servizi distribuiti in LAN (printer, file, graphics, ..) • Un programma applicativo OGL e’ un processo client che usa un graphics server, i due processi possono risiedere sul medesimo sistema o essere distribuiti 17 Programmazione dell’interazione event driven • • • • • • Uso del puntatore Terminazione programma Resize della finestra Gestione della finestra Menu Picking 18 Uso puntatore • Il puntatore può essere usato per la terminazione del programma; distinguiamo tra – move event: il mouse viene mosso col bottone premuto – passive move event: mosso con bottone alzato Dopo un move event la “measure” del device (posizione del mouse) è resa disponibile – mouse event - un bottone del mouse è premuto o rilasciato (il bottone mantenuto premuto non genera eventi, è la transizione che conta!) 19 Terminazione programma glutMouseFunction(mouse_callback_func); void mouse_callback_func(int button, int state, int x, int y,); int x, int y; { if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) exit(); } Altri bottoni o uno stato differente del mouse non provocano nulla, non ci sono callback registrate col WM 20 main int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLLUT_RGB); glutCreateWindow(“square”); myinit(); glutReshapeFunc(myReshape); glutMouseFunc(mouse); glutDisplayFunc(display); glutMainLoop(); } Reshape è invocato quando la finestra viene modificata dall’utente Ogni programma deve avere una display callback, anche vuota: void display() {} 21 Disegnare un rettangolo al click del mouse void mouse(int button, int state, int x, int y); { if(button==GLUT_LEFT_BUTTON && state==GLUT_DOWN) drawSquare(x,y); if(button==GLUT_MIDDLE_BUTTON && state==GLUT_DOWN) exit(); } La drawSquare si limita a disegnare, gli attributi del disegno vanno definiti altrove, nell’esempio in myinit 22 myinit /* globals */ Glsizei wh = 500, ww = 500; /* dim finestra iniziale */ Glfloat size = 3.0; /* metà del lato del quadrato */ void myinit(void) { glViewport(0,0,ww,wh); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, (GLdouble) ww , 0.0, (GLdouble) wh , -1.0, 1.0); glMatrixModel(GL_MODELVIEW); glClearColor (0.8, 0.8, 0.8, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); } 23 Disegno del quadrato L’origine del sistema di riferimento nel WM è in alto a sinistra, con y verso il basso, in OGL è in basso a sinistra con y verso l’alto - bisogna “flippare” la y ritornata dal mouse: void drawSquare(int x, int y) { y=wh-y; /* flippa y */ glColor3ub( (char) random()%256, (char) random()%256, (char) random()%256); glBegin(GL_POLYGON); glVertex2f(x+size, y+size); glVertex2f(x-size, y+size); glVertex2f(x-size, y-size); glVertex2f(x+size, y-size); glEnd(); } 24 Il programma è completo • Inizializza una finestra, disegna un quadrato di colore casuale nel punto selezionato dal mouse col tasto di sinistra; termina premendo il tasto di mezzo del mouse • Non ha menu, se la finestra viene modificata (resize) il programma non sa che fare 25 Window events • Es: resize, che fare? – Ridisegnare tutto? – Che fare se l’aspect ratio cambia? – Si cambiano le dimensioni degli oggetti se la finestra cambia dimensioni? • Nell’esempio si ridisegna tutto, mantenendo i rapporti degli oggetti (quadrati restano quadrati) e si applica il clipping • L’evento di resize restituisce altezza e larghezza 26 della window myReshape void myReshape(GLsizei w, GLsizei h) { /* adjust clipping box */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* adjust viewport and clear */ glViewport(0,0,w,h); glClearColor (0.8, 0.8, 0.8, 1.0); glClear(GL_COLOR_BUFFER_BIT); display(); glFlush(); } 27 Display callback La glutDisplayFunc(display) viene eseguita quando GLUT determina che la finestra va ridisegnata, certamente alla prima apertura; in questo caso è utile disegnare le parti statiche della finestra La display callback può essere usata per animazione, quando cambiano parametri nel programma Per iconizzare una finestra si usa la glutPostDisplay() Per disabilitare una callback si può settare la sua callback function a NULL 28 Finestre multiple id=glutCreateWindow(“second window”); L’intero ritornato permette di attivare la prima o la seconda finestra: glutSetWindow(id); Si possono modificare parametri e proprietà della seconda finestra prima di definirla: glutInitDisplayMode( …); id=glutCreateWindow(“third window”); 29 Menu Pull-down e pop-up menus glutCreateMenu(demo_menu); glutAddMenuEntry(“quit”,1); glutAddMenuEntry(“increase square size”,2); glutAddMenuEntry(“decrease square size”,3); glutAttachMenu(GLUT_RIGHT_BUTTON); void demo_menu(int id); { if(id==1) exit(); else if (id==2) size = 2*size; else if (id==3) size=size/2; glutPostRedisplay(); } 30 Menu pull-down gerarchici sub_menu = glutCreateMenu(size_menu); glutAddMenuEntry(“increase square size”,2); glutAddMenuEntry(“decrease square size”,3); glutCreateMenu(top_menu); glutAddMenuEntry(“quit”,1); alutAddSubMenu(“Resize”, sub_menu); glutAttachMenu(GLUT_RIGHT_BUTTON); Esercizio: scrivere le callback function di: size_menu top_menu 31