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