How do I translate mouse coordinates into world coordinates using GDI+? Or get bounding boxes (or even better) old skool regions for SVG shapes drawn using GDI+?
Anyway. I've been looking for SVG code and found:
http://development.mwcs.de/svgimage.html
This is the first Delphi component that actually works for SVG, but I digress.
This component uses GDI+ to display circles, curves etc.
GDI+ uses matrixes to convert world coordinates, rotations and distortions into screen coordinates.
This part I understand. You use matrix multiplication to do the translation.
The problem is this
If I point my mouse cursor over a closed shape:
In other words
The shapes that are read in from an SVG image are primitives that get distorted by matrixes into screen-coordinates.
How do I do the reverse from a screen coordinate into the coordinates that I can use to see if I'm inside a shape or not.
please note
I need to know which shape I am in.
Because of the way the SVG image is set up, each shape has an id, and I want to use that to see what region I have hit with my mouse.
EDIT
Alternatively
Hope you can help me find my way with all these distorted paths :-).
For all elements, SVG uses a coordinate system or grid system similar to the one used by canvas (and by a whole lot of other computer drawing routines). That is, the top left corner of the document is considered to be the point (0,0), or point of origin.
I didn't dug into code, but I can help a little with matrices (point 3).
I guess, that the three basic transformation matrices are used: the rotation, scale and translation matrix. Let's call them R, S and T, respectively.
There's a tricky part about applying matrices to the point. Say, you want to translate the point, and then rotate around the center of origin. In other words, you want to apply the rotation to the effect of the translation of the point. So, matrices will be applied in the following way:
R(T(P)) = R * T * P = S
Where * is the matrix multiplication. Note, that the order of multiplied matrices is reversed in relation to your intent.
However, if you want to make the inverse transformation, apart from reversing the order of matrices, you also have to evaluate their inverses. We translated the point, then rotated - so now we shall rotate it back and then translate back:
T^-1 ( R^-1 (S)) = T^-1 * R^-1 * S = P
Please note, that you don't have to calculate each matrice's inverse, as obviously T^-1(x) = T(-x), R^-1(angle) = R(-angle) and so on. You would have to deduce the transformation's argument, however, which may not be easy if you have access only to the transformation matrix.
I'd guess, that world coordinates are converted to screen coordinates by a combination of translate and scale matrix. The last one is responsible for "changing the unit" from world coordinates to pixels in respect to zoom factor of the whole scene (and, possibly, DPI of the display). The translation matrix, on the other hand, reflects the scene panning and may be applied either before or after the scale matrix; in the first case the panning is stored in world coordinates, in the second - panning is stored in screen coordinates.
I would also guess, that all the object transformations are being done in the world coordinates (it sounds more convenient to me than doing so in screen coordinates). So, you may expect, that each object's point is subjected to following transformation:
W(S(R(T(P)))) = W * S * R * T * P,
where W is the World-to-screen transformation, S is scale, R is rotation and T is translation.
Hope I helped at least a little...
Updated 17-04-2011
Ok, I've looked inside the code now. The PaintTo method of SVG object looks like this:
procedure TSVG.PaintTo(Graphics: TGPGraphics; Bounds: TGPRectF;
Rects: PRectArray; RectCount: Integer);
var
M: TGPMatrix;
MA: TMatrixArray;
begin
M := TGPMatrix.Create;
try
Graphics.GetTransform(M);
try
M.GetElements(MA);
FInitialMatrix.Cells[0, 0] := MA[0];
FInitialMatrix.Cells[0, 1] := MA[1];
FInitialMatrix.Cells[1, 0] := MA[2];
FInitialMatrix.Cells[1, 1] := MA[3];
FInitialMatrix.Cells[2, 0] := MA[4];
FInitialMatrix.Cells[2, 1] := MA[5];
FInitialMatrix.Cells[2, 2] := 1;
SetBounds(Bounds);
Paint(Graphics, Rects, RectCount);
finally
Graphics.SetTransform(M);
end;
finally
M.Free;
end;
end;
Prior to any drawing, the method calls Graphics.GetTransform(M). This one, in turn, calls GdipGetWorldTransform, which appears to be a wrapper function on WinAPI's GetWorldTransform.
I guess, that it might be a good place to start :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With