昨天发现邓俊辉译的计算几何上讲的凸包算法非常清晰简洁,今天也尝试写一个。
判断向量是左转还是右转,最简便的方法是用叉乘,叉乘小于0表示右转,=0表示共线,大于0表示左转。
在MFC上做的时候还要注意坐标的问题,逻辑上的上凸包画出来实际上是下凸包,下凸包对应于图上的上
凸包。
bool operator < (CPoint& pt1, CPoint& pt2)
{
long diff = pt1.x - pt2.x;
if (diff < 0)
{
return true;
}
else if (!diff)
{
return pt1.y < pt2.y;
}
else
{
return false;
}
}
bool operator ==(CPoint& pt1, CPoint& pt2)
{
return pt1.x == pt2.x && pt1.y == pt2.y;
}
void CConvexHullTestView::DrawPts(CDC* pDC)
{
for (std::vector<CPoint>::iterator itr = m_vecPts.begin(); itr != m_vecPts.end();
++itr)
{
pDC->Ellipse(itr->x - 3, itr->y - 3, itr->x + 3, itr->y + 3);
}
}
void CConvexHullTestView::OnDraw(CDC* pDC)
{
CConvexHullTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
this->DrawPts(pDC);
if (!m_bDrawHull)
{
return;
}
std::sort(m_vecPts.begin(), m_vecPts.end());
std::unique(m_vecPts.begin(), m_vecPts.end());
if (m_vecPts.size() < 3)
{
m_bDrawHull = false;
return;
}
std::list<CPoint> lowerhull;
lowerhull.push_back(m_vecPts[0]);
lowerhull.push_back(m_vecPts[1]);
std::list<CPoint>::iterator itrlast, itrmid, itrfirst;
CPoint a, b;//向量a,b
long i;
for (i = 2; i < m_vecPts.size(); ++i)
{
lowerhull.push_back(m_vecPts[i]);
itrlast = lowerhull.end();
--itrlast;
while (lowerhull.size() >= 3)
{
itrmid = itrlast;
itrfirst = --itrmid;
--itrfirst;
a.x = itrmid->x - itrfirst->x;
a.y = itrmid->y - itrfirst->y;
b.x = itrlast->x - itrmid->x;
b.y = itrlast->y - itrmid->y;
if (a.x * b.y - a.y * b.x >= 0) //叉乘大于0向左转,=0共线
{
lowerhull.erase(itrmid);
}
else
{
break;
}
}
}
for (itrfirst = lowerhull.begin(), itrlast = itrfirst, ++itrlast; itrlast !=
lowerhull.end(); itrfirst = itrlast, ++itrlast)
{
pDC->MoveTo(*itrfirst);
pDC->LineTo(*itrlast);
}
std::list<CPoint> upperhull;
upperhull.push_back(m_vecPts[m_vecPts.size() - 1]);
upperhull.push_back(m_vecPts[m_vecPts.size() - 2]);
for (i = m_vecPts.size() - 3; i >= 0; --i)
{
upperhull.push_back(m_vecPts[i]);
itrlast = upperhull.end();
--itrlast;
while (upperhull.size() >= 3)
{
itrmid = itrlast;
itrfirst = --itrmid;
--itrfirst;
a.x = itrmid->x - itrfirst->x;
a.y = itrmid->y - itrfirst->y;
b.x = itrlast->x - itrmid->x;
b.y = itrlast->y - itrmid->y;
if (a.x * b.y - a.y * b.x >= 0) //叉乘大于0向左转,=0共线
{
upperhull.erase(itrmid);
}
else
{
break;
}
}
}
for (itrfirst = upperhull.begin(), itrlast = itrfirst, ++itrlast; itrlast !=
upperhull.end(); itrfirst = itrlast, ++itrlast)
{
pDC->MoveTo(*itrfirst);
pDC->LineTo(*itrlast);
}
}
CConvexHullTestView::CConvexHullTestView()
{
// TODO: add construction code here
m_bDrawHull = false;
m_vecPts.push_back(CPoint(200,300));
m_vecPts.push_back(CPoint(400,600));
m_vecPts.push_back(CPoint(500,30));
m_vecPts.push_back(CPoint(550,400));
m_vecPts.push_back(CPoint(600,130));
m_vecPts.push_back(CPoint(610,450));
m_vecPts.push_back(CPoint(740,530));
}
void CConvexHullTestView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
if (m_bDrawHull)
{
m_vecPts.clear();
m_bDrawHull = false;
}
m_vecPts.push_back(point);
this->Invalidate(TRUE);
}
void CConvexHullTestView::OnRButtonDblClk(UINT nFlags, CPoint point)
{
m_bDrawHull = true;
this->Invalidate(TRUE);
}
第一个graham扫描法求的凸包程序
最新推荐文章于 2024-09-06 13:10:24 发布