dojox.gfx.Moveable конвертирует координаты экрана в мировые координаты для перетаскивания

TL; dr Как бы я использовал CTM, возвращаемый:

var ctm = canvas.rawNode.getScreenCTM();

изменить вектор dx, dy в экранных координатах, чтобы он был в мировых координатах? то есть { dx: 1, dy: 0} должно стать { dx: 3.6, dy: 0}, учитывая приведенный выше пример SVG с viewBox 0 0 1800 1800 в окне шириной 500 пикселей.

Я думал, что следующее будет работать:

  var ctm = canvas.rawNode.getScreenCTM();
  // Turn this into a dojox/gfx/matrix Matrix2D
  var ctmm = new matrix.Matrix2D({xx: ctm.a, xy: ctm.b, dx: ctm.c,
                                  yx: ctm.d, yy: ctm.e, dy: ctm.f});
  // Invert this
  var itm = matrix.invert(ctmm);
  // Multiply screen coords by transform matrix
  var worldshift = matrix.multiplyPoint(itm, shift.dx, shift.dy);
  console.log('ctm ', ctm, ', shift ', shift, ' became worldshift ', worldshift);
  shift.dx = worldshift.x;
  shift.dy = worldshift.y;

Но itm выходит полным NaN и Infinity.

длинная версия вопроса следует с примерами CodePen

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

  • Узел SVG имеет viewBox, определяющий мировые координаты, скажем, от 0,0 до 1800,1800.
  • Узел SVG находится в документе, который масштабируется по размеру окна, шириной около 500 пикселей. Таким образом, мировые координаты в SVG (1800 x 1800 единиц) не сопоставляются 1: 1 с координатами экрана. Каждый пиксель составляет 1800/500 = 3,6 мира. единицы
  • dojox/gfx/Moveable использует dojox/gfx/Mover, чья функция onMouseMove передает величину перемещения в экранных координатах:

    this.host.onMove(this, {dx: x - this.lastX, dy: y - this.lastY});

Этот последний аргумент передается в dojox/gfx/Moveable.onMoving как аргумент shift и может быть, например. { dx: 1, dy: 0 } если мышь переместилась вправо на пиксель.

Если мы тупо позволим фреймворку применить это к преобразованию перемещения перетаскиваемой фигуры, ее положение не будет точно соответствовать координатам мыши: https://codepen.io/neekfenwick/pen/RxpoMq (это прекрасно работает в демонстрациях dojox, потому что их система координат SVG совпадает с системой координат экрана 1:1).

Я нашел вдохновение на http://grokbase.com/t/dojo/dojo-interest/08anymq4t9/gfx-constrainedmoveable, где Юджин говорит: «Переопределите onMoving для вашего объекта и измените объект «shift», чтобы он никогда не перемещал фигуру за пределы указанных границ». изменить объект shift, поэтому мои следующие попытки объявляют новый тип dojox/gfx/Moveable и переопределяют onMoving.

Я попытался использовать матричные преобразования, чтобы получить экран CTM SVG (который входит в объект { a, b, c, d, e, f }) и использовать его как dojox/gfx Matrix2D ({ xx, xy, dx, yx, yy, dy }), используя прямые матричные операции. Цель состоит в том, чтобы изменить объект shift, чтобы преобразовать единицы экрана в мировые единицы, прежде чем он будет использоваться в матрице преобразования формы, но я очень запутался. Для начала кажется, что CTM имеет большой dy около 50, что сразу же заставляет форму вылетать из нижней части экрана. Вот моя последняя очень грязная и неудачная попытка: https://codepen.io/neekfenwick/pen/EoWNOb

Я могу вручную взять значения шкалы CTM x и y и применить их к объекту сдвига: https://codepen.io/neekfenwick/pen/KZWapa

Как можно использовать матричные операции, такие как Matrix2D.invert() и Matrix2D.multiplyPoint(), чтобы взять систему координат SVG, произвести матрицу преобразования для преобразования экранных координат в мировые координаты и применить это к dx,dy, по которым перемещалась мышь?


person Neek    schedule 28.12.2017    source источник


Ответы (1)


Я получил рабочий пример, взяв только a и d из DOMMatrix, заполнив только элементы xx и yy Matrix2D: https://codepen.io/neekfenwick/pen/mpWgrO

 var MyMoveable = declare(Moveable, {
    onMoving: function(/* dojox/gfx/Mover */ mover, /* Object */ shift){
      // console.log('mover ', mover, ' shift ', shift);
      // shift comes in screen coordinates
      // Get the Screen Coordinate Transform Matrix
      var ctm = canvas.rawNode.getScreenCTM();
      // Turn this into a dojox/gfx/matrix Matrix2D
      var ctmm = new matrix.Matrix2D({xx: ctm.a, yy: ctm.d});
      // Invert this
      var itm = matrix.invert(ctmm);
      // Multiply screen coords by transform matrix
      var worldshift = matrix.multiplyPoint(itm, shift.dx, shift.dy);
      console.log('ctm ', ctm, ', shift ', shift, ' became worldshift ', worldshift);
      // Replace the shift dx,dy vector with the transformed coordinates
      shift.dx = worldshift.x;
      shift.dy = worldshift.y;
    }
  });

При этом красный круг перемещается синхронно с курсором мыши.

Похоже, я переусердствовал с использованием атрибутов DOMMatrix, пытаясь использовать все элементы для заполнения элементов xy, yx и т. д. моего Matrix2D, чтобы использовать их в качестве основы матрицы преобразования для вектора движения мыши. Я нашел документацию https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix довольно сложно понять, потому что он не размещает элементы матрицы в сетке, поэтому я думал, что a, b, c, d, e, f просто уложены в сетке 3x3:

{ a, b, c,
  d, e, f,
  0, 0, 1 }

Однако часть документов «3D-эквивалент» указывает, что на самом деле это какая-то матрица 4x4 (здесь я использую? для неуказанного элемента, я не уверен, какие будут значения по умолчанию):

{ a, b, ?, ?,
  c, d, ?, ?,
  ?, ?, ?, ?,
  e, f, ?, ? }

Так что я все еще довольно запутался, но, по крайней мере, у меня есть решение, использующее Matrix2D операций.

Я также пытался кратко использовать чистые операции SVG для создания SVGPoint и преобразования его с помощью операций DOMMatrix, но матрица преобразования выходит со смещениями 100 или 200, и я отказался от нее: https://codepen.io/neekfenwick/pen/BJWEZw

person Neek    schedule 29.12.2017