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?