tl; dr How would I use the CTM returned by:
var ctm = canvas.rawNode.getScreenCTM();
to modify a dx, dy
vector in screen coordinates, such that it is in world coordinates? i.e. { dx: 1, dy: 0}
should become { dx: 3.6, dy: 0}
given the above example of an SVG with viewBox 0 0 1800 1800
in a window 500px wide.
I thought the following would work:
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;
But itm
comes out full of NaN and Infinity.
long version of question follows with CodePen samples
I know the basic maths behind this but found myself stumped trying to do it with matrix transformations. The documentation seems to avoid this subject. The situation is:
- SVG node has viewBox definiting world coordinates, say 0,0 to 1800,1800
- SVG node is in a document that scales its to the window size, about 500px wide So world coords in the SVG (1800 x 1800 units) do not map 1:1 to screen coords.. each pixel across is 1800/500 = 3.6 world units
dojox/gfx/Moveable uses dojox/gfx/Mover whose
onMouseMove
function passes the amount it moved by in screen coordinates:this.host.onMove(this, {dx: x - this.lastX, dy: y - this.lastY});
That last argument is passed into dojox/gfx/Moveable.onMoving
as the shift
argument and might be e.g. { dx: 1, dy: 0 } if the mouse moved right by a pixel.
If we dumbly allow the framework to apply this to the translation transform of the shape being dragged, its position does not exactly match the mouse coordinates: https://codepen.io/neekfenwick/pen/RxpoMq (this works fine in the dojox demos because their SVG coordinate system matches the screen coordinate system 1:1).
I found some inspiration at http://grokbase.com/t/dojo/dojo-interest/08anymq4t9/gfx-constrainedmoveable where Eugene says "override onMoving on your object and modify the "shift" object so it never moves a shape outside of a specified boundaries.", this seems a good point to modify the shift
object, so my next attempts declare a new type of dojox/gfx/Moveable
and override onMoving
.
I have tried using matrix transformations to get the Screen CTM of the SVG (which comes in an object of { a, b, c, d, e, f }
) and use it as a dojox/gfx
Matrix2D
({ xx, xy, dx, yx, yy, dy }
) using straight matrix operations. The aim is to modify the shift
object to convert screen units to world units before it is used on the shape's transformation matrix, but found myself very confused. For a start the CTM seems to have a large dy of about 50 which immediately makes the shape shoot off the bottom of the screen. Here's my latest very messy and broken attempt: https://codepen.io/neekfenwick/pen/EoWNOb
I can manually take the CTM's x and y scale values and apply them to the shift object: https://codepen.io/neekfenwick/pen/KZWapa
How would one use matrix operations such as Matrix2D.invert()
and Matrix2D.multiplyPoint()
to take the coordinate system of the SVG, product a transformation matrix to convert from screen coordinates to world coordinates, and apply that to the dx,dy that the mouse moved by?
I got a working example, by taking only
a
andd
from theDOMMatrix
populating only thexx
andyy
elements of theMatrix2D
: https://codepen.io/neekfenwick/pen/mpWgrOWith this, the red circle moves in sync with the mouse cursor.
Looks like I was overzealous in using the
DOMMatrix
attributes, trying to use all elements to fill out thexy
,yx
etc elements of myMatrix2D
to use as the basis of the transformation matrix to use on the mouse movement vector. I've found the documentation https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix rather difficult to follow, because it doesn't lay out the matrix elements in a grid so I thought a, b, c, d, e, f were simply laid out in a 3x3 grid:However the '3D Equivalent' part of the docs indicate it's actually some kind of 4x4 matrix (here I use ? for an unspecified element, I'm not sure what the defaults would be):
So I'm still rather confused, but do at least have a solution using
Matrix2D
operations.I also tried briefly to use pure SVG operations to create a SVGPoint and transform it using DOMMatrix operations, but the transformation matrix comes out with offsets of 100 or 200 and I've given up on it: https://codepen.io/neekfenwick/pen/BJWEZw