понедельник, 26 декабря 2016 г.

Шпаргалки по mathutils и линейной алгебре.

   После того как убил кучу времени, пытаясь собрать матрицу ориентации на объект, решил завести шпаргалку. Пост будет дополняться, по мере понимания мной всяких фишек, для памяти. Банальные и неинтересные вещи.



Vector.negate() - удобная штука, разворачивает вектор, после применения Vector становится направлен в противоположную сторону. Удобно если надо получить вектор из координаты к объекту. Дело в том, что можно было бы ручками рассчитать вектор, вычтя одну координату из другой, но у объектов есть функция .getVectTo(), которая написана на С, следовательно быстрее, а заодно возвращает еще пару параметров, которые могут быть нужны.

vector = vector1.cross(vector2) - создает вектор, перпендикулярный двум векторам. Например, строит вектор ориентации по оси Z, если задать ориентацию по осям X и Y. Если направление не нравится, можно поменять на противоположное, поменяв вектора местами.

vector.normalize() - делает длину вектора равной 1. Весьма важная штука. Не возвращает значение, а изменяет сам объект, в отличие от
vector1 = vector2.normalized(), а вот эта функция удобна когда надо сохранить значение вектора, но и получить нормализованную копию.

Создание матрицы ориентации на объект. Аналогично выравниванию object.alignAxisToVect(), но в отличии от последнего, объект не стремится "лечь набок", разве что резко перевернуться вверх тормашками, но организовать проверку направленности оси Z не сложно. Для начала получаем вектор vectY направления на объект, затем получаем его проекцию на плоскость XY, тупо отбрасывая третий компонент вектора.
tempY = mathutils.Vector([vectY[0], vectY[1]])
Затем нормализуем полученный вектор,потому что иначе его длинна будет не равна 1.
tempY.normalize()
Затем получаем вектор ориентации оси X, который всегда будет параллелен плоскости XY. Просто меняем местами компоненты вектора tempY, ну и умножаем второй компонент на -1, что бы получившийся вектор был направлен как в Блендере, направо. А можно просто развернуть получившийся вектор. Поскольку он будет параллелен плоскости XY, то компонент Z равен нулю.
vectX = mathutils.Vector([tempY[1], tempY[0] * (-1), 0.0])
Почему tempY?, потому что он уже нормализован. Итого имеем два вектора, vectX и vectY, осталось с помощью .cross() найти перпендикулярный вектор, он же vectZ:
vectZ = vectX.cross(vectY)
Собираем матрицу. Только не просто ориентации, а матрицу трансформации? Не знаю, как точно сказать, короче в этой матрице будет записана информация о ориентации, позиции и Scale объекта. Со scale проще, оно записывается в виде длинны векторов ориентации по соответствующей оси, если объект после применения матрицы "плющит" и "корежит", значит где-то вектора не равна 1. Нормализуйте, если что. Можно просто создать матрицу и прописать все значения компонентов, как делал для векторов, но очень уж длинно. Есть и другой способ, как описано в примерах кода. Сразу создадим или рассчитаем координату объекта:
pos = mathutils.Vector([0.0, 5.0, 1.0])
Теперь создаем матрицу:
matT = mathutils.Matrix()
И пишем значения в неё, не забываем, что вектора ориентации пишутся в столбец, а не в строку:
matT[0][0], matT[0][1], matT[0][2], matT[0][3] = vectX[0], vectY[0], vectZ[0], Pos[0]
matT[1][0], matT[1][1], matT[1][2], matT[1][3] = vectX[1], vectY[1], vectZ[1], Pos[1]
matT[2][0], matT[2][1], matT[2][2], matT[2][3] = vectX[2], vectY[2], vectZ[2], Pos[2]
Матрица имеет размер 4x4, но мы прописали только три строки.

Вектора можно вращать, создавая матрицы вращения, как в документации написано, создаем матрицу вращения, кстати, можно создать один раз, а потом много-много раз пользоваться:
rotMat = mathutils.Matrix.Rotation(angle, 4, 'X')
angle - угол, на который надо повернуть, в радианах, если что, есть math.radians(degrees_angle)
4 - это у нас размер, матрица получится 4х4, можно еще 2.
Пересчитываем в 3х3:
rotMat = rotMat.to_3x3()
И просто перемножаем вектор на получившуюся матрицу:
vectR = vect * rotMat

Еще опишу получение координаты, заданной вектором vectPos в системе координат объекта, который, как известно может быть повернут. Это как раз просто:
newPos = vectPos[0] * vectX + vectPos[1] * vectY + vectPos[2] * vectZ
А можно просто перемножить вектор на матрицу ориентации объекта, или наоборот матрицу на вектор, правда результат в обоих случаях будет разным:
newPos = object.worldOrientation * vectPos
Чтобы получить мировые координаты, нужно просто добавить полученный вектор к позиции объекта, в системе координат которого и ищем новую точку.
wPos = newPos + objectPos

Вектор ориентации какого либо объекта, то есть, куда смотрит объект направлен какой-либо вектор локальной системы координат объекта можно получить из матрицы ориентации, просто посмотрев соответствующий столбец. Например:
vector = mathutils.Vector([matOr[0][1], matOr[1][1], matOr[2][1]]), такая вот форма записи, нужно брать нужный компонент, обращаясь каждый раз к разным элементам, ведь матрица состоит из строк, неудобно, да, но можно воспользоваться функцией доступа к столбцам .col:
vector = matOr.col[1]
Если все же надо вытащить ряд из матрицы, можно использовать аналогичную функцию .row. Обе функции возвращают mathutils.Vector, поэтому специально создавать его не надо, но есть один нюанс - они вернут не объект вектора, а скорее ссылку на вектор внутри матрицы, поэтому если хотим изменить какие-то циферки, то изменим и матрицу ориентации. Это касается вообще всех векторов, а также матриц, даже если вы создали их сами. Для создания копии этих объектов нужно использовать функцию .copy():
vect1 = vect2.copy()
Но вообще со списками и словарями та же история, так что ничего особенного я тут не описал.