现在的位置: 首页 > 综合 > 正文

使用数学方法绘制一光栅盘

2013年10月10日 ⁄ 综合 ⁄ 共 6471字 ⁄ 字号 评论关闭

//前两天看论坛上有人问怎么程序画齿轮的问题,觉得蛮有意思的,所以自己索性也画一个来玩玩
//其实那严格说来不是齿轮,而是光栅编码器的光栅盘
//程序以三角函数为基础,可以绘制任意角度下的任意齿数光栅盘
//多余的话就不说了,上代码和图
 

 

#include <math.h>

//绘制光栅盘
//pDC目标DC
//rect目标区域
//fAngleDeg 基线角度
//TGrid 光栅栅格数
void DrawGridCoder(CDC *pDC, CRect &rect, double fAngleDeg, int TGrid=64)
{
  #define PI (4*atan(1.0)) //π
  #define AngDeg2Rad(x) ((x)*PI/180.0) //角度换算到弧度
  #define AngRad2Deg(x) ((x)*180.0/PI) //弧度换算到角度
  
  //基线角度换算为弧度
  while(fAngleDeg > 360)fAngleDeg-=360;
  while(fAngleDeg < 0) fAngleDeg+=360;
  double fAngleRad = AngDeg2Rad(fAngleDeg); //

  //保存DC
  int iSaveDC = pDC->SaveDC();

  //填充背景色
  pDC->FillSolidRect(rect, RGB(255, 255, 255)); 

  //坐标映射 X轴向右 Y轴向上 中心点取rect的中心
  int OldMapMode = pDC->SetMapMode(MM_ISOTROPIC);
  CSize ptOldWinExt = pDC->SetWindowExt(1700, 1700);
  CSize ptOldViewportExt = pDC->SetViewportExt(rect.Width(), -rect.Height());
  CPoint ptOldOrigin = pDC->SetViewportOrg(rect.Width()/2, rect.Height()/2);

  //创建和选入画笔
  CPen LinePen(PS_SOLID, 1, RGB(200, 200, 0));
  CPen *pOldPen = pDC->SelectObject(&LinePen);

  //小数四舍五入
  #define CutToLong(x) (LONG)(((x) >= 0)? ((x)+0.5):((x)-0.5))

  //绘制带限位缺口的内壁
  {
    double fDeltRad = AngDeg2Rad(10.0); //
    double fAngStart = fAngleRad + fDeltRad;
    double fAngEnd = fAngleRad - fDeltRad;
    int r1 = 60;
    int r2 = r1 + 20;

    POINT pt1 = { CutToLong(r1 * cos(fAngStart)), CutToLong(r1 * sin(fAngStart)) };
    POINT pt2 = { CutToLong(r1 * cos(fAngEnd)), CutToLong(r1 * sin(fAngEnd)) };
    double fDAng2 = fDeltRad - asin((r1 * sin(fDeltRad)) / r2);
    double fAngStart2 = fAngStart - fDAng2;
    double fAngEnd2 = fAngEnd + fDAng2;
    POINT pt3 = { CutToLong(r2 * cos(fAngStart2)), CutToLong(r2 * sin(fAngStart2)) };
    POINT pt4 = { CutToLong(r2 * cos(fAngEnd2)), CutToLong(r2 * sin(fAngEnd2)) };

    pDC->MoveTo(pt2); pDC->LineTo(pt4); pDC->LineTo(pt3); pDC->LineTo(pt1);
    pDC->AngleArc(0, 0, r1, (float)AngRad2Deg(-fAngStart), -(float)(360-2*AngRad2Deg(fDeltRad)));
  }

  //绘制圆
  {
    int r3 = 100;
    pDC->MoveTo(r3, 0);
    pDC->AngleArc(0, 0, r3, 0, 360);
  }

  //绘制4个小标注孔
  {
    int r4 = 300;
    int r5 = 10;
    POINT pt5 = { CutToLong(r4 * cos(fAngleRad)), CutToLong(r4 * sin(fAngleRad)) };
    pDC->MoveTo(pt5.x + r5, pt5.y);
    pDC->AngleArc(pt5.x, pt5.y, r5, 0, 360);

    double fDivAngRad = AngDeg2Rad(360.0/8.0);
    double fAngRad1 = fAngleRad+fDivAngRad/2;
    double fAngRad2 = fAngleRad-fDivAngRad/2;

    double r6 = r4 / cos(fDivAngRad/2);
    POINT pt6 = { CutToLong(r6 * cos(fAngRad1)),  CutToLong(r6 * sin(fAngRad1)) };
    pDC->MoveTo(pt6.x + r5, pt6.y);
    pDC->AngleArc(pt6.x, pt6.y, r5, 0, 360);

    POINT pt7 = { CutToLong(r6 * cos(fAngRad2)),  CutToLong(r6 * sin(fAngRad2)) };
    pDC->MoveTo(pt7.x + r5, pt7.y);
    pDC->AngleArc(pt7.x, pt7.y, r5, 0, 360);

    int r7 = 470;
    POINT pt8 = { CutToLong(r7 * cos(fAngleRad)), CutToLong(r7 * sin(fAngleRad)) };
    pDC->MoveTo(pt8.x + r5, pt8.y);
    pDC->AngleArc(pt8.x, pt8.y, r5, 0, 360);
  }

  //绘制8个大孔
  {
    double fDivAngRad = AngDeg2Rad(360.0/8.0);
    int r8 = 500;
    int r9 = 50;
    double fAngStart3 = (fAngleRad + fDivAngRad/2);
    for(int i=0; i<8; i++)
    {
      POINT pt8 = {CutToLong(r8 * cos(fAngStart3)), CutToLong(r8 * sin(fAngStart3))};
      pDC->MoveTo(pt8.x + r9, pt8.y);
      pDC->AngleArc(pt8.x, pt8.y, r9, 0, 360);
      fAngStart3 += fDivAngRad;
    }
  }

  //绘制大圆
  {
    int r10 = 600;
    POINT pt9 = {CutToLong(r10 * cos(fAngleRad)), CutToLong(r10 * sin(fAngleRad))};
    pDC->MoveTo(r10, 0);
    pDC->AngleArc(0, 0, r10, 0, 360);
  }

  //绘制齿
  {
    int r11 = 800;
    int r12 = r11 - 50;
    int r13 = r12 - 100;

    double fDivAngRad = AngDeg2Rad( 360.0/TGrid );
    double fAngStart4 = (fAngleRad + fDivAngRad/2);
    double fDAng5 = fDivAngRad/8;
    double fDAng6 = fDivAngRad/2 - fDAng5;

    for(int i=0; i<TGrid; i++)
    {
      double f6 = fAngStart4 - fDivAngRad/2;
      double f5 = fAngStart4 - fDivAngRad/4;

      if(i==0)
      {
        pDC->MoveTo(CutToLong(r13*cos(f6)), CutToLong(r13*sin(f6)));
      }

      pDC->AngleArc(0, 0, r13, (float)AngRad2Deg(-f6), (float)AngRad2Deg(f6-f5));
      
      POINT pt6 = {CutToLong(r12 * cos(f5)), CutToLong(r12 * sin(f5))};
      pDC->LineTo(pt6);
  
      double f4 = fAngStart4 - fDAng6;
      POINT pt5 = {CutToLong(r12 * cos(f4)), CutToLong(r12 * sin(f4))};
      pDC->LineTo(pt5);

      POINT pt4 = {CutToLong(r11 * cos(f4)), CutToLong(r11 * sin(f4))};
      pDC->LineTo(pt4);

      double f3 = fAngStart4 + fDAng6;
      pDC->AngleArc(0, 0, r11, (float)AngRad2Deg(-f4), (float)AngRad2Deg(f4-f3) );

      POINT pt3 = {CutToLong(r12 * cos(f3)), CutToLong(r12 * sin(f3))};
      pDC->LineTo(pt3);

      double f2 = fAngStart4 + fDivAngRad/4;
      POINT pt2 = {CutToLong(r12 * cos(f2)), CutToLong(r12 * sin(f2))};
      pDC->LineTo(pt2);

      POINT pt1 = {CutToLong(r13 * cos(f2)), CutToLong(r13 * sin(f2))};
      pDC->LineTo(pt1);
      
      double f1 = fAngStart4 + fDivAngRad/2;
      pDC->AngleArc(0, 0, r13, (float)AngRad2Deg(-f1), (float)AngRad2Deg(f1-f2));

      fAngStart4 += fDivAngRad;
    }
  }

  //标注LOG
  /*
  {
    LOGFONT logFont = {0};
    _tcscpy_s( logFont.lfFaceName, _T("Arial"));
    logFont.lfHeight = -MulDiv(40, pDC->GetDeviceCaps(LOGPIXELSY), 72);
    logFont.lfEscapement = CutToLong((-fAngleDeg + 90)*10);
    logFont.lfOrientation = logFont.lfEscapement;
    //logFont.lfWeight = FW_HEAVY;

    CFont font;
    font.CreateFontIndirect(&logFont);
    CFont *pOldFont = pDC->SelectObject(&font);

    LPCTSTR szLog = _T("Grid Coder");
    CSize txtSize = pDC->GetTextExtent(szLog);
    pDC->LPtoDP(&txtSize);
    double r20 = 200;
    double fRad2 = fAngleRad + PI - 0.1;//asin( (txtSize.cx/2)/r20 );

    POINT pt20 = { CutToLong(r20 * cos(fRad2)), CutToLong(r20 * sin(fRad2)) } ;

    pDC->SetTextColor(RGB(255, 0, 0));
    pDC->SetBkMode(TRANSPARENT);

    pDC->TextOut(pt20.x, pt20.y, szLog);

    pDC->SelectObject(pOldFont);
  }
  */

  //恢复映射模式
  pDC->SetWindowExt(ptOldWinExt);
  pDC->SetViewportExt(ptOldViewportExt);
  pDC->SetViewportOrg(ptOldOrigin);
  pDC->SetMapMode(OldMapMode);

  //恢复画笔
  pDC->SelectObject(pOldPen);

  //恢复DC
  pDC->RestoreDC(iSaveDC);
}

//为测试程序绘制情况,类中添加一double型变量,以记录角度,使用定时器不断的更改角度并刷新
//因此在菜单添加一个启动、停止和复位三个按钮
//可以观察动态和静态运行情况

 

//双缓冲绘制
void CSDI22View::OnDraw(CDC* pDC)
{
  CSDI22Doc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if (!pDoc)
    return;

  CRect rect;
  GetClientRect(rect);

  CMemDC memDC(*pDC, this);
  DrawGridCoder(&memDC.GetDC(), rect, fStartAngleSet);// (double)rand()/RAND_MAX*360.0);//0);//
  // TODO: 在此处为本机数据添加绘制代码
}

//为防止闪烁 WM_ERASEBKGND 直接返回TRUE
BOOL CSDI22View::OnEraseBkgnd(CDC* pDC)
{
  // TODO: 在此添加消息处理程序代码和/或调用默认值
  return TRUE;
  return CView::OnEraseBkgnd(pDC);
}

//处理菜单消息和刷新
BOOL CSDI22View::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
  // TODO: 在此添加专用代码和/或调用基类

  CCmdUI*pCmdUI = (CCmdUI*) pExtra;
  
  if(pHandlerInfo == NULL)
  {
    BOOL bDone = FALSE;
    switch(nID)
    {
    case(ID_POPUP_START)://启动
      {
        if(nCode == CN_UPDATE_COMMAND_UI)
        {
          pCmdUI->Enable(mRefreshTimerID==0);
          pCmdUI->SetCheck(mRefreshTimerID? TRUE:FALSE);
          bDone = TRUE;
        }
        else if(nCode == CN_COMMAND)
        {
          mRefreshTimerID = SetTimer(10, 10, NULL);
          bDone = TRUE;
        }
        break;
      }
    case(ID_POPUP_STOP)://停止
      {
        if(nCode == CN_UPDATE_COMMAND_UI)
        {
          pCmdUI->Enable(mRefreshTimerID? TRUE:FALSE);
          pCmdUI->SetCheck(mRefreshTimerID==0);
          bDone = TRUE;
        }
        else if(nCode == CN_COMMAND)
        {
          if(mRefreshTimerID)
          {
            KillTimer(mRefreshTimerID);
            mRefreshTimerID = 0;
          }
          bDone = TRUE;
        }
        break;
      }
    case (ID_POPUP_RESET)://复位
      {
        if(nCode == CN_UPDATE_COMMAND_UI)
        {
          pCmdUI->Enable(TRUE);
          bDone = TRUE;
        }
        else if(nCode == CN_COMMAND)
        {
          fStartAngleSet = 0;
          InvalidateRect(NULL);
          bDone = TRUE;
        }
        break;
      }
    }

    if(bDone)
      return TRUE;
  }
  return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

//定时器消息
void CSDI22View::OnTimer(UINT_PTR nIDEvent)
{
  // TODO: 在此添加消息处理程序代码和/或调用默认值
  fStartAngleSet += 1.0;
  InvalidateRect(NULL);

  CView::OnTimer(nIDEvent);
}

抱歉!评论已关闭.