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