Рассчитать матрицу поворота из одного вектора в другой в 2D пространстве нам может понадобиться, например, при работе с UV – картами развертки. Часто это бывает нужно, чтобы подогнать один UV-остров развертки под другой.
Для того чтобы высчитать матрицу трансформации из двух имеющихся векторов, для начала получим угол между этими векторами:
1 |
angle = src_vector.angle(dest_vector) |
Угол считается в радианах.
Затем нам нужно понять, в какую сторону нужно осуществить поворот. В 2D пространстве может быть два варианта – изначальный вектор расположен “выше” второго, тогда нам нужно вращать против часовой стрелки, или, если он “ниже” – вращать нужно по часовой стрелке.
Направление вращения нам может дать знак векторного произведения двух исходных векторов.
1 |
sign = src_vector.cross(dest_vector) |
Векторное произведение двух векторов в 2D пространстве – число, знак которого указывает на поворот, который как раз нам и нужен.
Если это число меньше нуля – поворот по часовой стрелке. Если число больше нуля – вращать нужно против часовой стрелки.
В итоге, регулировать направление поворота мы можем задавая знак для полученного угла поворота.
1 2 |
if sign < 0.0: angle = -angle |
Теперь, используя метод Rotation() объекта Matrix из модуля mathutils, мы можем получить матрицу поворота.
1 |
matrix = Matrix.Rotation(angle, 2, 'Z') |
В первом параметре метода мы передали полученный угол поворота. Во втором – размер матрицы 2 на 2, именно такая матрица нам нужна для вращения в 2D пространстве. В третьем параметре мы передаем ось вращения “Z”, которая всегда будет направлена перпендикулярно плоскости экрана.
Соберем все в одну функцию.
1 2 3 4 5 6 |
def rotation_matrix(src_vector, dest_vector): angle = src_vector.angle(dest_vector) sign = src_vector.cross(dest_vector) if sign < 0.0: angle = -angle return Matrix.Rotation(angle, 2, 'Z') |
Проверим, как работает наша функция на примере поворота острова UV развертки.
Например, пусть остров расположен вертикально, а нам нужно повернуть его параллельно другому острову, который расположен под углом в 45 градусов.
Тогда исходный и конечный вектора для поворота будут следующими:
1 2 |
src_vector = Vector((0.0, 1.0)) dest_vector = Vector((1.0, 1.0)) |
На основе этих векторов получим матрицу поворота, вызвав нашу функцию.
1 2 3 4 |
rotation_matrix = rotation_matrix(src_vector, dest_vector) # <Matrix 2x2 ( 0.7071, 0.7071) # (-0.7071, 0.7071)> |
Переключимся на объект bmesh и получим указатель на текущий активный слой развертки.
1 2 |
bm = bmesh.from_edit_mesh(bpy.context.object.data) uv_layer = bm.loops.layers.uv.active |
Пройдем по всем выделенным полигонам (для простоты пусть наш UV остров будет выделен) и через слой развертки умножим координаты всех точек развертки на полученную матрицу.
1 2 3 |
for face in (_face for _face in bm.faces if _face.select): for loop in face.loops: loop[uv_layer].uv = rotation_matrix @ loop[uv_layer].uv |
И вернем обновленные данные из bmesh обратно в текущий меш сцены.
1 2 |
bmesh.update_edit_mesh(bpy.context.object.data) bm.free() |
Как мы видим, весь выделенный остров развертки повернулся точно как нам требуется – параллельно другому острову.