求两条线断相交的算法 (100分)

  • 主题发起人 主题发起人 SS2000
  • 开始时间 开始时间
S

SS2000

Unregistered / Unconfirmed
GUEST, unregistred user!
已知两条线段的端点,线段一((x1,y1),(x2,y2)),线段二((x3,y3),(x4,y4))
求两条线段是否相交的公式?

再详细说明一下:
实际上我的用法是: 图上有N条线段(可能好几百根,甚至上千)
我按住鼠标左键,在屏幕上拖出一个矩形,我需要知道,哪些
线段全部或部分在矩形内?
实际上是已知一条线段和一个矩形,如何判断这线段全部或部分在矩形内?

我觉得可以分解为只要矩形的任意一边和线段相交或线段的一个端点在矩形内。
一个端点在矩形内可以用PtInRect,但是两条线段相交就不好做了
 
我这里正好有一个例子,你看看
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

function CCW(p0, p1, p2: TPoint): Integer;

{Purpose:
Determines, given three points, if when travelling from the first to the second
to the third, we travel in a counterclockwise direction.
Return Value:
(int) 1 if the movement is in a counterclockwise direction, -1 if not.}

var
dx1, dx2: LongInt;
dy1, dy2: LongInt;
begin
dx1 := p1.x - p0.x;
dx2 := p2.x - p0.x;
dy1 := p1.y - p0.y;
dy2 := p2.y - p0.y;
{This is basically a slope comparison: we don't do divisions because
of divide by zero possibilities with pure horizontal and pure vertical lines.}
if ((dx1 * dy2) > (dy1 * dx2)) then
Result := 1
else
Result := -1;
end;

function Intersect(p1, p2, p3, p4: TPoint): Boolean;

{Purpose: Given two line segments, determine if they intersect.
Return Value: TRUE if they intersect, FALSE if not.}

begin
Result := (((CCW(p1, p2, p3) * CCW(p1, p2, p4)) <= 0) and
((CCW(p3, p4, p1) * CCW(p3, p4, p2) <= 0)));
end;

function G_PtInPolyRect(PolyPoints: array of TPoint; ptTest: TPoint;
var prbound: TRect): Boolean;

{Purpose:
This routine determines if a point is within the smallest rectangle that encloses a polygon.
Return Value:
(BOOL) True or False depending on whether the point is in the rect or not.}

var
xmin, xmax, ymin, ymax: Integer;
pt: TPoint;
i: Word;
begin
xmin := MaxInt;
ymin := MaxInt;
xmax := -MaxInt;
ymax := -MaxInt;
for i := 0 to High(PolyPoints) do
begin
pt := PolyPoints;
if (pt.x < xmin) then
xmin := pt.x;
if (pt.x > xmax) then
xmax := pt.x;
if (pt.y < ymin) then
ymin := pt.y;
if (pt.y > ymax) then
ymax := pt.y;
end;
prbound := Rect(xmin, ymin, xmax, ymax);
Result := PtInRect(prbound, ptTest);
end;


function G_PtInPolygon(PolyPoints: array of TPoint; ptTest: TPoint): Boolean;

{Purpose:
This routine determines if the point passed is in the polygon. It uses the classical polygon
hit-testing algorithm: A horizontal ray starting at the point is extended infinitely rightwards
and the number of polygon edges that intersect the ray are counted. If the number is odd,
the point is inside the polygon.
Return Value:
(BOOL) True if the point is inside the polygon, False if not.}

var
i: Integer;
pt1, pt2: TPoint;
wnumintsct: Word;
prbound: TRect;
begin
wnumintsct := 0;
Result := False;
if (not G_PtInPolyRect(PolyPoints, ptTest, prbound)) then
Exit;
pt1 := ptTest;
pt2 := ptTest;
pt2.x := prbound.Right + 50;
{Now go through each of the lines in the polygon and see if it intersects}
for i := 0 to High(PolyPoints) - 1 do
if (Intersect(ptTest, pt2, PolyPoints, PolyPoints[i + 1])) then
Inc(wnumintsct);
{And the last line}
if (Intersect(ptTest, pt2, PolyPoints[High(PolyPoints)], PolyPoints[0])) then
Inc(wnumintsct);
{If wnumintsct is odd then the point is inside the polygon}
Result := Odd(wnumintsct);
end;



procedure TForm1.Button1Click(Sender: TObject);
begin
if Intersect(point(0, 4), point(50, 90), point(40, 30), point(70, 500))
then
begin
ShowMessage('两直线相交');
end else
ShowMessage('两直线不相交');
end;

end.
 
to huazai:
多谢你的指点,不过好像有点问题,我会在测试你的方法

实际上我的用法是: 图上有N条线段(可能好几百根,甚至上千)
我按住鼠标左键,在屏幕上拖出一个矩形,我需要知道,哪些
线段全部或部分在矩形内?
实际上是已知一条线段和一个矩形,如何判断这线段全部或部分在矩形内?

我觉得可以分解为只要矩形的任意一边和线段相交或线段的一个端点在矩形内。
一个端点在矩形内可以用PtInRect,但是两条线段相交就不好做了
 
to huazai:
是我搞错了,你的方法很好,50分是你的了
看看还有没有更好的方法解决我的线段在矩形内的问题
没有的话,另外50分也是你的了
 
还有好算法吗?
 
线段是否全部在矩形中,可以判断线段两个端点是否都在矩形中!

部分包含的问题,当一个在矩形内,另一个不在的情况也简单
主要是两点都不在矩形内但分部包含的情况。

最后一种情况可以反过来做,由线段所形成的矩形是否包含了“那个矩形”的某个点。
 
>>部分包含的问题,当一个在矩形内,另一个不在的情况也简单
怎么简单?我觉得还是比较难
>>最后一种情况可以反过来做,由线段所形成的矩形是否包含了“那个矩形”的某个点。
这是不对的,比如又一条线段(0,0),(100,100),还有一个矩形以(80,0),(100,20)对角线
这种情况就不成立
 
如果是线段的一部分包含在矩形以内但是两端都不在矩形内的话,
直接判断一下线段有没有和矩形的四条边相交就可以解决了。
所以算法可以改成:

if 线段的两端点都在矩形内
then 线段在矩形内
else if 线段与矩形某一条边相交
then 线段与矩形相交
else 线段既不在矩形内,又不与矩形相交
 
部分包含的问题,当一个在矩形内,另一个不在的情况也简单
=======================================================
假设点为 p 矩形对角线的两点为 rp1,rp2
如果p 在r 内,则 (p.x >=min(rp1.x,rp2.x) ) and (p.x <=max(rp1.x,rp2.x) )
并且(p.y >=min(rp1.y,rp2.y) ) and (p.y <=max(rp1.x,rp2.y) )
否则就不在 r 内!

>>最后一种情况可以反过来做,由线段所形成的矩形是否包含了“那个矩形”的某个点。
这是不对的,比如又一条线段(0,0),(100,100),还有一个矩形以(80,0),(100,20)对角线
这种情况就不成立
==========================================================================
这怎么可能不成立??????????????
这条线段所围成的矩形为 (0,0)(100,100) (0,100) (100,0)
当然包含 (80,0)-(100,20) !!!!!
 
找一本《计算机图形学》看看
我有一本是清华大学出版社的
 
这里有一段.Net 的代码不知道行不行。你用最后一个函数判断两次就可以了。
structure Point2D
public x as double
public y as double
end structure
Function GetDistancePL(ByVal x As Double, ByVal y As Double, ByVal x1 As Double, ByVal y1 As Double, ByVal x2 As Double, ByVal y2 As Double) As Double
Dim a, b, c As Double
a = y2 - y1
b = x1 - x2
c = x2 * y1 - x1 * y2
Return (a * x + b * y + c) / Math.Sqrt(a * a + b * b)

End Function

Function GetDistancePL(ByVal p As Point2D, ByVal p1 As Point2D, ByVal p2 As Point2D) As Double
Return GetDistancePL(p.x, p.y, p1.x, p1.y, p2.x, p2.y)
End Function

Function Is2PointSecantLine(ByVal p1 As Point2D, ByVal p2 As Point2D, ByVal l1 As Point2D, ByVal l2 As Point2D) As Boolean
Return GetDistancePL(p1, l1, l2) * GetDistancePL(p2, l1, l2) < 0
End Function
 
to:jsxjd
我说的是线断是否在矩形内,不是说线端围成的矩形和已知矩形相交,
如果那样,我也不麻烦大家了,Windows自带的函数IntersectRect就
解决了,jsxjd,谢谢你的解答,不过,在我第二次回复你的问题后,
你还是理解错了,很遗憾:(
to:Wiley_liu我要的正是线端相交的算法,你的思路我前面已经说过了,
但是没有线端相交的算法:(
to:NoSwing,看不懂如何用:(
 
接受答案了.
 

Similar threads

后退
顶部