MFC+GDI+绘制出雷达余晖效果

MFC+GDI+绘制出雷达余晖效果

这个水印有点烦
1.首先要画出静态的坐标轴,用双缓冲方法在onpain消息中绘制。绘制方法都比较简单。声明一个内存DC,绘制一个圆形,再把坐标轴画上去。

void CDlg_RadarScanning::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	// TODO: 在此处添加消息处理程序代码
	// 不为绘图消息调用 CDialogEx::OnPaint()
	GetClientRect(&WinRect);
	CenterPoint.X = WinRect.Height()/2.0;
	CenterPoint.Y = CenterPoint.X;
	ellipseRect2 = Rect(2,2,WinRect.Height()-4,WinRect.Height()-4); //外切矩形区域
	BitRect.Equals(ellipseRect2);

	pDC = this->GetDC();
	pDC->FillSolidRect(0,0,WinRect.right,WinRect.bottom,RGB(0,0,0));
	//随后建立与屏幕显示兼容的内存显示设备
	MemDC.DeleteDC();
	MemDC.CreateCompatibleDC(pDC);

	MemBitmap.DeleteObject();
	MemBitmap.CreateCompatibleBitmap(pDC,WinRect.Width(),WinRect.Height());
	//将位图选入到内存显示设备中
	//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
	CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

	Graphics gp(MemDC.m_hDC);
	gp.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);//抗锯齿 
	REAL startAngle = 0.0f; // 设置起点角度
	REAL sweepAngle = 360.0f; // 设置旋转角度

	 Brush* brush = NULL;
	 brush = new SolidBrush(Color::Green);
	gp.FillEllipse(brush,ellipseRect2.Height/2,ellipseRect2.Height/2,5,5);//圆心

// 	PointF* StarPoint = new PointF(1,ellipseRect2.Height/2);
// 	PointF* EndPoint= new PointF(ellipseRect2.Width,ellipseRect2.Height/2);
	PointF StarPoint(1,ellipseRect2.Height/2);
	PointF EndPoint(ellipseRect2.Width,ellipseRect2.Height/2+2);
	gp.DrawLine(&Pen(Color::Green,2),*(&StarPoint),*(&EndPoint));//水平坐标轴

	StarPoint.X = ellipseRect2.Width/2+2;
	EndPoint.X = StarPoint.X;
	StarPoint.Y = 1;
	EndPoint.Y = ellipseRect2.Height;
	gp.DrawLine(&Pen(Color::Green,2),*(&StarPoint),*(&EndPoint));//竖直坐标轴

	int Gap =ellipseRect2.Height/10+2;//刻度间隙
	int count = 5;
	for (int i=0;i<count;i++)//同心圆
	{
		gp.DrawArc(&Pen(Color::Green,2),ellipseRect2,startAngle,sweepAngle);//画圆
		ellipseRect2.X = ellipseRect2.X +Gap;
		ellipseRect2.Y = ellipseRect2.Y +Gap;
		ellipseRect2.Width = ellipseRect2.Width - 2*Gap;
		ellipseRect2.Height = ellipseRect2.Height-2*Gap;
	}

	FontFamily fontFamily(L"微软雅黑"); 
	Gdiplus::Font myFont(&fontFamily, 10, FontStyleRegular, UnitPoint); //第二个是字体大小
	SolidBrush blackBrush(Color(255, 215, 0));  //半透明+文字RGB颜色
	int iOverrange = WinRect.Height()/10;
	CString strTemp;
	StringFormat format;                         
	format.SetAlignment(StringAlignmentNear);    //文本排列方式,即在对应位置居中、靠左、靠右

	for (int i=5;i>=0;i--)//标明刻度
	{
		strTemp.Format("%d",i*2);
		WCHAR* wchar= strTemp.AllocSysString();	//wcscpy_s(string, CT2CW(str));              //如果使用MFC中的CString,需要这样转换成WCHAR
		PointF school_site((REAL)(5-i)*iOverrange, (REAL)WinRect.Height()/2);//文字放置的像素坐标
		gp.DrawString(wchar, wcslen(wchar), &myFont, school_site, &format, &blackBrush );//把string绘制到图上 
	}

	strTemp.Format("刻度/KM");
	WCHAR* wchar1= strTemp.AllocSysString();
	PointF school_site1((REAL)2, (REAL)2);//文字放置的像素坐标
	gp.DrawString(wchar1, wcslen(wchar1), &myFont, school_site1, &format, &blackBrush );//
	//pDC->TransparentBlt(0,0,WinRect.Height()-1,WinRect.Height()-1,&MemDC,0,0,ellipseRect2.Width,ellipseRect2.Height,TRANSPARENT);
	pDC->BitBlt(0,0,WinRect.Height()-1,WinRect.Height()-1,&MemDC,0,0,SRCCOPY);
	m_iMetersPerPx = 10000.0/(WinRect.Height()/2.0);//10KM与图像刻度比例
	MemDC.SelectObject(pOldBit);
	gp.ReleaseHDC(MemDC.m_hDC);

}

2.其次是绘制一个渐变的扇形区域,原点和角度要定好,而且要求是能够旋转的扇形。从简单的旋转开始就是模仿秒针旋转。一开始我也是只绘制一根线旋转。图形旋转时会留有上一次的图形,所以我们还需要把上一次角度的图形抹掉,重新定位新的角度的图形。我选择的办法是开启一个定时器,用之前画好的内存DC不断地去覆盖上一张图,在覆盖完成后再在上面绘制新角度的旋转图形即可。图形旋转我是用矩阵旋转。

			//该代码写在定时器即可
			static double fPi = 4.0*atan(1.0);//180度
			double fAngle = fPi/2,fDAngle = 2.0*fPi/60.0;//360度分成60份
			//转盘看为时钟盘,摆针为秒针
			SYSTEMTIME tmNow;
			GetLocalTime(&tmNow);

			//s为中心点,e为变化后的点,两点连起来形成摆针
			//Pen sPen(Color(255, 0, 0), 3); 
			CPen mypen(PS_SOLID,2,RGB(255,255,255));
			CPen* pOldPen;
			CDC* MyDC = this->GetDC();
			pOldPen=MyDC->SelectObject(&mypen);
			Point s(CenterPoint.X,CenterPoint.Y), e; 
			double fTime = tmNow.wSecond + tmNow.wMilliseconds/1000.0 + 0;//以系统时间为当前比例
			//double fTime =30;
			double fAng = fPi/2.0 - fTime * (2.0*fPi) /30.0;//360度分的刻度影响摆针旋转速度,分得越少越快
			e.X = (int)(CenterPoint.X + (WinRect.Height()/2.0) * cos(fAng)); 
			e.Y = (int)(CenterPoint.Y -  (WinRect.Height()/2.0) * sin(fAng));

			MyDC->BitBlt(0,0,WinRect.Height(),WinRect.Height(),&MemDC,0,0,SRCCOPY);//将内存位图贴上再划线可将上一次图形覆盖

			//旋转扇形
 			static int angl = 10;//每次旋转角度
 			Graphics graphics(MyDC->m_hDC);
 			graphics.SetSmoothingMode(SmoothingModeAntiAlias);
			//矩阵旋转
			Matrix matrix;
			matrix.Translate(CenterPoint.X, CenterPoint.Y, MatrixOrderAppend);
			//matrix.Scale(2, 2, MatrixOrderAppend);
			matrix.RotateAt(angl, PointF(CenterPoint.X, CenterPoint.Y), MatrixOrderAppend);
			matrix.Translate(-CenterPoint.X, -CenterPoint.Y);
			graphics.SetTransform(&matrix); 

			LinearGradientBrush linGrBrush(Point(0,0),Point(120,120),Color(0,255,255,255),Color(255,0,255,100));//渐变画刷
			//graphics.DrawPie(&Pen(Color::Green,2),(int)0, (int)0,WinRect.Height(),WinRect.Height(),0,60);
			graphics.FillPie(&linGrBrush,(int)0, (int)0,WinRect.Height(),WinRect.Height(),0,60);//绘制渐变扇形
			//graphics.FillRectangle(&linGrBrush,(int)CenterPoint.X,(int)CenterPoint.Y, 50, WinRect.Height()/2);
			angl++;

			//以下均为旋转秒针
//			graphics.DrawLine(&Pen(Color::White,2),(int)CenterPoint.X,(int)CenterPoint.Y,(int)e.X,(int)e.Y);
// 			MyDC->MoveTo(CenterPoint.X,CenterPoint.Y);
// 			MyDC->LineTo(e.X,e.Y);
			MyDC->SelectObject(pOldPen);
			ReleaseDC(MyDC);
			pOldPen->DeleteObject();
			mypen.DeleteObject();

3.下面是添加目标。新建一个透明的对话框叠加到当前雷达扫描的对话框,新对话框重绘透明的按钮类,再贴图到按钮上,还要添加按钮的点击响应消息。然后在雷达扫描窗口上声明新窗口的对象Create窗口。至于每一个按钮都是对应一个封装的结构体,里面含有距离、高度和按钮句柄等信息,不断地调用movewindow即可实现移动。

//.h
struct FlyBtInfo
{
	double iDistance;
	double iHeight;
	CCDrawButtonTM flyBt;//句柄
	BOOL bShow;
	BOOL bSelected;
};
CDlg_DrawFly *m_DrawFlyDlg;
FlyBtInfo m_DrawFly[3];

//.cpp
if(m_DrawFlyDlg==NULL)
	{
		m_DrawFlyDlg = new CDlg_DrawFly(this);
		m_DrawFlyDlg->Create(IDD_DLG_DRAWFLY,this);
		m_DrawFlyDlg->ShowWindow(SW_SHOW);
	}
	memcpy(&m_DrawFly[0].flyBt,&m_DrawFlyDlg->m_OneFlyBt,sizeof(CCDrawButton));
	m_DrawFly[0].bShow=FALSE;
	m_DrawFly[0].bSelected =FALSE;
	memcpy(&m_DrawFly[1].flyBt,&m_DrawFlyDlg->m_TwoFlyBt,sizeof(CCDrawButton));
	m_DrawFly[1].bShow=FALSE;
	m_DrawFly[1].bSelected =FALSE;
	memcpy(&m_DrawFly[2].flyBt,&m_DrawFlyDlg->m_ThreeFlyBt,sizeof(CCDrawButton));
	m_DrawFly[2].bShow=FALSE;
	m_DrawFly[2].bSelected =FALSE;
	
//第一个定时器
//飞机1
		static int x1 =WinRect.Height()/2.0*(1-sin(3.14/4));
		if (x1<(WinRect.Height()/2))
		{
			m_DrawFly[0].flyBt.MoveWindow(x1-10,x1-10,25,25);
			//m_DrawFly[0].flyBt.Invalidate();
			m_DrawFly[0].flyBt.ShowWindow(SW_SHOW);
			//根据刻度算出距离,高度这里为十分之一距离
			double a =WinRect.Height()/2-x1+10;
			m_DrawFly[0].iDistance = sqrt(pow(a,2)*2)*m_iMetersPerPx;
			m_DrawFly[0].iHeight = m_DrawFly[0].iDistance/10;
			if (m_DrawFly[0].bSelected)
			{
				UpdateFlyDataToList(0);
			}
			x1 ++;
		} 
		else
		{
			x1 =WinRect.Height()/2.0*(1-sin(3.14/4));
		}
		//飞机2
		static int x2 = WinRect.Height()/2-5;
		static int y2 =WinRect.Height()/2-5;
		int x21 = WinRect.Height()/2*(1+sin(3.14/4));
		int y21 = WinRect.Height()/2*(1-sin(3.14/4));
		if (x2<x21 || y2>y21)
		{
			m_DrawFly[1].flyBt.MoveWindow(x2,y2,25,25);
			//m_DrawFly[1].flyBt.Invalidate();
			m_DrawFly[1].flyBt.ShowWindow(SW_SHOW);

			double a1 = pow(WinRect.Height()/2.0-x2,2);
			double a2 = pow(WinRect.Height()/2.0-y2,2);
			m_DrawFly[1].iDistance = sqrt(a1+a2)*m_iMetersPerPx;
			m_DrawFly[1].iHeight = m_DrawFly[1].iDistance/10;
			if (m_DrawFly[1].bSelected)
			{
				UpdateFlyDataToList(1);
			}
			x2 ++;
			y2 --;
		}
		else
		{
			x2 = WinRect.Height()/2-5;
			y2 =WinRect.Height()/2-5;
		}

下面是用到的几个方法:

//更新列表数据
void CDlg_RadarScanning::UpdateFlyDataToList(int iTranceBatch)
{
	 int BatchTemp;
	 int count = m_FlydataList.GetItemCount();
	 for (int i =0;i<count;i++)
	 {
		 BatchTemp = atoi(m_FlydataList.GetItemText(i,0));
		 if (BatchTemp == iTranceBatch)
		 {
			 CString strDistance;
			 strDistance.Format("%.2f",m_DrawFly[iTranceBatch].iDistance);
			 m_FlydataList.SetItemText(i,1,strDistance);
			 strDistance.Format("%.2f",m_DrawFly[iTranceBatch].iHeight);
			 m_FlydataList.SetItemText(i,2,strDistance);
		 }
	 }
}
//点击按钮时切换位图
void CDlg_RadarScanning::ChangeFlyColor(int iTranceBatch)
{
	switch(iTranceBatch)
	{
	case 0:
		m_DrawFlyDlg->m_OneFlyBt.UpdateBtn("res\\airport.png","res\\airport.png","res\\airport.png","res\\airport.png","");
		m_DrawFlyDlg->m_OneFlyBt.Invalidate();
		m_DrawFlyDlg->m_TwoFlyBt.UpdateBtn("res\\airport4.png","res\\airport4.png","res\\airport4.png","res\\airport4.png","");
		m_DrawFlyDlg->m_TwoFlyBt.Invalidate();

		break;
	case 1:
		m_DrawFlyDlg->m_OneFlyBt.UpdateBtn("res\\airport2.png","res\\airport2.png","res\\airport2.png","res\\airport2.png","");
		m_DrawFlyDlg->m_OneFlyBt.Invalidate();
		m_DrawFlyDlg->m_TwoFlyBt.UpdateBtn("res\\airport5.png","res\\airport5.png","res\\airport5.png","res\\airport5.png","");
		m_DrawFlyDlg->m_TwoFlyBt.Invalidate();

		break;
	}
}

//按钮点击消息,通过发送消息给主窗口类
void CDlg_DrawFly::OnBnClickedOnedrawbutton()
{
	m_Parent->SendMessage(WM_DRAWFLY_BT_MSG,IDC_ONEDRAWBUTTON,0);
	// TODO: 在此添加控件通知处理程序代码
}
void CDlg_DrawFly::OnBnClickedTwodrawbutton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Parent->SendMessage(WM_DRAWFLY_BT_MSG,IDC_TWODRAWBUTTON,0);
}
void CDlg_DrawFly::OnBnClickedThreedrawbutton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Parent->SendMessage(WM_DRAWFLY_BT_MSG,IDC_THREEDRAWBUTTON,0);
}

//这个方法用于响应按钮点击事件
LRESULT (主窗口类)::OnDrawFlyBTDepose(WPARAM wParam, LPARAM lParam)
{
	switch(wParam)
	{
	case IDC_ONEDRAWBUTTON:
		{
			m_RadarScanningDlg->ChangeFlyColor(0);
			m_RadarScanningDlg->m_CurSelectAim =0;
			m_RadarScanningDlg->m_DrawFly[0].bSelected =TRUE;
			CString strTemp;
			strTemp.Format("00 跟踪");
			m_RadarScanningDlg->m_FlydataList.SetItemText(0,0,strTemp);

			m_RadarScanningDlg->m_FlydataList.SetItemText(1,0,"01");
		}
		//SelectRadarTrackBatch(0);
		break;
	case IDC_TWODRAWBUTTON:
		{
			m_RadarScanningDlg->ChangeFlyColor(1);
			m_RadarScanningDlg->m_CurSelectAim =1;
			m_RadarScanningDlg->m_DrawFly[1].bSelected =TRUE;

			CString strTemp;
			strTemp.Format("01 跟踪");
			m_RadarScanningDlg->m_FlydataList.SetItemText(1,0,strTemp);

			m_RadarScanningDlg->m_FlydataList.SetItemText(0,0,"00");
		}
		//SelectRadarTrackBatch(1);
		break; 

	}
	return 1;
}

总结:
使用MFC确实不好做,我查了一下很多都是QT。关键是GDI+绘图和矩阵旋转比较难。虽然这个是我自己写的,但是工程是公司的,我不好公开全部代码,有什么问题可以问我。

以下是提供可学习

1.GDI+学习及代码总结之------画线、区域填充、写字
https://blog.csdn.net/harvic880925/article/details/9023329
2.GDI+绘制极坐标图、雷达图
https://blog.csdn.net/iteye_15968/article/details/82334555
3.GDI+旋转图片的几种方法
https://blog.csdn.net/fyl_077/article/details/44456213

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值