Animation d'une boîte

entre Vous et Nous

Accès direct au résultat

Le problème

Ici aussi nous voulons simuler l'animation d'une boîte sous forme de parallélépipède rectangle et contenant un couvercle constitué de 4 pans comme le présente une boîte en carton qui sera ici aussi représenté par une image SVG.

L'idée de cet exercice est similaire à celle de l'exercice précédent, la seule différence réside dans la complexité de l'objet représenté ainsi que dans les contrôles qui nous permettront d'agir sur chaque pan du couvercle de la boîte.

Les interactions devront donc permettre en plus des 3 degrés de liberté précédents les rotations des pans du couvercle.

La solution

Comme d'habitude il faudra créer une image en SVG intégrant un script javascript

L'image au format SVG

L'image qui subira les transformations contiendra les 5 faces de la boîte plus les 4 pans du couvercle. Comme dans l'exercice précédent cette image intégrera des scripts externalisés qui sont :

<?xml-stylesheet type="text/css" href="svg_style.css" ?>
<svg
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   width="100%"
   height="100%"
   version="1.0"
   viewBox="0 0 700 400"
   id="svg2">
  <!-- chargement des comportements entièrement déportés dans les fichiers ecmascript -->
  <script xlink:href="cursors.es" type="text/ecmascript"/>
  <script xlink:href="utils_3d.es" type="text/ecmascript"/>
  <script xlink:href="box.es" type="text/ecmascript"/>
  <defs>
    <!-- les marqueurs pour le repère Oxyz -->
    <marker id="cercle"
	    markerUnits="userSpaceOnUse"
	    markerWidth="8"
	    markerHeight="8"
	    viewBox="-2 -2 4 4"
	    orient="auto">
      <circle r="2" cx="0" cy="0" style="stroke-width:0.2px;stroke:#666"/>
    </marker>
    <marker id="fleche"
            markerWidth="4"
            markerHeight="4"
            orient="auto"
            refY="2"
            refX="3">
      <path d="M0,0 L4,2 0,4"/>
    </marker>
  </defs>
  <!-- l'ensemble des faces de la boîte à faire tourner -->
  <g id="orth" transform="translate(200,150)">
    <g>
      <g id="g_bas" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_bas" x="0" y="0" width="150" height="100" style="fill:#884"/>
	<text x="75" y="50" class="vect">Fond</text>
      </g>
      <g id="g_droit" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_droit" x="0" y="0" width="150" height="80" style="fill:#884"/>
	<text x="75" y="40" class="vect">droit</text>
      </g>
      <g id="g_gauche" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_gauche" x="0" y="0" width="150" height="80" style="fill:#884"/>
	<text x="75" y="40" class="vect">gauche</text>
      </g>
      <g id="g_avt" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_avt" x="0" y="0" width="100" height="80" style="fill:#884"/>
	<text x="50" y="40" class="vect">avant</text>
      </g>
      <g id="g_arr" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_arr" x="0" y="0" width="100" height="80" style="fill:#884"/>
	<text x="50" y="40" class="vect">arrière</text>
      </g>
      <g id="g_dess_av" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_dess_av" x="0" y="0" width="100" height="45" style="fill:#884"/>
	<text x="50" y="25" class="vect">H AV</text>
      </g>
      <g id="g_dess_ar" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_dess_ar" x="0" y="0" width="100" height="45" style="fill:#884"/>
	<text x="50" y="25" class="vect">H AR</text>
      </g>
      <g id="g_dess_d" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_dess_d" x="0" y="0" width="150" height="45" style="fill:#884"/>
	<text x="75" y="25" class="vect">H D</text>
      </g>
      <g id="g_dess_g" transform="matrix(1,0,0,1,0,0)">
	<rect id="r_dess_g" x="0" y="0" width="150" height="45" style="fill:#884"/>
	<text x="75" y="25" class="vect">H G</text>
      </g>
      <!-- l'axe de rotation autour duquel le cube devra tourner -->
      <line id="axe" x1="0" y1="0" x2="200" y2="200" class="axe" marker-start="url(#cercle)"/>
    </g>
    <line id="debug_dess_av" x1="0" y1="0" x2="150" y2="0" class="axe" style="stroke:#44F" marker-end="url(#fleche)"/>
    <line id="debug_dess_g" x1="0" y1="0" x2="150" y2="0" class="axe" style="stroke:#4F4" marker-end="url(#fleche)"/>
    <circle id="dbgpt_dess_g" cx="0" cy="0" r="2" style="fill:#FF0"/>
    <circle id="dbgpt_dess_av" cx="0" cy="0" r="2" style="fill:#0F0"/>
  </g>

  <!-- les sept curseurs des rotations à appliquer -->
  <g id="cursors" transform="translate(0, 300)"/>
  <!-- <text id="debug" x="30" y="300">Debug</text> -->
</svg>

la feuille de style utilisée (indiquée par la première ligne <?xml-stylesheet type="text/css" href="svg_style.css" ?>) sera la même que celle de l'exercice précédent.

L'image étant créée, passons maintenant à la partie comportementale assurée par javascript.

L'interactivité

Pour la partie IHM la seule différence par rapport à l'exercice précédent porte sur la fonction d'initialisation qui utilisera un jeu de 9 matrices (une par élément) et qui nécessitera l'interaction permettant les opérations suivantes :

Il nous faudra donc une interface pourvue de 7 curseurs couvrant les plages des valeurs correspondantes et dont le tableau de déclaration est ici :

controls = {lat: {min: -90,  max: 90,  def: 20,  txt: 'latitude',  cursorStyle: 'fill:#FF0'},
	    lon: {min: -180, max: 180, def: -60, txt: 'longitude', cursorStyle: 'fill:#0FF'},
	    rot: {min: 0,    max: 360, def: 60,  txt: 'rotation',  cursorStyle: 'fill:#F0F'},
	    hd:  {min: 0,    max: 180, def: 20,  txt: 'haut droit', cursorStyle: 'fill:#FFF'},
	    hg:  {min: 0,    max: 180, def: 20,  txt: 'haut gauche', cursorStyle: 'fill:#FFF'},
	    hf:  {min: 0,    max: 180, def: 20,  txt: 'haut avant', cursorStyle: 'fill:#FFF'},
	    hr:  {min: 0,    max: 180, def: 20,  txt: 'haut arrière', cursorStyle: 'fill:#FFF'}};

Le traitement de l'action

Afin de finaliser le traitement nous devrons fournir les fonctions suivantes :

  1. la fonction destinée à afficher des informations de debug matrix_debug(key,matrx)

    Cette fonction est la même que la précédente, mais adaptée à notre image.

    Elle est donnée par exemple par :

    function matrix_debug(key, matrx) {
        var dbgid = document.getElementById('debug_'+key);
        var dbgpt = document.getElementById('dbgpt_'+key);
        var rid = document.getElementById('r_'+key);
        var pv = prodVector([matrx[0][0],matrx[1][0],matrx[2][0]],[matrx[0][1],matrx[1][1],matrx[2][1]]);
        var dx = rid.getAttribute('width');
        var dy = rid.getAttribute('height');
        var cf = prodMatrixVector(matrx, [dx/2,dy/2,0,1])
        var dbg = document.getElementById('debug');
        if (dbg) dbg.textContent += ' Z:'+key+'='+pv[2].toFixed(0)+'/'+matrx[0][3].toFixed(0)+'/'+matrx[1][3].toFixed(0)+'/'+matrx[2][3].toFixed(0);
        var s = 100;
        if (dbgid) {
    	dbgid.setAttribute('x1', matrx[0][3]);
    	dbgid.setAttribute('y1', matrx[1][3]);
    	dbgid.setAttribute('x2', s*(pv[0])+matrx[0][3]);
    	dbgid.setAttribute('y2', s*(pv[1])+matrx[1][3]);
        }
        if (dbgpt) {
    	dbgpt.setAttribute('cx', cf[0]);
    	dbgpt.setAttribute('cy', cf[1]);
        }
    }
  2. la fonction transform() réalisant les actions depuis l'interface.

    Cette fonction contient la déclaration du tableau des matrices de transformation à appliquer aux 9 faces.

    function transform() {
        var faces = {
    	bas: [[0,-1,0,100], [-1,0,0,0], [0,0,1,80], [0,0,0,1]],
    	gauche: [[0,0,1,100], [-1,0,0,0], [0,1,0,0], [0,0,0,1]],
    	droit: [[0,0,-1,0], [1,0,0,-150], [0,1,0,0], [0,0,0,1]],
    	avt: [[1,0,0,0], [0,0,1,0], [0,1,0,0], [0,0,0,1]],
    	arr: [[-1,0,0,100], [0,0,1,-150], [0,1,0,0], [0,0,0,1]],
    	
    	// tableau des matrices de transformation à appliquer aux 4 parties du couvercle
    	// avec possibilité de contrôler l'angle d'ouverture de la boîte par les curseurs hf, hr, hg, hd
    	dess_av: prodMatrix(getMatrix(cursors['hf'].text.textContent, [-1,0,0], [0,0,0]), [[1,0,0,0], [0,-1,0,45], [0,0,-1,0], [0,0,0,1]]),
    	dess_ar: prodMatrix(getMatrix(cursors['hr'].text.textContent, [1,0,0], [0,-150,0]), [[-1,0,0,100], [0,1,0,-45], [0,0,-1,0], [0,0,0,1]]),
    	
    	dess_g: prodMatrix(getMatrix(cursors['hg'].text.textContent, [0,1,0], [100,0,0]),[[0,-1,0,45], [-1,0,0,0], [0,0,1,0], [0,0,0,1]]),
    	dess_d: prodMatrix(getMatrix(cursors['hd'].text.textContent, [0,-1,0], [0,0,0]),[[0,1,0,-45], [1,0,0,-150], [0,0,1,0], [0,0,0,1]])};
    
        setAxe(cursors['lon'].text.textContent, cursors['lat'].text.textContent);
        rotateOrth(faces);  
    }

Et c'est fini, il n'y a plus qu'a charger l'image SVG dans un navigateur et jouer avec, mais si vous n'avez pas le courage de copier/coller tous les fragments de code de cet exercice vous pouvez directement voir et agir sur le résultat ci-dessous :

Un navigateur moderne devrait vous montrer ici le résultat, en désespoir de cause vous pouvez essayer le lien suivant sur cette image SVG, mais je doute du résultat car un navigateur qui ne connaît pas <object> a peu de chances de connaître le SVG !