【问题标题】:Hit-testing line segments命中测试线段
【发布时间】:2013-10-16 09:14:07
【问题描述】:

1) 我有一个线段列表(由它们的两个端点和一个宽度定义。)
2) 这些线段是在面板中绘制的。
3) 当我的鼠标移动时(Panel.MouseMove 事件),我循环遍历线段列表。
4)Foreach:

  gPath.Reset();
  Pen Pen = new Pen(Color.White, 20);
  gPath.AddLine(P1, P2);
  return gPath.IsOutlineVisible(cursorPos, Pen);

5) 如果我得到 true,那么我知道我的光标悬停在当前线段上。

这适用于大约... 300 行左右。当我达到 1,000 时,我的程序减速停止(分析显示它是由 IsOutlineVisible 引起的)。那么,有什么方法可以提高我的命中测试算法的性能吗?我不知道 IsOutlineVisible 的效率如何,所以我不想实现该方法已经使用的任何优化。有什么想法吗?

编辑:
在深入研究我的数据后,我注意到有些行非常大。例如:
端点 1 = (16000, -16000)
端点2 = (5041448, -32868734)

(是的,其中一个坐标在负数千万……)

我验证了仅针对一个这样的线段进行命中测试就足以使我的程序停止运行,(IsOutlineVisible 需要 2-3 秒来进行测试,并且只要光标移动就会运行测试......) .

我应该在发布之前对此进行更彻底的测试。感谢所有回复(如果我最终处理数千行,二维空间索引是一个很好的建议)。

附言如果有人知道为什么大线段对 IsOutlineVisible 来说是个大问题,那就太好了。

【问题讨论】:

  • 您可能想看看My Example 使用当前相关的.Net Windows UI 技术的非常相似的东西。它包括一些有趣的功能,例如行选择和突出显示。
  • 谢谢,但我目前正在使用 winforms。
  • 只是一个评论。我认为它会变成矩形敌人命中测试。以数学方式执行此操作将提高性能,例如边界框和向量距离应该很快。 1000 行虽然太多了

标签: c# winforms system.drawing hittest


【解决方案1】:

试试这个:

public Line GetHotSpottedLine(Point mousePos){
        var line = lines.Where(line =>
        {
            Point p1 = new Point(Math.Min(line.P1.X, line.P2.X), Math.Min(line.P1.Y, line.P2.Y));
            Point p2 = new Point(Math.Max(line.P1.X, line.P2.X), Math.Max(line.P1.Y, line.P2.Y));
            return mousePos.X >= p1.X && mousePos.X <= p2.X && mousePos.Y >= p1.Y && mousePos.Y <= p2.Y;
        }).FirstOrDefault(line => {
            using (GraphicsPath gp = new GraphicsPath())
            {
                gp.AddLine(line.P1, line.P2);
                //You can declare your pen outside and this pen should be used to draw your lines.
                using (Pen p = new Pen(Color.Red, 20))
                {
                    return gp.IsOutlineVisible(mousePos, p);
                }
            }
        });
        return line;
 }
 public class Line{
        public Point P1 { get; set; }
        public Point P2 { get; set; }
 }
 List<Line> lines = new List<Line>();

这取决于你想如何使用你的lines,如果你想绘制它们,我们必须注意drawing而不是detecting the hovered line的性能,是的,在这种情况下,drawing是你的问题。我想我们可以在这里使用一些Thread。无论如何,我用1000 线进行了测试,它可以正常工作(在Form Paint 中绘制所有线)而不使用任何线程。

【讨论】:

    【解决方案2】:

    IsOutlineVisible 调用 Gdi+,也许这会减慢它的速度。

    public bool GraphicsPath.IsOutlineVisible(PointF pt, Pen pen, Graphics graphics)
    {
      int num;
      if (pen == null)
      {
          throw new ArgumentNullException("pen");
      }
      int status = SafeNativeMethods.Gdip.GdipIsOutlineVisiblePathPoint(new HandleRef(this, this.nativePath), pt.X, pt.Y, new HandleRef(pen, pen.NativePen), new HandleRef(graphics, (graphics != null) ? graphics.NativeGraphics : IntPtr.Zero), out num);
      if (status != 0)
      {
          throw SafeNativeMethods.Gdip.StatusException(status);
      }
      return (num != 0);
    }
    

    除此之外,这些命中测试不使用优化,例如为所有使用的图形元素构建二维索引。为了提高性能,我会尝试

    1. 自己实现命中测试并迭代所有元素
    2. 也许使用二维索引,尽管对于

    【讨论】:

      猜你喜欢
      • 2015-01-30
      • 1970-01-01
      • 1970-01-01
      • 2018-05-18
      • 1970-01-01
      • 2011-08-31
      • 2011-01-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多