Затерянная документация или transform: matrix3d [перевод] / Хабр
Когда погружаешься в документацию о CSS3 transform: matrix3d, находишь короткое определение «Задает 3D трансформацию как матрицу 4х4.», сопровождаемое определением функции в виде:
matrix3d(m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m31, m33)
И если не являешься Богом математики, скорее всего, внутри возникает беспокойство о недостатке документации, за которым следует вопрос, как создать по-настоящему крутую штуку? Описанный подход не претендует называться математическим или полным — я всего лишь пытаюсь заполнить маленькую брешь документации.
демо | первоисточник
Немного линейной алгебры
Всякая комплексная трансформация может быть представленна тремя базовыми:
Поворот (rotate)
Масштабирование (scale)
Перемещение (translate)
Эти 3 базовых преобразования могут быть совмещены в одну огромную всеобъемлющую матрицу трансформации.
Единичная матрица
Эта матрица не делает ничего! Nil! Null! Nada! Ни один пиксель не пострадал! Я разделил эту матрицу на 2 секции. Красная секция это область где описываются Поворот (Rotate) и Масштабирование (Scale). В желтой секции описывается cдвиг или перемещение (translate). Остальные параметры используются очень редко, за исключением понастоящему странных FX демок в LSD стиле.
Мы начнем созданием матрицы масштабирования, умножив единичную матрицу на коэффициент масштабирования.
scaleMatrix = indentityMatrix.multiply(s)
Матрица масштабирования
scaleMatrix = $M([
[s,0,0,0],
[0,s,0,0],
[0,0,0,s]
])
Так как мы не хотим трансформировать координаты перемещения (translate), давайте заменим последний параметр масштабирования на 1:
scaleMatrix = $M([
[s,0,0,0],
[0,s,0,0],
[0,0,s,0],
[0,0,0,1]
])
Матрицы поворота (Rotate)
Поворот может осуществляться вокруг собственной оси X, Y, Z на заданный угол. Давайте примем значения углов для каждой оси как a, b, c. Соотвествующие матрицы, представляющие такую трансформацию:
rotationXMatrix = $M([
[1,0,0,0],
[0,Math.cos(a), Math.sin(-a), 0],
[0,Math.sin(a), Math.cos( a), 0],
[0,0,0,1]
])
rotationYMatrix = $M([
[Math.cos( b), 0, Math.sin(b),0],
[0,1,0,0],
[Math.sin(-b), 0, Math.cos(b), 0],
[0,0,0,1]
rotationZMatrix = $M([
[Math.cos( c), Math.sin(-c), 0, 0],
[Math.sin( c), Math.cos( c), 0, 0],
[0,0,1,0],
[0,0,0,1]
])
Каждая матрица описывает поворот вокруг одной оси.
Матрица перемещения (translate)
translationMatrix = $M([
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[tx,ty,tz,1]
])
Матрица перемещения не затрагивает большенство пикселей, но добавляет значения tx, ty и tz к результирующему вектору направления.
Веселье
Да, математика может быть развлечением, а каждая из этих матриц может быть использованна в композиции. Итак, если нужно повернуть что-нибудь вокруг каждой оси и потом переместить это на несколько пикселей, просто перемножте эти матрицы. Вот и все:
tM = rotationXMatrix
.x(rotationYMatrix)
.x(rotationZMatrix)
.x(scaleMatrix)
.x(translationMatrix)
И в конце применим трансформацию к изображению:
s = «matrix3d(»
s += tM.e(1,1).toFixed(10) + «,» + tM.e(1,2).toFixed(10) + «,» + tM.e(1,3).toFixed(10) + «,» + tM.e(1,4).toFixed(10) + «,»
s += tM.e(2,1).toFixed(10) + «,» + tM.e(2,2).toFixed(10) + «,» + tM.e(2,3).toFixed(10) + «,» + tM.e(2,4).toFixed(10) + «,»
s += tM.e(3,1).toFixed(10) + «,» + tM.e(3,2).toFixed(10) + «,» + tM.e(3,3).toFixed(10) + «,» + tM.e(3,4).toFixed(10) + «,»
s += tM.e(4,1).toFixed(10) + «,» + tM.e(4,2).toFixed(10) + «,» + tM. e(4,3).toFixed(10) + «,» + tM.e(4,4).toFixed(10)
s += «)»
document.getElementById(‘darth-vader’).style[‘-webkit-transform’] = s
Предостережения
Первое — если погуглить линейную трансформацию и найти примеры таких матриц, можно удивится что матрицы немного отличаются. Дело в том, что CSS матрица транспонированная — вот так просто, выполните транспонирование матрицы и она должна работать.
Второе — CSS не поддерживает научную форму числа (например 123е-15) в качестве параметров — поэтому нужно использовать toFixed(numberOfDigits) чтобы нормализовать их.
Среда разработки
Подход работает в -webkit- браузерах, таких как Chrome или Safari, Firefox 10+ и IE 10. Префиксы можно посмотреть здесь caniuse.com/transforms3d. Демо работает только в -webkit- и написанно на coffeescript который немного круче javascript — но скомплилированный код должен быть читабельным. Вы можете забрать весь урок и исходники на github.
Демо
Оригинал статьи
Facebook автора
Twitter автора
Потрясающая Javascript библиотека Sylvester
Статья о линейных трансформациях на Wikipedia
Метод Matrix3D.Transform (System.Windows.Media.Media3D) | Microsoft Узнайте
Редактировать
Твиттер LinkedIn Фейсбук Электронное письмо
- Артикул
Определение
- Пространство имен:
- Система.Windows.Media.Media3D
- Сборка:
- PresentationCore.
dll
Важный
Некоторая информация относится к предварительной версии продукта, который может быть существенно изменен до его выпуска. Microsoft не дает никаких явных или подразумеваемых гарантий в отношении представленной здесь информации.
Преобразует указанный Point3D, массив объектов Point3D, Point4D, массив объектов Point4D, Vector3D или массив объектов Vector3D с помощью текущего Matrix3D.
Перегрузки
Преобразование (Point3D) | Преобразует указанный Point3D с помощью Matrix3D и возвращает результат. |
Преобразовать(Точка3D[]) | Преобразует указанные объекты Point3D в массив с помощью Matrix3D. |
Преобразование (Point4D) | Преобразует указанный Point4D с помощью Matrix3D и возвращает результат. |
Преобразование (Point4D []) | Преобразует указанные объекты Point4D в массиве с помощью Matrix3D и возвращает результат. |
Преобразование (Vector3D) | Преобразует указанный Vector3D с помощью этого Matrix3D. |
Преобразование (Vector3D []) | Преобразует указанные объекты Vector3D в массиве с помощью этого Matrix3D. |
Преобразование (Point3D)
Преобразовать(Точка3D[])
Преобразование (Point4D)
Преобразование (Point4D [])
Преобразование (Vector3D)
Преобразование (Vector3D [])
Вычисление преобразований CSS matrix3d
Я очищал некоторые старые заметки со своей предыдущей работы и нашел несколько математических каракулей для вычисления преобразований CSS и решил поделиться ими. Для некоторого контекста я работал над страницей с изображением, которое выглядело так:
Я хотел добавить пасхальное яйцо, где я мог бы использовать экраны этих устройств для отображения произвольных вещей.
Разочарованный, я вместо этого попытался решить ее аналитически. Это означает, что для любой заданной формы мне нужно найти перспективное преобразование, которое деформирует элемент в эту форму. Как только это решено, легко написать вспомогательный скрипт WYSIWYG для вывода CSS. Вот окончательный результат:
См. Pen ifnqH Франклина Та (@fta) на CodePen.
См. код на вкладке coffeescript. Или вставьте этот текст в консоль, чтобы попробовать его на любой странице с jQuery. Вам нужно будет изменить селектор на любой элемент, к которому вы хотите добавить точки.
Используя это, вы можете перетаскивать предметы в любую форму (выпуклый четырехугольник):
Бла-бла-бла-бла-бла-бла-бла-бла-бла
В итоге я ни для чего не использовал это, но надеюсь, что кому-то еще это покажется полезным!
В оставшейся части этого поста будет объяснено, как получить уравнение для преобразования, так как я помню, что тогда я не смог найти много об этом. Глядя на код, вы увидите, что основная логика состоит всего из нескольких строк для настройки и решения системы линейных уравнений. Теперь мы увидим, как вывести эту систему.
Допустим, у нас есть 4 угла элемента, который мы хотим преобразовать, \((x_i, y_i)\), где \(i \in {0, 1, 2, 3}\), и мы хотим сопоставить каждый \((x_i, y_i)\) к некоторому \((u_i, v_i)\). Согласно документам matrix3d, преобразование, которое мы хотим, представляет собой однородную матрицу, поэтому мы должны представлять каждую точку, используя однородные координаты. В однородных координатах точка \((x, y)\) представляется как \((k x, k y, k)\) для любого \(k \neq 0\). Например, \((3, 2, 1)\) и \((6, 4, 2)\) представляют точку \((3, 2)\).
Таким образом, матрица преобразования \(H\), которую мы хотим найти, должна удовлетворять
$$
\underbrace{
\begin{pmatrix}
h_0 & h_1 & h_2 \\
h_3 & h_4 & h_5 \\
h_6 & h_7 & h_8 \\
\end{pmatrix}
}_{H}
\begin{pmatrix}
x_i \\
y_i \\
1 \\
\end{pmatrix}
= k_i
\begin{ pmatrix}
u_i \\
v_i \\
1 \\
\end{pmatrix}
$$
для каждого \(i\), где известными являются \(x_i, y_i, u_i, v_i\).
Обратите внимание, что \(H\), удовлетворяющее этому требованию, не уникально. Например, вы можете масштабировать \(H\) на некоторую константу, и результирующая матрица по-прежнему будет правильно отображать точки (поскольку вы также можете масштабировать \(k_i\) на ту же величину и по-прежнему представлять ту же однородную точку). Таким образом, предполагая \(h_8 \neq 0\) (см. сноску [1] ), мы всегда должны иметь возможность масштабировать обе стороны до \(h_8 = 1\), что немного упростит нам задачу:
$$
\begin{pmatrix}
h_0 & h_1 & h_2 \\
h_3 & h_4 & h_5 \\
h_6 & h_7 & 1 \\
\end{pmatrix}
\begin{pmatrix}
x_i \\
y_i \\
1 \\
\ end{pmatrix}
= k_i
\begin{pmatrix}
u_i \\
v_i \\
1 \\
\end{pmatrix}
$$
Теперь мы должны попытаться привести его к виду, который мы можем решить . Умножая получаем:
$$
\begin{align*}
x_i h_0 + y_i h_1 + h_2 & = k_i u_i \\
x_i h_3 + y_i h_4 + h_5 & = k_i v_i \\
x_i h_6 + y_i h_7 + 1 & = k_i \\
\end{align*}
$$
От \(k_i\) можно избавиться, подставив его из третьего в первые два уравнения:
$ $
\begin{align*}
x_i h_0 + y_i h_1 + h_2 & = u_i x_i h_6 + u_i y_i h_7 + u_i \\
x_i h_3 + y_i h_4 + h_5 & = v_i x_i h_6 + v_i y_i h_7 + v_i \\
\end{align*}
$$
Помните, что мы пытаемся найти \(h_i\), поэтому мы должны попытаться разделить их:
$$
\begin{array}{rcccl}
x_i h_0 + y_i h_1 + h_2 & & — u_i x_i h_6 — u_i y_i h_7 = u_i \\
& x_i h_3 + y_i h_4 + h_5 & — v_i x_i h_6 — v_i y_i h_7 = v_i \\
\end{array}
$$
Что в матричной записи:
$$
\begin{pmatrix}
x_i & y_i & 1 & 0 & 0 & 0 & -u_i x_i & -u_i y_i \\
0 & 0 & 0 & x_i & y_i & 1 & -v_i x_i & -v_i y_i \\
\end{pmatrix}
\begin{pmatrix}
h_0 \\
h_1 \\
h_2 \\
h_3 \\
h_4 \\
h_5 \\
h_6 \\
h_7 \\
\end{pmatrix} = \begin{pmatrix}
u_i \\
v_i \\
\end{pmatrix}
$$
Поскольку у нас есть 4 таких отображения, мы можем записать их так:
$$
\begin{pmatrix}
x_0 & y_0 & 1 & 0 & 0 & 0 & -u_0 x_0 & -u_0 y_0 \ \
0 & 0 & 0 & x_0 & y_0 & 1 & -v_0 x_0 & -v_0 y_0 \\
x_1 & y_1 & 1 & 0 & 0 & 0 & -u_1 x_1 & -u_1 y_1 \\
0 & 0 & 0 & x_1 & y_1 & 1 & -v_1 x_1 & -v_1 y_1 \\
x_2 & y_2 & 1 & 0 & 0 & 0 & -u_2 x_2 & -u_2 y_2 \\
0 & 0 & 0 & x_2 & y_2 & 1 & -v_2 x_2 & -v_2 y_2 \\
x_3 & y_3 & 1 & 0 & 0 & 0 & -u_3 x_3 & -u_3 y_3 \\
0 & 0 & 0 & x_3 & y_3 & 1 & -v_3 x_3 & -v_3 y_3 \\
\end{pmatrix}
\begin{pmatrix}
h_0 \\
h_1 \\
h_2 \\
h_3 \
h_4 \
h_5 \
h_6 \
h_7 \
\ end{pmatrix} = \begin{pmatrix}
u_0 \
v_0 \
u_1 \
v_1 \ 901 \\
v_2 \\
u_3 \\
v_3 \\
\end{pmatrix}
$$
На этом мы закончили, потому что это в форме \(Ah = b\), так что мы можем просто бросить это в библиотека матричной алгебры для решения \(h\). Он должен выдать \(h_i\), что позволит нам восстановить желаемое преобразование:
$$
H =
\begin{pmatrix}
h_0 & h_1 & h_2 \\
h_3 & h_4 & h_5 \\
h_6 & h_7 & h_8 \\
\end{pmatrix}
$$
Последняя проблема заключается в том, что matrix3d фактически принимает матрица 4 на 4, а не 3 на 3. Поскольку нам не нужны значения \(z\) (поскольку все наши точки находятся в одной плоскости, \(z=0\)) мы можем просто сделать \( z\) отображать обратно на себя. Вот так:
\[
\begin{pmatrix}
h_0 & h_1 & 0 & h_2 \\
h_3 & h_4 & 0 & h_5 \\
0 & 0 & 1 & 0 \\
h_6 & h_7 & 0 & h_8 \\
\end{pmatrix}
\]
И это последняя матрица, которую вы используете для matrix3d. Не забудьте указать его в основном порядке столбцов, а также установить значение преобразования-происхождения для всего, что вы измеряли в своих точках.
Когда я впервые сделал это, я не знал, что искать в Google, поэтому мне пришлось выводить это вручную.