Hittest using screen coordinates in SVG images in World coordinates
Asked Answered
C

2

17

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

Carmelcarmela answered 12/4, 2011 at 23:5 Comment(3)
Are you basically asking how to translate the literal Screen position of the Cursor (X, Y) to a RELATIVE position of the cursor (also X, Y) within your SVG display object?Jambeau
My first program using Delphi 1, in 1996, was a software to calculate area in hectare and draw polygons (azimuth + distance). I have created some code that worked then, translating word coordinates to screen, using calls like SetMapMode(), SetWindow(Org|Ext), SetViewPort(Org|Ext). GDI+ use this same calls?Hurricane
Nope, GDI+ uses Matrixes to translate. Matrixes do rotation and sheer (new) as well as translating and scaling (old).Carmelcarmela
T
7

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

Tops answered 16/4, 2011 at 17:47 Comment(2)
I have the functions to do the matrix transformations, that's not the problem. I just don't know where to get the matrixes (multiple) from that the world -> screen transtransformation used. I don't know the order in which the original transformations where done. Therefore I cannot create the inverse matrix(es) to to the reverse transformation.Carmelcarmela
Added some more informations. Although still you cannot retrieve the single transformation matrices, you can invert the final world transformation matrix and apply it to the screen point. I believe, that it's worth a try.Tops
A
-1

With GDI+ you have to keep track of what your doing / have done as GDI+ itself forgets everything but the pixels themselves after the pixels are drawn. The SVG unit must be keeping tack of everything needed to draw all the shapes and set the GDI+ viewport. So it would be easier to get the information your after from the SVG library.

Alkane answered 30/5, 2011 at 22:7 Comment(2)
Transforms aren't just used for the world to viewport mapping. They can be passed in with various drawing primitives as well. Even a pen has a transform property.Alkane
Responding to your answer, I know that the question is how?Carmelcarmela

© 2022 - 2024 — McMap. All rights reserved.