dojox.gfx.Moveable конвертировать экранные координаты в мировые координаты для перетаскивания
ТЛ; 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
,
Я пытался использовать матричные преобразования, чтобы получить Screen 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
Я могу вручную взять значения шкалы x и y CTM и применить их к объекту сдвига: https://codepen.io/neekfenwick/pen/KZWapa
Как можно использовать матричные операции, такие как Matrix2D.invert()
а также Matrix2D.multiplyPoint()
взять систему координат SVG, создать матрицу преобразования для преобразования из экранных координат в мировые координаты и применить это к dx, dy, по которому двигалась мышь?
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 довольно трудной для понимания, потому что она не размещает матричные элементы в сетке, поэтому я подумал, b, c, d, e, f были просто размещены в сетке 3x3:
{ a, b, c,
d, e, f,
0, 0, 1 }
Однако часть "3D-эквивалента" в документах указывает, что это на самом деле какая-то матрица 4х4 (здесь я использую? Для неопределенного элемента, я не уверен, какими будут значения по умолчанию):
{ a, b, ?, ?,
c, d, ?, ?,
?, ?, ?, ?,
e, f, ?, ? }
Так что я все еще в замешательстве, но, по крайней мере, есть решение, использующее Matrix2D
операции.
Я также кратко попытался использовать чистые операции SVG для создания SVGPoint и преобразования его с помощью операций DOMMatrix, но матрица преобразования выходит со смещением 100 или 200, и я отказался от него: https://codepen.io/neekfenwick/pen/BJWEZw