Interfaces multitactiles LOG 745
Rappel de matière vue en LOG350: les widgets contextuels pour lancer des commandes (menus contextuels, menus radiaux, Marking Menus, etc.)
Verbes (actions, commandes, outils, opérations) Noms (objets, endroits)
Verbes dans un menu déroulant Noms (objets, endroits)
Verbes dans un menu contextuel
Les modes créent la possibilité d’avoir des erreurs de mode, où l’utilisateur se croît en un mode lorsqu’il est dans un autre Un retour visuel indiquant le mode actuel est bien, mais souvent n’est pas assez pour empêcher les erreurs de mode –Exemples de retours visuels indiquant le mode: icône d’outil surligné, forme de curseur, barre d’état Les menus contextuels aided… –À éviter les erreurs de mode, via des modes temporaires et (parfois) un retour kinesthésique (pression dans le doigt qui tient une touche appuyée) –À augmenter l’espace d’écran disponible pour montrer le contenu/données (quoique ce contenu/données seront cachés temporairement pendant que le menu est affiché) –Diminuent la distance à traverser avec le curseur –Peuvent fusionner la sélection de nom et verbe (sélection plus rapide; meilleur couplage mental (“mental chunking” – Buxton 1986))
Étant donné tous ces avantages des menus contextuels, pouvons- nous améliorer leur conception? Y a t-il des widgets ou des techniques d’interaction encore mieux?
Menu radial (“Radial Menu”, “Pie Menu”)
Exemple utilisant, effectivement, des menus radiaux Yatani et al., CHI 2008
Menus radiaux versus menus linéaires Les directions sont plus mémorables et plus faciles à reproduire que les distances.
Menu radial hiéarchique
« Mouse Gestures » pour Firefox
Marking Menu “Scale invariant recognition”: Reconnaissance des gestes (marques) qui ne dépend pas de la longueur des segments; seule les angles des segments importe. Donc, les marques peuvent être dessinées en petit et donc rapidement, de façon balistique. Un utilisateur qui sait quelle marque dessiner n’a même pas besoin de voir le menu s’afficher.
Ensemble de marques découvrables (“self-revealing”), contrairement aux interfaces gestuelles habituelles
Présentation graphique améliorée
Marking Menus Vidéo Démonstration (cobaye voluntaire s.v.p.?)
Transition de néophyte en expert Menus traditionels: Pointage versus racourcis Marking Menus: Transition graduelle et naturelle !
Les Marking Menus Permettent une sélection plus rapide qu’avec les menus linéaires (marques directionnelles et ballistques) Peuvent être utilisés sans regarder l’écran (“eyes-free operation”) Ont un ensemble de gestes découvrables Permettent une transition graduelle et naturelle de novice en expert Peuvent être utilisés pour sélectionner nom et verbe Sont limités à environ 8 commandes par sous- menu, et à une profondeur d’environ 3 niveaux
D’autres menus et widgets contextuels Control Menus (Pook et al., 2000) Flow Menus (Guimbretière et Winograd, 2000) ( FaST Sliders (McGuffin et al., 2002) ) Menu radial dans Scriboli (Hinckley et al., 2005) Tracking Menus (Fitzmaurice et al., 2003) ( Trailing Widget (Forlines et al., 2006) ) Hover Widgets (Grossman et al., 2006) PieCursor (Fitzmaurice et al., 2008) Hotbox (Kurtenbach et al., 1999) ToolGlass Ces widgets et techniques d’interaction sont adaptés pour: –Un grand nombre de commandes –Le contrôle de variables continues –L’entrée de texte et de nombres avec des gestes –L’utilisation d’un stylet (par exemple, sur un “tablet PC”) –Travailler avec deux mains
Exemple de Control Menu (dans SimplePaint)
Exemple de Control Menu (dans BumpTop) Anand Agarawala, Ravin Balakrishnan. (2006). Keepin' it real: Pushing the desktop metaphor with physics, piles and the pen. Proceedings of CHI the ACM Conference on Human Factors in Computing Systems. p Keepin' it real: Pushing the desktop metaphor with physics, piles and the pen
FlowMenus (Guimbretière et al., 2000)
Manipulation 2D avec FlowMenus (Guimbretière et al., 2000)
Scriboli Hinckley et al., CHI
Tracking Menu Fitzmaurice et al., UIST
HoverWidgets Grossman et al., CHI
Pie Cursor Fitzmaurice et al., CHI
Le « hotbox »: un menu 2D dans Maya
Toolglass: entrée bimanuelle Clic-à-travers (click-through): sélection simultanée d’objet et de commande !
Comment analyser et comparer ces différentes techniques pour lancer des commandes ?
Modèle à trois états de Buxton (1990) État 0: pas de coordonnées (x,y) États 1 et 2: la position (x,y) est captée Exemples: Tablette numérisante: états 0, 1, 2 Souris: états 1, 2 Écran tactile: états 0, 1
Modèle à trois états de Buxton (1990) État 0: pas de coordonnées (x,y) États 1 et 2: la position (x,y) est captée Exemples: Tablette numérisante: états 0, 1, 2 Souris: états 1, 2 Écran tactile: états 0, 1 État 0: hors de portée sans coordonnées État 1: survol (x,y) État 2: glissement (x,y)
TouchMouse (Hinckley et Sinclair 1999) États 0, 1, 2
TouchMouse (Hinckley et Sinclair 1999)
Taxonomie de menus 2 points d’entrée1 point d’entrée + 1 bouton sous l’autre main 1 point d’entrée Distingue entre l’état “hors de portée” (sans coordonnées) et au moins un état avec coordonnées (x,y) Distingue entre 2 états avec coordonnées (x,y) (c.-à-d. distingue les états de survol et de glissement)
Taxonomie de menus 2 points d’entrée1 point d’entrée + 1 bouton sous l’autre main 1 point d’entrée Distingue entre l’état “hors de portée” (sans coordonnées) et au moins un état avec coordonnées (x,y) ToolglassBarre de menu fixe Palette flottante Marking Menus ControlMenus Flow Menus Menus de Scriboli Distingue entre 2 états avec coordonnées (x,y) (c.-à-d. distingue les états de survol et de glissement) HotboxTracking Menus HoverWidget Pie Cursor
De grands écrans
Al Gore
Des grands écrans collaboratifs
Des grands écrans collaboratifs
1920×2160 pixel, 90×100 cm multitouch surface at ETS (made with two 1920×1080 pixel, 42- inch displays), designed and built by Christophe Viau (a Ph.D. student of McGuffin) and Jean-François Lahos.
3M Multi-touch Developer Kit US $ 1600 Ancien modèle: écran LCD, 19 pouces, 1440×900 pixels, 10 doigts Nouveau modèle: écran LCD, 22 pouces, 1680×1050 pixels, 20 doigts
Écran 32 pouces de 3M ~5000 $ 10 doigts 1920x1080
perceptivepixel.com 27 pouces, 2560x1440, détecte la pression et le survol des doigts, fonctionne avec stylet et plusieurs doigts, ~14000$
perceptivepixel.com 82 pouces
Multitouchmedia.com Entreprise montréalaise Plusieurs modèles d’écrans peu chers
Question pour discussion Dans un logiciel de dessin, quelle technique de menu choisir pour lancer des commandes ? –Pour un petit écran, grand écran ? –Pour un utilisateur vs plusieurs ? –Si chaque utilisateur Peut utiliser plusieurs doigts Peut utiliser un seul stylo
Quelques informations utiles pour votre travail pratique concernant un tableau blanc électronique multitactile multi-utilisateur
TUIO ( Un protocol réseau pour les surfaces tangibles multitactiles Des libraries TUIO sont disponibles pour plusieurs langages de programmation En Java, libTUIO.jar permet d’obtenir des événements de toucher, mouvement, et relâchement en définissant un “listener” (écouteur) d’événements Utilisé pour lire les événements de notre grande surface multitactile (Peut être aussi utilisé pour lire les événements des écrans 3M sur Windows 7, si on se sert du “bridge” W2TUIO qui traduit les événements WM_TOUCH en événements TUIO, mais cela ne fonctionne pas aussi bien que lire les événements WM_TOUCH directement.)
Machine Cliente Logiciel Client Grande surface multitactile TUIO avec notre grande surface multitactile Machine serveur qui fait le traitement d’image Caméra IR Écrans LCD libTUIO.jar Événements TUIO transmis sur une connexion réseau Câble vidéo TuioListener
Machine tournant MS Windows 7 TUIO avec un écran 3M Écran 3M Événements WM_TOUCH W2TUIO Câble vidéo Câble USB Logiciel Client libTUIO.jar TuioListener Événements TUIO
Événements “WM_TOUCH”de Windows 7 Windows 7 reconnaît automatiquement les écrans de 3M comme un dispositif multitactile, sans installer de pilote. Le toucher, mouvement, relâchement sur l’écran génère des événements WM_TOUCH On utilise une partie de la librarie MT4j ( pour lire les événements WM_TOUCH dans Javahttp:// Malheureusement, au lieu d’utiliser un “listener”, on doit interroger (“poller”) activement et fréquemment pour obtenir ces événements
Écran 3M et lecture des événements WM_TOUCH Écran 3M Machine tournant MS Windows 7 Événements WM_TOUCH Logiciel Client MT4j interrogation Câble vidéo Câble USB
Code pour interroger while (true) { if ( multitouchInterface.pollForInputEvent() ) { client.processMultitouchInputEvent(... ); } multitouchPollingThread.sleep( …? // millisecondes ); } Comment choisir l’interval de sommeil ? Qu’arrive-t-il si l’interval est trop court ? Et si c’est trop long ?
Interval de sommeil adaptif public void run() { // adaptive sleep interval int [ ] sleepInterval = {0,0,0,1,1,1,2,2,2,3,3,3,5,5,5,10,10,10,20,25}; int indexIntoSleepInterval = 0; try { multitouchInterface.initialize( … ); while (true) { if ( multitouchInterface.pollForInputEvent() ) { indexIntoSleepInterval = 0;... client.processMultitouchInputEvent(... ); } else { // There was no input event waiting for us, // so we can sleep a little bit longer than last time. if ( indexIntoSleepInterval < sleepInterval.length-1 ) { ++ indexIntoSleepInterval; } if ( sleepInterval[ indexIntoSleepInterval ] > 0 ) { multitouchPollingThread.sleep( sleepInterval[ indexIntoSleepInterval ] // millisecondes ); } catch (InterruptedException e) { } } Ce code est tiré du framework qui vous sera fourni.
Quelques classes de base et routines qui vous seront disponibles …
Point2D public class Point2D { public float [ ] p = new float[2]; public Point2D() { p[0] = p[1] = 0; } public Point2D( float x, float y ) { p[0] = x; p[1] = y; } public Point2D( Vector2D V ) { p[0] = V.v[0]; p[1] = V.v[1]; } public void copy( Point2D P ) { p[0] = P.p[0]; p[1] = P.p[1]; } public float x() { return p[0]; } public float y() { return p[1]; } // used to pass coordinates directly to OpenGL routines public float [ ] get() { return p; } // return the difference between two given points static public Vector2D diff( Point2D a, Point2D b ) { return new Vector2D( a.x()-b.x(), a.y()-b.y() ); } // return the sum of the given point and vector static public Point2D sum( Point2D a, Vector2D b ) { return new Point2D( a.x()+b.x(), a.y()+b.y() ); } // return the difference between the given point and vector static public Point2D diff( Point2D a, Vector2D b ) { return new Point2D( a.x()-b.x(), a.y()-b.y() ); } public float distance( Point2D otherPoint ) { return diff( this, otherPoint ).length(); } static Point2D average( Point2D a, Point2D b ) { return new Point2D( (a.x()+b.x())*0.5f, (a.y()+b.y())*0.5f ); }
Vector2D import java.lang.Math; public class Vector2D { public float [ ] v = new float[2]; public Vector2D() { v[0] = v[1] = 0; } public Vector2D( float x, float y ) { v[0] = x; v[1] = y; } public Vector2D( Point2D P ) { v[0] = P.p[0]; v[1] = P.p[1]; } public void copy( Vector2D V ) { v[0] = V.v[0]; v[1] = V.v[1]; } public float x() { return v[0]; } public float y() { return v[1]; } public float lengthSquared() { return x()*x() + y()*y(); } public float length() { return (float)Math.sqrt( lengthSquared() ); } public Vector2D negated() { return new Vector2D(-x(),-y()); } public Vector2D normalized() { float l = length(); if ( l > 0 ) { float k = 1/l; // scale factor return new Vector2D(k*x(),k*y()); } else return new Vector2D(x(),y()); } // returns the dot-product of the given vectors // Notez: “dot product” veut dire produit scalaire static public float dot( Vector2D a, Vector2D b ) { return a.x()*b.x() + a.y()*b.y(); } // returns the sum of the given vectors static public Vector2D sum( Vector2D a, Vector2D b ) { return new Vector2D( a.x()+b.x(), a.y()+b.y() ); } // returns the difference of the given vectors static public Vector2D diff( Vector2D a, Vector2D b ) { return new Vector2D( a.x()-b.x(), a.y()-b.y() ); } // returns the product of the given vector and scalar static public Vector2D mult( Vector2D a, float b ) { return new Vector2D( a.x()*b, a.y()*b ); }
Point2DUtil public class Point2DUtil { static public Point2D computeCentroidOfPoints( ArrayList points ) { float x = 0, y = 0; for ( Point2D p : points ) { x += p.x(); y += p.y(); } if ( points.size() > 1 ) { x /= points.size(); y /= points.size(); } return new Point2D( x, y ); }... }
Point2DUtil public class Point2DUtil {... static public boolean isPointInsidePolygon( ArrayList polygonPoints, Point2D q ) {... }... } Utile pour réaliser la sélection en lasso Aussi utile pour réaliser la sélection de polygones
Point2DUtil public class Point2DUtil {... static public ArrayList computeConvexHull( ArrayList points ) {... }... } “Convex hull” = enveloppe convexe Utile pour générer un polygone à partir d’un ensemble de points
Comment rajouter une marge autour d’un polygone convexe ? Ensemble de points Enveloppe convexe Enveloppe convexe avec une “marge” rajoutée Calcul de l’enveloppe convexe Comment ?
Point2DUtil public class Point2DUtil {... static public ArrayList computeExpandedPolygon( ArrayList points, // input float marginThickness ) {... }... }
ArrayList points = …; points = Point2DUtil.computeConvexHull( points ); points = Point2DUtil.computeExpandedPolygon( points, marginThickness ); points initialsenveloppe convexe marge rajoutée, méthode naïve marge rajouté avec Point2DUtil. computeExpandedPolygon()
Point2DUtil public class Point2DUtil {... static public void transformPointsBasedOnDisplacementOfOnePoint( ArrayList points, Point2D P_old, Point2D P_new ) { Point2D centroid = computeCentroidOfPoints( points ); Vector2D v1 = Point2D.diff( P_old, centroid ); Vector2D v2 = Point2D.diff( P_new, centroid ); float rotationAngle = Vector3D.computeSignedAngle( new Vector3D(v1.x(),v1.y(),0), new Vector3D(v2.x(),v2.y(),0), new Vector3D(0,0,1) ); float lengthToPreserve = v1.length(); Point2D newCentroid = Point2D.sum( P_new, Vector2D.mult( v2.normalized(), - lengthToPreserve ) ); Vector2D translation = Point2D.diff( newCentroid, centroid ); float cosine = (float)Math.cos( rotationAngle ); float sine = (float)Math.sin( rotationAngle ); for ( Point2D p : points ) { float relativeX = p.x() - centroid.x(); float relativeY = p.y() - centroid.y(); p.get()[0] = (cosine*relativeX - sine*relativeY) + translation.x() + centroid.x(); p.get()[1] = (sine*relativeX + cosine*relativeY) + translation.y() + centroid.y(); }... }
Transformation en fonction du déplacement de un point Michel Beaudouin-Lafon, “Novel Interaction Techniques for Overlapping Windows”, UIST
C new =P new –(D old ).length()×(D new ).normalized() D new = (P new -C old ) D old =P old –C old [difference] Transformation en fonction du déplacement de un point Composantes de la transformation: Translation: C new – C old Rotation: l’angle entre D new et D old C old [centroid] P new P old
B old Point2DUtil public class Point2DUtil {... static public void transformPointsBasedOnDisplacementOfTwoPoints( ArrayList points, Point2D A_old, Point2D B_old, Point2D A_new, Point2D B_new ) {... }... } B new A new A old
Transformation en fonction du déplacement de deux points Composantes de la transformation: Translation: M new – M old Rotation: l’angle entre D new et D old Changement d’échelle (“scale”): (D new ).length() / (D old ).length() B new M new =(1/2)(A new +B new ) [midpoint] D new = (A new -B new ) [difference] A new B old M old D old A old
Transformation en fonction du déplacement de deux points