Обоссал вектор, поклонился матрице

Sovka Ivanov
7 min readMar 4, 2019

--

для заголовочка.

Это продолжение или расширение другой статьи, её лучше бы прочитать прямо сейчас, перед этой, чтоб хотя бы примерно понимать чо здесь происходит. Если с телефона читаешь — поверни его горизонтально, а то там формулы поехают. Алсо рекомендую на бумаге повторять все формулы. Там одна арифметика без спецзнаний, но её слишком много, чтобы разбираться в уме. 18+.

Напоминалка-1, точка — это вектор, набор координат. И ниже процедура вращения вектора (0, 1) вокруг начала координат в 2д.

шпаргалка

Напоминалка-2 — любой вектор может быть представлен как сумма базисных векторов, умноженных на числа, ща это пригодится.

[15, 0, 7.5] = 15 * [1, 0, 0] + 0 * [0, 1, 0] + 7.5 * [0, 0, 1]
зелёные координаты известны, красные хочу узнать

А теперь хояк, задачка: Пускай у меня в одном пространстве задано два базиса:

  1. ахуевший ([1, 0, 0], [0, 1, 0], [0, 0, 1])
  2. ебанутый, такой же как ахуевший, но повёрнутый вокруг оси Z (т.е. в плоскости XY)на угол a.

В ебанутом есть вектор [x, y, z], и мне зачем-то надо узнать его координаты в ахуевшем, назову их [xa, ya, za]. Идея такая: выясняю ахуевшие координаты каждого ебанутого базисного вектора, умножаю их на x y z соответсвенно, складываю. Получится [xa, ya, za] . Выяснить координаты каждого ебанутого вектора — одно и то же, что повернуть каждый ахуевший вектор вокруг оси Z на угол a (именно так получен ебанутый базис).

Первый на очереди [1, 0, 0], и на первой шпаргалке явно обозначено, что с ним происходит при повороте. Т.е. первый ебанутый вектор имеет координаты [cos(a), sin(a), 0]. Второй [0, 1, 0] аналогично, только с нюансом — это ведь тот же первый, только повёрнутый на 90°, значит всё точно так же, но с оффсетом угла на 90°. И по этой теме есть одна братская формула:

cos(a + 90) = -sin(a); 
sin(a + 90) = cos(a)
.

Второй получился [-sin(a), cos(a), 0]. Третий вектор останется без изменений, т.к. совпадает с осью поворота: [0, 0, 1]. И вот ахуевше разложенный ебанутый базис:

[ cos(a), sin(a), 0]
[-sin(a), cos(a), 0]
[0, 0, 1]

Ну вот и всё, эти вектора по очереди домножаю на x y z и складываю:

[xa, ya, za] = 
= x * [cos(a), sin(a), 0] + y * [-sin(a), cos(a), 0] + z * [0, 0, 1]
= [x * cos(a) - ye * sin(a), x * sin(a) + y * cos(a), 1]

Ты ведь ни хера не понял последнюю формулу, да? Давай глянем, чо происходит с каждой координатой отдельно. Умножение вектора на число происходит покоординатно, сложение векторов покоординатно и соответственно. Третье, нужное тебе, равенство следует из первых двух:

1. a * [x, y] = [a * x, a * y]
2. [x1, y1] + [x2, y2] = [x1 + x2, y1 + y2]
3. a * [x1, y1] + b * [x2, y2] = [a * x1 + b * x2, a * y1 + b * y2]

Тык вот. Ебанутый базис в ахуевшем назову EA, к его векторам буду обращаться через квадратные скобки и индекс: EA[0], EA[1], EA[2]. К координатам этих векторов через точку и имя: EA[0].x, EA[0].y, EA[0].z. Это чтоб не запутаться.

Итоговый вектор:
x * EA[0] + y * EA[1] + z * EA[2]
Отдельно координата x итогового:
xa = (x * EA[0]).x + (y * EA[1]).x + (z * EA[2]).x =
= x * EA[0].x + y * EA[1].x + z * EA[2].x
Если вдруг забыл: EA[0] = [cos(a), sin(a), 0], получается
xa = x * cos(a) + y * sin(a) + z * 0 = x * cos(a) + y * sin(a)
Остальные координаты аналогично.

Если когда-то видел умножение матриц, то ща словишь флешбек. Я заменил обращение к координате на такое же, как к вектору — через квадратные скобки и индекс. Итоговый вектор назову ea.

ea[0] = e[0] * EA[0][0] + e[1] * EA[1][0] + e[2] * EA[2][0]
ea[1] = e[0] * EA[0][1] + e[1] * EA[1][1] + e[2] * EA[2][1]
ea[2] = e[0] * EA[0][2] + e[1] * EA[1][2] + e[2] * EA[2][2]
Они на деревьях.

АфтерЕффектный проект в конце.

Это было умножение матрицы на вектор кароч. Получился перевод вектора из ебанутого базиса в ахуевший. А теперь давай глянем на это по-другому. Пускай вектор был взят до того, как базис стал ебанутым. Т.е. ахуевшие координаты известны до поворота и после. Значит он вращнулся прямо в ахуевшем пространстве, а ебанутый базис с синусами понадобился лишь для вычисления новых координат. Называть одноразовую хуйню базисом — много чести. Просто матрица преобразования. Опущенная.

Ещё раз вывод: та залупа с синусами и косинусами является матрицей вращения в плоскости XY, и чтоб чото повращать, надо это на неё умножить. Матрицы вращения вокруг оставшихся осей можно вычислить так же, как я вычислял эту. Вот как всё в итоге выглядит:

Осторожно, есть говно: координатные сетки бывают левосторонние и правосторонние, разница окажется в знаках синуса. Не заморачивайся пока этой хуйнёй, просто предупредил.
[ cos(a), sin(a), 0]
Mz = [-sin(a), cos(a), 0]
[0, 0, 1]
[cos(a), 0, -sin(a)]
My = [0, 1, 0]
[sin(a), 0, cos(a)]
[1, 0, 0]
Mx = [0, cos(a), sin(a)]
[0, -sin(a), cos(a)]

Настоятельно рекомендую всё это сделать самостоятельно на бумаге, может это и очень сложно, но точно реально, я проверял. Мозги качнутся так, что охуеешь заживо.

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

Теперь я хочу вращать не один ссаненький вектор, а аж ёбаный базис целиком, все его нахер вектора одновременно. Пускай есть базис E и матрица поворота M, итоговый базис назову I. Обращаться к векторам и координатам буду так же, как в первый раз: к вектору через квадратные скобки и индекс, к координате через точку и название.

С первым вектором происходит то ж самое, как если бы это был просто вектор (потому что это просто вектор лол):
I[0].x = (E[0].x * M[0].x) + (E[0].y * M[1].x) + (E[0].z * M[2].x)
I[0].y = (E[0].x * M[0].y) + (E[0].y * M[1].y) + (E[0].z * M[2].y)
I[0].z =
(E[0].x * M[0].z) + (E[0].z * M[1].z) + (E[0].z * M[2].z)
Блять, да и со вторым так же, он ж не особенный ни хуя
I[1].x = (E[1].x * M[0].x) + (E[1].y * M[1].x) + (E[1].z * M[2].x)
I[1].y = (E[1].x * M[0].y) + (E[1].y * M[1].y) + (E[1].z * M[2].y)
I[1].z =
(E[1].x * M[0].z) + (E[1].z * M[1].z) + (E[1].z * M[2].z)
Ну этого уже достаточно для понимания алгоритма. Довай зоресую.
строку умножаешь на столб, получаешь ячейку под номером строки и столба. Хуль тут непонятного.

Раньше мне думалось, что всю эту залупу придумал какой-то поехавший под грибами. Но нет, я только что от одного лишь определения вектора логически пришёл к этому умножению. Перед финалом кратко повторю логическую цепочку.

  1. Вектор — набор чисел, можно складывать с другим вектором и умножать на число.
  2. Любой вектор можно разложить на сумму векторов, каждый из которых нельзя разложить на сумму остальных, они типа перпендикулярны друг другу: [x, y] = x * [1, 0] + y * [0, 1]. Систему таких векторов обозвали базисом. Алсо систему векторов удобно писать в столбик и называть матрицей.
  3. Чтобы совершить преобразование вектора (поворот, перемещение, хуещение), можно совершить преобразование его базисных векторов. Кайф в том, что его локальные координаты не изменятся.
  4. глобальные координаты после преобразования можно найти таким макаром: x * E[0] + y * E[1] + z * E[2], где x, y, z — локальные координаты, Е — базис, E[0], E[1], E[2] — вектора базиса
  5. x * E[0].a + y * E[1].a + z * E[2].a — формула из п.4 для каждой отдельной координаты. Вместо a можно подставить x y или z. Это обозвали умножением вектора на матрицу.
  6. Каждый вектор базиса можно умножить на одну и ту же матрицу, тогда преобразуется весь базис. И это получается умножение матрицы на матрицу. Формула вычисления каждого вектора в п.4, и каждой координаты в п.5.

Некоторые из этих пунктов подробно расписаны в предыдущей статье, остальные здесь. Я особенно кайфанул от того, что всё это высосано из одного лишь первого пункта, не использовано никаких операций кроме сложения и умножения, не притянуто никаких знаний извне. Сухой анализ набора чисел с двумя операциями, божественно нахой.

Из такого психованного перемножения следует больная тема — количество строк левой матрицы должно быть равно количеству столбов правой. Ну с этим можно бороться так же, как с неодинаковыми векторами: там можно прибавлять нули в конец, здесь тоже, но с прибавлением нуля вправо, нужно добавлять ещё один базисный вектор снизу, которому этот ноль соответствует. Пример:

[a, b]   [a, b, 0]
[c, d] = [c, d, 0]
[0, 0, 1]
Кароч везде нули кроме главной диагонали, на ней единицы.

--

--

Sovka Ivanov

Папкин математик, мамкин программист, дизайнер папиной подруги.