Декомпозиция матрицы.
Для более полного понимания статьи желательно почитать раз, два или что-нибудь по линейной алгебре.
Анатомия
По сути своей матрица трансформации содержит в себе три вектора, базисные векторы осей, описывающие наклон и масштабирование осей дочернего объекта относительно родительского и вектор описывающий смещение начала системы координат дочернего объекта относительно родительского.
Если принять во внимание, что базисный вектор оси x обычно обозначается буквой i, а базисный вектор оси y обозначается как j, то матрица трансформации принимает такой вид:
Код:
i.x i.y t.x j.x j.y t.y 0 0 1
Извлечение
Я буду приводить по четыре варианта, мои изыскания (соответствуют поведению FP от Adobe, с некоторыми оговорками, которые я опишу ниже) и реализации в lightspark, shumway и Starling.
rotation
Начнем со свойства rotation (с ним меньше всего проблем)
private function getRotation(m:Matrix):Number { return (getSkewY(m) * 180) / Math.PI; } private function getSkewY(m:Matrix):Number { return Math.atan2(m.b, m.a); }
Код:
number_t getRotation() const { return atan(yx/xx)*180/M_PI; }
DisplayObject
Код:
this._rotation = DisplayObject._clampRotation(this._skewY * 180 / Math.PI);
Код:
public getSkewY(): number { return Math.atan2(this._data[1], this._data[0]); }
mSkewX = Math.atan(-matrix.c / matrix.d); mSkewY = Math.atan( matrix.b / matrix.a); if (isEquivalent(mSkewX, mSkewY)) { mRotation = mSkewX; mSkewX = mSkewY = 0; } else { mRotation = 0; }
scaleX / scaleY
Определить величину масштабирования проблемы не составляет (
private function getScaleX(m:Matrix):Number { return Math.sqrt(m.a * m.a + m.b * m.b); } private function getScaleY(m:Matrix):Number { var res:Number = Math.sqrt(m.d * m.d + m.c * m.c); return (m.d > 0) && (m.a < 0) || (m.d < 0) && (m.a > 0) ? -res : res; }
Код:
scaleX = -1 scaleY = 1 rotation = 0 scaleX = 1 scaleY = -1 rotation = 180 (a=-1, b=0, c=0, d=1, tx=0, ty=0)
Код:
scaleX = -1 scaleY = -1 rotation = 0 scaleX = 1 scaleY = 1 rotation = 180 (a=-1, b=0, c=0, d=-1, tx=0, ty=0)
Таким образом, становится достаточно проверить свойства a и d, чтобы сделать выводы о направлении масштабирования по оси y. Если a и d имеют одинаковый знак, то scaleY положительный, если разный — отрицательный.
lightspark
Код:
number_t getScaleX() const { number_t ret=sqrt(xx*xx + yx*yx); if(xx>0) return ret; else return -ret; } number_t getScaleY() const { number_t ret=sqrt(yy*yy + xy*xy); if(yy>0) return ret; else return -ret; }
shumway
Код:
getDeterminant() { var m = this._data; return m[0] * m[3] - m[1] * m[2]; } getScaleX(): number { var m = this._data; if (m[0] === 1 && m[1] === 0) { return 1; } var result = Math.sqrt(m[0] * m[0] + m[1] * m[1]); return this.getDeterminant() < 0 ? -result : result; } getScaleY(): number { var m = this._data; if (m[2] === 0 && m[3] === 1) { return 1; } var result = Math.sqrt(m[2] * m[2] + m[3] * m[3]); return this.getDeterminant() < 0 ? -result : result; }
Starling
const PI_Q:Number = Math.PI / 4.0; mScaleY = (mSkewX > -PI_Q && mSkewX < PI_Q) ? matrix.d / Math.cos(mSkewX) : -matrix.c / Math.sin(mSkewX); mScaleX = (mSkewY > -PI_Q && mSkewY < PI_Q) ? matrix.a / Math.cos(mSkewY) : matrix.b / Math.sin(mSkewY);
Хотя прогресс по сравнению с состоянием до коммита от 16 января налицо.
Дополнительные свойства
Если в дополнение к skewY (наклон оси X) посчитать skewX (наколон оси Y), то можно дополнительно узнать были ли оси наклонены независимо друг от друга.
private function getSkewY(m:Matrix):Number { return Math.atan2(m.b, m.a); } private function getSkewX(m:Matrix):Number { return Math.atan2(-m.c, m.d); } private function isEquivalent(a:Number, b:Number, epsilon:Number=0.0001):Boolean { return (a - epsilon < b) && (a + epsilon > b); } ... var skewY:Number = getSkewY(m); var skewX:Number = getSkewX(m); var diff:Number = skewY - skewX; if (isEquivalent(diff, 0)) { //simple rotate } else if (isEquivalent(diff, Math.PI) || isEquivalent(diff, -Math.PI)) { //rotate and flip; } else { //skew; }
Всего комментариев 24
Комментарии
25.12.2014 16:32 | |
Кратенько, не мог бы ты резюмировать в чем Даниэлька был неправ?
|
25.12.2014 17:14 | |
Еще страннее, что никто с момента существования двигла не обратил на это внимание. До сегодняшнего дня.
Цитата:
skewY соответствует оси X, а skewX — оси Y
|
28.12.2014 12:15 | |
Цитата:
а о коэффициенте в линейном уравнении, который указывает на смещение относительно оси абсцисс.
|
29.12.2014 11:54 | |
@alatar, я, собтсвенно, о том, что нет необходимости вычислять что-то (например, вектор поворота из матрицы), можно просто хранить в одной структуре два представления.
|
12.01.2015 11:41 | |
Прошу прощение за невежество, но где это все применяется-то?
Примерчик бы или картиночку. |
12.01.2015 16:47 | |
nubideus, еще shumway и lightspark забыли. Но, вообще-то, статья не об "обокласть".
|
06.02.2015 14:08 | |
Ну, ещё про кватернионы "забыли", помимо лайтспарка =)
Тема интересная, спасибо. |
06.02.2015 15:02 | |
Тут просто конкретно о 2D матрице, потому и про кватернионы ни слова.
|
06.02.2015 17:00 | |
Я было подумал, что тема разовьётся
|
08.02.2015 13:40 | |
Вопрос куда её развивать? По 3D написана не одна тонна статей, если есть интересная проблема, то подскажите.
|
19.02.2015 23:18 | |
Статья реально крутая. Что-то из разряда "вот! вот как оно работает!" Но вот вопрос. Полез в гугл и... А на каком этапе мы смогли сделать декомпозицию матрицы?
|
20.02.2015 00:18 | |
Согласен. заголовок получился не очень корректным с математической точки зрения. Я был бы благодарен за предложенное, более корректное названия.
|
20.02.2015 00:21 | |
Ну а по поводу "крутости" для последнего параграфа есть более простой, с точки зрения вычислительной сложности, способ.
|
26.03.2015 14:47 | |
Очень полезная статья, я использовал способ "декомпозиции", которым пользуется flash runtime. Спасибо, alatar.
|
27.03.2015 18:12 | |
А Даниэль извещен об этом?
|
29.03.2015 15:24 | |
Я не извещал, я пока не использую Starling и мне пока безразлично, что там есть, а чего нет. Просто его код подруку попался. Да и времени, все это оформить в pull request нет.
|
Последние записи от alatar
- Трансформации вокруг произвольной точки (05.02.2015)
- Декомпозиция матрицы. (25.12.2014)
- Баги TextField в iOS (29.07.2013)
- [Spark] Стрелочный индикатор. (06.12.2012)
- RSLs Monkey Patching (23.01.2012)