几何算法--两条线段相交检测

两条线段相交检测

性质

利用向量叉积进行线段相交判断。
时间复杂度Θ(1)

接口设计

extern "C" class ALGORITHMLIB SegmentIntersection
{
public:
	enum POINT_TYPE
	{
		LEFT,
		RIGHT,
	};

	class LinePoint
	{
	public:
		LinePoint();
		LinePoint(Math::Point<2> poP_, POINT_TYPE Type_);
		~LinePoint();

	public:
		Math::Point<2> m_poPos;
		POINT_TYPE m_Type;
	};

实现

内部类LinePoint

SegmentIntersection::LinePoint::LinePoint()
{
	m_Type = POINT_TYPE::LEFT;
}

SegmentIntersection::LinePoint::~LinePoint()
{

}

SegmentIntersection::LinePoint::LinePoint(
	Math::Point<2> poP_, 
	POINT_TYPE Type_)
{
	m_poPos = poP_;
	m_Type = Type_;
}

内部类ComparableLineGeometry

SegmentIntersection::ComparableLineGeometry::ComparableLineGeometry()
{

}

SegmentIntersection::ComparableLineGeometry::ComparableLineGeometry(
	const PlaneGeometry::LineGeometry& line_)
{
	m_Line = line_;
}

SegmentIntersection::ComparableLineGeometry::~ComparableLineGeometry()
{

}

bool SegmentIntersection::ComparableLineGeometry::operator<(
	const ComparableLineGeometry& cline_)
{
	// 小于
	// 本线段对应向量 到 本线段起点到输入线段起点 构成向量,需要 逆时针旋转到达时,本线段 小于 输入线段
	if (m_Line.m_poStart.m_nPos[0] < cline_.m_Line.m_poEnd.m_nPos[0])
	{
		Math::Vector<2> _vector1(m_Line.m_poStart, m_Line.m_poEnd);
		Math::Vector<2> _vector2(m_Line.m_poStart, cline_.m_Line.m_poStart);
		Geometry::ROTATE_DIRECTION _direc = Geometry::TestDirection(_vector1, _vector2);
		return (_direc == Geometry::ROTATE_DIRECTION::ANTICLOCK);
	}
	else
	{
		assert(m_Line.m_poStart.m_nPos[0] == cline_.m_Line.m_poEnd.m_nPos[0]);
		return m_Line.m_poStart.m_nPos[1] < cline_.m_Line.m_poEnd.m_nPos[1];
	}
}

bool SegmentIntersection::ComparableLineGeometry::operator==(const ComparableLineGeometry& cline_)
{
	if (m_Line.m_poStart.m_nPos[0] < cline_.m_Line.m_poEnd.m_nPos[0])
	{
		Math::Vector<2> _vector1(m_Line.m_poStart, m_Line.m_poEnd);
		Math::Vector<2> _vector2(m_Line.m_poStart, cline_.m_Line.m_poStart);
		Geometry::ROTATE_DIRECTION _direc = Geometry::TestDirection(_vector1, _vector2);
		return (_direc == Geometry::ROTATE_DIRECTION::NO_ROTATE);
	}
	else
	{
		assert(m_Line.m_poStart.m_nPos[0] == cline_.m_Line.m_poEnd.m_nPos[0]);
		return m_Line.m_poStart.m_nPos[1] == cline_.m_Line.m_poEnd.m_nPos[1];
	}
}

bool SegmentIntersection::ComparableLineGeometry::operator!=(const ComparableLineGeometry& cline_)
{
	return !operator==(cline_);
}

bool SegmentIntersection::ComparableLineGeometry::operator>(const ComparableLineGeometry& cline_)
{
	return !(operator<(cline_) || operator==(cline_));
}

构造

SegmentIntersection::SegmentIntersection()
{

}

析构

SegmentIntersection::~SegmentIntersection()
{

}

算法运行

bool SegmentIntersection::Run(
	const PlaneGeometry::LineGeometry& line1_, 
	const PlaneGeometry::LineGeometry& line2_)
{
	Math::Vector<2> _vec1S2S(line1_.m_poStart, line2_.m_poStart);
	Math::Vector<2> _vec1S2E(line1_.m_poStart, line2_.m_poEnd);
	Math::Vector<2> _vec2S1S(line2_.m_poStart, line1_.m_poStart);
	Math::Vector<2> _vec2S1E(line2_.m_poStart, line1_.m_poEnd);
	Math::Vector<2> _vecLine1(line1_.m_poStart, line1_.m_poEnd);
	Math::Vector<2> _vecLine2(line2_.m_poStart, line2_.m_poEnd);

	// 对线段2
	// 考察 线段2起点到线段1起点构成的向量 如何旋转以到达 线段2代表的向量
	// 考察 线段2起点到线段1终点构成的向量 如何旋转以到达 线段2代表的向量
	Geometry::ROTATE_DIRECTION _dir1 = Geometry::TestDirection(_vec2S1S, _vecLine2);
	Geometry::ROTATE_DIRECTION _dir2 = Geometry::TestDirection(_vec2S1E, _vecLine2);

	// 对线段1
	// 考察 线段1起点到线段2起点构成的向量 如何旋转以到达 线段1代表的向量
	// 考察 线段1起点到线段2终点构成的向量 如何旋转以到达 线段1代表的向量
	Geometry::ROTATE_DIRECTION _dir3 = Geometry::TestDirection(_vec1S2S, _vecLine1);
	Geometry::ROTATE_DIRECTION _dir4 = Geometry::TestDirection(_vec1S2E, _vecLine1);

	bool _bSect = false;
	if (_dir1 == Geometry::ROTATE_DIRECTION::ANTICLOCK
		&& _dir2 == Geometry::ROTATE_DIRECTION::CLOCK)
	{
		_bSect = true;
	}
	else if (_dir1 == Geometry::ROTATE_DIRECTION::CLOCK
		&& _dir2 == Geometry::ROTATE_DIRECTION::ANTICLOCK)
	{
		_bSect = true;
	}
	else if (_dir3 == Geometry::ROTATE_DIRECTION::ANTICLOCK
		&& _dir4 == Geometry::ROTATE_DIRECTION::CLOCK)
	{
		_bSect = true;
	}
	else if (_dir3 == Geometry::ROTATE_DIRECTION::CLOCK
		&& _dir4 == Geometry::ROTATE_DIRECTION::ANTICLOCK)
	{
		_bSect = true;
	}

	if (_bSect)
	{
		return _bSect;
	}

	if (_dir1 == Geometry::NO_ROTATE 
		&& Geometry::IsPointOnLine(line2_, line1_.m_poStart))
	{
		_bSect = true;
	}
	else if (_dir2 == Geometry::ROTATE_DIRECTION::NO_ROTATE 
		&& Geometry::IsPointOnLine(line2_, line1_.m_poEnd))
	{
		_bSect = true;
	}
	else if (_dir3 == Geometry::ROTATE_DIRECTION::NO_ROTATE 
		&& Geometry::IsPointOnLine(line1_, line2_.m_poStart))
	{
		_bSect = true;
	}
	else if (_dir4 == Geometry::ROTATE_DIRECTION::NO_ROTATE 
		&& Geometry::IsPointOnLine(line1_, line2_.m_poEnd))
	{
		_bSect = true;
	}

	return _bSect;
}

算法目标&正确性证明

p1 叉乘 p2 > 0,按右手定则,认为从向量p1经过逆时针旋转到达p2
正确性证明:
直接证明法
假设线段1,线段2相交。
情形1:
一个交点,且交点在线段1,线段2中间部分
则必有:
基于线段1,到达线段2起点和终点旋转方向不同

情形2:
一个交点且交点在线段端点
若端点为,线段1起点,则必有 线段2,不经旋转到达线段1起点
此时,若进一步判断,线段1起点位于线段2之上,则可证明相交。
由于算法中,对端点的4种可能均进行了,判断,
所以,算法可以覆盖到此类情形。

情形3:
多个交点,
此时必有线段1和线段2共线
此时若相交,则必须有重叠部分
如果重叠部分存在,则必有 被重叠段的起始端点位于另一线段之上
此时必有
另一线段不经过选转即可到达被重叠段的起始端点+被重叠段的起始端点位于另一线段之上
算法可以覆盖到此类情形。

综合,算法在三种情形下,均可得到正确结果。正确性得证。

你可能感兴趣的:(Algorithm,&,DataStruct)