【问题标题】:How do I define an area and check if the mouse is in it?如何定义一个区域并检查鼠标是否在其中?
【发布时间】:2021-11-13 11:07:32
【问题描述】:

我正在尝试定义一个三角形区域并检查鼠标是否在其中。我可以使用下面的代码找到鼠标是否在某个正方形区域中。我的程序需要检测三角形或更复杂形状的鼠标。

if (Mouse.CursorPos.X < 20) or (50 > tbmn.Left + tbmn.Width) or (Mouse.CursorPos.Y < 20) or (Mouse.CursorPos.Y > tbmn.Top + 60) then 
begin

end;

所以基本上,我想要做的是在屏幕上的任何地方都有一个形状并检查鼠标是否在其中。

有没有一种方法可以轻松计算屏幕区域并检测其中是否存在鼠标?

【问题讨论】:

  • 有多种方法可以实现这一点。你的最终目标是什么?也许您的最终目标可以通过完全不同的方法来实现。
  • @SilverWarior 我有一个分成 8 块的圆圈。每一块都是我点击的按钮。我想知道我的鼠标在哪个按钮上方。按钮是 8 个三角形。
  • 如果你只需要确定一个点是否在三角形内,你当然可以天真地做,就像我在这里做的那样:stackoverflow.com/a/7224075/282848GetTriangleAt 函数)。
  • 在 GDI 的上下文中,你有 PtInRegion 函数。

标签: delphi


【解决方案1】:

假设您有一个组件,您在其中绘制了一个三角形,并且只想让该组件在光标位于形状的可见部分上方时检测鼠标点击,那么您可以执行以下操作:

在被绘制的组件上有一个 alpha 层。然后拦截 Windows CM_HITTEST 消息。然后在命中测试消息过程中检查 alpha 值是否为 0。如果为 0,则鼠标位于具有某些可见颜色值的区域上。

Type  
  TSomeComponent = class(TGraphicControl)
  private
    FPNG : TGraphic;
    procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; Override;
  end;

procedure Register;

implementation
uses
  GR32,  GR32_Brushes,Winapi.Windows;   

procedure TSomeComponent.CMHitTest(var Message: TCMHitTest);
var
  colorEntry: TColor32Entry;
  bmp : TBitmap32;
begin
  bmp := TBitmap32.Create();
  try
    try
      bmp.Assign(FPNG);
      colorEntry := TColor32Entry(bmp.Pixels[Message.XPos,Message.YPos]);

      if colorEntry.A <> 0 then
           Message.Result := HTCLIENT
      else
           Message.Result := HTNOWHERE;
    except
      Message.Result := HTCLIENT;
    end;
  finally
    bmp.Free;
  end;
end;

【讨论】:

    【解决方案2】:

    您可以使用 WinApi 中的region functions。 以下是简单三角形的示例:

    function PtInTriangle(ptX,ptY,X1,Y1,X2,Y2,X3,Y3:integer):Boolean;
    var rgn:THandle; pts:array [0..2] of TPoint;
    begin
      pts[0].X:=X1; pts[0].Y:=Y1;
      pts[1].X:=X2; pts[1].Y:=Y2;
      pts[2].X:=X3; pts[2].Y:=Y3;
      rgn := CreatePolygonRgn( pts[0], 3, WINDING);
      Result := PtInRegion(rgn, ptX, ptY);
      DeleteObject(rgn);
    end;
    

    这个函数在我的机器上大约需要大约 30..40us,而 PtInRegion() 只需要大约 10% 的时间(所以,你可以通过缓存 Region 来优化它em> 对象)。以下是带有简单基准的代码:

    function PtInTriangle(ptX,ptY,X1,Y1,X2,Y2,X3,Y3:integer):Boolean;
    var rgn:THandle; pts:array [0..2] of TPoint;
        t,t1,t2,t3:Int64;
    begin
      // Create region
      QueryPerformanceCounter(t);
        pts[0].X:=X1; pts[0].Y:=Y1;
        pts[1].X:=X2; pts[1].Y:=Y2;
        pts[2].X:=X3; pts[2].Y:=Y3;
        rgn := CreatePolygonRgn( pts[0], 3, WINDING);
      QueryPerformanceCounter(t1); Dec(t1,t);
      // Check point
      QueryPerformanceCounter(t);
        Result := PtInRegion(rgn, ptX, ptY);
      QueryPerformanceCounter(t2); Dec(t2,t);
      // Delete region
      QueryPerformanceCounter(t);
        DeleteObject(rgn);
      QueryPerformanceCounter(t3); Dec(t3,t);
      // Debug output
      QueryPerformanceFrequency(t);
      OutputDebugString(PChar(Format('All:%d(%.1fus)  Create:%d  PtInRect:%d(%.1f%%)  Delete:%d',
        [t1+t2+t3,(t1+t2+t3)/t*1E6,t1,t2,t2*100/(t1+t2+t3),t3])));
    end;
    

    此外,您可以使用CreatePolyPolygonRgn()CombineRgn() 创建复杂区域。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-10-17
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-05
      相关资源
      最近更新 更多