извлекать вращение, масштабировать значения из двумерной матрицы преобразования - PullRequest
21 голосов
/ 06 декабря 2010

как я могу извлечь значения поворота, масштаба и перемещения из матрицы преобразования 2d? я имею в виду 2d преобразование

matrix = [1, 0, 0, 1, 0, 0]

matrix.rotate(45 / 180 * PI)
matrix.scale(3, 4)
matrix.translate(50, 100)
matrix.rotate(30 / 180 * PI)
matrix.scale(-2, 4)

теперь моя матрица имеет значения [a, b, c, d, tx, ty]

давайте забудем о вышеописанных процессах и представим, что у нас есть только значения a, b, c, d, tx, ty

как найти значения поворота и шкалы с помощью a, b, c, d, tx, ty

извините за мой английский

Спасибо за ваше продвижение

EDIT

Я думаю, что это должен быть ответ где-то ...

Я только что попробовал во Flash Builder (AS3) вот так

   var m:Matrix = new Matrix;
   m.rotate(.25 * Math.PI);
   m.scale(4, 5);
   m.translate(100, 50);
   m.rotate(.33 * Math.PI);
   m.scale(-3, 2.5);

   var shape:Shape = new Shape;
   shape.transform.matrix = m;

   trace(shape.x, shape.y, shape.scaleX, shape.scaleY, shape.rotation);

и вывод:

x = -23.6 
y = 278.8 
scaleX = 11.627334873920528 
scaleY = -13.54222263865791 
rotation = 65.56274134518259 (in degrees)

Ответы [ 4 ]

40 голосов
/ 06 декабря 2010

Не все значения a, b, c, d, tx, ty приведут к правильной последовательности вращения. Я предполагаю, что приведенные выше значения являются частью матрицы однородного вращения 3x3 в 2D

    | a  b  tx |
A = | c  d  ty |
    | 0  0  1  |

, который преобразует координаты [x, y, 1] в:

[x', y', 1] = A * |x|
                  |y|
                  |z|
  • Таким образом установите перевод в [dx, dy]=[tx, ty]
  • Масштаб sx = sqrt(a² + c²) и sy = sqrt(b² + d²)
  • Угол поворота t = atan(c/d) или t = atan(-b/a), также они должны быть одинаковыми.

В противном случае у вас нет действительной матрицы вращения.


Вышеуказанное преобразование расширено до:

x' = tx + sx (x Cos θ - y Sin θ)
y' = ty + sy (x Sin θ + y Cos θ)

когда ордер вращается, за ним следует масштаб, а затем перевод.

6 голосов
/ 09 марта 2012

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

Это работает, только если x и y всегда масштабируются одинаково (равномерное масштабирование).

Учитывая вашу матрицу m, которая подверглась серии преобразований,

var translate:Point;
var rotate:Number;
var scale:Number;

// extract translation
var p:Point = new Point();
translate = m.transformPoint(p);
m.translate( -translate.x, -translate.y);

// extract (uniform) scale
p.x = 1.0;
p.y = 0.0;
p = m.transformPoint(p);
scale = p.length;

// and rotation
rotate = Math.atan2(p.y, p.x);

Вот, пожалуйста!

4 голосов
/ 06 декабря 2010

Если при масштабировании вы бы масштабировали на одну и ту же величину по x и y, то определитель матрицы, то есть ad-bc, который говорит вам, что множитель площади также сообщит вам о линейном изменении масштаба - быть квадратный корень из определителя. atan (b / a) или лучше atan2 (b, a) скажет вам полный угол, на который вы повернули.

Однако, поскольку ваше масштабирование не является равномерным, обычно не будет способа уплотнить вашу серию вращений и масштабирование до одного поворота, за которым следует одно неравномерное масштабирование по x и y.

2 голосов
/ 19 августа 2018

Термин для этого матричная декомпозиция .Вот решение, которое включает перекос , как описано Фредерик Ван .

function decompose_2d_matrix(mat) {
  var a = mat[0];
  var b = mat[1];
  var c = mat[2];
  var d = mat[3];
  var e = mat[4];
  var f = mat[5];

  var delta = a * d - b * c;

  let result = {
    translation: [e, f],
    rotation: 0,
    scale: [0, 0],
    skew: [0, 0],
  };

  // Apply the QR-like decomposition.
  if (a != 0 || b != 0) {
    var r = Math.sqrt(a * a + b * b);
    result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
    result.scale = [r, delta / r];
    result.skew = [Math.atan((a * c + b * d) / (r * r)), 0];
  } else if (c != 0 || d != 0) {
    var s = Math.sqrt(c * c + d * d);
    result.rotation =
      Math.PI / 2 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
    result.scale = [delta / s, s];
    result.skew = [0, Math.atan((a * c + b * d) / (s * s))];
  } else {
    // a = b = c = d = 0
  }

  return result;
}
...