Les matrices 2D de WPF et Silverlight

Les matrices 2D de WPF et Silverlight

Pour gérer les transformations 2D, WPF et Silverlight disposent de classes de haut-niveau (TranslateTransform, ScaleTransform, RotateTransform et SkewTransform). Mais pour différentes raisons, il peut être intéressant d'utiliser la technique des matrices, plus bas-niveau. A des fins pédagogiques tout d'abord. Mais également pour garder des réflexes acquis avec des frameworks tels que Direct3D ou OpenGL.

Une matrice est un tableau de valeurs qui permet de définir une transformation de coordonnées.

Matrix

La structure Matrix représente une matrice à 2 colonnes et 3 lignes :

M11M12
M21M22
OffsetX (M31)OffsetY (M32)

M11, M12, M21, M22, OffsetX et Offset sont des propriétés de la structure Matrix (M31 et M32 n'existent pas en tant que propriétés).

Une transformation peut être appliquée à un point en le multipliant par une matrice (il est également possible d'utiliser les méthodes Transform des classes Matrix et MatrixTransform). Le point résultant dispose des coordonnées suivantes :

x’ = M11x + M21y + OffsetX
y’ = M12x + M22y + OffsetY

Une transformation peut être appliquée à un vecteur selon le même principe.

La matrice unité (ou identité) dispose d'une valeur de 1 pour M11 et M22, et de 0 pour les autres cellules. Le constructeur de la structure Matrix crée par défaut une matrice unité.

Mise à l'échelle

Les propriétés M11 et M22 déterminent le facteur de mise à l'échelle, respectivement sur l'axe des X et sur celui des Y, comme le montre l'équation :

x’ = M11x 
y’ = M22y

La classe GeometryHelper de la bibliothèque Perspective 2.0 expose la méthode statique suivante, qui renvoit une matrice de mise à l'échelle :

public static Matrix GetScaleMatrix(double scaleX, double scaleY)
{
    return new Matrix
    {
        M11 = scaleX,
        M22 = scaleY
    };
}

Déplacement

Un déplacement est défini au moyen des propriétés OffsetX et OffsetY

public static Matrix GetTranslationMatrix(double offsetX, double offsetY)
{
    return new Matrix
    {
        OffsetX = offsetX,
        OffsetY = offsetY
    };
}

Cette matrice correspond à l'équation suivante :

x’ = x + OffsetX
y’ = y + OffsetY

Rotation

Pour une rotation, les choses se compliquent un peu, et font appel à la trigonométrie.

Soit un point de coordonnées x et y. Par rapport à l'origine du repère, il correspond à un angle de rotation initial alpha. Si r est la distance entre l'origine et le point (c'est à dire le rayon d'un cercle fictif), on peut écrire :

x = r * cos(alpha)
y = r * sin(alpha)

La rotation du point sur le même cercle selon un angle bêta résulte en un point de coordonnées :

x' = r * cos(alpha + bêta)
y' = r * sin(alpha + bêta)

C'est à dire :

x' = r * cos(alpha) * cos(bêta) - r * sin(alpha) * sin(bêta)
y' = r * sin(alpha) * cos(bêta) + r * cos(alpha) * sin(bêta)

Soit en simplifiant :

x' = x * cos(bêta) - y * sin(bêta)
y' = y * cos(bêta) + x * sin(bêta)

La matrice correspondant à cette équation est donc :

M11 = cos(bêta)M21 = -sin(bêta)
M12 = sin(bêta)M22 = cos(bêta)

La méthode suivante renvoit un objet Matrix définissant une rotation selon un angle donné, exprimé en degrés :

public static Matrix GetRotationMatrix(double angle)
{
    var radianAngle = GeometryHelper.DegreeToRadian(angle);
    return new Matrix
    {
        M11 = Math.Cos(radianAngle), M21 = -Math.Sin(radianAngle),
        M12 = Math.Sin(radianAngle), M22 = Math.Cos(radianAngle)
    };
}

Cette méthode s'utilise de la façon suivante :

private double _angle = 15.0;
private Point _currentPoint;
...
_currentPoint *= GeometryHelper.GetRotationMatrix(_angle);

L'axe des Y étant vers le bas, la rotation a lieu dans le sens inverse du sens trigonométrique conventionnel.

Rotation

Le code de cet exemple est disponible dans la page MatrixDemo.xaml de l'application exemple de la bibliothèque Perspective 2.0.

Multiplication de matrices

Plusieurs matrices (représentant chacune une transformation) peuvent être combinées en les multipliant. Attention, la multiplication de matrices n'est pas commutative (m1*m2 ne donne pas le même résultat que m2*m1).

Exemple WPF :

Matrix m1 = GeometryHelper.GetRotationMatrix(45.0);
Matrix m2 = GeometryHelper.GetTranslationMatrix(50.0, 0.0);
_currentPoint *= m1 * m2;

Cependant, contrairement à celle de WPF, la structure Matrix de Silverlight n'implémente pas l'opérateur de multiplication. Une alternative consiste à utiliser la méthode MultiplyMatrices de la classe GeometryHelper de la bibliothèque Perspective.

Matrix m1 = GeometryHelper.GetRotationMatrix(45.0);
Matrix m2 = GeometryHelper.GetTranslationMatrix(50.0, 0.0);
Matrix m3 = GeometryHelper.MultiplyMatrices(m1, m2);
_currentPoint = m3.Transform(_currentPoint);

Téléchargements

Perspective 2.0 pour WPF

Perspective 2.0 pour Silverlight

A propos de cet article

Auteur : Olivier Dewit

Historique :

  • 10 octobre 2010 : description de GeometryHelper.MultiplyMatrices. MatrixDemo.xaml est dorénavant disponible dans les sources de Perspective 2.0 pour Silverlight.
  • 24 août 2010 : corrections, mise à jour des liens.
  • 27 juin 2010 : création.