Android: Matrix → в чем разница между preconcat и postconcat?

Я использую Matrix для масштабирования и поворота растровых изображений. Теперь мне интересно, какая разница между preconcat и postconcat, или, точнее, разница между:

Из того, что я мог выяснить, setRotate всегда перезаписывает всю матрицу, а с preRotate и postRotate я могу применить несколько изменений к матрице (например, масштабирование + вращение). Однако либо использование postRotate, либо preRotate не приводило к другим результатам для случаев, в которых я их использовал.

Ответ 1

Ответ на ваш вопрос не совсем специфичен для Android; это графический и математический вопрос. В этом ответе есть много теории - вас предупреждают! Для поверхностного ответа на ваш вопрос пропустите снизу. Кроме того, поскольку это такая длинная тирада, у меня может быть опечатка или две вещи, которые не понятны. Приносим извинения заранее, если это дело.

В компьютерной графике мы можем представлять пиксели (или в 3D, вершины) в качестве векторов. Если ваш экран 640x480, вот 2D-вектор для точки в середине экрана (простите мою дрянную разметку):

[320]
[240]
[  1]

Я объясню, почему 1 это важно позже. Преобразования часто представлены с использованием матриц, потому что тогда очень просто (и очень эффективно) объединить их вместе, как вы упомянули. Чтобы масштабировать точку выше в 1,5 раза, вы можете умножить налево на следующую матрицу:

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

Вы получите эту новую точку:

[480]
[360]
[  1]

Которая представляет исходную точку, масштабированную на 1,5 относительно угла экрана (0, 0). Это важно: масштабирование всегда выполняется относительно источника. Если вы хотите масштабировать какую-то другую точку в качестве своего центра (например, середину спрайта), вам нужно "обернуть" масштаб в переводах в начало и из начала. Здесь матрица для перевода исходной точки в начало координат:

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

Что дает:

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

Вы узнаете выше, как матрицу identity с координатами смещения, расположенными в верхнем правом углу. Вот почему 1 ( "однородная координата" ) необходимо: освободить место для этих координат, что позволяет переводить с использованием умножения. В противном случае это должно было бы быть представлено добавлением матрицы, которое более интуитивно для людей, но сделало бы графические карты еще более сложными, чем они есть.

Теперь матричное умножение вообще не является коммутативным, поэтому, когда "добавление" преобразования (путем умножения вашей матрицы), необходимо указать, вы умножаетесь налево или умножаетесь вправо. Разница, которую он делает, - это то, к чему привязаны ваши преобразования. Путем правильного умножения вашей матрицы (используя preRotate()) вы указываете, что шаг вращения должен произойти до всех других преобразований, о которых вы только что просили. Это может быть то, что вы хотите, но обычно это не так.

Часто это не имеет значения. Например, если у вас есть только одно преобразование, это никогда не имеет значения. Иногда ваши преобразования могут происходить в любом порядке с таким же эффектом, как масштабирование и вращение - моя линейная алгебра ржавая, но я считаю, что в этом случае матричное умножение фактически является коммутативным, потому что масштабная матрица symmetric, то есть она зеркально отражается по диагонали. Но на самом деле, просто подумайте об этом: если я поворачиваю изображение на 10 градусов по часовой стрелке, а затем масштабирую его до 200%, оно выглядит так же, как если бы я сначала масштабировал его, а затем поворачивал.

Если вы делаете какие-то более сложные сложные преобразования, вы начнете замечать несоответствие. Мой совет - придерживаться postRotate().

Ответ 2

Я ответил на вопрос вчера, но сегодня я чувствую, что неправильно, поэтому я исправляю ответ здесь:

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 say our matrix values are the values above.

1, когда мы делаем преобразование, как показано ниже:

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, когда мы делаем преобразование, как показано ниже:

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, когда мы делаем преобразование, как показано ниже:

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, когда мы делаем преобразование, как показано ниже:

 matrix.preSkew(0.4f,0.4f);

равно преобразованию последовательности в матрицу 2, как показано ниже:

 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);

Ответ 3

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

Скажем, наши значения матрицы - это значения выше.

  • когда мы делаем преобразование, как показано ниже:

    matrix.preTranslate(-50, -50);
    

    равно преобразованию последовательности в матрицу 2, как показано ниже:

    matrix2.postTranslate(-50, -50);
    matrix2.postScale(1.2f, 1.2f);
    matrix2.postTranslate(30, 30);
    
  • когда мы делаем преобразование, как показано ниже:

    matrix.preRotate(50);
    

    равно преобразованию последовательности в матрицу 2, как показано ниже:

    matrix2.postRotate(50);
    matrix2.postScale(1.2f, 1.2f);
    matrix2.postTranslate(30, 30);
    
  • когда мы делаем преобразование, как показано ниже:

    matrix.preScale(1.3f,1.3f);
    

    равно преобразованию последовательности в матрицу 2, как показано ниже:

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

Однако, если ваша матрица повернута раньше (например, {1.2f,-1f,30,-1f,1.2f,30,0,0,1};), то это не так просто, как указано выше, потому что при повороте вы также масштабировали матрицу одновременно.