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 :
- cursors.es en charge de la gestion de l'interface,
- utils_3d.es en charge des utilitaires pour la simulation 3D,
- box.es pour la partie spécifique à cet exercice.
<?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 :
- modification de la latitude de -90° à 90°,
- modification de la longitude de -180° à 180°,
- modification du lacet de 0 à 360°,
- modification de l'angle du pan supérieur droit du couvercle hd de 0 à 180°,
- modification de l'angle du pan supérieur gauche du couvercle hg de 0 à 180°,
- modification de l'angle du pan supérieur frontal du couvercle hf de 0 à 180°,
- modification de l'angle du pan supérieur arrière du couvercle hr de 0 à 180°.
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 :
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]); } }
- 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 :