web-dev-qa-db-fra.com

Android: Matrix -> quelle est la différence entre preconcat et postconcat?

J'utilise Matrix pour mettre à l'échelle et faire pivoter les bitmaps. Je me demande maintenant quelle est la différence entre preconcat et postconcat, ou plus précisément la différence entre:

D'après ce que j'ai pu comprendre jusqu'à présent, setRotate écrase toujours la matrice entière, tandis qu'avec preRotate et postRotate, je peux appliquer plusieurs modifications à une matrice (par exemple, mise à l'échelle + rotation). Cependant, l'utilisation de postRotate ou preRotate n'a pas entraîné de résultats différents pour les cas où je les ai utilisés.

46
znq

La réponse à votre question n'est pas vraiment spécifique à Android; c'est une question graphique et mathématique. Il y a beaucoup de théorie dans cette réponse - vous avez été prévenu! Pour une réponse superficielle à votre question, passez au bas. En outre, comme il s'agit d'une tirade de longue haleine, je pourrais avoir une faute de frappe ou deux, ce qui rend les choses floues. Je m'excuse à l'avance si c'est le cas.

En infographie, nous pouvons représenter des pixels (ou en 3D, des sommets) comme vecteurs. Si votre écran est 640x480, voici un vecteur 2D pour le point au milieu de votre écran (pardonnez mon balisage de mauvaise qualité):

[320]
[240]
[  1]

Je vais expliquer pourquoi le 1 est important plus tard. Les transformations sont souvent représentées à l'aide de matrices car il est alors très simple (et très efficace) de les enchaîner, comme vous l'avez mentionné. Pour mettre à l'échelle le point ci-dessus par un facteur de 1,5, vous pouvez multiplier à gauche par la matrice suivante:

[1.5   0   0]
[  0 1.5   0]
[  0   0   1]

Vous obtiendrez ce nouveau point:

[480]
[360]
[  1]

Ce qui représente le point d'origine, mis à l'échelle de 1,5 par rapport au coin de votre écran (0, 0). C'est important: la mise à l'échelle se fait toujours par rapport à l'origine. Si vous souhaitez mettre à l'échelle avec un autre point comme centre (comme le milieu d'un Sprite), vous devez "encapsuler" l'échelle dans les traductions vers et depuis l'origine. Voici la matrice pour traduire notre point d'origine à l'origine:

[1  0  -320]
[0  1  -240]
[0  0     1]

Ce qui donne:

[320*1 + 1*-320]   [0]
[240*1 + 1*-240] = [0]
[     1*1      ]   [1]

Vous reconnaîtrez ce qui précède comme la matrice identité avec les coordonnées de déplacement giflées dans le coin supérieur droit. C'est pourquoi le 1 (la "coordonnée homogène") est nécessaire: faire de la place à ces coordonnées, permettant ainsi de traduire par multiplication. Sinon, cela devrait être représenté par un ajout de matrice, qui est plus intuitif pour l'homme, mais rendrait les cartes graphiques encore plus compliquées qu'elles ne le sont déjà.

Maintenant, la multiplication matricielle est généralement n'est pas commutative , donc lorsque vous "ajoutez" une transformation (en en multipliant votre matrice) vous avez besoin pour spécifier si vous multipliez à gauche ou à droite. La différence que cela fait est l'ordre dans lequel vos transformations sont enchaînées. En multipliant à droite votre matrice (en utilisant preRotate()), vous indiquez que l'étape de rotation doit se produire avant toutes les autres transformations que vous venez de demander. C'est peut-être ce que vous voulez, mais ce n'est généralement pas le cas.

Souvent, cela n'a pas d'importance. Si vous n'avez qu'une seule transformation, par exemple, cela n'a jamais d'importance :) Parfois, vos transformations peuvent se produire dans l'un ou l'autre ordre avec le même effet, comme la mise à l'échelle et la rotation - mon algèbre linéaire est rouillée, mais je crois que dans ce cas le la multiplication matricielle est en fait commutative car la matrice d'échelle est symétrique , c'est-à-dire qu'elle se reflète sur la diagonale. Mais vraiment, pensez-y: si je fais pivoter une image de 10 degrés dans le sens des aiguilles d'une montre et que je la redimensionne à 200%, cela ressemble à la première mise à l'échelle, puis à la rotation.

Si vous faisiez des transformations de composés plus étranges, vous commenceriez à remarquer une différence. Mon conseil est de rester avec postRotate().

131
Cheezmeister

J'ai répondu à la question hier , mais je me sens quelque chose de mal aujourd'hui, donc je corrige la réponse ici:

matrix:  float[] values ={1.2f,0.5f,30,0.5f,1.2f,30,0,0,1};

//as we all know, the basic value in matrix,means no transformation added
matrix2:  float[] values2 ={1f,0,0,0,1f,0,0,0,1};

Let's say our matrix values are the values above.

1 、 lorsque nous faisons la transformation comme ci-dessous:

matrix.preTranslate(-50, -50);

is equals to do sequence transformation to matrix2 above like below:

matrix2.postTranslate(-50, -50);
matrix2.postSkew(0.5f/1.2f,0.5f/1.2f);// note here
matrix2.postScale(1.2f, 1.2f);
matrix2.postTranslate(30, 30);

2 、 lorsque nous faisons la transformation comme ci-dessous:

matrix.preRotate(50);

is equals to do sequence transformation to matrix2 like below:

matrix2.postRotate(50);
matrix2.postSkew(0.5f/1.2f,0.5f/1.2f);
matrix2.postScale(1.2f, 1.2f);
matrix2.postTranslate(30, 30);

3 、 lorsque nous faisons la transformation comme ci-dessous:

matrix.preScale(1.3f,1.3f);

is equals to do sequence transformation to matrix2 like below:

matrix2.postScale(1.3f,1.3f);
matrix2.postSkew(0.5f/1.2f,0.5f/1.2f);
matrix2.postScale(1.2f, 1.2f);
matrix2.postTranslate(30, 30);

4 、 lorsque nous faisons la transformation comme ci-dessous:

 matrix.preSkew(0.4f,0.4f);

est égal à faire une transformation de séquence en matrice2 comme ci-dessous:

 matrix2.postSkew(0.4f,0.4f);
 matrix2.postSkew(0.5f/1.2f,0.5f/1.2f);
 matrix2.postScale(1.2f, 1.2f);
 matrix2.postTranslate(30, 30);
4
chikeong