【问题标题】:Test for rectangle-line intersection测试矩形线相交
【发布时间】:2018-07-06 21:01:12
【问题描述】:

我正在尝试查看图像框是否与 e 图形线交互。 我当前的代码:

e.Graphics.DrawLine(SystemPens.ButtonShadow, a, b);

if (pictureBox1.Bounds.IntersectsWith(e.GraphicsLine.Bounds))
{
    dead();
}

我不确定该怎么做。

【问题讨论】:

  • 您需要考虑如何使用铅笔、纸张和一些几何图形来做到这一点。然后用代码编写你的解决方案。
  • 什么是图像框? Winforms中没有这样的控制! - 总是用他们的正确名字来称呼事物!

标签: c# winforms intersection


【解决方案1】:

要测试矩形和直线的交集,您可以使用Wikipedia 文章中的多种方法之一,也可以使用涉及GraphicsPathsRegions.. 的GDI+ 技巧:

using System.Drawing.Drawing2D;
..

bool Intersects(Point a, Point b, Rectangle r)
{
    if (Math.Min(a.X, b.X) > r.Right)  return false;   // *
    if (Math.Max(a.X, b.X) < r.Left)   return false;   // *
    if (Math.Min(a.Y, b.Y) > r.Bottom) return false;   // *
    if (Math.Max(a.Y, b.Y) < r.Top)    return false;   // *

    if (r.Contains(a)) return true;   // **
    if (r.Contains(b)) return true;   // **

    using (GraphicsPath gp = new GraphicsPath())
    using (Pen pen = new Pen(Color.Empty, 0.5f))
    using (Region reg = new Region(r))
    using (Graphics g = CreateGraphics())
    {
        gp.AddLine(a,b);
        gp.Widen(pen);   // we need to widen the line path just a little
        reg.Intersect(gp);
        if (reg.IsEmpty(g)) return false;
    }
    return true;
}

这是一个小测试结果:

由于Region.IsEmpty 的速度可能不是很快,因此我在调用前进行了一些简单的测试以提高速度。有关区域see here 的性能问题的讨论。由此我想可以得出结论,用一个简单的矩形进行测试实际上仍然相当快..

对于真正快速的测试,您可能需要实现真正的裁剪方法。 This looks nice..

但是与分析方法相比,使用区域的 GDI+ 技巧有一个很大的优势:我可以处理任何可以放入 GraphicsPath 的形状,包括圆形、椭圆形、多边形、各种组合以及复杂的跟踪路径.而且由于Region支持所有集合操作,你的想象力是极限..

这使您可以测试复杂形状的宇宙飞船或怪物;通过一些额外的技巧,您甚至可以测试旋转的形状。

请注意,如果您要测试复杂的形状,您需要:

  • 传入GraphicsPath
  • 使用它的边界矩形 (gp.GetBounds()) 进行前四个测试 (*)
  • 删除其他两个测试 (**),因为边界矩形现在不起作用;事实上,它们只适用于矩形;为了下面的椭圆演示,我不得不删除它们。

下面是同样的例子,只是用椭圆代替矩形:

【讨论】:

  • 这实际上是不正确的。或者在很多情况下它是不正确的。你只是在用对角线做一个矩形。此矩形中有太多区域与 picbox 相交但不与实际线相交。
  • 看看这个:Line clipping。主要算法有CC++ 实现,可以很容易地转换为C#
  • @Jim :好吧,如果他想测试与矩形相交的线,你是对的。不过,他谈到了线路的界限。但我想你对他想要的东西是对的..
  • 我不知道区域解决方案。我过去总是使用线条方程和一些复杂的五线谱。我要测试它有多快。不错的解决方案! +1
  • 我不知道为什么,但我真的很想解决这个问题。您的解决方案效果很好!我测试了 1200 行的速度:time = ~30 毫秒。然后我寻找一种更快的方法,我找到了头奖。 来自 Windows 游戏编程大师技巧的 Andre LaMothe 的代码。它检查两条线段是否相交。所以我用矩形的所有 4 个线段检查了 1200 条线。结果令人惊叹:~200 微秒或更好的 0.2 毫秒!!!
【解决方案2】:

给你。原始代码是 C 语言,我翻译成 vb.net。该代码适用于一条线和一个矩形。

'//min_clip_x is the distance of the left side of rect
'//min_clip_y is the distance of the upper side of rect
'//max_clip_x is the distance of the right side of rect
'//min_clip_y is the distance of the down side of rect

Private Function Clip_Line(ByVal x1 As Integer, ByVal y1 As Integer, ByVal x2 As Integer, ByVal y2 As Integer,
                             ByVal min_clip_x As Integer, ByVal max_clip_x As Integer, ByVal min_clip_y As Integer,
                             ByVal max_clip_y As Integer) As Boolean

    Const CLIP_CODE_C As Integer = 0
    Const CLIP_CODE_N As Integer = 8
    Const CLIP_CODE_S As Integer = 4
    Const CLIP_CODE_E As Integer = 2
    Const CLIP_CODE_W As Integer = 1
    Const CLIP_CODE_NE As Integer = 10
    Const CLIP_CODE_SE As Integer = 6
    Const CLIP_CODE_NW As Integer = 9
    Const CLIP_CODE_SW As Integer = 5

    Dim p1_code As Integer = 0
    Dim p2_code As Integer = 0
    Dim xc1, yc1, xc2, yc2 As Integer

    xc1 = x1
    yc1 = y1
    xc2 = x2
    yc2 = y2

    '//determine codes for p1 And p2
    If y1 < min_clip_y Then
        p1_code = p1_code Or CLIP_CODE_N
    Else
        If y1 > max_clip_y Then
            p1_code = p1_code Or CLIP_CODE_S
        End If
    End If

    If (x1 < min_clip_x) Then
        p1_code = p1_code Or CLIP_CODE_W
    Else
        If (x1 > max_clip_x) Then
            p1_code = p1_code Or CLIP_CODE_E
        End If
    End If

    If (y2 < min_clip_y) Then
        p2_code = p2_code Or CLIP_CODE_N
    Else
        If (y2 > max_clip_y) Then
            p2_code = p2_code Or CLIP_CODE_S
        End If
    End If

    If (x2 < min_clip_x) Then
        p2_code = p2_code Or CLIP_CODE_W
    Else
        If (x2 > max_clip_x) Then
            p2_code = p2_code Or CLIP_CODE_E
        End If
    End If

    '//try And trivially reject
    If CBool(p1_code And p2_code) Then
        Return False
    End If

    '//test for totally visible, if so leave points untouched
    If (p1_code = 0 And p2_code = 0) Then
        Return True
    End If

    '//determine end clip point for p1
    Select Case p1_code
        Case CLIP_CODE_C
            Exit Select
        Case CLIP_CODE_N
            yc1 = min_clip_y
            xc1 = CInt(x1 + 0.5 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1))

            Exit Select
        Case CLIP_CODE_S
            yc1 = max_clip_y
            xc1 = CInt(x1 + 0.5 + (max_clip_y - y1) * (x2 - x1) / (y2 - y1))

            Exit Select
        Case CLIP_CODE_W
            xc1 = min_clip_x
            yc1 = CInt(y1 + 0.5 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1))

            Exit Select
        Case CLIP_CODE_E
            xc1 = max_clip_x
            yc1 = CInt(y1 + 0.5 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1))

            Exit Select
        Case CLIP_CODE_NE
            '//north hline intersection
            yc1 = min_clip_y
            xc1 = CInt(x1 + 0.5 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc1 < min_clip_x) Or (xc1 > max_clip_x) Then
                '//east vline intersection
                xc1 = max_clip_x
                yc1 = CInt(y1 + 0.5 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1))
            End If

            Exit Select
        Case CLIP_CODE_SE
            '//south hline intersection
            yc1 = max_clip_y
            xc1 = CInt(x1 + 0.5 + (max_clip_y - y1) * (x2 - x1) / (y2 - y1))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc1 < min_clip_x) Or (xc1 > max_clip_x) Then
                '//east vline intersection
                xc1 = max_clip_x
                yc1 = CInt(y1 + 0.5 + (max_clip_x - x1) * (y2 - y1) / (x2 - x1))
            End If

            Exit Select
        Case CLIP_CODE_NW
            '//north hline intersection
            yc1 = min_clip_y
            xc1 = CInt(x1 + 0.5 + (min_clip_y - y1) * (x2 - x1) / (y2 - y1))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc1 < min_clip_x) Or (xc1 > max_clip_x) Then
                xc1 = min_clip_x
                yc1 = CInt(y1 + 0.5 + (min_clip_x - x1) * (y2 - y1) / (x2 - x1))
            End If

            Exit Select
        Case CLIP_CODE_SW
            '//south hline intersection
            yc1 = max_clip_y
            xc1 = CInt(x1 + 0.5 + (max_clip_y - y1) * (x2 - x1) / (y2 - y1))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc1 < min_clip_x) Or (xc1 > max_clip_x) Then
                xc1 = min_clip_x
                yc1 = CInt(CDbl(y1) + 0.5 + CDbl(min_clip_x - x1) * CDbl(y2 - y1) / CDbl(x2 - x1))
            End If

            Exit Select
        Case Else
            Exit Select
    End Select

    '//determine end clip point for p2
    Select Case p2_code
        Case CLIP_CODE_C
            Exit Select
        Case CLIP_CODE_N
            yc2 = min_clip_y
            xc2 = CInt(x2 + (min_clip_y - y2) * (x1 - x2) / (y1 - y2))

            Exit Select
        Case CLIP_CODE_S
            yc2 = max_clip_y
            xc2 = CInt(x2 + (max_clip_y - y2) * (x1 - x2) / (y1 - y2))

            Exit Select
        Case CLIP_CODE_W
            xc2 = min_clip_x
            yc2 = CInt(y2 + (min_clip_x - x2) * (y1 - y2) / (x1 - x2))

            Exit Select
        Case CLIP_CODE_E
            xc2 = max_clip_x
            yc2 = CInt(y2 + (max_clip_x - x2) * (y1 - y2) / (x1 - x2))

            Exit Select
        Case CLIP_CODE_NE
            '//north hline intersection
            yc2 = min_clip_y
            xc2 = CInt(x2 + 0.5 + (min_clip_y - y2) * (x1 - x2) / (y1 - y2))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc2 < min_clip_x) Or (xc2 > max_clip_x) Then
                '//east vline intersection
                xc2 = max_clip_x
                yc2 = CInt(y2 + 0.5 + (max_clip_x - x2) * (y1 - y2) / (x1 - x2))
            End If

            Exit Select
        Case CLIP_CODE_SE
            '//south hline intersection
            yc2 = max_clip_y
            xc2 = CInt(x2 + 0.5 + (max_clip_y - y2) * (x1 - x2) / (y1 - y2))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc2 < min_clip_x) Or (xc2 > max_clip_x) Then
                '//east vline intersection
                xc2 = max_clip_x
                yc2 = CInt(y2 + 0.5 + (max_clip_x - x2) * (y1 - y2) / (x1 - x2))
            End If

            Exit Select
        Case CLIP_CODE_NW
            '//north hline intersection
            yc2 = min_clip_y
            xc2 = CInt(x2 + 0.5 + (min_clip_y - y2) * (x1 - x2) / (y1 - y2))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc2 < min_clip_x) Or (xc2 > max_clip_x) Then
                xc2 = min_clip_x
                yc2 = CInt(y2 + 0.5 + (min_clip_x - x2) * (y1 - y2) / (x1 - x2))
            End If

            Exit Select
        Case CLIP_CODE_SW
            '//south hline intersection
            yc2 = max_clip_y
            xc2 = CInt(x2 + 0.5 + (max_clip_y - y2) * (x1 - x2) / (y1 - y2))
            '//test if intersection Is valid,
            '//if so then done, else compute next
            If (xc2 < min_clip_x) Or (xc2 > max_clip_x) Then
                xc2 = min_clip_x
                yc2 = CInt(y2 + 0.5 + (min_clip_x - x2) * (y1 - y2) / (x1 - x2))
            End If

            Exit Select
        Case Else
            Exit Select
    End Select

    '//do bounds check
    If (xc1 < min_clip_x) Or (xc1 > max_clip_x) Or (yc1 < min_clip_y) Or (yc1 > max_clip_y) Or
        (xc2 < min_clip_x) Or (xc2 > max_clip_x) Or (yc2 < min_clip_y) Or (yc2 > max_clip_y) Then

        Return False '//no collision
    End If

    Return True '//collision
End Function

【讨论】:

    猜你喜欢
    • 2011-04-14
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    相关资源
    最近更新 更多