Comprendre les transformations affines avec les mathématiques matricielles

Inspiré par le professeur Wildberger dans sa série de conférences sur l'algèbre linéaire, j'ai l'intention de mettre en œuvre ses idées mathématiques avec Flash. Nous n'allons pas approfondir la manipulation mathématique des matrices par le biais de l'algèbre linéaire: simplement par le biais de vecteurs. Cette compréhension, bien que diluant l'élégance de l'algèbre linéaire, est suffisante pour nous lancer dans des possibilités intéressantes de manipulation de matrice 2x2. Nous l'utilisons en particulier pour appliquer divers effets de cisaillement, d'inclinaison, de retournement et de redimensionnement aux images au moment de l'exécution..


Aperçu du résultat final

Jetons un coup d'œil au résultat final sur lequel nous allons travailler. Appuyez sur les quatre touches directionnelles - haut, bas, gauche, droite - pour voir certains effets que nous pouvons obtenir avec des transformations affines.

Si vous utilisez uniquement les flèches gauche et droite, le poisson semble nager dans un espace isométrique pseudo-3D.


Étape 1: Différents espaces de coordonnées

Les graphiques sont dessinés sur des espaces de coordonnées. Donc, pour pouvoir les manipuler, en particulier pour traduire, faire pivoter, redimensionner, refléter et incliner des graphiques, il est essentiel de comprendre les espaces de coordonnées. Nous utilisons généralement non pas un, mais plusieurs espaces de coordonnées dans un même projet - cela vaut non seulement pour les concepteurs utilisant Flash IDE, mais également pour les programmeurs écrivant ActionScript..

Dans Flash IDE, cela se produit chaque fois que vous convertissez vos dessins en symboles MovieClip: chaque symbole a sa propre origine..

L'image ci-dessus montre l'origine de l'espace de coordonnées de la scène (point rouge) et celui de l'espace de coordonnées du symbole (point de repère marqué par une croix). Pour savoir dans quel espace vous vous trouvez, observez la barre située sous la timeline de Flash IDE, comme le montre l'image ci-dessous..

(J'utilise Flash CS3. Son emplacement peut donc être différent pour CS4 et CS5.) Ce que je veux souligner, c'est l'existence de différents espaces de coordonnées et le fait que vous les connaissez déjà..


Étape 2: la justification

Maintenant, il y a une bonne raison à cela. Nous pouvons utiliser un espace de coordonnées comme référence pour modifier l’autre espace de coordonnées. Cela peut paraître étrange, j'ai donc inclus la présentation Flash ci-dessous pour faciliter mon explication. Cliquez et faites glisser les flèches rouges. Jouer avec elle.

À l'arrière-plan, une grille bleue et à l'avant-plan, une grille rouge. Les flèches bleue et rouge sont initialement alignées le long des axes x et y de l'espace de coordonnées Flash, dont le centre s'est déplacé au centre de la scène. La grille bleue est une grille de référence; les lignes de la grille ne changeront pas si vous interagissez avec les flèches rouges. La grille rouge, en revanche, peut être réorientée et mise à l'échelle en faisant glisser les flèches rouges.

Notez que les flèches indiquent également une propriété importante de ces grilles. Ils indiquent la notion d'une unité de x et d'une unité de y sur leur grille respective. Il y a deux flèches rouges sur la grille rouge. Chacun d’eux indique la longueur d’une unité sur l’axe des x et l’axe des y. Ils dictent également l'orientation de l'espace de coordonnées. Prenons la flèche rouge pointant le long de l'axe des x et étendons-la deux fois plus longtemps que la flèche d'origine (en bleu). Observez les images suivantes.

Nous voyons que l'image (la boîte verte) dessinée sur la grille rouge est maintenant étirée horizontalement, en raison du fait que cette grille rouge sur laquelle elle est dessinée est maintenant deux fois plus large. Ce que j'essaie de dire est assez simple: vous pouvez utiliser un espace de coordonnées comme base pour modifier un autre espace de coordonnées..


Étape 3: espace de coordonnées affine

Alors qu'est-ce qu'un "espace de coordonnées affine"? Eh bien, je suis sûr que vous êtes suffisamment attentif pour observer que ces espaces de coordonnées sont dessinés à l'aide de grilles parallèles. Prenons par exemple l'espace affine rouge: rien ne garantit que l'axe des x et l'axe des y sont toujours perpendiculaires, mais sachez que, quelle que soit votre tentative, vous ne modifierez jamais les flèches, vous n'arriverez jamais à un tel cas. comme ci-dessous.


Cet espace de coordonnées est ne pas un espace de coordonnées affine.

En fait, les axes x et y se réfèrent généralement à l'espace de coordonnées cartésien, comme indiqué ci-dessous.

Notez que les grilles horizontales et verticales sont perpendiculaires l'une à l'autre. Cartésien est un type d’espace de coordonnées affines, mais nous pouvons le transformer en d’autres espaces affines comme nous le souhaitons. Les grilles horizontales et verticales ne doivent pas nécessairement être perpendiculaires entre elles.


Exemple d'espace de coordonnées affine
Un autre exemple d'espace de coordonnées affine

Étape 4: Transformations Affines

Comme vous l'avez peut-être deviné, les transformations affines sont la translation, la mise à l'échelle, la réflexion, l'inclinaison et la rotation.


Espace affine original
Espace affine mis à l'échelle
Espace affine réfléchi
Espace affine asymétrique
Espace affine tourné et mis à l'échelle

Inutile de dire que des propriétés physiques telles que x, y, scaleX, scaleY et rotation dépend de l'espace. Lorsque nous appelons ces propriétés, nous transformons des coordonnées affines..


Étape 5: Comprendre la matrice

J'espère que les images présentées ci-dessus sont suffisamment explicites pour faire passer l'idée à la maison. En effet, pour un programmeur travaillant avec FlashDevelop, nous ne verrons pas les grilles que Flash IDE affiche pour les concepteurs. Tous doivent vivre dans votre tête.

En plus d’imaginer ces grilles, nous devons également solliciter l’aide de Matrice classe. Il est donc important d’avoir une compréhension mathématique des matrices. Nous allons donc revoir ici les opérations de la matrice: addition et multiplication..


Étape 6: Signification géométrique de l'addition de matrice

Les opérations matricielles convergent les significations géométriquement. En d'autres termes, vous pouvez imaginer ce qu'ils signifient sur un graphique. Supposons que nous ayons quatre points dans notre espace de coordonnées et aimerions les déplacer vers un ensemble de nouveaux emplacements. Cela peut être fait en utilisant l'addition de matrice. Regardez l'image ci-dessous.

Comme vous pouvez le constater, nous déplaçons tout l’espace de coordonnées local (grilles rouges) où ces quatre points sont dessinés. La notation pour effectuer ces opérations est la suivante:

Nous pouvons également voir que ce décalage peut en réalité être représenté en utilisant un vecteur de (tx, ty). Différencions les vecteurs et les points statiques dans les espaces de coordonnées en utilisant des parenthèses et des crochets. Je les ai réécrites dans l'image ci-dessous.


Étape 7: Implémentation d'ActionScript

Voici une implémentation simple de l'addition de matrice. Découvrez les commentaires:

 public class Addition étend Sprite public function Addition () var m: Matrix = new Matrix (); // instancie la matrice m.tx = stage.stageWidth * 0.5; // décalage dans x m.ty = stage.stageHeight * 0.5; // décalage dans y var d: DottedBox = new DottedBox (); // crée le graphique personnalisé (la boîte en pointillés est un Sprite) addChild (d); d.transform.matrix = m; // applique la matrice à notre graphique

Étape 8: Signification géométrique de la multiplication de matrices

La multiplication matricielle est un peu plus sophistiquée que l'addition matricielle, mais le professeur Wildberger l'a élégamment décomposée en une interprétation simple. J'essaierai humblement de réitérer son explication. Pour ceux qui souhaitent approfondir la compréhension de l'algèbre linéaire qui y conduit, consultez la série de conférences du professeur..

Commençons par aborder le cas de la matrice identité, je.

De l'image ci-dessus, nous savons que la multiplication d'une matrice arbitraire, A, par la matrice d'identité, I, produira toujours A. Voici une analogie: 6 x 1 = 6; la matrice d'identité est assimilée au nombre 1 dans cette multiplication.

Alternativement, nous pouvons écrire le résultat dans le format vectoriel suivant, ce qui simplifiera grandement notre interprétation:

L'interprétation géométrique de cette formule est montrée dans l'image ci-dessous.

À partir de la grille cartésienne (grille de gauche), nous pouvons voir que le point bleu est situé à (2, 1). Maintenant, si nous devions transformer cette grille originale de x et y en une nouvelle grille (grille de droite) en fonction d'un ensemble de vecteurs (sous la grille de droite), le point bleu serait déplacé vers (2, 1) sur la nouvelle grille. - mais lorsque nous mappons cela à la grille d'origine, c'est le même point qu'avant.

Comme nous transformons la grille d'origine en une autre grille partageant les mêmes vecteurs pour x et y, nous ne voyons aucune différence. En fait, les changements de x et y dans cette transformation sont nuls. C'est ce que cela voulait dire par matrice d'identité, d'un point de vue géométrique.

Cependant, si nous essayons de réaliser un mappage en utilisant d'autres transformations, nous verrons une différence. Je sais que ce n'était pas l'exemple le plus révélateur pour commencer, alors passons à un autre exemple.


Étape 9: Mise à l'échelle le long de X

L'image ci-dessus montre une mise à l'échelle de l'espace de coordonnées. Découvrez le vecteur de x dans l'espace de coordonnées transformé: une unité du x transformé représente deux unités du x d'origine. Sur l'espace de coordonnées transformé, la coordonnée du point bleu est toujours (2, 1). Cependant, si vous essayez de mapper cette coordonnée à partir de la grille transformée sur la grille d'origine, elle est (4, 1).

Toute cette idée est capturée par l'image ci-dessus. Que diriez-vous de la formule? Le résultat devrait être cohérent. Regardons ça.

Je suis sûr que vous vous souvenez de ces formules. Maintenant, j'ai ajouté leurs significations respectives.

Maintenant, pour vérifier le résultat numérique de notre exemple de mise à l'échelle.

  • Coordonnée d'origine: (2, 1)
  • Vecteur sur l’axe des x transformé: (2, 0)
  • Vecteur sur l'axe des y transformé: (0, 1)
  • Résultat attendu: (2 * 2 + 0 * 1, 0 * 2 + 1 * 1) = (4, 1)

Ils sont d'accord les uns avec les autres! Nous pouvons maintenant appliquer cette idée à d’autres transformations. Mais avant cela, une implémentation ActionScript.


Étape 10: Implémentation d'ActionScript

Découvrez l'implémentation d'ActionScript (et le fichier SWF résultant) ci-dessous. Notez que l'une des cases qui se chevauchent est étirée le long de x sur une échelle de 2. J'ai mis en évidence les valeurs importantes. Ces valeurs seront modifiées dans les étapes suivantes pour représenter différentes transformations..

 Classe publique Multiplication étend Sprite fonction publique Multiplication () var réf: DottedBox = new DottedBox (); // crée un graphique de référence addChild (ref); ref.x = stage.stageWidth * 0.5; ref.y = stage.stageHeight * 0.5; var m: Matrix = new Matrix (); // instancie la matrice m.tx = stage.stageWidth * 0.5; // décalage dans x m.ty = stage.stageHeight * 0.5; // décalage dans y m.a = 2; m.c = 0; m.b = 0; m.d = 1; var d: DottedBox = new DottedBox (); // crée le graphique personnalisé addChild (d); d.transform.matrix = m // applique la matrice sur notre graphique

Étape 11: Mise à l'échelle X et Y

Ici, nous avons redimensionné la grille par un facteur deux le long des axes x et y. Le point bleu correspond à (2, 1) dans la grille d'origine avant la transformation et à (4, 2) dans la grille d'origine après la transformation. (Bien sûr, il est toujours à (2, 1) dans le Nouveau grille après la transformation.)

Et pour confirmer le résultat numériquement…

… Ils correspondent à nouveau! Pour voir cela dans l'implémentation d'ActionScript, changez simplement la valeur de Maryland de 1 à 2.

(Notez que la direction de l'étirement à partir de y est vers le bas, pas vers le haut, car y s'incrémente vers le bas dans Flash mais vers le haut dans l'espace de coordonnées cartésien normal que j'ai utilisé dans le diagramme.)


Étape 12: réflexion

Nous avons ici reflété la grille le long de l'axe des x à l'aide de ces deux vecteurs, de sorte que la position du point bleu dans la grille d'origine passe de (2, 1) à (-2, 1). Le calcul numérique est le suivant:

L'implémentation d'ActionScript est la même que précédemment, mais en utilisant ces valeurs: m.a = -1, m.b = 0 représenter le vecteur de la transformation x, et: m.c = 0 et m. d = 1 pour représenter le vecteur de la transformation y.

Ensuite, qu’en est-il de la réflexion simultanée sur x et y? Regardez l'image ci-dessous.

Aussi, calculé numériquement dans l'image ci-dessous.

Pour l'implémentation d'ActionScript… eh bien, je suis sûr que vous connaissez les valeurs à mettre dans la matrice. m.a = -1, m.b = 0 représenter le vecteur de la transformation x; m.c = 0 et m. d = -1 pour représenter le vecteur de la transformation y. J'ai inclus le fichier SWF final ci-dessous.


Étape 13: inclinaison et cisaillement

Le biaisage vient avec un peu de plaisir. Dans le cas de l'image ci-dessous, l'axe des x de la grille transformée a été réorienté et mis à l'échelle. Comparez les flèches rouges des deux grilles ci-dessous: elles sont différentes mais l’axe des ordonnées reste inchangé..


Fausser

Visuellement, il semble que la distorsion se produise dans la direction y. Cela est vrai car notre axe x transformé a maintenant une composante y dans son vecteur.

Numériquement, c'est ce qui se passe…

En termes de mise en œuvre, j'ai énuméré les réglages ci-dessous.

  • m.a = 2
  • m.b = 1
  • m.c = 0
  • m.d = 1

Je suis sûr qu'à ce stade vous voudriez essayer des choses vous-même, alors allez-y et ajustez

  • l'orientation de l'axe des y transformé tout en maintenant l'axe des x
  • l'orientation des deux axes tout à fait

J'ai inclus la sortie Flash pour les deux cas comme ci-dessous. Pour les lecteurs qui souhaitent obtenir de l’aide sur ces valeurs, consultez Multiplication_final.as dans le téléchargement source.


Étape 14: rotation

Je considère la rotation comme un sous-ensemble de biais. La seule différence est qu'en rotation, la grandeur d'une unité d'axes x et y est maintenue, de même que la perpendicularité entre les deux axes..

ActionScript fournit en fait une méthode dans le Matrice classe, tourner(), pour faire ça. Mais passons par là quand même.

Maintenant, nous ne voulons pas modifier la magnitude d'une unité de longueur en x et y par rapport à la grille d'origine; juste pour changer l'orientation de chacun. Nous pouvons utiliser la trigonométrie pour arriver au résultat indiqué dans l'image ci-dessus. Étant donné un angle de rotation, a, nous obtiendrons le résultat souhaité en utilisant les vecteurs de (cos a, sin a) pour l’axe des x et de (-sin a, cos a) pour l’axe des y. La magnitude pour chaque nouvel axe sera toujours d'une unité, mais chaque axe fera un angle d'un, comparé aux originaux.

Pour la mise en œuvre Actionscript, en supposant que l'angle a soit égal à 45 degrés (c'est-à-dire 0,25 * Pi radians), ajustez simplement les valeurs de la matrice comme suit:

 var a: Number = 0.25 * Math.PI m.a = Math.cos (a); m.c = -1 * Math.sin (a); m.b = Math.sin (a); m.d = Math.cos (a);

La source complète peut être mentionnée dans Multiplication_final.as.


Étape 15: application

Avoir une interprétation vectorielle d'une matrice 2x2 ouvre un espace à explorer. Son application à la manipulation de bitmaps (BitmapData, LineBitmapStyle, LineGradientStyle, etc.) est répandu - mais je pense que je vais garder cela pour un autre tutoriel. Dans le cas de cet article, nous allons essayer d'incliner notre sprite au moment de l'exécution pour qu'il ressemble réellement à un retournement en 3D..


Vue d'un monde isométrique pseudo-3D

Dans l'image ci-dessus, nous pouvons voir que, dans un monde avec une vue isométrique, tout graphique "en position debout" conserve son vecteur de l'axe des ordonnées inchangé pendant que le vecteur de l'axe des abscisses est en rotation. Notez qu’une unité de longueur pour les axes x et y ne change pas - en d’autres termes, aucune mise à l’échelle ne doit se produire sur l’un ou l’autre axe, mais uniquement une rotation autour de l’axe x..

Voici un exemple de cette idée en Flash. Cliquez n'importe où sur la scène et commencez à glisser pour voir le poisson de travers. Relâchez pour arrêter votre interaction.

Voici le bit important de Actionscript. J'ai mis en évidence les lignes cruciales qui gèrent la rotation de l'axe des x. Vous pouvez également vous référer à FakeIso.as.

 private var f1: poisson, m: matrice; var var privé: Point; variable privée axisX: Point, axeY: Point; fonction publique FakeIso () disp = new Point (stage.stageWidth * 0.5, stage.stageHeight * 0.5); m = nouvelle matrice (); m.tx = disp.x; m.ty = disp.y; // déplace au centre de l'étape f1 = new Fish (); addChild (f1); f1.transform.matrix = m; // applique la transformation sur le poisson axisX = new Point (1, 0); // vecteur pour l'axe des xY = new Point (0, 1); // vecteur pour y-axis stage.addEventListener (MouseEvent.MOUSE_DOWN, début); // commence l'interaction stage.addEventListener (MouseEvent.MOUSE_UP, end); // fin de l'interaction fonction privée start (e: MouseEvent): void f1.addEventListener (Event.ENTER_FRAME, update);  fonction privée end (e: MouseEvent): void f1.removeEventListener (Event.ENTER_FRAME, update);  mise à jour de fonction privée (e: événement): void axisX.setTo (mouseX - f1.x, mouseY - f1.y); // détermine l'orientation (mais l'amplitude a également changé) axisX.normalize (1); // fixe la magnitude du vecteur avec une nouvelle orientation à 1 unité apply2Matrix (); // applique la matrice sur le poisson fonction privée apply2Matrix (): void m.setTo (axisX.x, axisX.y, axisY.x, axisY.y, disp.x, disp.y); f1.transform.matrix = m; 

Ici, j'ai utilisé la classe Point pour stocker des vecteurs.


Étape 16: Ajouter le contrôle du clavier

Dans cette étape, nous allons essayer d’ajouter des contrôles au clavier. L'emplacement du poisson sera mis à jour en fonction de sa vitesse, velo. Nous définirons également les pas incrémentiels pour la rotation positive (dans le sens des aiguilles d'une montre) et négative (dans le sens inverse des aiguilles d'une montre).

 velo = nouveau point (1, 0); // velo sera utilisé pour définir l'axe des x.Y = new Point (0, 1); delta_positive = new Matrix (); delta_positive.rotate (Math.PI * 0.01); // rotation positive delta_negative = new Matrix (); delta_negative.rotate (Math.PI * -0.01); // rotation négative

Sur une touche, velo tournera:

 fonction privée keyUp (e: KeyboardEvent): void if (e.keyCode == Keyboard.LEFT) velo = delta_negative.transformPoint (velo) // tourne le velo dans le sens inverse des aiguilles d'une montre else if (e.keyCode == Keyboard.RIGHT ) velo = delta_positive.transformPoint (velo) // faire pivoter velo dans le sens des aiguilles d'une montre

Maintenant, pour chaque image, nous allons essayer de colorer la face avant du poisson et de l'incliner également. Si la vitesse, velo, a une magnitude de plus de 1 et on l'applique à la matrice du poisson, m, nous aurons aussi un effet de mise à l'échelle - donc, pour éliminer cette possibilité, nous normaliserons la vitesse et ne l'appliquerons alors qu'à la matrice du poisson..

 mise à jour des fonctions privées (e: Event): void var front_side: Boolean = velo.x> 0 // recherche le côté du poisson si (front_side) f1.colorBody (0x002233,0.5) // colorie le côté avant of fish else f1.colorBody (0xFFFFFF, 0.5) // blanc appliqué à l'arrière du poisson disp = disp.add (velo); // met à jour le déplacement actuel avec la vélocité var velo_norm: Point = velo.clone (); // Si velo> 0, nous devons recalculer 1 unité de longueur pour x. velo_norm.normalize (1); // notez que l'axe des x supérieur à 1 effectuera la mise à l'échelle. Nous ne voulons pas que pour l'instant m.setTo (velo_norm.x, velo_norm.y, axisY.x, axisY.y, disp.x, disp.y); f1.transform.matrix = m; 

Étape 17: votre poisson

Cliquez sur la scène, puis appuyez sur les touches fléchées gauche et droite pour faire changer le sens du poisson.


Étape 18: Un autre contrôle du clavier

Pour pimenter les choses, permettons également le contrôle du vecteur axe des ordonnées.

 fonction privée keyUp (e: KeyboardEvent): void if (e.keyCode == Keyboard.LEFT) velo = delta_negative.transformPoint (velo) sinon if (e.keyCode == Keyboard.RIGHT) velo = delta_positive.transformPoint (velo) if (e.keyCode == Keyboard.UP) axisY = delta_negative.transformPoint (axisY) sinon if (e.keyCode == Keyboard.DOWN) axisY = delta_positive.transformPoint (axisY)

Également pour déterminer la face avant du poisson, nous devons maintenant incorporer l’axe des y dans. Voici le code pour cela:

 var front_side: Boolean = velo.x * axisY.y> 0 if (front_side) f1.colorBody (0x002233,0.5) sinon f1.colorBody (0xFFFFFF, 0.5)

Étape 19: Votre poisson peu régulier

Pour certains, le résultat du contrôle des deux axes peut sembler un peu déroutant, mais vous pouvez maintenant biaiser votre poisson, le traduire, le refléter et même le faire pivoter! Essayez les combinaisons suivantes: haut + gauche, haut + droite, bas + gauche, bas + droite.

En outre, voyez si vous pouvez conserver le côté "avant" du poisson (le poisson sera grisé). Astuce: Tapez continuellement vers le haut, puis à gauche, puis vers le bas, puis à droite. Vous faites une rotation!

Conclusion

J'espère que vous trouverez les mathématiques matricielles un atout précieux pour vos projets après avoir lu cet article. J'espère écrire un peu plus sur les applications de la matrice 2x2 dans les petits conseils rapides qui sortent de cet article, et sur Matrix3d ce qui est essentiel pour les manipulations 3D. Merci pour la lecture, terima kasih.