VC++点一线编辑联动

作者:zhu

1 引言

    很多的图形应用如统筹图应用、GIS应用、标图应用中都存在着编辑联动的问题。所谓编辑联动,是指在对一个图元编辑修改包括位置的变动后,其他图元由于与这个图元有某种关系,而能自动同步被修改。按图元类型,可将图形联动划分为点一点、点一线、点一面、线一线、线一而、面。面6种。文中以统筹图为例,讨论节点一箭线联动编辑问题。
    节点与箭线图元的实现以VC自带的面向对象图形例程DRAWCLI为基础,添加自定义类CDrawNode和CDrawArrow来实现。依据功能设计,程序的主要工作包括:
    (1)基本图元的绘制。包括绘制节点和箭线。绘制的节点图形表示为圆心标有编号圆圈;箭线的两侧标注有文字说明,且文字行与箭线保持平行,箭线带有起节点和终节点。
    (2)处理图元及相互位置关系。包括删除操作和移动操作。删除某一节点,与该节点相连的箭线全部删除,且调整编号在其后面的节点编号,删除箭线不影响节点。移动节点时,与之相连的箭线相应移动,其关键是准确计算箭线的位置。

2基本图元及联动

2.1 基本图元实现
    设在VC++ 6.0的环境下已建立了工程Draw.dsp。节点类和箭线类都是以CDrawObj为基类派生。在建立这两个类之前,需要添加以下全局变量:

    enum DrawShape{...,circle,arrow,…};//图元类型以及相应
    //的工具circleTool和arrowTool(arrow)、arrowlineTool

    2.1.1 节点类CDrawNode的设计
    class CDrawNode:public CDrawObj
    {
      public:
      CDrawNode(const CRect& position,int Radius);
      void SetCircleIndex(int Index){m_nIndex=Index};
        ∥设置节点编号
      virtual int GetObjIndex(){retum m_nindex};
        //获取编号
      public:
        int m_Radius;∥节点半径
        int m_CentreX,m_CentreY;∥圆心
        ∥在圆中心写上编号,要考虑编号的位数情况
        virtual void Draw(CDC* pDC,CDrawView* pView);
      protected:
        int m_nIndex;//编号

        CDrawNode* m_pDrawObj;
        friend class CCircleTool;
    };

    绘制节点的工具CCircleTool在鼠标左键按下OnLButtonDown的消息处理函数中,实际分配内存,产生一个节点对象。

    2.1.2 箭线类CDrawArrow的设计
    class CDrawArrow: public CDrawObj
    {
      public:
       CDrawRect(const CRect& position);
       void SetProjectName(CString name)
         {m_ProjectName=name;};
       void SetProjectTime(int time) {m_ProjectTime=time;};
       CString GetProjectName(){return m_ProjectName;}
       void SetProjectTime(int time){return m_ProjectTime;}
       bool m_bForeConnect;//箭线始节点标志
       bool m_bBackConnect;//箭线终节点标志
       int m_ForeConnect;//箭线连接的始节点序号
       int m_BackConnect;//箭线连接的终节点序号
       virtual int GetObjIndex(){return o};
    //绘制时,计算平行于箭线的说明文字的角度、位置
    //项目名称总在线的上方
       virtual void Draw(CDC* pDC,CDrawView* pView);
       enum ArrowType{ none, pureArrow, openArrow,
        stealthArrow, diamondArrow, ovaLArrow};
       enum Shape{ rectangle, roundRectangle, ellipse,
        line,arrow };
      protected
        CString m_ProjectName;  //项目名称
        int m_ProjectTime;    //时间
        Shape m_nShape;
        ArrowType m_lArrow, m_rArrow;   //for arrow only
         int m_lArrowSize, m_rArrowSize; //for arrow only
      private:
        void DrawArrow(ArrowType arrowType, POINT pl,
         POINT p2,int arrowSize, CDC *,int,int,int,CBrush *);
        friend class CArrowTool;
    };

    绘制箭线的工具CArrowTool在OnLButtonDown中实际生成箭线对象,并记录箭线所连的开始节点,箭线的终节点确定在后面说明。

2.2图元联动操作

    2.2.1删除节点及箭线
    void CDrawView::OnEditClear()
    {
      CDrawDoc* pDoc=(CDrawDoc*)GetDocument();
      POSITION pos=m_selection.GetHeadPosition();
      while (pos!=NULL)
      {
        CDrawObj* pObj=m_selection.GetNext(pos);
        GetDocument()->Remove(pObj); //删除节点
        CDrawNode* pDrawCircIe=(CDrawNode*)pObj;
        nIndex=pDrawCircle->GetObjInclex();
        //获取节点编号,若不是节点对象,返回O
        if(nIndex)
        { 
          pDoc->m_nIndex=nIndex;
          pos=GetDocument()->m_objects.GetHeadPosition();
          int i=0;
          int num=pDoc->m_objects.GetCount();
          m_num=pDoc->m_nIndex;
          while(i < num)
          {//序号位于被删除节点之后的所有节点序号减1
          CDrawNode* pDrawCircle=(CDrawNode*)pDoc->
            m_objects.GetNext(pos);
           if(pDrawCircle->GetObjIndex()>m_num)
           {
            pDraeCircle->SetCircleIndex(m_num);
            m_num++;
           }
           i++;
         }
         pos=pDoc->m_objects.GetHeadPosition();
         if(pos!=NULL)
         {
           i=0;
           num=pDoc->m_objectsGetCount();
           while(i < num)
           {∥删除与节点相连的所有箭线
           CDrawRect* pDrawRect=(CDrawRect*)pDoc->
             m_objects.GetNext(pos);
            if(pDrawRect!=NULL)
            {
              if(nIndex==pDrawRect->m_ForeConnect)
              {
                pDoc->Remove(pDrawRect);
                pDrawRect->Remove();
              }
              if(nIndex==pDrawRect->m_BackConnect)
              {
                pDoc->Remove(pDrawRect);
                pDrawRect->Remove();
              }
              if(nIndex < pDrawRect-> m_ForeConnect)
              {
                pDrawRect->m_ForeConnect--;
              }
              if(nIndex < pDrawRect-> m_BackConnect)
              {
                pDrawRect->m_BackConnect--;
              }
         }
         i++;
           }
         }
         Invalidate();
         pDoc->m_nIndex=m_num;
        }
        pObj->Remove();
        }
        m_selection.RemoveAll();
        UINT nFlags;
        OnLButtonDown(nFlags,CPoint(0,0));
        OnLButtonUp(nFlags,CPoint(0,0));
    }

    2.2.2用鼠标移动某一节点时,同步移动与其相连接的箭线
    void CSelectTool::OnMouseMove(CDrawView* pView,
      UINT nFlags, const CPoint& point)
    {
      if (pView->GetCapture() != pView)
      {
        ...
      CPoint local=point;
      int BackConnect;
      pView->ClientToDoc(local);
      POSITION pos;
      if(c_drawShape == arrow)
      //判断鼠标移动绘制箭线过程中是否与某一节点相连 
      //此时函数是 CRectTool::OnMouseMove()调用
      //selectTool.OnMouseMove();而跳到这里
      {
        CRect circlerect;
        CDrawNode* pForeCircle;
        CDrawNode* pBackCircle;
        pos=pView->GetDocument()->
          m_objects GetHeadPosition();
        if(pos!=NULL)
        {
          int i=0;
          int num=pView->GetDocument()->
            m_objects.GetCount();
          while(i < num)
          {
            CDrawNode* pDrawCircle=(CDfawNode*)
              pView->GetDocument()->m_objects
              GetNext(pos);
            if(pDrawCircle!=NULL)
            {
              int index=pDrawCircle->GetObjIndex();
              if(index)
              {//判断
                circlerect=pDrawCircle->                m_position;
                pView->DocToClient(circlerect);
                if(circlerect.PtInRect(point))
                {
                  local=pDrawCircle->GetHandle(8);
                  BackConnect=pDrawCircle->
                    GetObjIndex();
                }
    }}i++;}}
      CPoint delta =(CPoint)(local - lastPoint);
      pos=pView->m_selection.GetHeadPosition();
      while (pos!=NULL)
      {
        CDrawObj* pObj = pView->m_selection.GetNext(pos);
        CRect position=pObj->m_position;
        if(c_drawShape==arrow)
        {∥记录终节点
          pObj->m_BackConnect=BackConnect;
          pObj->m_bBackConnect=true;
        }
        //以下实现移动节点,使所有的箭线跟随节点同步
        //移动
        if(selectMode==move)
        {
          if(pView->m_selection.GetCount()==l)
          {
            CDrawNode*pDrawCircle=(CDrawNode*)pObj;
            int Index=pDrawCircle->GetObjIndex();
            if(index)    ∥移动节点
            {
        POSITION Rectpos=pView->GetDocument()->
            m_objects.GetHeadPosition();
              if(Rectpos!=NULL)
              {
                int i=0;
                int num=pView->GetDocument()->
                  m_objects.GetCount();
                while(i< num)
                {//所有与之相连的箭线同步移动
                CDrawRect*pDrawObj=(CDrawRect*)
                  pView->GetDocument()->m_
                  objects.GetNext(Rectpos);
                if(pDrawObj!=NULL)
                {
                                    if(Index==pDrawObj->m_ForeConnect)
                  {//节点是箭线始节点
                   CRect deltarect=pDrawObj->m_position;
                     deltarect.BottomRight()+=delta;
                  int length=deltarect.top-deltarect.bottom;
                  int width=deltarect.left-deltarect.right;
            //节点移动,箭线与节点的连接点改变,计算
            //连接点位置
            if(abs(length) < abs(width)&&width < 0)
              deltarect.BottomRight()=pDrawCircle->
              GetHandle(8);
            if(abs(length) < abs(width)&&width > 0)
              deltarect.BottomRight()=pDrawCircle->
              GetHandle(4);
            if((abs(length) > abs(width)&&width >
              O&&length < O)|| (abs(length) > abs(width)&&
              width < O&&length < 0))
               deltarect.BottomRight()=pDrawCircle->
                GetHandle(2);
            if((abs(length)>abs(width)&&length>
              O&&width > 0)||(abs(length)>abs(width)
              &&length > O&&width < 0))
    deltarect.BottomRight()=pDrawCircle->GetHandle(6);
      int nIndex=pDrawObj->m_BackConnect;
      POSITION circlepos=pView->GetDocument()->
        m_objects.GetHeadPosition();
      if(circlepos!=NULL)
        {
        int i=0;
        int num=pView->GetDocument()->
          m_objects.GetCount();
        while(i < num)
        {
         CDrawNode* pDrawNode=(CDrawNode*)pView->
         GetDocument()->m_objects.GetNext(circiepos);
         if(pDrawNode!=NULL)
         {
          if(nIndex==pDrawNode->GetObjindex())
          {//计算箭线的新位置
          if(abs(length) < abs(width)&&width < 0)
          deltarect.TopLeft()=pDrawNode->GetHandle(4);
          if(abs(length) < abs(width)&&width > 0)
          deltarect.TopLeft()=pDrawNode->GetHandle(8);
          if((abs(length)>abs(width)&&width>
            O&&length < O)|| (abs(length)>abs(width)
            &&width < O&&length < 0))
          deltarect.TopLeft()=pDrawNode->GetHandle(6);
          if((abs(length)>abs(width)&&length>
            O&&width>O)||(abs(length)>abs(width)
            &&length>O&&width<0))
          deltarect.TopLeft()=pDrawNode->
            GetHandle(2);
      }}i++;}}}
    pDrawObj->MoveTo(deltarect,pView);
    }
    if(Index==pDrawObj->m_BackConnect)
    {//节点是箭线终节点
      CRect deltarect=pDrawObj->m_position;
      deltarect.TopLeft()+=delta;
      int length=deltarect.top-deltarect.bottom;
      int width=deltarect.left-deltarect.right;
      if(abs(length) < abs(width)&&width < 0)
      deltarect.TopLeft()=pDrawCircle->GetHandle(4);
      if(abs(length) < abs(width)&&width > 0)
      deltarect.TopLeft()=pDrawCircle->GetHandle(8);
      if((abs(length)>abs(width)&&width>O&&length<0)||
        (abs(length>abs(width)&&width < O&&length < 0))
      deltarect.TopLeft()=pDrawCircle->GetHandle(6);
      if((abs(length)>abs(width)&&length>O&&width>O)||
      (abs(length)>abs(width)&&length>O&&width<0))
      deltarect.TopLeft()=pDrawCircle->GetElandle(2);
      int nIndex=pDrawObj->m_ForeConnect;
      POSITION circlepos=pView->GetDocument()->
        m_objects.GetHeadPosition();
      if(pos!=NULL) {
        int i=0,
        int num=pView->GetDocument()->
          m_objects GetCount();
            while(i < num)
          {CDrawNode* pDrawNode=(CDrawNode*)
           pView->GetDocument()->m_objects.
           GetNext(circlepos);
          if(pDrawNode!=NULL)
          {
          if(nIndex==pDrawNode->GetObjindex())
           {
            if(abs(Length) < abs(width)&&width < 0)
            deltarect.BottomRight()=pDrawNode->
             GetHandle(8);
            if(abs(length) < abs(width)&&width > 0)
            deltarect.BottomRight()=pDrawNode->
             GetHamile(4);
            if((abs(length)>abs(width)&&width>
             O&&length<0)|| (abs(length)>
             abs(width)&&width < O&&length  0))
            deltarect.BottomRight()=pDrawNode->
             GetHandle(2);
            if((abs(iength)>abs(width)&&length>
             O&&width>O)||(abs(length)>abs(width)
             &&length>O&&width<0))
            deltarect.BottomRight()=pDraxwNode->
             GetHandle(6);
        }}i++;}}}
    pDrawObj ->MoveTo(deltarect,pView);       } }i++; } } } }
        position += delta;
        pObj->MoveTo(position, pView);
      }
      else if (nDragHandle != O) pObj->MoveHandleTo
        (nDragHandle, local, pView);
      }
     lastPoint = Iocal;
     if(selectMode == size && c_drawShape==selection)
     {
       c_last = point;
       SetCursor(pView->m_selection.GetHead()->
         GetHandleCursor(nDragHandle));
       return; //bypass CDrawTool
     }
     c_last=point;
     if(c_drawShape==selection)
       CDrawTool::OnMouseMove(pView, nFlags, point) ;
    }

3 结束语

    至此,基本上就达到了预定目标。图l是按以上叙述实现的绘制统筹图程序的运行图,示出了正在对编号为2的节点进行移动或删除的联动操作。本示例中没有对实现的效率问题有过多考虑,程序在MicrosoftWindows 2000和Visual C++ 6.0下完成。


图1绘制统筹图程序运行界面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值