几何算法--线段集合内是否存在相交线段检测

线段集合内是否存在相交线段检测

性质

检测线段集合内是否存在两个相交线段。
相比穷举的方式,
利用事件点扫描法,最坏下时间复杂度为Θ(nlg(n))

接口设计

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;
	};

	class ComparableLineGeometry
	{
	public:
		ComparableLineGeometry();
		ComparableLineGeometry(const PlaneGeometry::LineGeometry& line_);
		~ComparableLineGeometry();

		bool operator<(const ComparableLineGeometry& cline_);
		bool operator==(const ComparableLineGeometry& cline_);
		bool operator!=(const ComparableLineGeometry& cline_);
		bool operator>(const ComparableLineGeometry& cline_);
	private:
		PlaneGeometry::LineGeometry m_Line;
	};

	SegmentIntersection();
	~SegmentIntersection();

	static bool Run(const PlaneGeometry::LineGeometry& line1_, const PlaneGeometry::LineGeometry& line2_);
	static bool Run(const DataStruct::Array::DynArray& arrLines_);
};

实现

内部类、构造、析构

算法实现

bool SegmentIntersection::Run(
	const DataStruct::Array::DynArray& arrLines_)
{
	// 基于n个线段,得到2n个含左右属性的端点
	DataStruct::Array::DynArray _arrCritialPoint;
	typedef DataStruct::Tree::SortedBalanceBinaryTree Map;
	typedef DataStruct::Tree::SortedBalanceBinaryTree::Pair MapPair;
	typedef DataStruct::Tree::SortedBalanceBinaryTree::Node MapNode;
	Map _mapPointToLine;
	// 通过n个线段得到2n个事件点
	// 建立事件点到线段的映射
	for (int _i = 0; _i < arrLines_.GetSize(); _i++)
	{
		if (arrLines_[_i].m_poStart.m_nPos[0] < arrLines_[_i].m_poEnd.m_nPos[0])
		{
			LinePoint* _pLPo = new LinePoint(arrLines_[_i].m_poStart, POINT_TYPE::LEFT);
			_arrCritialPoint.Add(_pLPo);
			MapPair _nPair1(_pLPo, arrLines_[_i]);
			_mapPointToLine.Add(_nPair1);
			_pLPo = new LinePoint(arrLines_[_i].m_poEnd, POINT_TYPE::RIGHT);
			_arrCritialPoint.Add(_pLPo);
			MapPair _nPair2(_pLPo, arrLines_[_i]);
			_mapPointToLine.Add(_nPair2);
		}
		else
		{
			LinePoint* _pLPo = new LinePoint(arrLines_[_i].m_poStart, POINT_TYPE::RIGHT);
			_arrCritialPoint.Add(_pLPo);
			MapPair _nPair1(_pLPo, arrLines_[_i]);
			_mapPointToLine.Add(_nPair1);
			_pLPo = new LinePoint(arrLines_[_i].m_poEnd, POINT_TYPE::LEFT);
			_arrCritialPoint.Add(_pLPo);
			MapPair _nPair2(_pLPo, arrLines_[_i]);
			_mapPointToLine.Add(_nPair2);
		}
	}

	// 对2n个事件点
	// 按优先级
	// X位置从小到大
	// 从左端点到右端点
	// Y位置从小到大
	// 排序
	_arrCritialPoint.Sort(
		[](const LinePoint*& lpo1_, const LinePoint*& lpo2_)->int
	{
		double _nDeltaX = lpo1_->m_poPos.m_nPos[0] - lpo2_->m_poPos.m_nPos[0];
		if (_nDeltaX != 0.0)
		{
			if (_nDeltaX > 0.0)
			{
				return 1;
			}
			else
			{
				return -1;
			}
		}

		if (lpo1_->m_Type != lpo2_->m_Type)
		{
			if (lpo1_->m_Type == POINT_TYPE::LEFT)
			{
				return -1;
			}
			else
			{
				return 1;
			}
		}

		double _nDeltaY = lpo1_->m_poPos.m_nPos[1] - lpo2_->m_poPos.m_nPos[1];
		if (_nDeltaY > 0.0)
		{
			return 1;
		}
		else if (_nDeltaY < 0.0)
		{
			return -1;
		}
		else
		{
			return 0;
		}
	}
	);

	typedef DataStruct::Tree::SortedBalanceBinaryTree LineMap;
	typedef DataStruct::Tree::SortedBalanceBinaryTree::Pair LineMapPair;
	typedef DataStruct::Tree::SortedBalanceBinaryTree::Node LineMapNode;
	LineMap _lineMap;
	for (int _i = 0; _i < _arrCritialPoint.GetSize(); _i++)
	{
		if (_arrCritialPoint[_i]->m_Type == POINT_TYPE::LEFT)
		{
			MapNode* _pNode = _mapPointToLine.Search(_arrCritialPoint[_i]);
			LineMapPair _nPair(ComparableLineGeometry(_pNode->GetPair().m_nValue), _pNode->GetPair().m_nValue);
			if (_lineMap.Add(_nPair))
			{
				return true;
			}

			LineMapNode* _pLineNode = _lineMap.Search(ComparableLineGeometry(_pNode->GetPair().m_nValue));
			LineMapNode* _pPreLineNode = _lineMap.Pre(_pLineNode);
			LineMapNode* _pSucLineNode = _lineMap.Suc(_pLineNode);
			if (_pPreLineNode 
				&& SegmentIntersection::Run(_pPreLineNode->GetPair().m_nValue, _pLineNode->GetPair().m_nValue))
			{
				return true;
			}
			else if (_pSucLineNode 
				&& SegmentIntersection::Run(_pSucLineNode->GetPair().m_nValue, _pLineNode->GetPair().m_nValue))
			{
				return true;
			}
		}
		else
		{
			MapNode* _pNode = _mapPointToLine.Search(_arrCritialPoint[_i]);
			LineMapNode* _pLineNode = _lineMap.Search(ComparableLineGeometry(_pNode->GetPair().m_nValue));
			LineMapNode* _pPreLineNode = _lineMap.Pre(_pLineNode);
			LineMapNode* _pSucLineNode = _lineMap.Suc(_pLineNode);
			if (_pPreLineNode
				&& _pSucLineNode
				&& SegmentIntersection::Run(_pPreLineNode->GetPair().m_nValue, _pSucLineNode->GetPair().m_nValue))
			{
				return true;
			}

			_lineMap.Delete(_pLineNode->GetPair().m_nKey);
		}
	}

	for (int _i = 0; _arrCritialPoint.GetSize(); _i++)
	{
		delete _arrCritialPoint[_i];
		_arrCritialPoint[_i] = nullptr;
	}

	return false;
}

算法目标&正确性证明

算法目标:
给定线段集合
判断此线段集合中是否存在相交的线段

算法采用迭代实现,
用循环不变式来证明正确性
循环不变式:
每次迭代开始时,
1. _lineMap维护了一个线段集合,集合内每个线段均和上一事件点相交。
2. 线段集合按和事件点交点y坐标有序存储。
3. 线段集合内任意两个线段不相交。
4. 目前为止尚未检测到线段相交。


一个定理:
如果线段集合存在相交的两个线段,则按上述事件点扫描的方式,
必然会在某个事件点扫描处理后,
相交的两个线段变成_lineMap集合中连续的。
由于算法中会检测到所有_lineMap中线段变为连续的情况。
故,算法可以完成线段集合是否存在交点的检测。

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