我们首先将问题分成如下几个小问题讨论
首先要解决的第一个问题是判断直线是否平行 ,我们首先假设四个点的坐标为
前两个点为a(x1, y1), b(x2, y2) 后两个点为 c(x3, y3), d(x4, y4),求出两个直线的方向向量e1→=(x1−x2,y1−y2) , e2→=(x3−x4,y3−y4)。
通过e1→, e2→求得e1→×e2→=theta=(e1xe2y−e1ye2x) ,如果theta为0则表示平行。接着我们要判断是否共线,很简单,我们求出这两之间的任意向量,如e3→=(x1 - x3, y1 - y3),求e1→×e3→=beta=(e1xe3y−e1ye3x)
如果两个线段不平行的话,等于分成了3种情况。首先我们判断是否跨交。也就是判断a,b是不是在c,d两边,c,d同时是不是也在a,b的两边。我们可以求m⃗ =(x1−x3,y1−y3),n⃗ =(x1−x4,y1−y4),k⃗ =(x2−x4,y2−y4)
通过判断m⃗ ×e1→与n⃗ ×e1→是否异号就可以判断出c,d是否在a,b两侧,如果异号说明在两边,如果同号则不在。另外判断a,b是否在c,d两侧同理可得。
如果跨交的话求交点。
设交点为(x0, y0),则下列方程组必然成立:
- x0-x1=k1(x2-x1)
- y0-y1=k1(y2-y1)
- x0-x3=k2(x4-x3)
- y0-y3=k2(y4-y3)
其中k1和k2为任意不为0的常数(若为0,则说明有重合的端点,这种情况在上面已经被排除了)。1式与2式联系,3式与4式联立,消去k1和k2可得:
- x0(y2-y1)-x1(y2-y1)=y0(x2-x1)-y1(x2-x1)
- x0(y4-y3)-x3(y4-y3)=y0(x4-x3)-y3(x4-x3)
将含有未知数x0和y0的项移到左边,常数项移动到右边,得:
- (y2-y1)x0+(x1-x2)y0=(y2-y1)x1+(x1-x2)y1
- (y4-y3)x0+(x3-x4)y0=(y4-y3)x3+(x3-x4)y3
设两个常数项分别为b1和b2:
- a1=(y2-y1)x1+(x1-x2)y1
- a2=(y4-y3)x3+(x3-x4)y3
系数行列式的值为theta,用a1和a2替换x0的系数所得系数行列式的值为theta1,替换y0的系数所得系数行列式的值为D2,则有:
- theta=(x2-x1)(y4-y3)-(x4-x3)(y2-y1)
- theta1=a2(x2-x1)-a1(x4-x3)
- theta2=a2(y2-y1)-a1(y4-y3)
由此,可求得交点坐标为:
- x0=|theta1|/|theta|, y0=|theta2|/|theta|
如果是交于端点的话通过判断集合位置关系就可以判断出来了。
下面是代码:
#include <iostream>
#include <cmath>
//判断两个像素是否相等
bool equal(double x, double y)
{
return std::abs(x - y) < 1e-7;
}
class point
{
public:
point(double x = 0.0, double y = 0.0) :x(x), y(y) {}
bool operator==(const point& rhs) const;
bool operator!=(const point& rhs) const;
bool operator<(const point& rhs) const;
bool operator>(const point& rhs) const;
point operator-(const point& rhs) const;
point operator+(const point& rhs) const;
static double cross_product(const point& p1, const point& p2);
double pointX() { return this->x; }
double pointY() { return this->y; }
private:
double x, y;
};
bool point::operator==(const point & rhs) const
{
return equal(x, rhs.x) && equal(y, rhs.y);
}
bool point::operator!=(const point & rhs) const
{
return (x != rhs.x)&&(y != rhs.y);
}
bool point::operator<(const point & rhs) const
{
return false;
}
bool point::operator>(const point & rhs) const
{
return false;
}
point point::operator-(const point & rhs) const
{
return point(x - rhs.x, y - rhs.y);
}
point point::operator+(const point & rhs) const
{
return point(x + rhs.x, y + rhs.y);
}
double point::cross_product(const point & p1, const point & p2)
{
return (p1.x*p2.y - p2.x*p1.y);
}
//判断两个线段是否相交
//相交返回1,重合返回2,不相交返回3
//出错返回-1
//www.coordinate.wang
int cross(point& a, point& b, point& c, point& d, point& f)//a,b表示一个线段;c,d表示一个线段, f表示交点坐标
{
//如果线段长度为0,则退出
if (a == b || c == d)
{
return -1;
}
//判断两个是否平行
//得到线段的向量x, y
point x = a - b;
point y = c - d;
//计算差乘
double theta = point::cross_product(x, y);
if (a > b)
{
std::swap(a, b);
}
if (c > d)
{
std::swap(c, d);
}
if (equal(theta, 0))
{
//平行时
point m = a - c;
point n = b - a;
double beta = point::cross_product(m, n);//计算是否共线
if (equal(beta, 0))
{
//平行共线
//通过判断坐标关系确定是否重合
if ((a < c && c < d) || (d < b && a < d))
{
//重合
return 2;
}
else if (b == c)
{
f = b;
return 1;
}
else if (a == d)
{
f = a;
return 1;
}
else
{
//不重合
return 3;
}
}
else
{
//平行不共线且无交点
return 3;
}
}
else
{//不平行时
point m = a - c;
point n = b - c;
point k = b - d;
double gama1 = point::cross_product(m, y);
double gama2 = point::cross_product(n, y);
double gama3 = point::cross_product(n, x);
double gama4 = point::cross_product(k, x);
//不平行且相交
if (((gama1 > 0 && gama2 < 0) || (gama1 < 0 && gama2 > 0)) &&
((gama3 > 0 && gama4 < 0) || (gama3 < 0 && gama4 > 0)))
{
double a1 = (b.pointY() - a.pointY())*a.pointX() + (a.pointX() - b.pointX())*a.pointY();
double a2 = (d.pointY() - c.pointY())*c.pointX() + (c.pointX() - d.pointX())*c.pointY();
double theta1 = (b.pointX() - a.pointX())*a2 - (d.pointX() - c.pointX())*a1;
double theta2 = (b.pointY() - a.pointY())*a2 - (d.pointY() - c.pointY())*a1;
double px = std::abs(theta1) / std::abs(theta);
double py = std::abs(theta2) / std::abs(theta);
f = point(px, py);
return 1;
}
else if (b == c)//不平行交于端点
{
f = b;
return 1;
}
else if (a == d)
{
f = a;
return 1;
}
else
{
//不平行不相交
return 3;
}
}
}
int main()
{
point p1(0, 0), p2(2, 2), p3(1, 1), p4(3, 3), f;
int n = cross(p1, p2, p3, p4, f);
system("pause");
}