65.9K
CodeProject 正在变化。 阅读更多。
Home

Weiler-Atherton 算法 MFC 版

2018年2月25日

CPOL

5分钟阅读

viewsIcon

21382

downloadIcon

946

Weiler-Atherton 算法 MFC 代码演示实现。

引言

多边形裁剪的Weiler-Atherton算法在许多教程中被引用。这个想法看起来不错且简单,但实际编码实现起来并不容易。在上述所有教程中,最好情况下也只能找到一些通用的流程图。本文提供了一个在 MFC 中实现任意多边形裁剪的完整代码。

背景

演示项目WeilerMFC是使用标准的MFC Appwizard**创建的。多边形性能与我在之前CodeProject**文章《Your Own Quadrics in OpenGL MFC》中的class Polygon_2D相同。多边形是使用我在之前CodeProject**文章《50 OpenGL MFC Projects in One》中的Lesson 42**技术随机创建的。

在开始构建提供的项目之前,强烈建议您先看一下附带的演示文稿,以便对预期的输出有所了解。

演示说明

可执行文件WeilerMFC.exe是用MSVS-2015 pro**使用MSVS-2010**的工具构建的。因此,WeilerMFC.exe即使对于Windows-XP**也有效(与我以前的CodeProject文章不同,那些文章不需要特殊的*.dll文件,因为只使用了内存)。

一些菜单和一些特殊的快捷键是为了演示WeilerMFC项目**的实现而设计的。

  • 文件菜单->播放 - 播放/停止对象旋转(也可按Ctrl+P**)
  • 编辑菜单->重置场景 - 创建两个具有随机旋转速率的多边形(也可按空格键)
  • 编辑菜单->开始裁剪 - 停止多边形旋转并开始裁剪(也可按回车键**)
  • 编辑菜单->下一场景 - 下一个场景(如果停止播放;也可按右箭头键)
  • 帮助菜单->帮助 - 显示帮助对话框(也可按F1**)

帮助对话框是非模态的,因此您可以直接使用菜单和快捷键命令,或者按帮助对话框中的OK**按钮(或双击对应项)。

构建说明

解决方案配置必须设置为 **Release**,平台设置为 **x86**。

提供的项目是使用MSVS-2015 pro**和MSVS-2010**的工具开发的。因此,EXE**文件即使对于Windows-XP**也有效。如果您不需要在Windows-XP**中运行该应用程序,只需将工具更改为MSVS-2015**即可。

默认编码属性为 UNICODE;尽管 MBCS 编码也可用,只需更改属性即可。

即使您是第一次使用MSVS**,只需选择菜单Debug**->Startwithout debugging,程序GLPlane.exe就应该可以开始构建和工作了。

项目源文件和数据存储

WeilerMFCproj路径下的标准源代码是使用标准的MFC Application Wizard**创建的。

  • WeilerMFC.cpp - 定义了应用程序的标准类行为。
  • MainFrm.cpp - 实现了标准的CMainFrame class
  • CChildView.cpp - 实现了标准的CWnd class;消息处理程序由作者使用标准的MFC Application Wizard**程序创建。
  • DlgHelp.cpp - 实现了标准的非模态CDialogEx class;消息处理程序由作者使用标准的MFC Application Wizard**程序创建。
  • WeilerMFC.rcresource.h - 菜单、对话框、快捷键资源由作者使用标准的Resource Wizard**程序创建。

WeilerMFCProj\GlobUse路径下的特殊源代码是作者基于标准的graphics**和几何例程开发的。

  • Vector_2D.cpp, Poligon_2D.cpp - 2D 对象处理

代码解释

以下所有菜单快捷键命令均使用标准的MFC AppWizard**技术实现。

1. 初始化应用程序。

CChildView class的变量声明在CChildView.h中。

CDC m_dcMem;          //memory device context
CBitmap m_memBmp;     //memory bitmap
CBitmap * m_pMemBmp;  //reference to device memory bitmap
BOOL m_bPlay;         //Flag of the scene to play
CDlgHelp * m_pDlgHelp;//reference to non-modal Help Dialog

全局变量声明在CChildView.cpp中。

CChildView * m_pView = NULL;  //reference this CChildView window
CObList m_polygonList;        // object list of polygons to draw

变量的初始化发生在CChildView class创建过程中。

int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CWnd::OnCreate(lpCreateStruct) == -1)
  return -1;
 m_pView = this;                              //reference to this CChidView window
 srand((unsigned)time(NULL));                 //set random seed 
 m_dcMem.CreateCompatibleDC(GetDC());         //create memory device context
 //create memory device bitmap:
 m_memBmp.CreateCompatibleBitmap(GetDC(),m_dcMem.GetDeviceCaps(HORZRES),
                                m_dcMem.GetDeviceCaps(VERTRES));
 m_pMemBmp = m_dcMem.SelectObject(&m_memBmp); //select memory device bitmap object 
 SetTimer(ID_TIMER_PLAY, 50, NULL);           //start timer for 20 scenes per second
 m_pDlgHelp = new CDlgHelp;                   //non-modal Help Dialog initialization
 return 0;
}   

2. 绘制场景过程

由虚拟过程OnPaint调用的绘图过程。

void CChildView::OnPaint() 
{
CPaintDC dc(this); // device context for painting
DrawMyScene(&dc);
}
void CChildView::DrawMyScene(CDC * pDC)
{
 CRect rect;                       //this window rectangle
 GetClientRect(&rect);             //get rect sizes
 DrawScene(&m_dcMem, rect);        //draw scene into memory device context
 //copy scene from memory device context into target context pDC;
 pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &m_dcMem, 0, 0, SRCCOPY);
}
void DrawScene(CDC * pDC, CRect rect)
{
 pDC->FillRect(rect, &CBrush(RGB(255, 245, 221)));                   //install background colour
 for (POSITION pos = m_polygonList.GetHeadPosition(); pos != NULL;)  //for all the polygons:
 {
  Polygon_2D * ppl = (Polygon_2D *)m_polygonList.GetNext(pos);       //get next polygon 
  ppl->DrawSolidStroke(pDC, Vector_2D(1), 0, 
      ppl->turnPoint, 0., 1., 1., FALSE, TRUE); //draw polygon
 }
}

其思想是Polygon_2 class**具有一个分形结构,其中包含m_PlaneList**对象列表,该列表包含子Polygon_2D**对象,并调用相同的DrawSolidStroke**过程进行绘制。

BOOL Polygon_2D::DrawSolidStroke(CDC * pDC, Vector_2D scl, int prrt, Vector_2D parentCenter,
 double parentAngle, double parentCoefX, double parentCoefY)
{
 Vector_2D myCenter = parentCenter;
 double myAngle = parentAngle + angle;
 double csn = cos(myAngle);
 double sns = sin(myAngle);
 double myCoefX = coefX * parentCoefX;
 double myCoefY = coefY * parentCoefY;
  ............................................................    
 for (POSITION pos = m_PlaneList.GetHeadPosition(); pos != NULL;)
 {
    Polygon_2D * pl = (Polygon_2D *)m_PlaneList.GetNext(pos);
    pl->DrawSolidStroke(pDC, scl, prrt, myCenter, myAngle, myCoefX, myCoefY);
 }
	return TRUE;
}

3. 随机多边形对象创建

使用编辑菜单->重置场景**命令(也可按空格键**),删除所有先前的多边形,并创建两个具有随机旋转速率的新多边形。

多边形是使用我在以前的CodeProject**文章《50 OpenGL MFC Projects in One》中的Lesson 42**技术随机创建的。

void CreateRandomPoly(void)
{
 CRect rect(50, 50, 600, 480);
 int marg = 50;
 Polygon_2D * pNew = new Polygon_2D;
 pNew->CreateRandomPoly(CRect(rect.left + marg, rect.top + marg, 
      rect.right - marg, rect.bottom - marg));
 m_polygonList.AddTail(pNew);
 CRect rct;
 m_pView->GetClientRect(&rct);
 pNew->turnPoint = Vector_2D((rct.left + rct.right) / 2, (rct.top + rct.bottom) / 2);
}

4. 多边形裁剪

使用编辑菜单->开始裁剪**命令(也可按回车键**),停止多边形旋转并开始裁剪过程。

在上图中,执行了经典的Weiler-Atherton演示:品红色线段显示红色多边形在蓝色多边形内的点和线段;浅蓝色线段显示蓝色多边形在红色多边形内的点和线段。

用于判断多边形内的点和线段的关键过程。

void Polygon_2D::IntersectSegmentList(Polygon_2D * pT, Polygon_2D * pRslt)
{
 //Polygon_2D * pT - target polygon
 //Polygon_2D * pRslt - result set of polygons consisted of points 
 //and segments of this polygon inside the target
 if (pT == NULL)
  return;
 Polygon_2D * pStroke = NULL;          //polygon of points and lines of pT inside this  polygon 
 BOOL bChain = FALSE;                  //flag of is current chain polygon continue
 for (POSITION pos = m_PointList.GetHeadPosition(); pos != NULL;)
 {
  Vector_2D * pti = (Vector_2D *)m_PointList.GetNext(pos);//current point
  Vector_2D * ptiN = GetNextPoint(pti, TRUE);             //next point
  if (dist2(pti, ptiN)< GeomTreshold)     //if current == next skip
   continue;
  Polygon_2D sCross;                      //polygon of intersection 
                                          //segment(pti, ptiN) with target polygon pT
  pT->IsCrossStroke(pti, ptiN, &sCross);  //if segment intersect target
  if (pT->IsPointInside(pti))             //if current point inside target
  {
   if (!bChain)                           //if chain broken
    if (sCross.m_PointList.GetCount() || pT->IsPointInside(ptiN))//if segment intersect target 
    {
     pStroke = new Polygon_2D;            //start new polygon chain
     pStroke->fillStyle = HS_TRANSPARENT;
     pStroke->penWidth = 3;
     pStroke->fgColor = pRslt->fgColor;
     pStroke->m_bClosed = FALSE;
     pRslt->m_PlaneList.AddTail(pStroke); //new polygon chain appended to result polygon
     bChain = TRUE;
    }//if(!bChain)
   if (bChain)
    pStroke->AppendNewPoint(pti);         //current point append to current polygon chain
  }//if(pT->IsPointInside(pti))
  else
   bChain = FALSE;

  while (sCross.m_PointList.GetCount())   //check all intersection points
  {
   Vector_2D * pN = (Vector_2D *)sCross.m_PointList.GetHead();
   sCross.m_PointList.RemoveHead();
   if (pN->dist(*pti) < GeomTreshold)
    continue;
   if (pStroke != NULL)
    if (pStroke->m_PointList.GetCount())
    {
     Vector_2D * pvl = (Vector_2D *)pStroke->m_PointList.GetTail();
     Vector_2D vd = (*pvl + *pN)*0.5;
     if (bChain)
      bChain = pT->IsPointInside(&vd);
    }

   if (!bChain)   //if chain broken
   {
    pStroke = new Polygon_2D;             //start new polygon chain
    pStroke->fillStyle = HS_TRANSPARENT;
    pStroke->penWidth = 3;
    pStroke->fgColor = pRslt->fgColor;
    pStroke->m_bClosed = FALSE;
    pRslt->m_PlaneList.AddTail(pStroke);  //new polygon chain appended to result polygon
    bChain = TRUE;
   }//if(!bChain)

   pStroke->AppendNewPoint(pN);           //intersection point append to current polygon chain
  }//while(sCross.m_PointList.GetCount())

  if (pT->IsPointInside(ptiN))            //if next point inside target
  {
   if (!bChain)
   {
    pStroke = new Polygon_2D;             //start new polygon chain    
    pStroke->fillStyle = HS_TRANSPARENT;
    pStroke->penWidth = 3;
    pStroke->fgColor = pRslt->fgColor;
    pStroke->m_bClosed = FALSE;

    pRslt->m_PlaneList.AddTail(pStroke);  //new polygon chain appended to result polygon
    bChain = TRUE;
   }
   pStroke->AppendNewPoint(ptiN);         //next point append to current polygon chain
  }
  else
   bChain = FALSE;
 }//for (POSITION pos = m_PointList.GetHeadPosition(); pos != NULL;)
}

在下一张图中,为了演示目的,一组链式多边形稍微错开了。

这组链式多边形的开始和结束点通过Polygon_2D::MergeChains(Polygon_2D * pPl)过程相互连接,如图所示。

结果是本篇文章标题图中所示的黄色多边形集合。

使用提供的项目开发您自己的应用程序。

您可以获取整个项目,将其重命名为我的前一篇Code Project**文章《MFC Project from Existing Code in One Click》的项目,并根据需要组合和改进代码。

或者,您可以获取此项目中的GlobUse 目录,并通过菜单Project**->Existing Item**将其包含到**您自己的任何图形项目中**。

如果您引用了我的代码,将不胜感激。

关注点

我相信这个演示和代码对于软件开发人员在多边形裁剪方面会很有帮助。

该项目是在MFC平台**上开发的。尽管如此,在MFC**中开发的所有内容都可以转换为Win32**,反之亦然。

历史

我以前所有的CodeProject文章都是本文的前身。

我相信本文是我即将发表的文章的前奏。

工作将继续...

© . All rights reserved.