Hittest使用世界坐标中的SVG图像中的屏幕坐标

时间:2011-04-12 23:05:21

标签: delphi svg gdi+ hittest

如何使用GDI +将鼠标坐标转换为世界坐标? 或者为使用GDI +绘制的SVG形状获得边界框(甚至更好)旧的skool区域?

反正。我一直在寻找SVG代码,发现:
http://development.mwcs.de/svgimage.html
这是第一个真正适用于SVG的Delphi组件,但我离题了。

该组件使用GDI +显示圆圈,曲线等 GDI +使用矩阵将世界坐标,旋转和扭曲转换为屏幕坐标 这一部分我明白了。您可以使用矩阵乘法进行翻译。

问题是这个
如果我将鼠标光标指向一个封闭的形状:

  1. 从哪里获取矩阵将我的鼠标屏幕点转换为一个世界点,我可以在屏幕上绘制的圆圈中找到它?
    在所有这些GDI对象中都有很多矩阵可供选择。
  2. 不要给我关于绘制到位图并测试光标下的魔法颜色的东西,这不是我想要的。
  3. 如果有一系列矩阵,我如何以正确的(倒置?)顺序遍历它们,以便我的屏幕坐标被正确引导到世界坐标?
  4. 换句话说
    从SVG图像读入的形状是基元被矩阵扭曲成屏幕坐标的基元。 如何从屏幕坐标反向到我可以用来查看我是否在形状内的坐标。

    请注意 我需要知道我的形状。
    由于SVG图像的设置方式,每个形状都有一个id,我想用它来查看我用鼠标击中的区域。

    修改

    替代地

    1. 我可以在屏幕坐标中获得每个形状的边界矩形,这样我就可以检查我的鼠标坐标。
    2. 我可以获得一个旧的skool GDI区域,我可以在屏幕坐标中进行PtInRegion。
    3. 希望你能帮我找到所有这些扭曲的路径: - )。

2 个答案:

答案 0 :(得分:7)

我没有挖掘代码,但我可以用矩阵帮助一点(第3点)。

我想,使用了三个基本的变换矩阵:旋转,缩放和平移矩阵。我们分别称它们为R,S和T.

将矩阵应用到关键点上有一个棘手的部分。比如说,您想要平移点,然后围绕原点中心旋转。换句话说,您希望将旋转应用于点的平移效果。因此,矩阵将以下列方式应用:

R(T(P))= R * T * P = S

其中*是矩阵乘法。请注意,乘法矩阵的顺序与您的意图相反。

但是,如果要进行逆变换,除了颠倒矩阵的顺序之外,还必须评估它们的逆。我们翻译了点然后旋转 - 所以现在我们将它旋转回来然后翻译回来:

T ^ -1(R ^ -1(S))= T ^ -1 * R ^ -1 * S = P

请注意,您不必计算每个矩阵的逆矩阵,显然T ^ -1(x)= T(-x),R ^ -1(角度)= R(-angle)等等。但是,您必须推导出变换的参数,如果您只能访问变换矩阵,这可能并不容易。

我猜,通过平移和缩放矩阵的组合将世界坐标转换为屏幕坐标。最后一个负责将整个场景的缩放因子(以及可能的显示器的DPI)从世界坐标“改变单位”到像素。另一方面,平移矩阵反映了场景平移,并且可以在比例矩阵之前或之后应用;在第一种情况下,平移存储在世界坐标中,在第二种情况下 - 平移存储在屏幕坐标中。

我还猜测,所有的物体变换都是在世界坐标中完成的(这听起来比在屏幕坐标中这样做更方便)。因此,您可以预期,每个对象的点都会受到以下转换:

W(S(R(T(P))))= W * S * R * T * P,

其中W是世界到屏幕的变换,S是比例,R是旋转,T是翻译。

希望我帮助至少一点......


更新于17-04-2011

好的,我现在已经查看了代码。 SVG对象的PaintTo方法如下所示:

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;

在任何绘图之前,该方法调用Graphics.GetTransform(M)。反过来,这个调用GdipGetWorldTransform,它似乎是WinAPI的GetWorldTransform上的包装函数。

我想,这可能是一个好的起点:)

答案 1 :(得分:-1)

使用GDI +你必须跟踪你在做什么/做了什么,因为GDI +本身会在绘制像素后忘记像素本身。 SVG单元必须保持绘制所有形状所需的所有内容并设置GDI +视口。因此,从SVG库中获取信息会更容易。