17

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

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

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

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

  1. 我从哪里得到矩阵,它将我的鼠标屏幕点转换为一个世界点,我可以在屏幕上看到绘制的圆圈中进行测试?
    在所有这些 GDI 对象中有很多矩阵可供选择。
  2. 不要给我关于绘制位图和在光标下测试魔术颜色的内容,这不是我想要的。
  3. 如果有一系列矩阵,我如何以正确的(反转?)顺序遍历它们,以便我的屏幕坐标被正确引导到世界坐标?

换句话说
,从 SVG 图像中读取的形状是被矩阵扭曲为屏幕坐标的图元。如何从屏幕坐标反转到可以用来查看我是否在形状内的坐标。

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

编辑

或者

  1. 我可以在屏幕坐标中获得每个形状的边界矩形,这样我就可以检查我的鼠标坐标。
  2. 我可以获得一个旧的 skool GDI 区域,我可以在屏幕坐标中执行 PtInRegion。

希望你能帮助我找到所有这些扭曲的路径:-)。

4

2 回答 2

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(angle) = R(-angle) 等等。但是,您必须推断出转换的参数,如果您只能访问转换矩阵,这可能并不容易。

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

我也猜想,所有的对象变换都是在世界坐标中完成的(这对我来说听起来比在屏幕坐标中这样做更方便)。因此,您可能会期望,每个对象的点都会经历以下变换:

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

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

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


2011 年 4 月 17 日更新

好的,我现在已经查看了代码。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上的一个包装函数。

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

于 2011-04-16T17:47:26.057 回答
-1

使用 GDI+,您必须跟踪您在做什么/已经做了什么,因为 GDI+ 本身会在绘制像素后忘记除像素本身之外的所有内容。SVG 单元必须跟踪绘制所有形状和设置 GDI+ 视口所需的一切。因此,从 SVG 库中获取您想要的信息会更容易。

于 2011-05-30T22:07:23.097 回答