Не срабатывает ассоциативность умножения матриц в случае QTransform

Есть в наличии код формирования сцены для Qt OpenGL. Матрицы преобразования координат текстур - QTransform, вертексов QMatrix4x4. Пример рабочего кода:

QTransform textureBase, textureLocal;
QMatrix4x4 vertexBase, vertexLocal;
QVector<QVector3D> textureScene, vertexScene;
...
for(const QVector3D &point: texturePoints)
    textureScene << textureBase * (textureLocal * point);

for(const QVector3D &point: vertexPoints)
    vertexScene << vertexBase * (vertexLocal * point);

Тут есть проблема, что точка умножается на две матрицы. Хотелось бы перемножить матрицы заранее и с QMatrix4x4 всё как и положено vertexBase * (vertexLocal * point) == (vertexBase * vertexLocal) * point ассоциативность умножения матриц в действии.

А вот для QTransform так не получается: textureBase * (textureLocal * point) != (textureBase * textureLocal) * point. Почему так происходит, ведь по описанию они обе - матрицы? И как это исправить?

Уточняю по просьбе @Andrej-Levkovitch. Сравнивал в отладчике. Отличается сильно - в разы, то есть не из-за погрешности операций с плавающей точкой.


Ответы (2 шт):

Автор решения: Andrej Levkovitch

Вангую: вы сравнивали два случая прямо через оператор != котороый определен в классе QMatrix4x4. Но, как и сказано в документации, он сравнивает матрицы на идентичность - не забывайте, что числа с плавающей точкей нельзя сравнивать через == или != для случаев когда аргументы были вычеслены

→ Ссылка
Автор решения: mr NAE

Решение проблемы - преобразовать явно QTransform в QMatrix4x4:

...
QMatrix4x4(textureBase)* QMatrix4x4(textureLocal) * point;
...

В таком варианте ассоциативность выполняется.

Почему такое возникает?

QTransform не имеет оператора умножения на QVector3D. А у QMatrix4x4 он есть, что логично, а так же есть оператор преобразования типа из QTransform. Поэтому, корректно работает textureBase * (textureLocal * point) так как оно разворачивается в следующую последовательность:

QVector3D result1 = textureBase * (textureLocal * point);
// эквивалентно:
QMatrix4x4 tmp1(textureLocal);
QVector3D tmp2 = tmp1 * point;
QMatrix4x4 tmp3(textureBase);
QVector3D result1 = tmp3 * point;

А если применить ожидаемую ассоциативность:

QVector3D result2 = (textureBase * textureLocal) * point;
// эквивалентно:
QTransform tmp4 = textureBase * textureLocal;
QMatrix4x4 tmp5(tmp1);
QVector3D result2 = tmp5 * point;

то результат будет некорректным: result1 отличается от result2 значительно (кроме частных случаев, естественно). И это не из-за каких-то ошибок вычисления или преобразований.

А дело тут вот в чём. Не вдаваясь в детали реализации, QMatrix4x4 транспонирует аффинную часть QTransform при перекладывании элементов матриц. На самом деле это не совсем так, чуть сложнее, но сути не меняет.

Пусть A B это матрицы QTransform тогда textureBase * (textureLocal * point) выглядит так:

AT * (BT * point) = AT * BT * point

а (textureBase * textureLocal) * point так:

(A * B)T * point = BT * AT * point

AT * BT != AT * BT

→ Ссылка