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
onMouseMovefunction 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
aanddfrom theDOMMatrixpopulating only thexxandyyelements 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
DOMMatrixattributes, trying to use all elements to fill out thexy,yxetc elements of myMatrix2Dto 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
Matrix2Doperations.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