设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 5673|回复: 7
打印 上一主题 下一主题

[已经解决] 如何判断2个点连成的一条线与某矩形区域有相交?

[复制链接]

Lv1.梦旅人

看不到我

梦石
0
星屑
50
在线时间
229 小时
注册时间
2005-11-6
帖子
1741

贵宾

跳转到指定楼层
1
发表于 2010-12-24 17:09:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
已知画面中2个点P1和P2的X和Y 和某矩形区域(无倾斜角度)的长宽和位置,如何判断P1和P2的连接线与这个矩形范围有相交的部分?

点评

一般的程序解决方法就好了,判断所在格反而多了一步吧,我查了下有的解决方法是判断线段和矩形的四条线段是否相交,不知道有没有更简的方法…  发表于 2010-12-24 19:54
其实我连怎么判断连接线的所在格都不会……  发表于 2010-12-24 19:38

Lv4.逐梦者

梦石
0
星屑
7986
在线时间
1184 小时
注册时间
2007-7-29
帖子
2055
2
发表于 2010-12-24 21:20:25 | 只看该作者
本帖最后由 enghao_lim 于 2010-12-24 21:31 编辑

我只想得出这个方法,就是算y=mx+c的说……然后逐条线判断,最后判断是否在里边……继续试看……

不想了……最后产物……没有效率……
  1. class Numeric
  2.   def new_between?(a,b)
  3.     return between?(a,b) if b > a
  4.     return between?(b,a)
  5.   end
  6. end

  7. def intersect?(p1,p2,rect)
  8.   # gradient
  9.   m = (p2[1]-p1[1])/(p2[0]-p1[0]).to_f
  10.   # c constant
  11.   c = p2[1] - m * p2[0]
  12.   # 区域x,y取得
  13.   x1 = rect[0]
  14.   x2 = x1 + rect[2]
  15.   y1 = rect[1]
  16.   y2 = y1 + rect[3]
  17.   # 其中一点在框内就是有相交
  18.   if (p1[0].new_between?(x1,x2) and p1[1].new_between?(y1,y2)) or
  19.      (p1[0].new_between?(x1,x2) and p1[1].new_between?(y1,y2))
  20.     return true
  21.   # 不在框内就逐线判断,如有相交一定会经过边缘。
  22.   else
  23.     if p1[0] == p2[0]
  24.       return true if p1[0].new_between?(x1,x2) and (y1.new_between?(p1[1],p2[1]) or y2.new_between?(p1[1],p2[1]))
  25.     elsif p1[1] == p2[1]
  26.       return true if p1[1].new_between?(y1,y2) and (x1.new_between?(p1[0],p2[0]) or x2.new_between?(p1[0],p2[0]))
  27.     else
  28.       _x = (y1 - c)/m
  29.       return true if _x.between?(x1,x2)
  30.       _x = (y2 - c)/m
  31.       return true if _x.between?(x1,x2)
  32.       _y = m * x1 + c
  33.       return true if _y.between?(y1,y2)
  34.       _y = m * x2 + c
  35.       return true if _y.between?(y1,y2)
  36.     end
  37.   end
  38.   return false
  39. end

  40. rect = [20,20,10,10] #x,y,width,height
  41. p1 = [21,24] #x,y
  42. p2 = [23,27] #x,y
  43. p intersect?(p1,p2,rect)
复制代码

点评

等待数学高手吧……只怪我高中不努力……TT  发表于 2010-12-24 21:44
辛苦了,这个效率确实太低了……因为我是作为一个条件判断,而且会循环判断,难道会怨念了……  发表于 2010-12-24 21:39

评分

参与人数 1星屑 +400 收起 理由
fux2 + 400 认可答案

查看全部评分

回复 支持 反对

使用道具 举报

Lv2.观梦者

旅之愚者

梦石
0
星屑
275
在线时间
812 小时
注册时间
2007-7-28
帖子
2148

贵宾

3
发表于 2010-12-24 23:37:28 | 只看该作者
本帖最后由 六祈 于 2010-12-25 01:09 编辑


比如两个点P1(px1,py1),P2(px2,py2)
矩形的四个顶点为(x1,y1),(x2,y2)等

先求连线方程:y - py1 = (x - px1)*(py2-py1)/(px2-px1)
化简得到y=ax + b(a,b系数可从上式得出)

取矩形一边,譬如(x1,y1)到(x2,y2),同样计算连线方程
得到y=cx + d
相交得到x=(b-d)/(c-a),判断交点x.between?(px1,px2),如果是则返回true
如果矩形四条边均返回false,则最终结果亦为false

最简单易懂的方法了

点评

这就是我用的方法……可惜效率啊……我尽力了……@@  发表于 2010-12-25 02:09

评分

参与人数 1星屑 +400 收起 理由
fux2 + 400 认可答案

查看全部评分

回复 支持 反对

使用道具 举报

Lv1.梦旅人

看不到我

梦石
0
星屑
50
在线时间
229 小时
注册时间
2005-11-6
帖子
1741

贵宾

4
 楼主| 发表于 2010-12-25 00:44:41 | 只看该作者
确实是个纠结的问题,我暂时把线段做成平行于X轴的线段吧,判断起来简单多了……楼上几位辛苦了……

点评

请参见板凳更新  发表于 2010-12-25 01:09
回复 支持 反对

使用道具 举报

头像被屏蔽

Lv1.梦旅人 (禁止发言)

梦石
0
星屑
50
在线时间
21 小时
注册时间
2007-7-3
帖子
573
5
发表于 2010-12-25 09:44:12 | 只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
签名被屏蔽
回复 支持 反对

使用道具 举报

Lv2.观梦者

旅之愚者

梦石
0
星屑
275
在线时间
812 小时
注册时间
2007-7-28
帖子
2148

贵宾

6
发表于 2010-12-25 13:04:39 | 只看该作者
稍稍看了下那个剪裁算法,似乎判断两个点是否一个在矩形内一个在矩形外会更容易一些
回复 支持 反对

使用道具 举报

Lv3.寻梦者

孤独守望

梦石
0
星屑
3137
在线时间
1535 小时
注册时间
2006-10-16
帖子
4321

开拓者贵宾

7
发表于 2010-12-25 13:31:44 | 只看该作者
本帖最后由 IamI 于 2010-12-25 13:34 编辑

大家好,II又来卖弄他的LP了
  1. POINT = Struct.new("Point", :x, :y)
  2. def POINT.initialize(x,y)
  3.   @x = x;@y = y
  4. end



  5. def go
  6.   p1 = POINT.new(2,3)
  7.   p2 = POINT.new(4,5)
  8.   r = Rect.new(1,3,6,7)
  9.   a = p2.y - p1.y
  10.   b = p1.x - p2.x
  11.   c = p1.y * (p2.x - p1.x) - p1.x * (p2.y - p1.y)
  12.   
  13.   # 直线方程 ax + by + c = 0
  14.   # 以下线规(LP)
  15.   r1 = POINT.new(r.x,r.y)
  16.   r2 = POINT.new(r.x + r.width,r.y)
  17.   r3 = POINT.new(r.x,r.y + r.height)
  18.   r4 = POINT.new(r.x + r.width,r.y + r.height)
  19.   
  20.   v1 = a * r1.x + b * r1.y + c
  21.   v2 = a * r2.x + b * r2.y + c
  22.   v3 = a * r3.x + b * r3.y + c
  23.   v4 = a * r4.x + b * r4.y + c
  24.   
  25.   return true if v1 == 0 or v2 == 0 or v3 == 0 or v4 == 0
  26.   
  27.   t = 0
  28.   t += 1 if v1 > 0
  29.   t += 1 if v2 > 0
  30.   t += 1 if v3 > 0
  31.   t += 1 if v4 > 0
  32.   
  33.   return false if t == 0 or t == 4
  34.   return true
  35. end
  36. p go
复制代码
OTL这玩意儿判断的是直线不是线段,懒得改了……
菩提本非树,明镜本非台。回头自望路漫漫。不求姻缘,但求再见。
本来无一物,何处惹尘埃。风打浪吹雨不来。荒庭遍野,扶摇难接。
不知道多久更新一次的博客
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
8
发表于 2010-12-26 05:55:12 | 只看该作者
本帖最后由 苏小脉 于 2010-12-26 06:07 编辑

  在 R2 中检测有两个端点的线段是否与一个矩形碰撞的问题可以被划分为检测线段是否和矩形的任意边相交,或线段的任意端点在矩形内部(包括刚好在边上)。

  前者是四次线段相交检测。对于线段相交检测,最明显的途径自然就是求包含这两条线段的直线的交点,然后判断是否在线段范围内。然而,这个方法无法避免浮点运算。为了提高效率,就需要避免浮点运算,只进行整数定点运算。下面的算法,是判断两条线段各自的端点是否分别在包含另一条线段的直线的两端。只有在这种情况下(当且仅当),两条线段才相交。这就涉及到另一个问题——判断两个点是否分别在一条直线的两端。这里可以分别使用两个点按固定的顺序连接线段的两个端点(即直线上的两个点),形成两个凸包(三角形),然后检测凸包的形成是通过顺时针还是逆时针旋转。如果这两个点各自在直线的一端,那么必然有着相反的旋转方向,一个是顺时针,一个是逆时针。
  1. def cw_test(ax, ay, bx, by, cx, cy)
  2.   lhs = (by - ay) * (cx - ax)
  3.   rhs = (cy - ay) * (bx - ax)
  4.   if lhs == rhs then 0
  5.   elsif lhs < rhs then 1
  6.   else 2
  7.   end
  8. end

  9. def bound?(x, a, b)
  10.   a <= x && x <= b || a >= x && x >= b
  11. end

  12. def bound_inside?(x, y, ax, ay, bx, by)
  13.   bound?(x, ax, bx) && bound?(y, ay, by)
  14. end

  15. def seg_intersect?(px1, py1, px2, py2, qx1, qy1, qx2, qy2)
  16.   t1 = cw_test(px1, py1, qx1, qy1, qx2, qy2)
  17.   t2 = cw_test(px2, py2, qx1, qy1, qx2, qy2)
  18.   t3 = cw_test(qx1, qy1, px1, py1, px2, py2)
  19.   t4 = cw_test(qx2, qy2, px1, py1, px2, py2)
  20.   3 == (t1 | t2) && 3 == (t3 | t4) ||
  21.   0 == t1 && bound_inside?(px1, py1, qx1, qy1, qx2, qy2) ||
  22.   0 == t2 && bound_inside?(px2, py2, qx1, qy1, qx2, qy2) ||
  23.   0 == t3 && bound_inside?(qx1, qy1, px1, py1, px2, py2) ||
  24.   0 == t4 && bound_inside?(qx2, qy2, px1, py1, px2, py2)
  25. end

  26. def point_lies_in?(x, y, ax, ay, bx, by, cx, cy, dx, dy)
  27.   (cw_test(x, y, ax, ay, bx, by) |
  28.    cw_test(x, y, bx, by, cx, cy) |
  29.    cw_test(x, y, cx, cy, dx, dy) |
  30.    cw_test(x, y, dx, dy, ax, ay)) != 3
  31. end

  32. def seg_rect_collide?(ax, ay, bx, by, tlx, tly, trx, try, brx, bry, blx, bly)
  33.   point_lies_in?(ax, ay, tlx, tly, trx, try, brx, bry, blx, bly) ||
  34.   point_lies_in?(bx, by, tlx, tly, trx, try, brx, bry, blx, bly) ||
  35.   seg_intersect?(ax, ay, bx, by, tlx, tly, trx, try) ||
  36.   seg_intersect?(ax, ay, bx, by, trx, try, brx, bry) ||
  37.   seg_intersect?(ax, ay, bx, by, brx, bry, blx, bly) ||
  38.   seg_intersect?(ax, ay, bx, by, blx, bly, tlx, tly)
  39. end
复制代码
seg_rect_collide? 是检测线段 (ax, ay)-(bx, by) 与矩形碰撞的函数。矩形的四个顶点 (tlx, tly), (trx, try), (brx, bry), (blx, bly) 是按时钟顺序排列的,比如“左上、右上、右下、左下”这样的顺序。

详情可参考:
http://szsu.wordpress.com/2010/12/25/seg_rect_collision/

附一段在 RM 中的随机化、可视化测试脚本:

  1. def init_sprite(sprite, color)
  2.   sprite.ox = sprite.oy = 5
  3.   sprite.bitmap = Bitmap.new(10, 10)
  4.   sprite.bitmap.fill_rect(0, 0, 10, 10, color)
  5. end

  6. yellow = Color.new(0xff, 0xff, 0)
  7. blue = Color.new(0, 0, 0xff)
  8. init_sprite(a = Sprite.new, yellow)
  9. init_sprite(b = Sprite.new, yellow)
  10. init_sprite(tl = Sprite.new, blue)
  11. init_sprite(tr = Sprite.new, blue)
  12. init_sprite(br = Sprite.new, blue)
  13. init_sprite(bl = Sprite.new, blue)

  14. loop do
  15.   ax = rand(640)
  16.   ay = rand(480)
  17.   bx = rand(640)
  18.   by = rand(480)
  19.   tlx = rand(320)
  20.   tly = rand(240)
  21.   brx = rand(320) + 320
  22.   bry = rand(240) + 240

  23.   a.x = ax
  24.   a.y = ay
  25.   b.x = bx
  26.   b.y = by
  27.   tl.x = tlx
  28.   tl.y = tly
  29.   tr.x = brx
  30.   tr.y = tly
  31.   br.x = brx
  32.   br.y = bry
  33.   bl.x = tlx
  34.   bl.y = bry

  35.   p  "(#{ax},#{ay}), (#{bx},#{by})",
  36.   "(#{tlx}, #{tly}), (#{brx}, #{tly}), (#{brx}, #{bry}), (#{tlx}, #{bry})",
  37.   seg_rect_collide?(ax, ay, bx, by, tlx, tly, brx, tly, brx, bry, tlx, bry)
  38.   
  39.   Graphics.update
  40. end
复制代码

评分

参与人数 1星屑 +400 收起 理由
fux2 + 400 追加认可

查看全部评分

[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-12-24 01:05

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表