利用GDI绘制雷达P显,遇到余辉残留无法清除的问题,请教各位达人

weixin_38048154 2015-08-03 07:10:50
最近研究使用GDI+绘制雷达P显,参考了开源代码,实现了一个初步版本,实现的主要功能如下: 1. 绘制P显底图,含距离环、距离标注、方位射线(即方位角)等; 2. 实时显示方位扫描波位,并添加余辉效果(即渐变颜色); 3. 依据给定的检测概率,随机生成一系列的探测点迹并显示在P显底图上; 4. 实现了特定区域刷新效果——即方位扫描波位覆盖的区域才进行重绘,其它区域保持不变; 5. 在界面上添加了其它的显示信息(很多尚未添加内核模型,仅为示意)。 通过前期编程努力,在Visual Studio 2013上完成了代码编写与测试,运行效果参加本帖最后的GIF图像。通过运行发现,系统距离预期的显示效果有一定的区别,主要表现为: 1.虽然使用了InvalidRgn()函数,仅对方位扫描波位覆盖的屏幕区域才进行重绘,但是通过实际运行发现,屏幕上遗留了较多的余辉残留(图中的绿色部分),具体原因尚未发现; 2.通过长时间运行发现,程序的CPU占用较为平稳(在笔者的电脑上维持在3%左右),但是它的内存占用会缓慢地增加,表明程序中存在者内存泄露,但通过仔细检查代码,未发现问题代码。 同时,我还想补充添加两个功能,具体为: 1.目前采用的是随机生成一系列的探测点迹并显示在P显底图上的方式,仅为示意。如果我想在P显上显示一个稳定的目标轨迹(即连续多次扫描都能发现这个目标,将每次的扫描探测结果用折线连接,显示在底图上),这会牵扯到较大范围的屏幕区域重绘,目前的程序结构无法完成,不知该如何修改; 2.想实现在屏幕上通过鼠标点击选择特定点迹的功能,相应的右键菜单已经做好,但不知道如何才能实现鼠标点击选择。粗略地考虑,应该把当前屏幕上所有点位置均记录下来,然后通过获取当前鼠标点击位置,借助于“最近邻”准则来判决,这样感觉有些繁琐,不知道有没有更好的解决方式? 恳请论坛里的各位达人指点。关键部分的源码附后,程序运行结果同样附后,请大家批评指正。希望能和大家一块儿,建立一个较为完整的雷达P显示例。 注:在OnPaint()函数中引用了网上的开源代码,对作者表示感谢。 //相关成员变量定义(在.h文件中,摘抄如下) double m_dCurSimTime; //当前扫描时间,s double m_dCurAzimuth; //当前波束扫描方位角,rad int m_nCurScanFrame; //当前扫描处理帧周期 int m_nCenterX; //P显中心X坐标,屏幕上 int m_nCenterY; //P显中心Y坐标,屏幕上 int m_nRadius; //P显半径,屏幕上 //其它公共变量定义 const double PI = 3.14159265358979; //圆周率 const double d2r = PI/180.0; //从°至rad的转换 const UINT TimeIntervalInMS = 100; //以ms形式表示的方位扫描时间间隔 const double BeamWidth = 6.0*d2r; //方位扫描波束宽度,rad const double ScanPeriod = 6.0; //一个完整的方位扫描周期,s const double ScanRate = 360.0/ScanPeriod*d2r; //方位波束扫描速率,rad/s const double MaxDetectRange = 500.0e3; //距离环代表的最大探测距离,m const int RANGERINGNUM = 10; //P显底图上的距离环数目 //用于实时显示状态的矩形区域(特定刷新) RECT rectTime,rectAzScan,rectFrame; RECT rectRate,rectDotNum,rectEchoNum,rectTgtNum,rectSysTime; RECT rectR,rectAz,rectID; //生成给定区间内的均匀分布随机数 double Rand(double dblStart, double dblFinish) { double minVal = min(dblStart, dblFinish); double maxVal = max(dblStart, dblFinish); return (maxVal - minVal) * (double)rand() / (RAND_MAX + 1) + minVal; } BOOL CPPIDispDemoDlg::OnInitDialog() { …… SetTimer(0, TimeIntervalInMS, NULL); } void CPPIDispDemoDlg::OnPaint() { if (IsIconic()) { …… } else { CPaintDC dc(this); CRect rect; This->GetClientRect(&rect); int nMargin = 1; //边距 int nHeight = rect.Height() - 2*nMargin; int nWidth = rect.Width() - 2*nMargin; m_nCenterX = nWidth/2; m_nCenterY = nHeight/2; m_nRadius = min(nWidth,nHeight)/2; CDC xDC; CBitmap xBMP; xDC.CreateCompatibleDC(&dc); xBMP.CreateCompatibleBitmap(&dc,nWidth,nHeight); xDC.SelectObject(xBMP); //这是显示的核心函数! OnDraw(&xDC); CDC yDC; CBitmap yBMP; yDC.CreateCompatibleDC(&dc); yBMP.CreateCompatibleBitmap(&dc,nWidth,nHeight); yDC.SelectObject(&yBMP); yDC.FillSolidRect(rect,GetSysColor(COLOR_3DFACE)); //采用双缓存机制,防止背景闪烁 yDC.BitBlt(nMargin,nMargin,nWidth,nHeight,&xDC,0,0,SRCCOPY); dc.BitBlt(0,0,nWidth,nHeight,&yDC,0,0,SRCCOPY); xBMP.DeleteObject(); xDC.DeleteDC(); yBMP.DeleteObject(); yDC.DeleteDC(); } } void CPPIDispDemoDlg::OnDraw(CDC *pDC) { CPen xPen(PS_DASH,1,RGB(0,0,0)); CPen *oPen = pDC->SelectObject(&xPen); int i = 0, j = 0; //绘制方位波束扫描余辉效果(可以进行更为精细的控制) double deltaT = BeamWidth/255; for(double dt = 0.0; dt < 1.0*BeamWidth; dt += deltaT) { long x = m_nCenterX + m_nRadius*cos(dt + m_dCurAzimuth-BeamWidth); long y = m_nCenterY + m_nRadius*sin(dt + m_dCurAzimuth-BeamWidth); pDC->MoveTo(m_nCenterX,m_nCenterY); xPen.DeleteObject(); xPen.CreatePen(0,4,RGB(0,j++,0)); pDC->SelectObject(&xPen); pDC->LineTo(x,y); } xPen.DeleteObject(); //绘制当前方位扫描波束中心线 xPen.CreatePen(0,4,RGB(0,255,0)); pDC->SelectObject(&xPen); pDC->MoveTo(m_nCenterX, m_nCenterY); pDC->LineTo(m_nCenterX + m_nRadius*cos(m_dCurAzimuth), m_nCenterY + m_nRadius*sin(m_dCurAzimuth)); xPen.DeleteObject(); //在当前方位扫描波束覆盖的区域里生成距离、方位均随机分布的目标 int Rmt = int(Rand(10, m_nRadius)); //注意:这里显示的实际上是上一个方位扫描波束的目标,模拟实际系统的处理延迟 double angleAZ = Rand(m_dCurAzimuth - 2*BeamWidth, m_dCurAzimuth - BeamWidth); //以给定的概率发现目标,并在P显上绘制出 double Threshold = 0.65; if (Rand(0.0,1.0) >= 1 - Threshold) { CPoint dot; dot.x = m_nCenterX + Rmt*cos(angleAZ); dot.y = m_nCenterY + Rmt*sin(angleAZ); pDC->SetPixel(dot,RGB(255,0,0)); pDC->FillSolidRect(dot.x, dot.y, 4, 4, RGB(255,255,0)); } CFont font; font.CreateFont(12,6,0,0,600,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_ROMAN,"宋体"); CString str(""); //绘制距离环 for (i = 0; i < RANGERINGNUM-1; i++) { xPen.CreatePen(PS_SOLID,1,RGB(128,118,105)); pDC->SelectObject(&xPen); pDC->Arc(m_nCenterX - m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterY - m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterX + m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterY + m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM,0,0,0,0); xPen.DeleteObject(); //显示距离标注 if (i % 2 == 1) { pDC->SelectObject(&font); pDC->SetTextColor(RGB(160,160,164)); pDC->SetBkMode(TRANSPARENT); str.Format("%d",int((RANGERINGNUM-i)*MaxDetectRange/1.0e3/RANGERINGNUM)); pDC->TextOut(m_nCenterX + m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterY, str); } } xPen.CreatePen(PS_SOLID,1,RGB(128,118,105)); pDC->SelectObject(&xPen); //绘制水平线 pDC->MoveTo(m_nCenterX - m_nRadius, m_nCenterY); pDC->LineTo(m_nCenterX + m_nRadius, m_nCenterY); //绘制垂直线 pDC->MoveTo(m_nCenterX, m_nCenterY - m_nRadius); pDC->LineTo(m_nCenterX, m_nCenterY + m_nRadius); //绘制30°方位射线 double dTheta = 30.0*d2r; pDC->MoveTo(m_nCenterX - cos(dTheta)*m_nRadius, m_nCenterY + sin(dTheta)*m_nRadius); pDC->LineTo(m_nCenterX + cos(dTheta)*m_nRadius, m_nCenterY - sin(dTheta)*m_nRadius); pDC->MoveTo(m_nCenterX - cos(dTheta)*m_nRadius, m_nCenterY - sin(dTheta)*m_nRadius); pDC->LineTo(m_nCenterX + cos(dTheta)*m_nRadius, m_nCenterY + sin(dTheta)*m_nRadius); //绘制60°方位射线 dTheta = 60.0*d2r; pDC->MoveTo(m_nCenterX - cos(dTheta)*m_nRadius, m_nCenterY + sin(dTheta)*m_nRadius); pDC->LineTo(m_nCenterX + cos(dTheta)*m_nRadius, m_nCenterY - sin(dTheta)*m_nRadius); pDC->MoveTo(m_nCenterX - cos(dTheta)*m_nRadius, m_nCenterY - sin(dTheta)*m_nRadius); pDC->LineTo(m_nCenterX + cos(dTheta)*m_nRadius, m_nCenterY + sin(dTheta)*m_nRadius); xPen.DeleteObject(); //对中心距离环线换用红色进行突出显示 xPen.CreatePen(PS_SOLID,2,RGB(255,0,0)); pDC->SelectObject(&xPen); i = RANGERINGNUM-1; pDC->Arc(m_nCenterX - m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterY - m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterX + m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterY + m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM,0,0,0,0); xPen.DeleteObject(); //显示距离标注 pDC->SelectObject(&font); pDC->SetTextColor(RGB(160,160,164)); pDC->SetBkMode(TRANSPARENT); str.Format("%d",int((RANGERINGNUM-i)*MaxDetectRange/1.0e3/RANGERINGNUM)); /单位km pDC->TextOut(m_nCenterX + m_nRadius*(RANGERINGNUM-i)/RANGERINGNUM, m_nCenterY, str); //显示当前仿真时间(注意:不是系统时间) str.Format("当前时间(s):%6.3f", m_dCurSimTime); rectTime.left = 5; rectTime.top = 5; rectTime.right = rectTime.left + strlen(str) * 7; rectTime.bottom = rectTime.top + 12; //pDC->FillSolidRect(&rectTime,RGB(255,255,0)); pDC->DrawText(str,&rectTime,DT_LEFT); //还有其它信息显示,受限于论坛发帖长度,不再列出 …… font.DeleteObject(); //换回原来的画笔 pDC->SelectObject(oPen); } void CPPIDispDemoDlg::OnTimer(UINT_PTR nIDEvent) { //更新当前方位扫描角,以及当前仿真时刻 m_dCurAzimuth += BeamWidth; m_dCurSimTime += 1.0e-3*TimeIntervalInMS; //将方位扫描角限定在[0,360°]之间,并且进行方位扫描帧周期的判断 if(m_dCurAzimuth > 2*PI) { m_dCurAzimuth -= 2*PI; m_nCurScanFrame++; } //刷新特定区域(显示各种实时状态信息) InvalidateRect(&rectTime); InvalidateRect(&rectAzScan); InvalidateRect(&rectFrame); InvalidateRect(&rectDotNum); InvalidateRect(&rectEchoNum); InvalidateRect(&rectTgtNum); InvalidateRect(&rectSysTime); InvalidateRect(&rectRate); InvalidateRect(&rectID); InvalidateRect(&rectR); InvalidateRect(&rectAz); CDC dc; dc.CreateCompatibleDC(&dc); dc.SetViewportOrg(m_nCenterX,m_nCenterY); CPoint topleft,bottomright; topleft.x = -m_nRadius ; topleft.y = -m_nRadius; bottomright.x = m_nRadius ; bottomright.y = m_nRadius ; CRgn rgn; CRect rect(topleft,bottomright); //当前方位扫描波束的起点、终点 CPoint pt1, pt2; pt1.x = m_nRadius*cos(m_dCurAzimuth); pt1.y = m_nRadius*sin(m_dCurAzimuth); pt2.x = m_nRadius*cos(m_dCurAzimuth - 2*BeamWidth); pt2.y = m_nRadius*sin(m_dCurAzimuth - 2*BeamWidth); //指定当前方位扫描波束覆盖的P显区域,并进行重绘 dc.BeginPath(); dc.MoveTo(CPoint(0,0)); dc.LineTo(pt1); dc.ArcTo(rect,pt1,pt2); dc.LineTo(CPoint(0,0)); dc.EndPath(); rgn.CreateFromPath(&dc); InvalidateRgn(&rgn,TRUE); rgn.DeleteObject(); dc.DeleteDC(); CDialog::OnTimer(nIDEvent); } void CPPIDispDemoDlg::OnClose() { KillTimer(0); …… }
...全文
23 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复

476

社区成员

发帖
与我相关
我的任务
社区描述
其他技术讨论专区
其他 技术论坛(原bbs)
社区管理员
  • 其他技术讨论专区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧