Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hittest using screen coordinates in SVG images in World coordinates

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:

  1. Where do I get the matrix from that will translate my screen-point of my mouse to a world point that I can hittest into the circle that I see drawn on the screen?
    There are soo many matrixes to choose from in all those GDI objects.
  2. Please don't give me stuff about drawing to a bitmap and testing for magic colors under the cursor, this is not what I'm looking for.
  3. If there is a chain of matrixes, how do I traverse them in the correct (inverted?) order so that my screen coordinate gets guided correctly to the world coordinate?

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

  1. Can I get a bounding rect per shape in screen coordinates so I can check my mouse coordinates against that.
  2. Can I get a old skool GDI region where I can do a PtInRegion with in screen coordinates.

Hope you can help me find my way with all these distorted paths :-).

like image 798
Johan Avatar asked Apr 12 '11 23:04

Johan


People also ask

What are coordinates on SVG?

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.


1 Answers

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 :)

like image 129
Spook Avatar answered Sep 21 '22 18:09

Spook