• 12 heures
  • {0} Facile|{1} Moyenne|{2} Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 30/04/2014

Utilisation des matrices

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous allons à présent nous pencher sur les matrices ! N'ayez pas peur, ce chapitre ne sera pas aussi complexe qu'il en à l'air. En fait, l'utilisation de ces matrices est grandement facilitée en Actionscript. En effet, vous verrez que nous disposons d'une classe nommée Matrix qui permet de manipuler une matrice sans besoin de comprendre comment elle fonctionne réellement. Là encore, je pense que nous pouvons remercier la POO et l'encapsulation.

Nous verrons donc comment utiliser cette classe pour décrire comment appliquer un dégradé dans un tracé ou encore faire effectuer des transformations géométriques à nos objets d'affichage.

Les matrices ou la classe Matrix

Introduction aux matrices

Les bases

Avant d'aller plus loin, je pense qu'il serait bien de vous expliquer un peu ce qu'est une matrice, si vous ne le savez pas déjà. Ne vous inquiétez pas, il ne s'agit pas d'un cours de mathématiques ! Nous ne verrons donc pas de longues formules où vous risqueriez de vous arracher les cheveux !

Malgré son nom barbare, une matrice n'est en réalité rien d'autre qu'un tableau de valeurs. Les nombres qui y sont stockés peuvent représenter tout un tas de choses suivant l'utilisation que l'on peut en avoir. Les matrices peuvent donc être utilisées pour des applications simples, mais permettent également de représenter des concepts mathématiques complexes. Toutefois, en ce qui nous concerne, nous nous contenterons de dire qu'une matrice est un tableau de nombres et nous nous limiterons aux matrices bidimensionnelles.
Ainsi, toute matrice est de taille $m \times n$$m$ est le nombre de lignes et $n$ le nombre de colonnes. Voici par exemple une matrice $3 \times 3$ quelconque :

$\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix}$
Comme dans un tableau, il est alors possible de désigner un élément particulier grâce à un couple d'indices noté $(i,j)$. Dans l'exemple précédent, supposons que nous nommions la matrice $A$. Nous pourrions par exemple préciser que $A(2,1)=4$.
En mathématiques, il est possible de réaliser tout un tas d'opérations avec les matrices. Cependant, dans le cadre de ce cours, l'objectif n'est pas de vous les présenter toutes, cela serait bien trop long et n'aurait pas vraiment d'utilité dans notre cas. Toutefois, retenez qu'il existe une matrice un peu particulière où les coefficients diagonaux sont égaux à 1 et les autres coefficients sont tous nuls : la matrice identité. En voici une :

$\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}$
Pour les débutants en la matière, vous retiendrez donc qu'une matrice peut globalement se manipuler comme un tableau de nombres.

Théorie pour les initiés

Malgré le titre, j'invite également les novices en termes de matrices à lire la suite ne serait-ce que pour le plaisir ! :lol:
En deux dimensions, nous avons l'habitude d'utiliser un couple de coordonnées noté $(x,y)$ pour repérer un point sur le plan. Lorsque l'on utilise la notation matricielle, il est alors préférable de noter ces coordonnées en vertical et nous appellerons cette nouvelle notation un vecteur. Voici donc notre vecteur :

$\begin{bmatrix} x\\ y\\ \end{bmatrix}$

Une des propriétés des matrices est la multiplication. Elle permet ainsi de multiplier deux matrices entre elles mais également une matrice avec un vecteur. Toutefois, les dimensions des deux entités de l'opération doivent être cohérentes. Dans notre cas, pour pouvoir multiplier notre vecteur de dimension $2 \times 1$, nous devrons utiliser une matrice $2 \times 2$. L'intérêt de tout cela est qu'à l'intérieur de notre matrice $2 \times 2$, nous pouvons définir différentes transformations géométriques :

$\begin{bmatrix} s_x & 0\\ 0 & s_y\\ \end{bmatrix} \times \begin{bmatrix} x\\ y\\ \end{bmatrix}$
L'exemple ci-dessus est une opération de mise à l'échelle où $s_x$ correspond au redimensionnement horizontal et $s_y$ au vertical. Ainsi, après multiplication, nous obtenons un nouveau vecteur où les nouvelles coordonnées ont été modifiées suivant la transformation.
Une opération qui, sans doute, vous fera certainement peur est la rotation. Nous y retrouvons nos fonctions préférés : sinus et cosinus. Voici donc comment se réalise une rotation d'un angle $\theta$ :

$\begin{bmatrix} \cos\theta & -\sin\theta\\ \sin\theta & \cos\theta\\ \end{bmatrix} \times \begin{bmatrix} x\\ y\\ \end{bmatrix}$
Nous arrivons maintenant au petit point pour lequel il nous a fallu faire toutes ces précisions. Lorsque l'on utilise une matrice de dimension $2 \times 2$, il est possible de réaliser beaucoup de transformations. Toutefois, la translation ne peut pas être prise en compte dans ces conditions. C'est pourquoi, il est impératif d'ajouter une dimension supplémentaire à l'ensemble de l'opération. Il devient alors possible de réaliser une translation de la manière suivante :

$\begin{bmatrix} 1 & 0 & t_x\\ 0 & 1 & t_y\\ 0 & 0 & 1 \end{bmatrix} \times \begin{bmatrix} x\\ y\\ 1\end{bmatrix}$
Dans l'exemple précédent, les valeurs $t_x$ et $t_y$ représentent les déplacements respectivement suivant les axes $\overrightarrow{x}$ et $\overrightarrow{y}$.

L'objet Matrix

Suite à cette petite introduction aux matrices, vous devriez maintenant être plus à l'aise et, surtout, vous ne devriez pas être surpris par tout ce qui va suivre.

Introduction

Comme vous l'imaginez maintenant, la classe Matrix permet de définir une matrice à l'intérieur du code. Celle-ci permettra donc de définir l'ensemble des transformations à appliquer à un objet.
Si vous avez bien suivi tout ce qui a été dit sur les matrices précédemment, vous ne devriez donc pas être surpris par la forme des matrices manipulées par cette classe :

$\begin{bmatrix} a & c & t_x \\ b & d & t_y \\ 0 & 0 & 1 \end{bmatrix}$
Nous retrouvons donc quatre coefficients $a$, $b$, $c$ et $d$ qui permettent de réaliser les transformations classiques telles que le redimensionnement, la rotation mais également l'inclinaison. Nous disposons également des valeurs de translation $t_x$ et $t_y$, permettant de déplacer un objet.
Cette classe Matrix va donc nous permettre de définir une matrice qui combinera une ou plusieurs de ces transformations en même temps.

Déclaration et initialisation

Comme toute classe, un objet Matrix s'initiale grâce au constructeur de la classe dans une variable.
Voici donc comment procéder :

var maTransformation:Matrix = new Matrix();

Lorsque vous initialisez une matrice comme ceci, le constructeur crée une matrice identité, qui, en termes de transformations, ne fait strictement rien.
En fait, ce constructeur possède les différents paramètres facultatifs suivant : a, b, c, d, tx et ty. Toutefois, comme nous ne voulons pas nous prendre la tête avec les mathématiques, nous utiliserons plutôt les méthodes de la classe qui simplifient énormément la gestion des transformations.

Création de dégradés

Dans le chapitre précédent, j'ai volontairement omis de vous parler des dégradés. Nous verrons dans la suite de ce chapitre que l'utilisation des matrices permet de mieux contrôler le dégradé en lui-même et ainsi parvenir plus facilement au rendu désiré.

Présentation des dégradés

La gestion des dégradés en Actionscript peut paraître complexe au premier abord, cependant ses possibilités sont larges.
Vous verrez au fil des explications que la création d'un dégradé se fait par étapes, de la même manière qu'on le ferait dans un logiciel de graphisme. Nous allons donc dans un premier temps définir le dégradé à l'aide de différents tableaux Array, comme ceci :

var couleurs:Array = [0xFF0000, 0x00FF00]; 
var alphas:Array = [1, 1]; 
var ratios:Array = [0, 255];

Dans l'exemple précédent, nous avons déclaré et initialisé trois tableaux : couleurs, alphas et ratios. Comme son nom l'indique, le premier sert à lister les différentes couleurs du dégradé, ici rouge et vert. Le deuxième permet de définir l'opacité de chacune des couleurs précédentes dans le même ordre. Enfin, la variable ratios nous permet de gérer la position des couleurs les unes par rapport aux autres. Pour comparer, vous pouvez imaginer qu'il s'agit des petits curseurs que vous pourriez déplacer dans un éditeur de dégradé, ici représentés par des valeurs comprises entre 0 l'extrémité gauche et 255 l'extrémité droite, comme le montre la figure suivante.

Un dégradé et des curseurs
Un dégradé et des curseurs

En théorie, il est possible d'appliquer un dégradé tel quel à un remplissage par exemple. Nous reviendrons dessus plus tard, mais vous pourriez très bien utiliser le code suivant :

var monDessin:Shape = new Shape;
monDessin.graphics.beginGradientFill(GradientType.LINEAR, couleurs, alphas, ratios); 
monDessin.graphics.drawRect(0, 0, 100, 100); 
addChild(monDessin);

Néanmoins, il est quasiment indispensable de se servir des matrices pour décrire comment les dégradés doivent être appliqués. Hormis les valeurs GradientType.LINEAR et GradientType.RADIAL qui permettent de définir le dégradé de type linéaire ou radial, il est nécessaire de préciser la largeur, la hauteur ou encore l'angle de ce dernier à l'intérieur du futur remplissage.
Ainsi un objet de type Matrix va permettre de mettre en place ces différents paramètres à l'intérieur d'une même matrice.

Ajouter une matrice de description

Pré-requis

Vous noterez que l'utilisation des matrices ici n'est pas tout à fait identique aux transformations décrites auparavant. Toutefois, vous verrez qu'au final, la description des dégradés y est très similaire et que les matrices seront alors définies de la même manière.

Dans la suite de ce cours, nous partirons du dégradé blanc et noir défini ci-dessous :

var couleurs:Array = [0xFFFFFF, 0x000000]; 
var alphas:Array = [1, 1]; 
var ratios:Array = [0, 255];
Préparation de la matrice

Comme nous l'avons dit, la classe Matrix possède des méthodes qui facilitent la définition d'une matrice. Parmi elles, la méthode createGradientBox() est particulièrement utile pour décrire la mise en place d'un dégradé.
Les différents paramètres de cette méthode sont listés dans le code d'exemple ci-dessous :

var matrix:Matrix = new Matrix(); 
var largeur:Number = 100;
var hauteur:Number = 100;
var rotation:Number = 0;
var tx:Number = 0; 
var ty:Number = 0; 
matrix.createGradientBox(largeur, hauteur, rotation, tx, ty);

Le nom des variables dans l'exemple précédent est assez explicite, cependant je vous propose à la figure suivante un tableau montrant l'influence de ces différents paramètres sur le remplissage final.

Les paramètres modifient l'affichage
Les paramètres modifient l'affichage
Appliquer un dégradé

Pour appliquer un dégradé à un remplissage, vous devez utiliser la méthode beginGradientFill() que nous avions rapidement vu précédemment. Vous pouvez alors définir le remplissage en renseignant les différents paramètres dans l'ordre suivant :

monDessin.graphics.beginGradientFill(GradientType.LINEAR, couleurs, alphas, ratios, matrix);

Je rappelle qu'un dégradé peut être linéaire ou radial, défini par les constantes GradientType.LINEAR et GradientType.RADIAL. Comme pour la méthode beginFill(), le remplissage est terminé lors de l'appel de la fonction endFill() qui termine le tracé d'un contour.

Contrairement à ce que vous pourriez croire, un dégradé peut tout autant s'appliquer aux lignes. Pour cela, utilisez la méthode lineGradientStyle() exactement de la même manière que beginGradientFill(). Toutefois, n'oubliez pas de définir la largeur de la ligne à l'aide de la méthode lineStyle(), sans quoi celle-ci ne sera pas visible.
Voici donc comment procéder :

monDessin.graphics.lineStyle(2);
monDessin.graphics.lineGradientStyle(GradientType.LINEAR, couleurs, alphas, ratios, matrix);

Exemple : création d'un bouton

Pour terminer les explications sur les dégradés, je vous propose de réaliser un petit bouton comme vous pourriez en trouver sur n'importe quelle page web.
Pour ce faire, nous allons donc utiliser un dégradé linéaire vertical que nous utiliserons comme remplissage. Nous dessinerons alors un rectangles aux coins arrondis avec une ligne de couleur pour marquer le contour de notre bouton.
Sans plus attendre, voici le code correspondant à la définition du dégradé et au tracé de ce bouton :

// Dégradé de base
var couleurs:Array = [0xEEBBBB, 0xDD0000]; 
var alphas:Array = [1, 1]; 
var ratios:Array = [0, 255];

// Matrice de description
var matrix:Matrix = new Matrix(); 
var largeur:Number = 100; 
var hauteur:Number = 15; 
var rotation:Number = Math.PI/2;
var tx:Number = 0; 
var ty:Number = 0; 
matrix.createGradientBox(largeur, hauteur, rotation, tx, ty); 

// Tracé du bouton
var monBouton:Shape = new Shape;
monBouton.graphics.lineStyle(1, 0x880000);
monBouton.graphics.beginGradientFill(GradientType.LINEAR, couleurs, alphas, ratios, matrix); 
monBouton.graphics.drawRoundRect(0, 0, 100, 25, 10); 
addChild(monBouton);

Nous avons donc ici réalisé la base d'un bouton typique d'une page Internet. Il manque encore le texte par-dessus celui-ci mais vous devriez normalement pouvoir vous en occuper vous-mêmes. La figure suivante montre à quoi ressemble notre bouton.

Notre bouton
Notre bouton

Les transformations matricielles

Un objet à transformer

Création de l'objet

Nous allons d'abord créer une forme que nous pourrons déformer plus tard. Pour ne pas se compliquer la vie, nous tracerons donc un carré dans un objet de type Shape. Nous partirons du code ci-dessous pour la suite de ce chapitre :

var monObjet:Shape = new Shape();
monObjet.graphics.beginFill(0x6688FF);
monObjet.graphics.drawRect(0, 0, 100, 100);
this.addChild(monObjet);

Pour que vous puissiez mieux comparer les effets des différentes transformations, voici, à la figure suivante, l'original que nous venons de créer.

Notre image originale
Notre image originale
La matrice de transformation

Tout objet héritant de la classe DisplayObject possède une matrice de transformation. Celle-ci n'a aucune incidence sur l'affichage s'il s'agit d'une matrice identité ou si sa valeur est null. Pour y accéder, vous devez atteindre la propriété matrix d'une propriété de votre objet nommée transform.
Voici donc comment y parvenir :

monObjet.transform.matrix = matrix;

Bien qu'il soit possible de modifier directement les valeurs à l'intérieur d'une matrice, nous préfèrerons utiliser les méthodes de la classe Matrix pour effectuer nos transformations.

Création d'une matrice de transformation

Méthodes de transformations individuelles

Pour commencer, nous allons voir revenir très souvent une matrice identité. Cela correspond à une matrice n'appliquant aucune transformation à l'objet. Pour cela, utilisez la fonction identity() simplement comme ceci :

matrix.identity();

Ensuite, voyons comment redimensionner notre objet d'affichage. Pour cela, nous utiliserons la méthode scale() où nous pouvons spécifier les valeurs de redimensionnement horizontal sx et vertical sx, la taille d'origine étant fixée à 1. Voici donc comment procéder :

matrix.scale(sx, sy);

Je vais maintenant vous présenter la méthode rotate() permettant de faire pivoter un objet par rapport à son centre. Il suffit alors uniquement de préciser l'angle de rotation en radians :

matrix.rotate(angle);

Voyons à présent ensemble la translation. Rien de plus simple, il suffit de préciser les décalages dx et dy à la méthode translate() de la façon suivante :

matrix.translate(dx, dy);

Enfin, je vous propose une série de transformations dans l'exemple suivant :

var matrix:Matrix = new Matrix();
matrix.identity();
matrix.scale(2, 1);
matrix.rotate(Math.PI / 4);
matrix.translate(72, 0);
// Application des transformations
monObjet.transform.matrix = matrix;

Dans l'exemple précédent, nous avons donc doublé notre objet dans le sens horizontal. Puis nous l'avons fait tourner de 45° par rapport à son centre confondu au coin supérieur gauche du rectangle. Enfin, nous avons translaté l'objet afin qu'il soit à nouveau entièrement dans la zone visible de l'écran. Regardez le résultat final à la figure suivante.

Le résultat final
Le résultat final
Une petite exception : l'inclinaison

Il est aussi possible de déformer réellement votre objet en l'inclinant. Toutefois, la classe Matrix ne possède pas de méthode pour ce genre de transformations. Cependant, il est possible de modifier directement les valeurs à l'intérieur de la matrice. C'est donc ce que nous ferons pour réaliser ceci, en agissant sur la valeur b pour une inclinaison verticale et sur la c pour une horizontale.
Voici donc comment faire :

matrix.c = - Math.tan(2);

Dans cet exemple, nous inclinons notre objet d'une valeur de 2 horizontalement. Ainsi, chaque pixel va être décalé vers la droite de deux fois sa distance verticale au centre de l'objet. Voyez plutôt le résultat :

Le résultat final
Le résultat final
Plusieurs transformations à la fois

Contrairement à ce que pourrait vous laissez croire le titre, les méthodes précédentes permettent également de combiner plusieurs transformations en une seule matrice. Nous allons créer une matrice combinant plusieurs transformations à l'aide d'une unique méthode : createBox().

Ainsi, lorsque vous utiliserez cette méthode, la matrice sera « réinitialisée » et effacera donc toute transformation de la matrice.
Voici donc comment s'utilise cette méthode :

matrix.createBox(sx, sx, angle, dx, dy);

Je vous propose donc un petit exemple d'utilisation :

var matrix:Matrix = new Matrix();
matrix.createBox(2, 1, Math.PI/4, 150, 0);
monObjet.transform.matrix = matrix;

Les opérations sont donc insérées en une seule fois à l'intérieur de la matrice et produisent le la figure suivante.

Le résultat final
Le résultat final

Pour finir avec les matrices

Ajouter des transformations

Depuis le départ, nous ne faisons qu'affecter une matrice de transformation à un objet. Pour faire ça, nous utilisons donc l'instruction suivante :

monObjet.transform.matrix = matrix;

Cependant, en effectuant cette affectation, vous effacez également d'éventuelles transformations déjà appliquées à l'objet. Or vous souhaiteriez peut-être conserver ces anciennes transformations et appliquer en plus les nouvelles. Pour faire cela, vous aurez donc besoin de récupérer la matrice existante, avant d'y ajouter de nouvelles opérations de transformation.
Voilà donc comment nous pourrions rajouter une rotation de 45° à notre objet, sans réinitialiser l'ensemble des transformations déjà existantes :

// Récupération de la matrice de l'objet
var matrix:Matrix = new Matrix();
matrix = monObjet.transform.matrix;
// Ajout d'une transformation
matrix.rotate(Math.PI / 4);
// Application des transformations
monObjet.transform.matrix = matrix;

Si vous avez bien suivi, vous devriez vous rappeler qu'en utilisant la méthode createBox(), vous commencez par redéfinir l'objet Matrix comme matrice identité, éliminant ainsi toute trace d'anciennes transformations. Il faut donc procéder différemment si vous souhaitez conserver ces transformations. Pour cela, la classe Matrix dispose d'une méthode nommée concat() qui permet de concaténer une matrice avec une autre, c'est-à-dire de combiner les effets géométriques des deux matrices.
Voilà donc comment je vous suggère de procéder :

// Récupération de la matrice de l'objet
var matrix:Matrix = new Matrix();
matrix = monObjet.transform.matrix;
// Création de la matrice de transformation
var transformation:Matrix = new Matrix();
transformation.createBox(2, 1, Math.PI/4, 150, 0);
// Combinaison des matrices
matrix.concat(transformation);
// Application des transformations
monObjet.transform.matrix = matrix;
Intérêt des transformations matricielles

Il est probable que vous vous demandiez depuis le début quel est l'intérêt d'utiliser les matrices alors que les propriétés x, y, rotation, scaleX et scaleY permettent de faire la même chose.

En réalité, les transformations matricielles sont plus puissantes, notamment car elles permettent les déformations impossibles avec les propriétés précédentes. Un autre avantage est également de limiter les modifications liées à l'affichage. En effet, lorsque vous modifiez les propriétés d'un objet d'affichage, celui-ci doit être entièrement retracé, c'est-à-dire pixel par pixel. Cette opération peut donc être longue suivant les dimensions de vos tracés et autres objets d'affichage. C'est pourquoi, utiliser les transformations matricielles accélère cette étape en combinant plusieurs opérations en une seule instruction de mise à jour de l'affichage.
Ainsi, il est préférable d'utiliser cette technique pour diminuer le temps d'exécution de ces transformations et fluidifier au maximum votre affichage.

En résumé
  • Une matrice est un tableau de nombres à $m$ lignes et $n$ colonnes.

  • La classe Matrix sert à créer et manipuler des matrices plus simplement.

  • Cette classe permet notamment de faciliter la mise en place des dégradés grâce à sa méthode createGradientBox().

  • L'objet Matrix est beaucoup utilisé pour effectuer des transformations matricielles.

  • Pour les transformations classiques, il existe les différentes méthodes scale(), rotate() et translate().

  • Il est possible de combiner plusieurs opérations géométriques grâce à la méthode createBox()

  • L'utilisation des matrices pour les transformations géométriques permet d'accélérer le rendu de l'affichage.

Exemple de certificat de réussite
Exemple de certificat de réussite