diff --git a/rapport/conclusion.tex b/rapport/conclusion.tex new file mode 100644 index 0000000..a558256 --- /dev/null +++ b/rapport/conclusion.tex @@ -0,0 +1 @@ +\part*{Conclusion} diff --git a/rapport/img/new/01.png b/rapport/img/new/01.png new file mode 100644 index 0000000..499a0e4 Binary files /dev/null and b/rapport/img/new/01.png differ diff --git a/rapport/img/new/02.png b/rapport/img/new/02.png new file mode 100644 index 0000000..3c606a6 Binary files /dev/null and b/rapport/img/new/02.png differ diff --git a/rapport/img/new/03.png b/rapport/img/new/03.png new file mode 100644 index 0000000..cba0d79 Binary files /dev/null and b/rapport/img/new/03.png differ diff --git a/rapport/img/new/04.png b/rapport/img/new/04.png new file mode 100644 index 0000000..2810b0b Binary files /dev/null and b/rapport/img/new/04.png differ diff --git a/rapport/img/new/buttons.png b/rapport/img/new/buttons.png new file mode 100644 index 0000000..9d8b63f Binary files /dev/null and b/rapport/img/new/buttons.png differ diff --git a/rapport/img/new/resize.m b/rapport/img/new/resize.m new file mode 100644 index 0000000..a4d0893 --- /dev/null +++ b/rapport/img/new/resize.m @@ -0,0 +1,17 @@ +i_min = 167; +j_min = 391; +i_max = 933; +j_max = 1524; + +repo = 'screenshots'; + +shots = dir(repo); + +for i = 1:length(shots) + i + if (strcmp(shots(i).name, '.') || strcmp(shots(i).name, '..')) + continue + end + im = imread(strcat(repo, '/', shots(i).name)); + imwrite(im(i_min:i_max,j_min:j_max,:), strcat('/home/thomas/stage/rapport/rapport/img/new/', shots(i).name)) ; +end diff --git a/rapport/img/new/screenshots/01.png b/rapport/img/new/screenshots/01.png new file mode 100644 index 0000000..142dfb4 Binary files /dev/null and b/rapport/img/new/screenshots/01.png differ diff --git a/rapport/img/new/screenshots/02.png b/rapport/img/new/screenshots/02.png new file mode 100644 index 0000000..9eb23c4 Binary files /dev/null and b/rapport/img/new/screenshots/02.png differ diff --git a/rapport/img/new/screenshots/03.png b/rapport/img/new/screenshots/03.png new file mode 100644 index 0000000..1bbc62f Binary files /dev/null and b/rapport/img/new/screenshots/03.png differ diff --git a/rapport/img/new/screenshots/04.png b/rapport/img/new/screenshots/04.png new file mode 100644 index 0000000..c039a1b Binary files /dev/null and b/rapport/img/new/screenshots/04.png differ diff --git a/rapport/interface.tex b/rapport/interface.tex new file mode 100644 index 0000000..4e7ef16 --- /dev/null +++ b/rapport/interface.tex @@ -0,0 +1,188 @@ +\part{L'interface} +\section{Interactions élémentaires} +\paragraph{} +La première interface a été pensée pour être la plus simple possible. +L'utilisateur contrôle une caméra qui se déplace librement dans un modèle 3D. + +\paragraph{} +La translation de la caméra est contrôlée par le clavier : les touches Z, Q, S, +et D servent respectivement à avancer, aller à gauche, reculer et aller à +droite (de même que les touches fléchées). + +\paragraph{} +On peut pivoter la caméra de plusieurs manières : +\begin{itemize} + \item via le pavé numérique (2, 4, 6, et 8 pour tourner respectivement vers + le bas, vers la gauche, vers la droite et vers le haut) + \item via la souris, comme \emph{drag-n-drop}, en cliquant un point de la + scène et en le déplaçant + \item via la souris, en mode \emph{pointer-lock}, comme dans un jeu video + de tir +\end{itemize} + + +\section{Les recommandations} +\paragraph{} +Les recommandations sont là pour suggérer des points de vue à l'utilisateur. +Elles permettent d'aider la navigation. Elles sont affichées sous forme +d'objets 3D ajoutés à la scène. Deux affichages ont été testés. + + +\subsection{Les \emph{viewports}} +\paragraph{} +Les \emph{viewports} sont les affichages les plus simples : ils représentent +une caméra, avec son centre optique et son plan image. +\begin{figure}[H] + \centering + \includegraphics[scale=0.275]{img/new/01.png} + \caption{Une recommandation \emph{viewport}} +\end{figure} +Cette façon d'afficher une recommandation a l'avantage d'être simple, de ne pas +beaucoup masquer le reste des modèles et suggère assez bien l'idée d'un +\emph{point de vue recommandé}, mais elle a l'inconvéniant d'être ambigüe à +cause de la perspective (dans cette image, il peut être difficile de savoir si +le point de vue et vers le modèle ou vers nous). + + +\subsection{Les flèches} +\subsubsection{Principe} +Les flèches sont supposées être plus intuitives pour un utilisateur qui n'a pas +l'habitude des \emph{viewports} précédemment utilisés. Plutôt que de suggérer +un point de vue, elles suggèrent le mouvemement qui va mener à ce point de vue. +\begin{figure}[H] + \centering + \includegraphics[scale=0.275]{img/new/02.png} + \caption{Des recommandations flèches} +\end{figure} + +\subsubsection{Courbure de la flèche} +Pour le dessin des flèches, plusieurs choses sont à prendre en compte : +\begin{itemize} + \item il faut éviter que la flèche soit trop \emph{présente} à l'écran et + qu'elle obstrue trop le reste de la scène + \item il faut que la flèche soit dans un plan qui ne soit pas orthogonal au + plan image de la caméra. En effet, si la flèche est dans un plan + orthogonal au plan image de la caméra, elle se projettera comme un + segment et on ne pourra pas voir la courbure de la flèche. +\end{itemize} + +\paragraph{} +Pour trouver la courbure de la flèche, nous posons $C$ le centre de la caméra, +$R$ le centre de la recommandation, et $R'$ le vecteur qui donne la direction +de la recommandation. Nous cherchons ensuite un polynôme $f$ tel que : +$$\left\{\begin{array}{lcl} + f(0) & = & C \\ + f(1) & = & R \\ + f'(1) & = & \lambda R' \text{ avec } \lambda \in \mathbb{R}^{+} +\end{array}\right.$$ + +\paragraph{} +Le problème de ce polynôme est qu'il ne vérifie aucune des contraintes énoncées +précédemment : puisque $f(0) = C$, le bout de la flèche est dans la caméra, et +va donc masquer toute la scène, et la courbe va en plus se projeter sur une +ligne sur l'écran. + +\paragraph{} +Pour solutionner le premier problème, nous nous contenterons d'afficher +seulement la flèche pour des instants $t \in [0.5, 1]$. + +\paragraph{} +Pour solutionner le deuxième problème, nous allons translater le centre de la +caméra vers le bas et le côté de la direction de la recommandation, pour +accentuer la courbure de la flèche, et nous résolvons plutôt le système suivant +: +$$\left\{\begin{array}{lcl} + f(0) & = & C - e_z + \lambda R' \\ + f(1) & = & R \\ + f'(1) & = & \lambda R' \text{ avec } \lambda \in \mathbb{R}^{+} +\end{array}\right.$$ + +\subsection{Les interactions} +\subsubsection{Au survol} +\indent Cette fonctionnalité est inspirée des récents lecteurs video sur le +web. Lorsque l'on regarde une video, on a la barre de \emph{seeking} en bas et +passer le curseur sur cette barre affiche l'image de la vidéo à l'instant visé. +Nous avons simplement adapté cette techniques à nos recommandations : lorsque +le curseur survole une recommandation, une prévisualisation est affichée dans +une petite boite au voisinage du curseur. + +\begin{figure}[H] + \centering + \includegraphics[scale=0.275]{img/new/03.png} + \caption{Une prévisualisation} +\end{figure} + +\subsubsection{Au clic} +\indent Lors d'un clic sur une recommandation, la caméra suit un mouvement +fluide jusqu'au point de vue recommandé. La trajectoire est définié par un +polynôme interpolant tel que : +\begin{itemize} + \item la position initiale est la position de la caméra + \item la position finale est la position de la recommandation + \item la dérivée de la trajectoire à l'instant final est la direction de la + recommandation +\end{itemize} + +\paragraph{} +Ce mouvement fluide est là pour ne pas perturber l'utilisateur qui pourrait +\emph{se perde} si jamais il était téléporté + +\paragraph{} +De plus, les recommandations se comportent comme des liens hypertextes : elles +sont bleues si elles n'ont jamais été clicées, et deviennent violettes si +l'utilisateur les a déjà consommées. Ceci est fait pour qu'un utilisateur +puisse savoir par où il est passé, et ce qui lui reste encore à visiter. + + +\section{Autres éléments de navigation} +\paragraph{} +Pour faciliter la navigation, quelques autres éléments de navigation sont +présents. + +\begin{figure}[H] + \centering + \begin{tikzpicture}[] + \node (myfirstpic) at (0,0) {\includegraphics[scale=0.35]{img/new/buttons.png}}; + \tikzvline{-7}{1} + \tikzvline{-5.8}{2} + \tikzvline{-5.2}{3} + \tikzvline{-4}{4} + \tikzvline{-1.5}{5} + + \draw (8,5) -- (7,5); + \draw (8,5) node[right]{6}; + + \draw (-8,5) -- (-7,5); + \draw (-8,5) node[left]{7}; + \end{tikzpicture} + \caption{Les différents éléments de l'interface} +\end{figure} + + +\paragraph{} +\begin{enumerate} + + \item \emph{Reset camera} : pour chaque scène, une position initiale est + définie. Cliquer sur ce bouton ramène la caméra à sa position initiale. + + \item \emph{Previous} : à chaque clic sur une recommandation, les positions + intiales et finales sont sauvegardées. Cliquer sur ce bouton ramène à + la position précédente. + + \item \emph{Next} : cliquer sur ce bouton ramène à la position suivante. + + \item \emph{Pointer lock} : permet de passer du mode \emph{pointer-lock} au + mode \emph{drag-n-drop} et vice-versa. + + \item \emph{Music} : un lecteur qui contrôle une petite musique qui permet + de se mettre dans l'ambiance de la scène. + + \item \emph{Coin gauge} : une jauge qui représente l'avancement de la + récupération des pièces. + + \item \emph{FPS counter} : indique la période de rafraîchissement du rendu. + +\end{enumerate} + + + diff --git a/rapport/intro.tex b/rapport/intro.tex new file mode 100644 index 0000000..9804a88 --- /dev/null +++ b/rapport/intro.tex @@ -0,0 +1 @@ +\part*{Introduction} diff --git a/rapport/streaming.tex b/rapport/streaming.tex new file mode 100644 index 0000000..a1808ae --- /dev/null +++ b/rapport/streaming.tex @@ -0,0 +1,220 @@ +\part{Streaming de modèle 3D} +Le but ultime de ce projet est de biaiser l'utilisateur avec les +recommandations de sorte à être capable de prévoir ses déplacements futurs, et +ainsi précharger les parties du modèle qui vont être vues. Cette section +présente le travail qui a été réalisé dans le domaine du chargement de modèle. + +\paragraph{} +Évidemment, cette partie est celle qui comment après la fin de la première +partie : il faut nous seulement connaître l'influence des recommandations sur +l'utilisateur, ensuite être capable de prévoir le comportement de +l'utilisateur, et enfin s'en servir pour précharger les bonnes parties du +modèles. Tout ceci n'étant pas encore possible, le travail qui a été fait est +nettement plus simpliste : il n'y aura aucune prévision du comportement ici. + +\section*{Introduction} +Notre problématique ici est de transférer des modèles 3D sur le réseau. Les +modèles sont stockés sur le serveur au format \texttt{.obj} et sont constitués +: +\begin{itemize} + \item des materiaux (\texttt{usemtl}) : cela définit le matériau utilisé + pour les faces qui vont suivre + \item de sommets (\emph{vertices}) : des points 3D + \item de coordonnées de textures : des points en 2D qui référence un point + d'une image + \item de normales : des vecteurs en 3D + \item de faces : une liste de 3 ou 4 sommets (représentés par leurs + indices), avec éventuellement leurs coordonnées de textures et/ou + normales +\end{itemize} + +\paragraph{} +La seule contrainte d'ordre des éléments est qu'une face qui contient des +sommets, coordonnées de textures ou normales n'arrive pas avant ses sommets, +coordonnées de textures ou normales dans le fichier : si l'on parcourt les +lignes du fichier, on doit déjà avoir toutes les informations sur la face. + +\paragraph{} +Généralement, le fichier \texttt{.obj} vient souvent avec un fichier +\texttt{.mtl} qui contient la définition des matériaux (leurs noms, leurs +textures, leurs constantes...). + + +\section{Streaming linéaire} +La première étape de cette fonctionnalité a été de faire un système +client-serveur permettant le streaming de modèle 3D. En effet, les +\emph{loaders} de modèles présents dans la libraire \threejs ne permettent pas +le chargement progressif : ils se content d'envoyer une requête vers le fichier +contenant le modèle et à créer un modèle une fois que le fichier est chargé +complètement. + +\paragraph{} +Pour commencer, nous avons donc utilisé \socketio, une librairie permettant de +gérer les sockets facilement avec JavaScript et Nodejs, pour faire une première +version simpliste du streaming : on travaillait sur un modèle ne contenant que +des sommets et des faces (donc pas de textures ni de normales) et le protocole +fonctionnait ainsi : + +\begin{figure}[H] + \centering + \begin{tikzpicture}[] + \draw (0,0) node[above]{Client}; + \draw (0,0) -- (0,-6); + \draw (5,0) node[above]{Serveur}; + \draw (5,0) -- (5,-6); + + \draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle}; + \draw[->] (0,-1) -- (5, -2); + + \draw (2.5,-2.3) node[rotate=12] {Des éléments}; + \draw[<-] (0,-3) -- (5, -2); + + \draw (2.5,-3.3) node[rotate=-12] {ACK}; + \draw[->] (0,-3) -- (5, -4); + + \draw (2.5,-4.3) node[rotate=12] {D'autres éléments}; + \draw[<-] (0,-5) -- (5, -4); + + \draw (2.5,-6) node{$\vdots$}; + \end{tikzpicture} + \caption{Transmission élémentaire\label{transmission1}} +\end{figure} + +\paragraph{} +Le serveur envoyait alors les éléments dans l'ordre dans lequel ils étaient +présents dans le fichier du modèle 3D. Le gros inconvéniant de cette méthode +est que souvent, les sommets sont présents au début du fichier, et les faces +vers la fin : nous recevons donc des informations de sommets au début qui ne +nous permettent rien d'afficher, puis toutes les faces d'un coup, ce qui fait +que le streaming n'est pas aussi progressif que l'on souhaiterais. + +\paragraph{} +Dans le cas d'un modèle sans coordonnées de textures et sans normales, la seule +condition pour pouvoir afficher une face est d'avoir envoyé les sommets qui la +composent. On peut donc améliorer la fluidité en réarrangeant le fichier +\texttt{.obj} : il suffit de faire apparaître les faces dès que les sommets +sont disponibles. + +\paragraph{} +Dans le cas où l'on souhaite gérer les textures et les normales, utiliser cette +technique est beaucoup plus compliqué puisqu'il faut aussi décider de l'ordre +relatif entre les sommets, coordonnées de textures et normales : en effet, pour +envoyer une face le plus tôt possible, il faut que toutes ces composantes +soient envoyés, et il est donc nécessaire de mélanger les sommets, coordonnées +de texture et normales. + + +\section{Streaming linéaire amélioré} +La remarque précédente conduit directement à cette méthode. Le principe reste +le même que celui précédent (figure \ref{transmission1}), mais les éléments +seront envoyés différemment : on va en fait parcourir les faces directement. + +\paragraph{} +Le serveur va garder en mémoire ce qui a déjà été envoyé et ce qui ne l'a pas +été, et envoyer les faces dans l'ordre : si certains éléments des faces n'ont +pas encore été envoyé, on les enverra juste avant d'envoyer la face en +question. + +\paragraph{} +On peut de cette façon à la fois gérer les coordonnées de texture et les +normales, tout en envoyant les faces le plus tôt possible (on peut aussi noter +que si certains sommets / coordonnées de textures / normales sont inutilisés +dans les faces, ils ne seront pas envoyés et on s'évite donc de transférer des +données inutiles). + +\paragraph{} +C'est dans cette version que nous avons commencé à nous intéresser à la façon +de gérer les matériaux. Dans \threejs, un objet 3D est lié à un materiau, et +nous sommes donc obligés de créer autant d'objets que de materiaux. Pour cela, +nous avons légèrement modifié notre protocole : + +\begin{figure}[H] + \centering + \begin{tikzpicture}[] + \draw (0,0) node[above]{Client}; + \draw (0,0) -- (0,-8); + \draw (5,0) node[above]{Serveur}; + \draw (5,0) -- (5,-8); + + \draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle}; + \draw[->] (0,-1) -- (5, -2); + + \draw (2.5,-2.3) node[rotate=12] {Liste des matériaux}; + \draw[<-] (0,-3) -- (5, -2); + + \draw[dashed, ->] (-0.1,-3) -- (-0.1,-4); + + \draw (2.5,-3.3) node[rotate=-12] {ACK}; + \draw[->] (0,-3) -- (5, -4); + + \draw (2.5,-4.3) node[rotate=12] {Des éléments}; + \draw[<-] (0,-5) -- (5, -4); + + \draw (2.5,-5.3) node[rotate=-12] {ACK}; + \draw[->] (0,-5) -- (5, -6); + + \draw (2.5,-6.3) node[rotate=12] {D'autres éléments}; + \draw[<-] (0,-7) -- (5, -6); + + \draw (2.5,-8) node{$\vdots$}; + \end{tikzpicture} + \caption{Transmission avec gestion des matériaux} +\end{figure} + +\paragraph{} +Les directives de matériau à utiliser (\texttt{usemtl}) seront ignorées, et les +faces seront envoyées avec l'indice de l'objet auquel elles appartiennent, ce +qui permettra au client de savoir dans quel objet (et donc avec quel matériau) +elles doivent être ajoutées. + + +\section{Streaming intelligent} +C'est la dernière version du streaming qui a été faite sur ce projet. À chaque +transfert, le client envoie sa position au serveur (ainsi que les plans +définissant son \emph{frustum}\footnote{les bords du champ de vision de la +caméra}) et le serveur va parcourir les faces du modèle en cherchant celles qui +apparaissent dans le \emph{frustum}\footnote{dans cette version, on considère +qu'une face apparaît dans le \emph{frsutum} si un de ces sommets y appartient. +Évidemment, une face très grande pourrait appraître dans le \emph{frustum} +sans qu'aucun de ses sommets n'y soit, mais nous n'avons pas traité ce cas +particulier ici.}. On évite ainsi d'envoyer les faces du modèle qui sont +derrière la caméra et que l'utilisateur ne voit pas. + +\paragraph{} +Bien sûr, si il n'y a plus de faces dans le \emph{frustum} de la caméra, on va +envoyer les faces en suivant la méthode de la section précédente. + +\begin{figure}[H] + \centering + \begin{tikzpicture}[] + \draw (0,0) node[above]{Client}; + \draw (0,0) -- (0,-8); + \draw (5,0) node[above]{Serveur}; + \draw (5,0) -- (5,-8); + + \draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle}; + \draw[->] (0,-1) -- (5, -2); + + \draw (2.5,-2.3) node[rotate=12] {Liste des matériaux}; + \draw[<-] (0,-3) -- (5, -2); + + \draw[dashed, ->] (-0.1,-3) -- (-0.1,-4); + + \draw (2.5,-3.3) node[rotate=-12] {Caméra / \emph{frustum}}; + \draw[->] (0,-3) -- (5, -4); + + \draw (2.5,-4.3) node[rotate=12] {Des éléments}; + \draw[<-] (0,-5) -- (5, -4); + + \draw (2.5,-5.3) node[rotate=-12] {Caméra / \emph{frustum}}; + \draw[->] (0,-5) -- (5, -6); + + \draw (2.5,-6.3) node[rotate=12] {D'autres éléments}; + \draw[<-] (0,-7) -- (5, -6); + + \draw (2.5,-8) node{$\vdots$}; + \end{tikzpicture} + \caption{Version finale} +\end{figure} + + diff --git a/rapport/techno.tex b/rapport/techno.tex new file mode 100644 index 0000000..02988c0 --- /dev/null +++ b/rapport/techno.tex @@ -0,0 +1,111 @@ +\part{Choix des technologies et prise en main} +\paragraph{} +La première phase de stage était de choisir les technologies qui allaient être +utilisées par la suite. Nous cherchions des technologies permettant la +visualisation 3D sur un navigateur web afin de pouvoir faire une étude +utilisateur simplement. + +\section{Côté client} +\paragraph{} +Pour le côté client, il y avait plusieurs possibilités : +\begin{itemize} + \item WebGL, la spécification des fonctions permettant la 3D dans le + navigateur + \item Une librairie facilitant l'utilisation de WebGL + \item Du code C++ compilé en JavaScript grâce à Emscripten + \item N'importe quel moteur graphique qui puisse exporter vers JavaScript +\end{itemize} + +\paragraph{} +La plupart des moteurs graphiques exportant vers JavaScript sont putôt lourd à +prendre en main, et nous voulions garder des solutions simples, c'est pourquoi +nous avons utilisé une librairie libre nommée \threejs permettant une +utilisation facile de WebGL. + +\paragraph{} +Pour des raisons de simplicité, nous avons décidé de développer le code client +pour Google Chrome et Firefox, les autres navigateurs ne sont donc pas +(officiellement) supportés. + +\section{Côté serveur} +\paragraph{} +Dans un premier temps, seul le côté client était pris en compte. Les programmes +étaient écrits en JavaScript et ne nécessitaient pas de serveur. Quand les +problématiques de dynamicité sont arrivées, il a fallu choisir une technologie +pour le côté serveur, et là, tous les langages étaient possibles. + +\paragraph{} +Plusieurs langages et framework ont été téstés. Quand les problématiques +étaient encore simples (passage d'un paramètre dans une requête), on a commencé +par utiliser le php, puis on s'est tourné vers des scripts CGI en python. Quand +de plus nombreuses pages ont été nécessaires, on a commencé à chercher un vrai +framework, et on s'est penché sur Django (framework web pour Python) qui est +très pratique mais assez coûteux en mémoire vive (le serveur était alors +herbergé sur une petite machine de 512Mo de RAM). + +\paragraph{} +Quand les problématiques de streaming ont commencé à apparaître, nous avons +choisi la simplicité en utilisant Node.js pour le côté serveur (un serveur +écrit en JavaScript) à cause de la présence d'une librairie nommée \socketio +qui s'avère très pratique pour la communication entre le client et le serveur. +Pour des raisons pratiques, le serveur a été herbergé sur un cloud gratuit +(OpenShift). + +\section{Base de données} +\paragraph{} +Pour le système de gestion de base de données, nous avons choisi Postgres (qui +est libre et qui a largement fait ses preuves). OpenShift propose d'héberger +lui-même la base de données, mais la version gratuite ne proposant qu'1 Go +d'espace de stockage, nous avons préféré l'héberger nous-même. + +\section{Développement, debug et déploiement} +\paragraph{} +Pour éviter d'avoir des fichiers trop longs, nous avons choisi de séparer les +sources dans de nombreux fichiers de taille plus petite, et de les fusionner +automatiquement. Pour le développement, ils seront simplement concaténés grâce +à un script développé spécialement pour cela, qui mime les paramètres de +Closure Compiler qui sera utilisé pour la fusion au moment du le déploiement +(ce dernier permet non seulement la fusion des fichiers mais aussi la +minification\footnote{la minification sert notamment à réduire la taille du +script : n'oublions pas que nous parlons de serveur web, et il est donc +intéressant de réduire la taille des programmes de sorte à les charger plus +rapidement} (effacement des commentaires et des retours à la ligne, +simplifications des noms de variables et plus \footnote{en JavaScript, il est +plus court d'écrire \texttt{!0} pour \texttt{true} par exemple.}). Pour le +développement, on a utilisé \href{https://github.com/remy/nodemon}{nodemon} et +inotify, qui permettent de relancer le serveur local lorsqu'une modification +est détectée (la fusion des fichiers est donc réeffectuée). + +\paragraph{} +En ce qui concerne le versionnage des fichiers, nous avons utilisé Git avec +deux \emph{repositories} : +\begin{itemize} + \item le premier, hébergé sur + \href{https://github.com/tforgione/3dinterface}{Github}, sert au + développement, et contient les fichiers fractionnés ainsi que les + outils permettant la génération des fichiers fusionnés. + \item le deuxième, hebergé chez OpenShift, qui contient la version finale + du programme, permet de déployer le code du serveur quand les + \emph{commits} sont \emph{pushés}. +\end{itemize} + +\paragraph{} +Pour nous aider au debug, nous avons utilisé \href{http://jshint.com/}{JSHint} +qui nous aide à détecter les erreurs potentielles liées aux subtilités du +langage. + +\section{Documentation} +Au delà des rapports, deux documentations sont présentes. + +\subsection{\href{https://github.com/tforgione/3dinterface/wiki}{\emph{Github Wiki}}} +Github permet la création de Wiki pour chaque \emph{repository} et nous nous en +sommes servi pour de la documentation de haut niveau : il ne présente que des +aspects théoriques de ce qui a été réalisé pendant ce projet. + +\subsection{\href{http://l3d.no-ip.org/}{L3D}} +Pour de la documentation de plus bas niveau (comment chaque classe ou méthode +fonctionne) nous avons utilisé \jsdoc (équivalent de javadoc mais pour +JavaScript) et nous générons automatiquement des pages html pour avoir une +documentation lisible et à jour sans avoir à parcourir le code. + + diff --git a/rapport/userstudy.tex b/rapport/userstudy.tex new file mode 100644 index 0000000..31bf09a --- /dev/null +++ b/rapport/userstudy.tex @@ -0,0 +1,148 @@ +\part{L'étude utilisateur} +\paragraph{} +Pour tester le comportement des utilisateurs face aux recommandations, nous +avons dissimulé des pièces rouges à travers ces modèles, et nous avons demandé +à des utilisateurs de les trouver. +\begin{figure}[H] + \centering + \includegraphics[scale=0.275]{img/new/04.png} + \caption{Une pièce rouge} +\end{figure} + +\paragraph{} +Pour éviter la dépendance entre les recomendations et les pièces rouges (si les +recommandations visent les pièces rouges, il est évident qu'il sera très facile +de les trouver avec les recommandations), un système de tirage aléatoire de +pièces rouges a été fait. + +\section{Déroulement de l'expérience} +\subsection{Première page} +La première page présente rapidement l'expérience. Elle vérifie aussi le +navigateur : si le client est sur Google Chrome ou Firefox, un lien apparaîtra +pour passer à la suite, sinon, un message d'erreur s'affichera. + +\subsection{Identification} +Cette page nous permet d'en savoir un peu plus sur l'utilisateur : nous allons +demander l'age, le sexe et les habitudes en terme de jeux video de +l'utilisateur (nous avons considéré que la capacité des utilisateurs à manier +notre interface allait dépendre fortement de leur habitude aux jeux video). +Nous leur demandons notamment de noter leurs capacités en terme de jeux videos +(entre 1 et 5 étoiles). + +\subsection{Tutoriel} +Ensuite, nous demandons à l'utilisateur de suivre un tutoriel : c'est une sorte +de réelle expérience mais guidée. Des messages indiquant les interactions à +faire aideront l'utilisateur à s'habituer à cette interface. On y présente les +moyens de déplacer la caméra, puis les recommandations et les différents +boutons de l'interface. + + +\subsection{Les trois vraies expériences} +C'est ensuite que la \emph{vraie} partie de l'étude utilisateur commence : à 3 +reprises, l'utilisateur va se retrouver dans une scène avec certaines pièces +rouges à trouver, et un certain style de recommandations\footnote{aucune +recommandation sera considéré comme un style de recommandation, de sorte à +comparer la présence à l'absence de recommandation pour la navigation} pour +l'aider. Nous expliquerons dans la sous-section \ref{selection} comment le +choix de la scène et du style de recommandation sera fait. + +\paragraph{} +Dans chacune des expériences, l'utilisateur devra chercher des pièces rouges, +en s'aidant (ou pas) des recommandations. L'expérience se terminera soit quand +l'utilisateur aura trouvé les huit pièces rouges, soit une minute après avoir +trouvé la 6\up{ème} pièce rouge. Pour éviter que l'utilisateur soit +\emph{frustré} de ne pas avoir trouvé toutes les pièces, nous n'indiquons pas +clairement le nombre de pièces qu'il a, ou qu'il lui reste à trouver. +Simplement, une \emph{vague} idée de sa progression. + +\subsection{Le \emph{feedback}} +Après avoir fini ces expériences, l'utilisateur se retrouvera sur un formulaire +lui demandant son avis quand à la difficulté de l'interface, et l'utilité des +recommandations pour se déplacer dans la scène. + +\section{Génération des expériences} +\subsection{Choix de la scène et du style de recommandations\label{selection}} +\paragraph{} +Il y a trois scènes disponibles, et sur chaque scène, nous avons placé des +pièces dans de nombreuses positions. Lorsqu'un utilisateur commence une +expérience, l'algorithme de selection fonctionne de façon à faire des +expériences sur les mêmes scènes avec les mêmes dispositions de pièces mais des +styles de recommandations différents pour des utilisateurs de même niveau de +sorte à pouvoir comparer l'efficacité des recommandations. + +\paragraph{} +Nous cherchons en fait des expériences qui permettraient de compléter les trio +d'expériences avec des pièces identiques sur des scènes identiques mais avec +des styles de recomendations différents. Si une telle expérience n'existe pas +(les trios sont déjà complets, ou bien il n'existe pas d'expérience pour un +niveau d'utilisateur donné), la scène, ainsi que les pièces à trouver et le +style de recommandations seront choisi aléatoirement : on choisira une scène et +un style de recommandations que l'utilisateur n'a pas encore effectué, et on +choisira 8 pièces aléatoirement parmi les positions possibles que nous avons +fixées au préalable. + +\subsection{Positions possibles des pièces} +Pour choisir les positions possibles des pièces dans chaque scène, un petit +outil à été développé permettant de se déplacer dans une scène et des créer des +pièces en cliquant. Cliquer sur une paroie de la scène crée une pièce devant +cette paroie, et cliquer sur une pièce la supprime. Un bouton permet d'envoyer +la liste des pièces par mail, lorsque l'on a fini de créer des pièces. + + +\section{Collecte des informations} +Ces expérienes n'auront d'interêt que si nous sommes capables de les analyser +par la suite et de comprendre comment les utilisateurs interagissent avec les +recommandations. Nous avons donc mis en place un système de collecte des +actions de l'utilisateur basé sur les XmlHttpRequests de JavaScript. + +Côté serveur, il y a quelques urls qui permettent d'enregistrer des +informations dans des tables prévues à cet effet. Chaque évènement contient la +date à laquelle il a été envoyé par le client ainsi que l'id du client et de +l'expérience qu'il est en train de faire (ces deux cerniers sont stockés dans +la session sur le serveur). Les évènements enregistrés sont les suivants : + +\paragraph{ArrowClicked} : crée quand l'utilisateur clique une recommandation, +accompagné de l'id de la recommandation cliquée. + +\paragraph{CoinClicked} : crée quand l'utilisateur récupère une pièce rouge. + +\paragraph{KeyboardEvent} : crée quand l'utilisateur appuie ou relâche une +touche du clavier, accompagné de la position courante de la caméra et d'un +booléen indiquant si elle a été appuyée ou relâchée. Dans le cas du +\emph{drag-n-drop} ou du \emph{pointer lock}, on créera de temps en temps +quelques évènements de type \texttt{KeyboardEvent} non associée à une touche de +sorte à connaître l'angle de la caméra. + +\paragraph{ResetClicked} : crée quand l'utilisateur réinitialise la position de +la caméra. + +\paragraph{PreviousNextClicked} : crée quand l'utilisateur clique sur les +boutons précédente ou suivante, on stockera la position finale en base de +données. + +\paragraph{Hovered} : crée quand l'utilisateur survole ou sort d'une +recommandation avec le curseur. + +\paragraph{PointerLocked} : dans le cas où l'utilisateur utilise l'option +\emph{pointer lock}, il sera crée au moment où le pointeur sera capturé et où +il sera libéré. + +\paragraph{SwitchedLockOption} : crée quand l'utilisateur change d'option entre +\emph{pointer locked} et \emph{drag-n-drop}. + +\paragraph{FpsCounter} : chaque seconde, cet évènement est crée pour connaître +le \emph{framerate} du client, de sorte à savoir si les performances de sa +machine ont pu lui poser problème dans ces expériences. + +\section{Replay} +Afin de nous assurer que toutes les informations nécessaires étaient bel et +bien récupérées en base de données, nous avons mis en place un programme +permettant de rejouer les expériences stockées. + +\paragraph{} +Pour le replay, nous avons simplement considéré les évènements +\texttt{ArrowClicked}, \texttt{CoinClicked}, \texttt{KeyboardEvent}, +\texttt{ResetClicked} et \texttt{PreviousNextClicked}, les autres évènements +n'ayant pas d'influence sur la position de la caméra. + +